程序、模块、参数无继承架构优化咨询及独立程序集合理性探讨
问题分析与解决方案
让我针对你提出的两个疑问逐一分析:
一、Program→Module→Parameter初始化方案的合理性与优化
首先要明确:Program、Module、Parameter是组合关系(has-a)而非继承关系(is-a),所以当前采用动态解析JSON并填充模块列表的方案是合理的,这本质上是一种基于约定的插件式架构,符合面向对象中“组合优于继承”的设计原则。不过当前实现可以从可维护性、扩展性和健壮性上做些优化:
当前方案的潜在问题
CreateModuleFromName如果是硬编码的判断逻辑(比如一堆if/else或switch),后续新增模块时需要修改这个方法,违反了开闭原则- 缺乏统一的模块注册机制,模块的创建逻辑集中在Program类里,耦合度较高
- 错误处理不足:比如JSON字段缺失、模块实例化失败时,没有明确的日志或容错机制
优化方向
引入依赖注入(DI)容器
把所有IModule的实现注册到DI容器中,CreateModuleFromName直接从容器中解析对应的模块实例,新增模块只需要注册到容器,无需修改原有初始化逻辑。示例代码:// 在Program类中注入IServiceProvider private readonly IServiceProvider _serviceProvider; public Program(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; Modules = new Modules(); } public void Initialize(JToken programToken) { JToken modulesToken = programToken["MODULES"]; Modules.Clear(); if (modulesToken is null || !modulesToken.Any()) return; foreach (var moduleToken in modulesToken) { var moduleName = (string)moduleToken["API"]; if (string.IsNullOrWhiteSpace(moduleName)) continue; // 从DI容器获取命名模块实例(以Autofac为例) var module = _serviceProvider.GetKeyedService<IModule>(moduleName); if (module is null) { Console.WriteLine($"未找到注册的模块:{moduleName}"); continue; } try { module.Initialize(moduleToken); Modules.Add(module); } catch (Exception ex) { Console.WriteLine($"模块{moduleName}初始化失败:{ex.Message}"); // 可添加日志记录逻辑 } } }实现自动模块发现
通过反射扫描指定程序集中的IModule实现,自动注册到DI容器,进一步减少手动配置。比如在启动时:// 扫描当前程序集及模块程序集 var moduleAssemblies = AppDomain.CurrentDomain.GetAssemblies() .Where(a => a.GetName().Name.Contains("YourModuleAssemblyPrefix")); foreach (var assembly in moduleAssemblies) { var moduleTypes = assembly.GetTypes() .Where(t => typeof(IModule).IsAssignableFrom(t) && !t.IsAbstract); foreach (var type in moduleTypes) { // 假设ModuleName是类的常量字段 var moduleName = type.GetField("ModuleName")?.GetValue(null) as string; if (!string.IsNullOrEmpty(moduleName)) { // 注册为命名服务 builder.RegisterType(type).Keyed<IModule>(moduleName).InstancePerDependency(); } } }抽象初始化逻辑
把从JToken解析参数的逻辑下沉到BaseModule或专门的参数解析器中,让每个模块只需要关注自身参数的映射,减少重复代码。比如在BaseModule中实现通用的参数初始化:public abstract class BaseModule : IModule { public Parameters Parameters { get; private set; } = new Parameters(); public virtual void Initialize(JToken moduleToken) { // 通用的参数解析逻辑,比如遍历DataMember属性映射到Parameters var parameterFields = GetType().GetProperties() .Where(p => p.PropertyType == typeof(IParameter)); foreach (var field in parameterFields) { var attribute = field.GetCustomAttribute<DataMemberAttribute>(); var paramName = attribute?.Name ?? field.Name; var paramToken = moduleToken[paramName]; if (paramToken is not null) { // 创建参数实例并赋值 var parameter = CreateParameterFromToken(paramToken); field.SetValue(this, parameter); } } } protected abstract IParameter CreateParameterFromToken(JToken token); }
二、Program、Module、Parameter分属独立程序集的合理性判断
这种设计是否合理,完全取决于你的业务规模、团队协作模式和架构目标,没有绝对的对错:
适合拆分程序集的场景
- 模块化团队协作:如果不同团队负责不同模块的开发,拆分程序集可以做到代码隔离,避免互相干扰,同时支持独立编译、测试和部署
- 复用需求:如果Parameter类(或某些通用模块)需要在其他系统中复用,独立程序集可以方便其他项目直接引用,无需引入整个Program的代码
- 关注点分离:每个程序集对应单一职责:Program负责整体编排,Module负责业务逻辑,Parameter负责参数定义,符合SOLID原则,便于后期维护和扩展
不适合拆分的场景
- 系统规模较小:如果你的系统只有少数几个模块,拆分程序集会增加项目复杂度(比如引用关系管理、打包部署流程),反而降低开发效率
- 模块间依赖紧密:如果Program、Module、Parameter之间存在大量跨程序集的交互,会增加调用开销,同时调试和排查问题的难度也会上升
建议
- 如果当前系统已经有明确的模块化需求,或者未来会扩展大量模块,保持拆分是可行的,但要严格控制依赖方向:Parameter程序集不依赖任何其他程序集,Module程序集依赖Parameter,Program程序集依赖Module和Parameter,绝对避免循环依赖
- 如果系统规模小、模块少,或者依赖关系紧密,合并成一个程序集可以简化开发流程,降低维护成本
内容的提问来源于stack exchange,提问作者stackMeUp
相关产品推荐
相关产品推荐

