В настоящее время я все еще использую Delphi 2009, поэтому я решил добавить несколько других способов расширения универсального класса. Они должны одинаково хорошо работать в новых версиях Delphi. Давайте посмотрим, как будет выглядеть метод ToArray
для класса List.
Перехватчик Классы
Классы-перехватчики - это классы, которым присвоено то же имя, что и классу, от которого они наследуются:
TList<T> = class(Generics.Collections.TList<T>)
public
type
TDynArray = array of T;
function ToArray: TDynArray;
end;
function TList<T>.ToArray: TDynArray;
var
I: Integer;
begin
SetLength(Result, self.Count);
for I := 0 to Self.Count - 1 do
begin
Result[I] := Self[I];
end;
end;
Обратите внимание, что вам нужно использовать полное имя, Generics.Collections.TList<T>
в качестве предка. В противном случае вы получите E2086 Type '%s' is not completely defined
.
Преимущество этого метода в том, что ваши расширения в основном прозрачны. Вы можете использовать экземпляры нового TList везде, где использовался оригинал.
У этой техники есть два недостатка:
- Это может вызвать замешательство у других разработчиков, если они не знают, что вы переопределили знакомый класс.
- Его нельзя использовать в запечатанном классе.
Путаница может быть смягчена путем тщательного присвоения имен единицам и избежания использования «оригинального» класса в том же месте, что и ваш класс-перехватчик. Запечатанные классы не являются большой проблемой в классах rtl / vcl, поставляемых Embarcadero. Я нашел только два запечатанных класса во всем дереве исходных текстов: TGCHandleList (используется только в ныне несуществующей Delphi.NET) и TCharacter. Однако вы можете столкнуться с проблемами со сторонними библиотеками.
Декоратор Выкройка
Шаблон декоратора позволяет динамически расширять класс, оборачивая его другим классом, который наследует его открытый интерфейс:
TArrayDecorator<T> = class abstract(TList<T>)
public
type
TDynArray = array of T;
function ToArray: TDynArray; virtual; abstract;
end;
TArrayList<T> = class(TArrayDecorator<T>)
private
FList: TList<T>;
public
constructor Create(List: TList<T>);
function ToArray: TListDecorator<T>.TDynArray; override;
end;
function TMyList<T>.ToArray: TListDecorator<T>.TDynArray;
var
I: Integer;
begin
SetLength(Result, self.Count);
for I := 0 to Self.Count - 1 do
begin
Result[I] := FList[I];
end;
end;
Еще раз, есть преимущества и недостатки.
Преимущества
- Вы можете отложить введение нового функционально, пока оно действительно не понадобится. Нужно сбросить список в массив? Создайте новый TArrayList, передав любой TList или его потомка в качестве параметра в конструкторе. Когда вы закончите, просто откажитесь от TArrayList.
- Вы можете создавать дополнительные декораторы, которые добавляют больше функциональности и комбинируют декораторы различными способами. Вы даже можете использовать его для симуляции множественного наследования, хотя интерфейсы все еще проще.
Недостатки
- Это немного сложнее понять.
- Применение нескольких декораторов к объекту может привести к многословным цепочкам конструктора.
- Как и в случае с перехватчиками, вы не можете расширять запечатанный класс.
Примечание на стороне
Похоже, что если вы хотите сделать класс практически невозможным для расширения, сделайте его запечатанным родовым классом. Тогда помощники класса не могут прикоснуться к нему, и он не может быть унаследован от. Единственный оставшийся вариант - это завернуть.