MetaClasses и "процедуры класса"
MetaClasses - все о "процедурах класса".Начиная с базового class
:
type
TAlgorithm = class
public
class procedure DoSomething;virtual;
end;
Поскольку DoSomething
является class procedure
, мы можем вызвать его без экземпляра TAlgorithm (он ведет себя как любая другая глобальная процедура).Мы можем сделать это:
TAlgorithm.DoSomething; // this is perfectly valid and doesn't require an instance of TAlgorithm
После того, как мы получим эту настройку, мы можем создать несколько альтернативных алгоритмов, каждый из которых будет использовать некоторые биты и мишени базового алгоритма.Вот так:
type
TAlgorithm = class
protected
class procedure DoSomethingThatAllDescendentsNeedToDo;
public
class procedure DoSomething;virtual;
end;
TAlgorithmA = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
TAlgorithmB = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
Теперь у нас есть один базовый класс и два класса потомков.Следующий код совершенно допустим, потому что мы объявили методы как «классовые» методы:
TAlgorithm.DoSomething;
TAlgorithmA.DoSomething;
TAlgorithmB.DoSomething;
Давайте представим тип class of
:
type
TAlgorithmClass = class of TAlgorithm;
procedure Test;
var X:TAlgorithmClass; // This holds a reference to the CLASS, not a instance of the CLASS!
begin
X := TAlgorithm; // Valid because TAlgorithmClass is supposed to be a "class of TAlgorithm"
X := TAlgorithmA; // Also valid because TAlgorithmA is an TAlgorithm!
X := TAlgorithmB;
end;
TAlgorithmClass - это тип данных, который можетиспользоваться как любой другой тип данных, он может храниться в переменной, передаваться в качестве параметра функции.Другими словами, мы можем сделать это:
procedure CrunchSomeData(Algo:TAlgorithmClass);
begin
Algo.DoSomething;
end;
CrunchSomeData(TAlgorithmA);
В этом примере процедура CrunchSomeData может использовать любой вариант алгоритма, если он является потомком TAlgorithm.
Вот пример того, какэто поведение может использоваться в реальных приложениях: представьте себе приложение типа зарплаты, где некоторые числа должны быть рассчитаны в соответствии с алгоритмом, определенным законом.Вполне возможно, что этот алгоритм со временем изменится, потому что Закон несколько раз менялся.Наше приложение должно рассчитывать зарплату как за текущий год (с помощью актуального калькулятора), так и за другие годы, используя более старые версии алгоритма.Вот как это может выглядеть:
// Algorithm definition
TTaxDeductionCalculator = class
public
class function ComputeTaxDeduction(Something, SomeOtherThing, ThisOtherThing):Currency;virtual;
end;
// Algorithm "factory"
function GetTaxDeductionCalculator(Year:Integer):TTaxDeductionCalculator;
begin
case Year of
2001: Result := TTaxDeductionCalculator_2001;
2006: Result := TTaxDeductionCalculator_2006;
else Result := TTaxDeductionCalculator_2010;
end;
end;
// And we'd use it from some other complex algorithm
procedure Compute;
begin
Taxes := (NetSalary - GetTaxDeductionCalculator(Year).ComputeTaxDeduction(...)) * 0.16;
end;
Виртуальные конструкторы
Конструктор Delphi работает так же, как "функция класса";Если у нас есть метакласс, и метакласс знает о виртуальном конструкторе, мы можем создавать экземпляры дочерних типов.Это используется редактором IDE TCollection для создания новых элементов при нажатии кнопки «Добавить».Все, что нужно TCollection, чтобы получить эту работу - это MetaClass для TCollectionItem.