// Общий интерфейс
ISomeIntf = interface
procedure SomeProc;
end;
// Общая реализация интерфейса
TSomeIntfImpl = class(TAggregatedObject, ISomeIntf)
public
procedure SomeProc;
end;
// Класс, наследующий интерфейс ISomeIntf с его реализацией в классе TSomeIntfImpl
TBar = class(TInterfacedObject, ISomeIntf)
private
FSomeIntfImpl: TSomeIntfImpl;
function GetSomeIntfImpl: ISomeIntf;
protected
property SomeIntfImpl: ISomeIntf read GetSomeIntfImpl implements ISomeIntf;
public
constructor Create;
destructor Destroy; override;
end;
...
constructor TBar.Create;
begin
inherited Create;
FSomeIntfImpl := TSomeIntfImpl.Create(Self);
end;
destructor TBar.Destroy;
begin
FSomeIntfImpl.Free;
inherited Destroy;
end;
function TBar.GetSomeIntfImpl: ISomeIntf;
begin
Result := FSomeIntfImpl;
end;
Таким образом, класс TBar наследует интерфейс ISomeIntf и делегирует его реализацию классу TSomeIntfImpl. Таким же образом, мы можем создать и другие классы, кроме TBar, и писать для них полиморфный код, имея на руках ссылку на интерфейс ISomeIntf.
TSomeIntfImpl объявлен как потомок TAggregatedObject. Это важно, так как в этом классе методы интерфейса IInterface реализованы таким образом, что просто передают вызовы внешнему объекту. Если бы TSomeIntfImpl сам реализовал эти методы, то клиент не смог бы, имея на руках ссылку на ISomeIntf получить другой интерфейс, даже если класс TBar его и поддерживал. Кроме того, при запросе ISomeIntf счетчик ссылок увеличивался бы не у экземпляра TBar, как следовало бы, а у внутреннего объекта класса TSomeIntfImpl.
5 comments:
Лично я не понимаю проблемы множественного наследования.
Если нужно T3 породить от T1 и T2, почему бы не написать так:
type
T3 = class(T1)
...
private
FT: T2; // создаётся в конструкторе
public
property T: T2 read FT;
end;
to Anonymous:
То, что Вы написали - это тоже в общем то агрегация :) Разница лишь в том, каким образом клиент получает доступ к "подмешанному" функционалу: в Вашем случае - через обращение к свойству, в моем - через приведение к интерфейсу.
Разница безусловно непринципиальная. Но мне мой вариант нравится больше с той точки зрения, что для клиентского кода достигается большая прозрачность. Внутренне объект действительно делегирует реализацию методов некому внутреннему объекту, однако "наружу" эти методы предоставляет как будто бы свои собственные. Клиенту не нужно знать, что эти методы реализуются другим объектом, он обращается к объекту так, как если бы он сам реализовал эти методы. В вашем случае, клиентский код должен "знать", что данный функционал реализуется именно вложенным объектом и обращаться к этому вложенному объекту явно.
Так что вопрос исключительно в прозрачности для клиента.
Интересно написано....но многое остается непонятнымb
Так и не понял как этим пользоваться.
Чтобы вызвать процедуру SomeProc приходится приводить её к типу
procedure TForm1.Button1Click(Sender: TObject);
var bar : TBar;
begin
bar := TBar.create;
TSomeIntfImpl(bar).SomeProc;
// или ISomeIntf(bar).SomeProc;
end;
а я бы хотел просто обратиться к процедуре:
bar.SomeProc;
Мне нужно объединить классы, производные, от TEdit и TLabel в одном, например, в TUniControl так:
TSEdit = class(TEdit, TUniControl)
. В обоих есть процедура SomeFunc. В классе TAnyClass она должна вызываться из массива так: for i := 0 to Length(FControls) - 1 do TUniControl(FControls[i]).SomeFunc;
Post a Comment