接口实现(接口隔离原则) [英] Interface Implementation (Interface Segregation Principle)

查看:111
本文介绍了接口实现(接口隔离原则)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've a situation where I need to call a third party service to fetch some information. Those service could be different for different clients. I've a authenticate function in my Interface as follows.

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)来进行身份验证。类似这样......

and so on... now I've came across a service provider (let's say XYZServiceProvider) that needs some additional information (agentid) for authentication. something like this...

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

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

Now if I provide another function for Authenticate in my interface with 3 parameters, and throw not implemented exception in all the classes except for XYZServiceProvider, wouldn't it violate Interface segregation principle? I've similar situation in some of my other part of the code aswell. Can anyone please tell me whats the best way to implement this type of scenrio? I would be really very thankful.

推荐答案

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

The best way to solve this would probably be to require agentId in the interface, and to simply ignore it in the cases of ABC and DEF where they don't need it. That way, the consuming class still wouldn't know the difference.

实际上,如果要提供ABC,DEF和XYZ供应商,最重要的是Liskov替代原则可互换使用; 鉴于A类依赖于X类,X应该能够使用从A派生的B类而不知道差异。

Actually it's the Liskov Substitution Principle that is most important if the ABC, DEF and XYZ providers are to be used interchangeably; "Given a class A that is depended upon by class X, X should be able to use a class B derived from A without knowing the difference".

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

The Interface Segregation Principle basically says that an interface should not contain members that any of its consumers do not need, because if the definition of those members were to change, classes that don't even use that method would have to be recompiled because the interface they depended on has changed. While this is relevant (you do have to recompile all consumers of IServiceProvider if you add an overload), you will have to do that anyway if you change Authenticate()'s signature, and of more pressing concern from a maintenance standpoint is that if you added an overload of Authenticate(), your consumers now have to know which overload they need to use. That requires your consuming classes to know the difference between implementations of a common interface, violating LSP. It's never a problem providing more information than a particular provider needs, but there would be a problem using XYZ from a usage that only provides two inputs. To avoid those problems, you would always use the three-parameter overload, so why have the two-parameter one at all?

现在,如果IServiceProvider的当前用法是没有和不关心agentId的区域,因此很难开始提供它,然后我会推荐一个具体的XYZ提供者插入的适配器,它实现你当前的IServiceProvider,并使新的提供者通过其他方式提供agentId,就像旧的一样:

Now, if current usages of IServiceProvider are in areas that don't have and don't care about agentId, and therefore it would be difficult to begin providing it, then I would recommend an Adapter that the concrete XYZ provider plugs into, that implements your current IServiceProvider, and makes the new provider work like the old ones by providing the agentId through some other means:

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接口提供它不知道的任何内容。

If this is feasible, it meets both LSP and ISP; the interface doesn't have to change to support LSP, therefore preventing the scenario (recompiling and redistributing dependencies) that ISP generally tries to avoid. However it increases class count, and forces new functionality in the Adapter to correctly get the needed agentId without its dependent having to provide anything it wouldn't know about through the IServiceProvider interface.

这篇关于接口实现(接口隔离原则)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆