Зависит от проблемы как всегда. Я использую интерфейсы, чтобы определить пользовательский интерфейс для набора классов. По крайней мере, когда я знаю, у меня будет более одной реализации базового фактического класса. Например, у вас может быть что-то вроде этого:
IAllInterfaced = interface(IInterface)
procedure ImplementMeEverywhere_1(const Params: TParams);
procedure ImplementMeEverywhere_2(const Params: TParams);
procedure ImplementMeEverywhere_3(const Params: TParams);
end;
TAllInterfaced_ClassA = class(TInterfacedObject, IAllInterfaced)
public
procedure ImplementMeEverywhere_1(const Params: TParams);
procedure ImplementMeEverywhere_2(const Params: TParams);
procedure ImplementMeEverywhere_3(const Params: TParams);
end;
TAllInterfaced_ClassB = class(TInterfacedObject, IAllInterfaced)
public
procedure ImplementMeEverywhere_1(const Params: TParams);
procedure ImplementMeEverywhere_2(const Params: TParams);
procedure ImplementMeEverywhere_3(const Params: TParams);
end;
Здесь у вас нет общего предка. Каждый класс реализует только интерфейс и не имеет общей базовой структуры в форме общего базового класса. Это возможно, если реализации настолько различны, что они не разделяют ничего, кроме самого интерфейса. Вам все еще нужно использовать тот же интерфейс, чтобы вы были последовательны с пользователями производных классов.
Второй вариант:
IAllAbstract = interface(IInterface)
procedure ImplementMeEverywhere_1(const Params: TParams);
procedure ImplementMeEverywhere_2(const Params: TParams);
procedure ImplementMeEverywhere_3(const Params: TParams);
end;
TAllAbstract_Custom = (TInterfacedObject, IAllAbstract)
private
...
public
procedure ImplementMeEverywhere_1(const Params: TParams); virtual; abstract;
procedure ImplementMeEverywhere_2(const Params: TParams); virtual; abstract;
procedure ImplementMeEverywhere_3(const Params: TParams); virtual; abstract;
end;
TAllAbstract_ClassA = class(TAllAbstract_Custom)
public
procedure ImplementMeEverywhere_1(const Params: TParams); override;
procedure ImplementMeEverywhere_2(const Params: TParams); override;
procedure ImplementMeEverywhere_3(const Params: TParams); override;
end;
TAllAbstract_ClassB = class(TAllAbstract_Custom)
public
procedure ImplementMeEverywhere_1(const Params: TParams); override;
procedure ImplementMeEverywhere_2(const Params: TParams); override;
procedure ImplementMeEverywhere_3(const Params: TParams); override;
end;
Здесь у вас есть базовый класс для всех классов. В этом классе вы можете иметь общие свойства или события других классов и т. Д. ... Но все процедуры помечены как абстрактные, потому что они не выполняют каких-либо общих задач. Abstract гарантирует, что они будут реализованы в производных классах, но вам не нужно реализовывать «FieldA» в каждом классе, вы реализуете его только в «TAllAbstract_Custom». Это гарантирует, что используется принцип СУХОЙ.
Последний вариант:
IAllVirtual = interface(IInterface)
procedure ImplementMeEverywhere_1(const Params: TParams);
procedure ImplementMeEverywhere_2(const Params: TParams);
procedure ImplementMeEverywhere_3(const Params: TParams);
end;
TAllVirtual_Custom = (TInterfacedObject, IAllVirtual)
private
...
public
procedure ImplementMeEverywhere_1(const Params: TParams); virtual;
procedure ImplementMeEverywhere_2(const Params: TParams); virtual;
procedure ImplementMeEverywhere_3(const Params: TParams); virtual;
end;
TAllVirtual_ClassA = class(TAllVirtual_Custom)
public
procedure ImplementMeEverywhere_1(const Params: TParams); override;
procedure ImplementMeEverywhere_2(const Params: TParams); override;
procedure ImplementMeEverywhere_3(const Params: TParams); override;
end;
TAllVirtual_ClassB = class(TAllVirtual_Custom)
public
procedure ImplementMeEverywhere_1(const Params: TParams); override;
procedure ImplementMeEverywhere_2(const Params: TParams); override;
procedure ImplementMeEverywhere_3(const Params: TParams); override;
end;
Здесь все производные классы имеют общую базовую виртуальную процедуру. Это гарантирует, что вам не нужно реализовывать каждую процедуру на уровне производных классов. Вы можете переопределить только некоторые части кода или вообще ничего.
Естественно, это все крайние случаи, в них есть место. Вы можете иметь смесь этих концепций.
Просто помните:
- Интерфейсы - это мощный инструмент, позволяющий скрыть реализацию от пользователя и иметь общую точку использования (интерфейс). Они также заставляют использовать некоторые нормы, потому что интерфейс должен быть реализован полностью.
- Аннотация - хороший инструмент, поэтому вам не нужно использовать пустые заглушки для процедур, в которых они не нужны. С другой стороны, они заставляют вас внедрять их в производные классы.
- Виртуал пригодится, когда у вас есть общий код, который должен быть реализован каждым классом и который обеспечивает принцип чистого OP и DRY. Они также приветствуются, когда у вас есть процедуры, которые есть не в каждом производном классе.
Извините за длинный ответ, но я не мог дать простое объяснение здесь, потому что его нет. Все зависит от проблемы. Это баланс между общим количеством производных классов и разностью их реализаций.