将不同的实现注入同一接口,然后在正确的项目/程序集中选择正确的实现

本文关键字:实现 项目 集中 选择 程序集 程序 然后 注入 接口 | 更新日期: 2023-09-27 18:11:57

我们有2个OpenLdap服务器。一个是最新的开箱即用版本。另一个是旧的、高度定制的版本。

两个实现都遵循类似的逻辑。以连接为例。

界面

namespace Infrastructure.Interfaces
{
    public interface ISessionService
    {
        LdapConnection GetConnection();       
    }
}

每个实现都将使用这个接口来获得连接。

新服务器

namespace Infrastructure.NewLdap.Service
{
    public class SessionService : ISessionService
    {
        LdapConnection GetConnection()
        {
        .....
        }
    }
}
旧服务器

namespace Infrastructure.OldLdap.Service
{
    public class SessionService : ISessionService
    {
        LdapConnection GetConnection()
        {
        .....
        }
    }
}

每个实现都在不同的项目中。每个项目都有不同的app.config,具有正确的证书等。与此同时,还有一个IConfigService,它将从app.config加载所有凭据等。

namespace Infrastructure.Interfaces
{
    public interface IConfigService
    {
        string ServerAddress { get; }   
        ...   
        ...   
    }
}

同样,每个项目都有自己的实现。

由于我们访问每个服务器中的数据的方式不同,每个项目中的存储库将是不同的。旧服务器将仅用于查询,因此我们使用它将用户导入到新服务器。

我如何使用Simple Injector将这些服务注入到相同的接口中,但取决于使用的存储库,将引入正确的实现?这有道理吗?

  • Infrastructure.OldLdap.SomeRepo使用Interfaces.ISessionService注册OldLdap.Service.SessionService
  • Infrastructure.NewLdap.SomeOtherRepo使用Interfaces.ISessionService注册NewLdap.Service.SessionService

这是可能的吗?这是否会破坏Liskov Substitution Principle .

我是不是最好把每个实现写成它自己的东西?如果是这样,这不会打破DRY原则吗?

希望这不是太宽泛,提前谢谢。

将不同的实现注入同一接口,然后在正确的项目/程序集中选择正确的实现

这也违反了Liskov替换原则。

是否破坏LSP取决于SessionService类的行为。您应该经常问自己:"如果我交换实现会发生什么?"如果将Oldldap注入NewRepo导致NewRepo在运行时失败,则违反了LSP,这基本上告诉您您的设计是错误的。另一方面,如果NewRepo保持正常运行,因为OldldapNewldap在合同上的行为是相同的,那么你就没事了。因此,存储库关心它的实现,还是只有您关心。注意,如果Oldldap的执行速度比NewRepo慢,可能没有违反LSP。在这种情况下,只有您关心(或者可能是您的客户,因为非功能需求)。

LSP违反的解决方案总是很简单:给每个实现自己的接口。开发人员往往会遇到这样的问题,因为这两种抽象似乎都是彼此的精确副本,而创建"副本"似乎违反了DRY。但表象是靠不住的;虽然它们具有相同的成员,但它们具有不同的、不兼容的契约。

但是如果你没有违反LSP,保持这个单一接口是可以的(根据LSP)。你可以使用RegisterConditional在Simple Injector 3中进行多个上下文注册,如下所示:

container.RegisterConditional<ISessionService,
    Infrastructure.OldLdap.Service.SessionService>(
    c => c.Consumer.ImplementationType.Namespace.Contains("Oldldap"));
container.RegisterConditional<ISessionService,
    Infrastructure.NewLdap.Service.SessionService>(
    c => c.Consumer.ImplementationType.Namespace.Contains("Newldap"));