接口实现(接口隔离原则)

时间:2011-10-21 16:29:32

标签: oop interface interface-segregation-principle

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

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之外的所有类中抛出未实现的异常,那么它是否会违反Interface隔离原则?我在代码的其他部分中也有类似的情况。谁能告诉我什么是实现这种类型的场景的最佳方式?我真的很感激。

1 个答案:

答案 0 :(得分:4)

解决这个问题的最好方法可能是在界面中要求agentId,并在ABC和DEF的情况下简单地忽略它,而不需要它。这样,消费阶层仍然不知道差异。

实际上,如果ABC,DEF和XYZ供应商可以互换使用,那么Liskov替代原则是最重要的; “鉴于A类依赖于X类,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接口提供它不会知道的任何内容。