Как инкапсулировать разные классы в одном классе, поддерживая их уникальные методы? (множественное наследование в 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 непосредственно в цифровом классе вначале обойти методы аналогового класса.

Другие вопросы по тегам