Реализация интерфейса (принцип разделения интерфейса) - PullRequest
3 голосов
/ 21 октября 2011

У меня есть ситуация, когда мне нужно вызвать стороннюю службу для получения некоторой информации.Эти услуги могут быть разными для разных клиентов.У меня в интерфейсе есть функция аутентификации следующим образом.

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}
}

Теперь, если я предоставлю другую функцию для Authenticate в моем интерфейсе с 3 параметрами и выброшу не реализованное исключение во всех классах, кроме XYZServiceProvider, это не нарушит сегрегацию интерфейсапринцип?У меня похожая ситуация в некоторых других частях кода.Может кто-нибудь сказать мне, каков наилучший способ реализовать этот тип сценария?Я был бы очень благодарен.

1 Ответ

4 голосов
/ 21 октября 2011

Лучшим способом решения этой проблемы, вероятно, было бы использование agentId в интерфейсе и простое игнорирование его в случаях ABC и DEF, когда им это не нужно.Таким образом, класс потребления все равно не будет знать о разнице.

На самом деле, именно принцип замены Лискова является наиболее важным, если поставщики ABC, DEF и XYZ должны использоваться взаимозаменяемо;«Учитывая класс A, от которого зависит класс X, X должен иметь возможность использовать класс B, производный от A, не зная различий».

Принцип разделения интерфейса в основном гласит, что интерфейс не должен содержать членовчто ни один из его потребителей не нуждается, потому что, если определение этих членов должно было измениться, классы, которые даже не используют этот метод, должны были бы перекомпилироваться, потому что изменился интерфейс, от которого они зависели.Хотя это важно (вам нужно перекомпилировать всех потребителей 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 обычно пытается избежать.Однако это увеличивает количество классов и вынуждает новые функциональные возможности адаптера правильно получать необходимый идентификатор агента, не требуя от его зависимого предоставления чего-либо, о чем он не узнает через интерфейс IServiceProvider.

...