среда, 15 июля 2015 г.

ToDo. Сделать на модели интерфейсы с частичной реализацией

ToDo. Сделать на модели интерфейсы с частичной реализацией.

Интерфейсы обычно маленькие, самодостаточные и непротиворечивые.

Не больше 3-5 методов.

Каждый их которых не выводится из остальных.

Это критерий "хорошего" интерфейса.

При "хорошем" проектировании.

Но бывают такие "монстры" как IStream, IStorage или IDataObject.

Где одни методы интерфейса ЯВНО выводятся через другие. Умолчательно.

IDataObject - тому САМЫЙ яркий пример.

С его GetData и GetDataHere.

Или EnumFormatEtc и EnumFormatEtc.

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

Например:

type
 ICString = interaface
  function S: PChar;
  function Len: Integer;
  function EQ(const anOther: ICString): Boolean;
 end;//ICString 

Понятное дело, что метод EQ может быть выведен из методов S и Len.

Но "обычно" разработчики "бедные" вынуждены реализовывать такие "дефолнтые методы".

Раз от разу.

Делая одну систематическую ошибку за другой.

Что можно сделать?

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

Можно просто убрать методы типа EQ с интерфейса и заменить их "вязанкой глобальных методов".

Но!

Иногда интерфейсы они - ЧУЖИЕ, а реализуются в своём коде.

А иногда - УДОБНО иметь умолчательную реализацию с возможностью её переопределять.

Что же делать?

А вот можно сделать на модели у метода интерфейса галку - "имеет умолчательную реализацию".

И интерфейс, у которого есть методы с такими галками - делаем примесь при кодогенерации.

Например поставим такую галку у ICString.EQ.

Тогда при кодогенерации получим примесь:

type
 _CString_ = class(_CString_Parent_)
  protected
   function EQ(const anOther: ICString): Boolean; virtual;
 end;//_CString_

...

function _CString_.EQ(const anOther: ICString): Boolean;
begin
 // - тут умолчательная реализация
end;

И пусть наш класс TCString реализует (на модели) интерфейс ICString.

Тогда получаем такой код:

type
 _CString_Parent_ = TInterfacedObject;
 {$Include CString.imp.pas}
 TCString = class(_CString_, ICString)
 protected
  function S: PChar;
  function Len: Integer;
  //function EQ(const anOther: ICString): Boolean;
  // - Реализован выше, в _CString_
 end;//TCString

А можно ещё получить TCString1:

type
 _CString_Parent_ = TInterfacedObject;
 {$Include CString.imp.pas}
 TCString1 = class(_CString_, ICString)
 protected
  function S: PChar;
  function Len: Integer;
  function EQ(const anOther: ICString): Boolean; override;
  // - Тут перекрываем умолчательную реализацию
 end;//TCString1

По-моему - интересная идея. мне давно такой штуки не хватает.

А можно пойти дальше.

Сделать "галку" трёхпозиционной - abstract/virtual/final.

И если:
abstract - не делаем умолчательную реализацию.
virtual - делаем виртуальную умолчательную реализацию.
final - делаем финальную (статическую) реализацию.

+Виктор Морозов
+Михаил Костицын
+Николай Зверев

Литература:

О шаблонах и примесях
Собственная реализация IUnknown и подсчёт ссылок. И примеси
Коротко. Много написал про "примеси и шаблоны"
И ещё раз про "примеси". Теперь - "серьёзно"
Языки "где так или иначе" возможны примеси
Зачем UML

Завтра думаю - сделаю.

Не самая простая задачка кстати. Там есть подводные камни.

Если мы копируем методы интерфейса в примесь, то в наследниках получаем реализацию этих методов.

Паразитную.

А если не копируем, то непонятно как их там показать.

Хотя есть вариант - "дополнительный цикл" типа %o и %O.

Но опять же - как сказать, что "метод уже реализован"?

Переносить его в PureMixIn?

Всё - ПРОЩЕ - ВООБЩЕ не реализовывать методы у которых стоит галка.

1 комментарий: