пятница, 29 марта 2013 г.

О шаблонах и примесях

Первая серия была тут:

http://18delphi.blogspot.com/2013/03/generic-generic.html

Теперь поговорим немного о примесях.

Теорию можно почитать тут:
http://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%81%D1%8C_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)

Мы же немного займёмся практикой.

Посмотрим на определение - "При́месь (англ. mix in) — элемент языка программирования (обычно класс или модуль), реализующий какое-либо чётко выделенное поведение. Используется для уточнения поведения других классов, не предназначен для порождения самостоятельно используемых объектов. В объектно-ориентированных языках программирования является способом реализации классов, отличным от широко используемых принципов, пришедших из языка программирования Simula. Механизм впервые реализован в Flavors. Преимуществом примесей является то, что повышая повторную используемость текстов программ, этот метод избегает многих проблем множественного наследования. Однако при этом метод накладывает свои ограничения."

Тем кто программирует на С++ - "повезло" - у них есть множественное наследование и вопросов "как встроить примесь в существую иерархию классов" - не возникает. Программистам на Delphi - "не повезло". Множественного наследования - нет. Да в общем и слава богу. Ибо примеси это гораздо более узкоспециализированный инструмент, чем множественное наследование.

Про конкретные примеры использования примесей в жизни я напишу отдельно. А сейчас рассмотрим чисто абстрактный пример иллюстрирующий лишь технику встраивания примесных классов в иерархию наследования проектных Классов.

В предыдущей статье у нас был пример:
Перерисуем диаграмму следующим образом:













Видим, что весь функционал Stack'а переехал в StackPrim.
И появилось ещё два класса - TIntStackFromPersisten наследующийся от TPersistent и StackPrim, и класс TIntStackFromComponent наследующийся от TComponent и StackPrim.

Множественное наследование - спросите вы. Логически - да. На уровне стрелочек диаграмм. 

Теперь посмотрим как это выглядит на Delphi:

StackPrim.imp.pas:


{$IfNDef StackPrim_imp}
 
{$Define StackPrim_imp}
 ItemsHolder = array of _ItemType_;
 
 _StackPrim_ = {mixin} class(_StackPrim_Parent_)
 private
 // private fields
   f_Items : ItemsHolder;
 public
 // public methods
   procedure Push(const anItem: _ItemType_);
   function Pop: _ItemType_;
 end;//_StackPrim_
 
{$Else StackPrim_imp}
 
// start class _StackPrim_
 
procedure _StackPrim_.Push(const anItem: _ItemType_);
var
 l_L : Integer;
begin
 l_L :=  Length(f_Items);
 SetLength(f_Items, l_L + 1);
 f_Items[l_L] := anItem;
end;//_StackPrim_.Push
 
function _StackPrim_.Pop: _ItemType_;
var
 l_L : Integer;
begin
 l_L :=  Length(f_Items) - 1;
 Result := f_Items[l_L];
 SetLength(f_Items, l_L);
end;//_StackPrim_.Pop
 
{$EndIf StackPrim_imp}

---------------------------------------------
Stack.imp.pas:


{$IfNDef Stack_imp}
 
{$Define Stack_imp}
 _StackPrim_Parent_ = TObject;
 {$Include StackPrim.imp.pas}
 _Stack_ = {mixin} class(_StackPrim_)
 end;//_Stack_
 
{$Else Stack_imp}
 
{$Include StackPrim.imp.pas}
 
{$EndIf Stack_imp}

--------------------------------------------
StringStack.pas:


unit StringStack;
 
interface
 
type
 _ItemType_ = AnsiString;
 {$Include Stack.imp.pas}
 TStringStack = class(_Stack_)
 end;//TStringStack
 
implementation
 
{$Include Stack.imp.pas}
 
end.
--------------------------------------------
IntStack.pas:


unit IntStack;
 
interface
 
type
 _ItemType_ = Integer;
 {$Include Stack.imp.pas}
 TIntStack = class(_Stack_)
 end;//TIntStack
 
implementation
 
{$Include Stack.imp.pas}
 
end.
--------------------------------------------
!!! А вот и два НОВЫХ класса:
--------------------------------------------
IntStackFromPersistent.pas:


unit IntStackFromPersistent;
 
interface
 
uses
  Classes
  ;
 
type
 _ItemType_ = Integer;
 _StackPrim_Parent_ = TPersistent;
 {$Include StackPrim.imp.pas}
 TIntStackFromPersistent = class(_StackPrim_)
 end;//TIntStackFromPersistent
 
implementation
 
{$Include StackPrim.imp.pas}
 
end.
--------------------------------------------
IntStackFromComponent.pas:


unit IntStackFromComponent;
 
interface
 
uses
  Classes
  ;
 
type
 _ItemType_ = Integer;
 _StackPrim_Parent_ = TComponent;
 {$Include StackPrim.imp.pas}
 TIntStackFromComponent = class(_StackPrim_)
 end;//TIntStackFromComponent
 
implementation
 
{$Include StackPrim.imp.pas}
 
end.
--------------------------------------------
По-моему - весело :-) Примесь - ОДНА, а включается в ЧЕТЫРЕ разных класса и даже в разные места иерархии наследования.

Попробуйте. Может и вам понравится.

В следующих сериях я постараюсь рассказать - ЗАЧЕМ я это использую.


3 комментария:

  1. А для чего нужен комент {mixin}?
    _Stack_ = {mixin} class(_StackPrim_)

    ОтветитьУдалить
  2. 1. Для того, чтобы подчеркнуть, что это не настоящий класс, а примесь.
    2. Чтобы искать grep'ом.

    ОтветитьУдалить
  3. 3. Мне бы понравилось бы такое ключевое слово компилятора.
    4. У меня Colorer настроен показывать подобные комментарии особым образом.

    ОтветитьУдалить