Design patterns 将依赖项传递到一个不使用依赖项本身但将依赖项传递给某些内部使用的类的类是否正确

Design patterns 将依赖项传递到一个不使用依赖项本身但将依赖项传递给某些内部使用的类的类是否正确,design-patterns,dependency-injection,constructor,Design Patterns,Dependency Injection,Constructor,正是这个博客让我感到困惑: 假设我有一个提供访问控制服务的类库。我不想在库本身中使用任何IoC容器来简化测试(因此所有内容都是注入的,并且库本身中没有container.Resolve)。(WCF服务和使用此库的其他网站将使用某些容器在构造函数中注入依赖项。) 假设我的核心类如下所示: public class UserAccessManagement { private readonly IUserRepository _repo; private readonly IHas

正是这个博客让我感到困惑:

假设我有一个提供访问控制服务的类库。我不想在库本身中使用任何IoC容器来简化测试(因此所有内容都是注入的,并且库本身中没有container.Resolve)。(WCF服务和使用此库的其他网站将使用某些容器在构造函数中注入依赖项。)

假设我的核心类如下所示:

public class UserAccessManagement
{
    private readonly IUserRepository _repo;
    private readonly IHashProvider _hash;
    private readonly ITokenEncryptor _tokenEnc;

    public UserAccessManagement(IUserRepository repo, IHashProvider hash, ITokenEncryptor tokenEnc)
    {
        _repo = repo; _hash = hash; _tokenEnc = tokenEnc;
    }

    public void GrantAccess(string username, string resource)
    {
        User user = repo.FindUser(username);
        new AccessGateKeeper(user.SpnTicket, _hash, _tokenEnc)
            .GrantAccess(resource);
    }
}
public interface IAccess
{
    void Grant(object ticket, string resource)
}
public class Access : IAccess
{
    private readonly IHashProvider _hash;
    private readonly ITokenEncryptor _tokenEnc;

    public Access(IHashProvider _hash, ITokenEncryptor _tokenEnc)
    {
        _hash = hash;
        _tokenEnc = tokenEnc;
    }

    public void Grant(object ticket, string resource)
    {
        new AccessGateKeeper(ticket, _hash, _tokenEnc)
            .GrantAccess(resource);
    }
}
正如您所看到的,hash和tokenEnc实际上并没有被类使用,但必须传递给构造函数,因为在内部它必须传递给gate keeper。出于安全原因,gatekeeper的具体实现必须是一个内部的、密封的类,因此它本身不能被注入到构造函数中,而构造函数本可以解决这个问题

因此,通过构造函数注入了大量依赖项,尽管这并不一定意味着类本身做得太多。博客上说这太可怕了。鉴于以下三个要求,是否有更好或更合适的方法来处理此问题:

  • 入口看门人必须是内部的并密封的
  • 由于测试原因,类库中没有IoC
  • 不希望使用属性/方法注入,因为它不太可靠

  • 关于不接受依赖项只是为了传递它的规则是正确的,但是从公共API的角度来看,这也不是您要做的。由于
    AccessGateKeeper
    是内部的,因此它不是公共API的一部分,而是一个实现细节。它也可能是
    UserAccessManagement
    类的一个私人助手函数

    因此,从公共的角度来看,
    UserAccessManagement
    类不会“仅仅为了传递”依赖关系。相反,您可以说,
    UserAccessManagement
    类需要依赖项
    IUserRepository
    IHashProvider
    ITokenEncryptor
    来执行其工作

    然而,从可维护性的角度来看,当前的实现将
    UserAccessManagement
    AccessGateKeeper
    紧密结合在一起,这可能是问题,也可能不是问题。但是,如果这是一个问题,您可以考虑在接口中封装所需的行为。机械提取可能如下所示:

    public class UserAccessManagement
    {
        private readonly IUserRepository _repo;
        private readonly IHashProvider _hash;
        private readonly ITokenEncryptor _tokenEnc;
    
        public UserAccessManagement(IUserRepository repo, IHashProvider hash, ITokenEncryptor tokenEnc)
        {
            _repo = repo; _hash = hash; _tokenEnc = tokenEnc;
        }
    
        public void GrantAccess(string username, string resource)
        {
            User user = repo.FindUser(username);
            new AccessGateKeeper(user.SpnTicket, _hash, _tokenEnc)
                .GrantAccess(resource);
        }
    }
    
    public interface IAccess
    {
        void Grant(object ticket, string resource)
    }
    
    public class Access : IAccess
    {
        private readonly IHashProvider _hash;
        private readonly ITokenEncryptor _tokenEnc;
    
        public Access(IHashProvider _hash, ITokenEncryptor _tokenEnc)
        {
            _hash = hash;
            _tokenEnc = tokenEnc;
        }
    
        public void Grant(object ticket, string resource)
        {
            new AccessGateKeeper(ticket, _hash, _tokenEnc)
                .GrantAccess(resource);
        }
    }
    
    使用此界面,您可以将
    UserAccessManagement
    的实现更改为:

    public class UserAccessManagement
    {
        private readonly IUserRepository _repo;
        private readonly IAccess _access;
    
        public UserAccessManagement(IUserRepository repo, IAccess access)
        {
            _repo = repo;
            _access = access;
        }
    
        public void GrantAccess(string username, string resource)
        {
            User user = repo.FindUser(username);
            _access.Grant(user.SpnTicket, resource);
        }
    }
    
    IAccess
    的实现可能如下所示:

    public class UserAccessManagement
    {
        private readonly IUserRepository _repo;
        private readonly IHashProvider _hash;
        private readonly ITokenEncryptor _tokenEnc;
    
        public UserAccessManagement(IUserRepository repo, IHashProvider hash, ITokenEncryptor tokenEnc)
        {
            _repo = repo; _hash = hash; _tokenEnc = tokenEnc;
        }
    
        public void GrantAccess(string username, string resource)
        {
            User user = repo.FindUser(username);
            new AccessGateKeeper(user.SpnTicket, _hash, _tokenEnc)
                .GrantAccess(resource);
        }
    }
    
    public interface IAccess
    {
        void Grant(object ticket, string resource)
    }
    
    public class Access : IAccess
    {
        private readonly IHashProvider _hash;
        private readonly ITokenEncryptor _tokenEnc;
    
        public Access(IHashProvider _hash, ITokenEncryptor _tokenEnc)
        {
            _hash = hash;
            _tokenEnc = tokenEnc;
        }
    
        public void Grant(object ticket, string resource)
        {
            new AccessGateKeeper(ticket, _hash, _tokenEnc)
                .GrantAccess(resource);
        }
    }
    

    感谢您的详细回复!你的解释很有道理,消除了我所有的困惑。你提出的解决方案简直太棒了。我喜欢这种模式,即将所有一起工作的依赖项抽象到自己的类中。我可以看到自己正在基于此重构代码库。再次非常感谢您!