将不同的实现注入同一接口,然后在正确的项目/程序集中选择正确的实现
Inject different implementations into same interface then pick up right implementation in right project / assembly
我们有 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
.[=27= 加载所有凭据等。 ]
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
原则吗?
希望这不会太宽泛,提前谢谢你。
Also does this break the Liskov Substitution Principle.
是否破坏 LSP 取决于 SessionService
类 的行为。你应该经常问自己:"What will happen if I swap the implementations?" 如果将 Oldldap
注入 NewRepo
会导致 NewRepo
在运行时失败,那么你违反了 LSP,这基本上是在告诉你你的设计是错误的。另一方面,如果 NewRepo
继续正常运行,因为 Oldldap
和 Newldap
在合同上的行为相同,那么你就没事了。那么存储库是否关心它的实现,或者只有你关心。请注意,如果 Oldldap
对 NewRepo
执行得太慢,那可能不违反 LSP。在那种情况下,只有您关心(或者可能是您的客户,因为非功能性需求)。
解决 LSP 违规的方法总是很简单:为每个实现提供自己的接口。开发人员往往对此有疑问,因为两个抽象似乎彼此完全相同,并且使 'a copy' 似乎违反了 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"));
我们有 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
.[=27= 加载所有凭据等。 ]
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
原则吗?
希望这不会太宽泛,提前谢谢你。
Also does this break the Liskov Substitution Principle.
是否破坏 LSP 取决于 SessionService
类 的行为。你应该经常问自己:"What will happen if I swap the implementations?" 如果将 Oldldap
注入 NewRepo
会导致 NewRepo
在运行时失败,那么你违反了 LSP,这基本上是在告诉你你的设计是错误的。另一方面,如果 NewRepo
继续正常运行,因为 Oldldap
和 Newldap
在合同上的行为相同,那么你就没事了。那么存储库是否关心它的实现,或者只有你关心。请注意,如果 Oldldap
对 NewRepo
执行得太慢,那可能不违反 LSP。在那种情况下,只有您关心(或者可能是您的客户,因为非功能性需求)。
解决 LSP 违规的方法总是很简单:为每个实现提供自己的接口。开发人员往往对此有疑问,因为两个抽象似乎彼此完全相同,并且使 'a copy' 似乎违反了 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"));