You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用模板模式(继承)实现依赖注入的优劣及强制依赖可行性分析

刚好对这个场景有过实践,来跟你聊聊我的看法:

基于继承的模板模式实现依赖注入的优缺点

优点

  • 强制依赖统一:所有子类必须遵循父类定义的依赖注入规则,像你提到的IBusinessContext,能确保每个业务服务都能拿到这个依赖,不会出现漏加的情况。
  • 减少重复代码:父类可以把依赖注入的通用逻辑(比如构造函数注入的模板、依赖校验)统一实现,子类只需要专注自己的业务逻辑,不用重复写相同的注入代码。
  • 标准化流程:可以在父类中定义固定的初始化或执行流程(比如拿到上下文后做统一的合法性校验),子类继承后自动遵循这套流程,避免各子类逻辑混乱。

缺点

  • 强耦合陷阱:子类和父类绑定得太死,一旦父类的依赖或逻辑变更,所有子类都会受影响,违反了开闭原则,后期维护成本会很高。
  • 单继承限制:如果你的业务服务类已经需要继承其他类(比如框架提供的基类),就没法再继承这个模板基类了,灵活性大打折扣。
  • 依赖隐藏:子类的依赖是通过父类注入的,从子类的构造函数或接口上看不到对IBusinessContext的依赖,可读性差,新人接手可能会疑惑上下文是从哪来的。
  • 测试复杂度上升:测试子类时,必须先初始化父类的所有依赖,哪怕子类没用到父类的某些逻辑,也得处理这些依赖,增加了测试的工作量。
用模板模式(继承)强制业务服务依赖IBusinessContext的可行方案

完全可以通过这种方式实现强制依赖,核心思路是用一个抽象基类封装IBusinessContext的注入逻辑,所有业务服务类都继承这个基类,通过构造函数的强制调用实现依赖绑定。

代码示例

首先定义上下文接口和模板基类:

// 业务上下文接口
public interface IBusinessContext
{
    string CurrentUserId { get; }
    // 其他上下文属性,比如当前租户、请求ID等
}

// 业务服务抽象基类,强制依赖IBusinessContext
public abstract class BaseBusinessService
{
    protected readonly IBusinessContext _businessContext;

    // 构造函数注入IBusinessContext,子类必须调用此构造函数
    protected BaseBusinessService(IBusinessContext businessContext)
    {
        _businessContext = businessContext ?? throw new ArgumentNullException(nameof(businessContext));
    }

    // 可选:定义模板方法,统一业务服务的执行流程
    public virtual void Execute()
    {
        PreExecute();
        DoBusinessLogic();
        PostExecute();
    }

    // 统一前置处理,比如上下文校验
    protected virtual void PreExecute()
    {
        if (string.IsNullOrEmpty(_businessContext.CurrentUserId))
        {
            throw new InvalidOperationException("用户上下文未初始化,请先登录");
        }
    }

    // 子类必须实现的核心业务逻辑
    protected abstract void DoBusinessLogic();

    // 统一后置处理,比如日志记录
    protected virtual void PostExecute()
    {
        // 可以在这里统一记录业务执行日志
    }
}

然后实现具体的业务服务:

// 安全服务接口
public interface ISecurityService
{
    bool HasPermission(string permissionCode);
}

// 安全服务实现,继承模板基类,强制依赖IBusinessContext
public class SecurityService : BaseBusinessService, ISecurityService
{
    // 子类构造函数必须调用父类构造函数,从而强制注入IBusinessContext
    public SecurityService(IBusinessContext businessContext) : base(businessContext)
    {
    }

    public bool HasPermission(string permissionCode)
    {
        // 使用父类注入的上下文获取当前用户ID,执行权限校验
        var currentUserId = _businessContext.CurrentUserId;
        // 具体权限校验逻辑,比如查询数据库判断权限
        return true;
    }

    // 如果需要使用模板方法Execute,重写核心逻辑
    protected override void DoBusinessLogic()
    {
        // 这里可以实现对应业务的执行逻辑
    }
}

方案关键点

  • 强制依赖的保障:因为BaseBusinessService的构造函数是protected,且必须传入IBusinessContext,子类如果不调用base(businessContext)会直接编译报错,从语法层面强制了所有业务服务类都必须依赖IBusinessContext。
  • 流程管控:通过模板方法(比如Execute)可以统一所有业务服务的执行流程,前置校验、后置处理都在父类统一实现,子类只需要关注核心业务逻辑。

注意事项

  • 要警惕继承带来的耦合问题,如果后续业务服务需要继承其他类,这个方案就会因为单继承限制而失效,此时可以考虑用组合模式替代继承来实现类似的效果。
  • 如果使用依赖注入容器(比如Autofac、Microsoft.Extensions.DependencyInjection),需要确保容器已正确注册IBusinessContext的实现类,这样所有子类在被容器解析时都会自动注入IBusinessContext。

内容的提问来源于stack exchange,提问作者Param

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:20:52