Как инкапсулировать разные классы в одном классе, поддерживая их уникальные методы? (множественное наследование в Delphi?)
В настоящее время я переписываю бесплатный обучающий симулятор цифровых схем, чтобы добавить инерционность его функциям. Моя проблема в том, как отправлять события в исходные классы, добавляя к ним предварительную разработку. У меня есть что-то вроде этого:
TC1 = class
ID: integer;
Connections : array [integer] of Pin;
function Func1; virtual;
function FuncN;
end;
TC2-1 = class (TC1)
function Func1; override;
function My1Func();
end;
TC2-n = class (TC1)
function Func1; override;
function MyNFunc();
end;
TContainer = class
C1 : TC1;
function ContFunc;
end;
function Container.ContFunc;
begin
c1.Func1;
end;
Теперь это означает, что ContFunc вызывает C2.Func1, как я хочу, специализируя поведение более 300 компонентов, наследующих форму TC1.
Но теперь я должен добавить некоторые специальные операции (равные для всех потомков компонентов из TC1 каждый раз, когда вызывается Func1, и выбирать во время этих операций, нужно ли мне вызывать TC2-n.Func1 или нет (после изменения какого-либо свойства предка TC1. Есть ли способ сделать это чисто, без изменения всех потомков TC1? Могу ли я использовать вспомогательный класс (устарел?), как это:
TH = class helper of TC1
function Func1 virtual; override;
end;
function TH.Func1;
begin
if x then TC2.Func1 else SaveActionData;
end
Если я добавлю TH, когда TContainer вызовет Func1, кто будет вызываться? Это называется TC2.Func1, а не TH.Func1, как я хотел? Есть ли способ переопределить метод потомков Func1 без написания вспомогательного класса для какого-либо одного (они будут выполнять все те же операции, что означает абсолютно одинаковый код)? Можно ли из TH вызывать 300 функций-потомков Func1 из TC2-n?
Другими словами, я пытаюсь найти способ получить такой вызов с помощью вызова Tcontainer c1.Func1;:
NewFunc1 (одинаковый для всех потомков TC1), которые вызывают TC2.Func1 (различный для любого потомка TC1).
Кто-нибудь может предложить способ сделать это?
3 ответа
У вас есть некоторые задачи, которые нужно выполнять всякий раз, когда кто-то звонит Func1
независимо от того, что потомки решили делать в своих переопределенных методах. Это работа для шаблона метода шаблона.
Предоставьте базовому классу публичный не виртуальный метод Func1
который выполняет необходимые операции, а затем вызывает защищенный виртуальный метод. Потомки могут переопределить этот виртуальный метод, но любой, использующий класс, может вызывать только открытый не виртуальный метод.
type
TC1 = class
protected
function InternalFunc1: Integer; virtual; // abstract?
public
function Func1: Integer;
end;
function TC1.Func1;
begin
if x then
Result := InternalFunc1
else
Result := SaveActionData;
end;
Теперь потомки могут переопределить InternalFunc1
и базовый класс будет вызывать его только при необходимости.
type
TC2 = class(TC1)
protected
function InternalFunc1: Integer; override;
end;
Вам нужно будет переименовать ваш текущий Func1
функция во всех ваших 300 классов потомков. Инструменты рефакторинга в IDE могут помочь с этим.
Помощники классов полезны для изменения классов, к которым у вас нет доступа. Если вы являетесь автором класса TC1, и вы сможете внести необходимые изменения, введя вспомогательный класс для TC1, то почему бы просто не изменить TC1.Func1, и тогда все готово? Это должно работать.
Вы можете создать класс-оболочку, следуя шаблону декоратора, чтобы описать специальные задачи, которые должны выполняться, когда ваша программа работает в аналоговом режиме. Он может содержать экземпляр вашего цифрового компонента и вызывать методы этого компонента после выполнения своих собственных задач.
type
TAnalogueDecorator = class(TC1)
private
FComponent: TC1;
public
constructor Create(Wrapped: TC1);
destructor Destroy; override;
function Func1: Integer; override;
end;
constructor TAnalogueDecorator.Create(Wrapped: TC1);
begin
inherited Create;
FComponent := Wrapped;
end;
destructor TAnalogueDecorator.Destroy;
begin
FComponent.Free;
inherited;
end;
function TAnalogueDecorator.Func1: Integer;
begin
SaveActionData;
Result := FComponent.Func1;
end;
Обратите внимание, что нет необходимости проверять x
состояние заранее. Вместо того, чтобы проверять его каждый раз, когда вы вызываете какой-либо метод, вы можете проверить его один раз, прежде чем обернуть цифровой компонент аналоговым. Теперь все места, где вы изначально звонили Func1
непосредственно в цифровом классе вначале обойти методы аналогового класса.