Ясность в классах, реализующих несколько интерфейсов (альтернатива делегированию): - PullRequest
4 голосов
/ 27 июля 2011

Допустим, у нас есть следующее:

IFirst = Interface(IUnknown)    
  function GetStuff: Integer;
end;

ISecond = Interface(IUnknown)
  function GetOtherStuff: Integer;
end;

TFirstSecond = class(TInterfacedObject, IFirst, ISecond)    
private 
  function GetStuff: Integer;        //implementation of IFirst
  function GetOtherStuff: Integer;   //implementation of ISecond;
end;

Мне никогда не нравился тот факт, что в TInterfacedObject, похоже, нет способа различить, какие методы реализуют какие интерфейсы. Я что-то пропустил? Кто-нибудь знает, как структурировать код для этого? Чтобы обозначить, что GetStuff - это реализация IFirst, а GetOtherStuff - это реализация ISecond? («Оставить комментарий» - это не тот ответ, который я ищу ...)

Я знаю, что могу использовать директиву 'Implements', чтобы определять свойства в TFirstSecond для каждого интерфейса и делегировать реализации экземплярам, ​​содержащимся в TFirstSecond, таким образом разделяя все. Но я бы хотел ярлык ...

Ответы [ 4 ]

15 голосов
/ 27 июля 2011

Полагаю, единственное, что вы действительно можете сделать, не используя комментарии, это добавить предложения по разрешению метода :

IFirst = interface
  function GetStuff: Integer;
end;

ISecond = interface
  function GetOtherStuff: Integer;
end;

TFirstSecond = class(TInterfacedObject, IFirst, ISecond)
private
  function GetStuff: Integer;
  function GetOtherStuff: Integer;
public
  function IFirst.GetStuff = GetStuff;
  function ISecond.GetOtherStuff = GetOtherStuff;
end;

Не думаю, что это действительно очень сильно добавляет к миксуи я лично считаю, что это хуже, чем без положений о методе разрешения.

11 голосов
/ 27 июля 2011

В случае, если вы публикуете, я тоже предпочитаю комментарии (имя интерфейса, как его называет NGLN), но я хотел бы объяснить, почему ключевое слово реализует может быть лучшим решением в некоторых других случаях, просто не в тривиальных случаях, когда есть только один метод для интерфейса, как в вашем тривиальном примере.

Я знаю, что вы сказали, что знаете об инструментах; но для людей, которые приходят вместе и не видели этого, я бы хотел документировать, когда это ПОЛЕЗНО, терпите меня. В некоторых случаях даже стоит потратить всю дополнительную работу на то, чтобы иметь больше классов.

Таким образом, я бы использовал инструменты не как ярлык (как вы видите, он длиннее!), А только тогда, когда каждый интерфейс включает в себя 100 методов, которые должны быть реализованы, и где полученный дизайн имеет меньшую связь, а также лучшую согласованность и удобочитаемость. *

Так что это, по общему признанию, глупый пример, но если бы у каждого из IFirst и ISecond было по 100 методов, то это могло бы стать большим скачком вперед ...

type
IFirst = interface
  function GetStuff: Integer;
end;

ISecond = interface
  function GetOtherStuff: Integer;
end;

TFirstLogic = class(TInterfacedObject, IFirst)
  function GetStuff: Integer;

end;

TSecondLogic = class(TInterfacedObject, ISecond)
  function GetOtherStuff: Integer;
end;

TFirstSecond = class(TInterfacedObject, IFirst, ISecond)
private
  FFirst:TFirstLogic;
  FSecond:TSecondLogic;
protected


  property First:TFirstLogic read FFirst implements IFirst;
  property Second:TSecondLogic read FSecond implements ISecond;
public
  constructor Create; // don't forget to create and free FFirst/FSecond.
  destructor Destroy; override; // don't forget to create and free FFirst/FSecond.


end;

Можно сказать, что Implements - это единственный способ, которым мы могли бы создавать «частичные классы», или, по крайней мере, создать один составной класс, который реализует группу интерфейсов и имеет несколько под-свойств (которые защищены или, возможно, даже закрыты). используется для выполнения "реализует" делегирование. Если вы переместите все остальное прямо из модуля, который содержит класс агрегирования, у вас может получиться действительно чистый дизайн.

2 голосов
/ 27 июля 2011

D2006 (но, возможно, и более ранние версии, D2006 - только самая ранняя из доступных на данный момент), поддерживает «отображение» методов интерфейса на определенные функции класса. Это избавляет от необходимости иметь свойство, как вы делаете с ключевым словом implements.

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

Пример:

IMyFirstInterface = interface(IInterface)
  procedure DoSomethingInteresting;
  procedure DoSomethingElse;
end;

IMySecondInterface = interface(IInterface)
  procedure DoSomethingInteresting;
end;

TMyCombinedObject = class(TInterfacedObject, IMyFirstInterface, IMySecondInterface)
private
  // Interface mappings 
  procedure IMyFirstInterface.DoSomethingInteresting = DoSomethingInterestingFirst;
  procedure IMySecondInterface.DoSomethingInteresting = DoSomethingInterestingSecond;
protected
  procedure DoSomethingInterestingFirst;
  procedure DoSomethingInterestingSecond;
  procedure DoSomethingElse;
end;

Недостаток, если у вас много интерфейсов или много методов: повторение. Однако, как видно из примера, вам не нужно указывать сопоставления для всех методов в интерфейсе.

В целях документирования вы можете поместить сопоставление непосредственно с фактическим объявлением метода, чтобы они оставались вместе и с меньшей вероятностью вышли из синхронизации (или, скорее, сопоставления пропустят новые методы, поскольку вам не нужно объявлять сопоставление для каждого метода интерфейса):

TMyCombinedObject = class(TInterfacedObject, IMyFirstInterface, IMySecondInterface)
protected
  procedure IMyFirstInterface.DoSomethingInteresting = DoSomethingInterestingFirst;
  procedure DoSomethingInterestingFirst;

  procedure IMySecondInterface.DoSomethingInteresting = DoSomethingInterestingSecond;
  procedure DoSomethingInterestingSecond;

  procedure DoSomethingElse;
end;
2 голосов
/ 27 июля 2011

Несмотря на то, что вы специально запрашиваете ответ без комментариев, могу ли я сказать, что использование комментариев является общим решением для более чем просто VCL Delphi, а именно:

TFirstSecond = class(TInterfacedObject, IFirst, ISecond)
private
  { IFirst }
  function GetStuff: Integer;
private
  { ISecond }
  function GetOtherStuff: Integer;
end;
...