3

我有一种情况,我需要调用第三方服务来获取一些信息。对于不同的客户,这些服务可能会有所不同。我的界面中有一个身份验证功能,如下所示。

interface IServiceProvider {

bool Authenticate(string username, string password);
}

class ABCServiceProvider : IserviceProvider 
{
 bool Authenticate(string username, string password) { // implementation}
}

class EFGServiceProvider : IserviceProvider 
{
 bool Authenticate(string username, string password) { // implementation}
}

等等......现在我遇到了一个服务提供商(比如说 XYZServiceProvider),它需要一些额外的信息(agentid)来进行身份验证。像这样的东西......

class XYZServiceProvider
{
 bool Authenticate(string username, string password, int agentid) { // implementation}
}

现在,如果我在我的接口中提供另一个具有 3 个参数的 Authenticate 函数,并在除 XYZServiceProvider 之外的所有类中抛出未实现的异常,这是否违反了接口隔离原则?我在代码的其他一些部分也有类似的情况。谁能告诉我实现这种场景的最佳方法是什么?我真的很感激。

4

1 回答 1

4

解决此问题的最佳方法可能是在接口中要求 agentId,并在不需要它的 ABC 和 DEF 的情况下简单地忽略它。那样的话,消费阶层仍然不会知道其中的区别。

实际上,如果要互换使用 ABC、DEF 和 XYZ 提供程序,最重要的是 Liskov 替换原则;“给定一个由 X 类依赖的 A 类,X 应该能够使用从 A 派生的 B 类而不知道其中的区别”。

接口隔离原则基本上是说接口不应该包含任何消费者不需要的成员,因为如果这些成员的定义发生变化,甚至不使用该方法的类都必须重新编译,因为接口他们所依赖的已经改变了。虽然这是相关的(如果添加重载,您必须重新编译 IServiceProvider 的所有使用者),但如果您更改 Authenticate() 的签名,无论如何您都必须这样做,并且从维护的角度来看更紧迫的问题是,如果您添加了 Authenticate() 的重载,您的消费者现在必须知道他们需要使用哪个重载。这需要您的消费类知道公共接口的实现之间的区别,这违反了 LSP。它' 提供比特定提供者需要的信息更多的信息从来都不是问题,但是在仅提供两个输入的用法中使用 XYZ 会出现问题。为了避免这些问题,您总是会使用三参数过载,那么为什么要使用二参数呢?

现在,如果 IServiceProvider 的当前用法是在没有也不关心 agentId 的领域,因此很难开始提供它,那么我会推荐一个适配器,具体的 XYZ 提供程序插入,它实现您当前的 IServiceProvider,并通过其他方式提供 agentId,使新的提供者像旧的提供者一样工作:

public class XYZAdapter: IServiceProvider
{
   private readonly XYZServiceProvider xyzProvider;
   public XYZAdapter(XYZServiceProvider provider)
   {
      xyzProvider = provider;
   }

   public void Authenticate(string username, string password)
   {
      xyzProvider.Authenticate(username, password, GetAgentId());
   }

   public int GetAgentId()
   {
      //Retrieve the proper agent Id. It can be provided from the class creator,
      //retrieved from a known constant data source, or pulled from some factory 
      //method provided from this class's creator. Any way you slice it, consumers 
      //of this class cannot know that this information is needed.
   }
}

如果可行,则同时满足LSP和ISP;无需更改接口即可支持 LSP,因此可以防止 ISP 通常试图避免的情况(重新编译和重新分发依赖项)。但是,它增加了类数,并强制适配器中的新功能正确获取所需的 agentId,而无需其依赖项提供任何通过 IServiceProvider 接口不知道的内容。

于 2011-10-21T16:44:57.857 回答