Я пишу простую инъекцию / инверсию зависимостей системы управления на основе TDictionary, содержащей ссылки на абстрактные классы с соответствующими классами их разработчиков.
Мои цели:
- Избегайте прямого создания экземпляров по типу (очевидно).
- Включение единицы класса в dpr должно быть достаточно, чтобы зарегистрировать его и быть доступным для выбора и реализации через систему di / ioc.
- Объявите конкретные реализующие классы только в реализации только в разделе.
- Используйте конструкторы классов вместо разделов инициализации.
Кстати, я знаю, что использование конструкторов классов, чтобы воспользоваться умными связями, и желание, чтобы включения модуля было достаточно, чтобы сделать класс доступным, побеждают друг друга. Я хочу использовать конструкторы классов вместо разделов инициализации и по другим причинам. И я хотел бы объединить весь код инициализации / регистрации класса вместо того, чтобы разбивать его между конструктором класса и разделом инициализации.
Задача
Я хочу, чтобы регистрация класса на фабрике была в конструкторе классов. К сожалению, компилятор не считает, что класс «затронут», просто используя его тип в своем собственном конструкторе класса.
Когда я помещаю функцию регистрации в раздел инициализации, компилятор думает, что класс затронут, и вызывает конструктор класса. Но это лишает меня смысла хранить весь код инициализации класса в конструкторе класса.
Два вопроса
- Должен ли компилятор рассмотреть использование класса в своем собственном конструкторе класса, "касающегося класса", или это слишком много, чтобы ожидать от компилятора?
- У кого-нибудь есть какие-нибудь умные идеи о том, как я все еще могу достичь своих целей, не используя раздел инициализации?
* 1 036 ** +1037 * Пример * * тысяча тридцать-девять
Абстрактные классы, используемые в приложении:
TSite = class abstract (TObject)
function GetURL: string; virtual; abstract;
property URL: string read GetURL;
end;
TSites = class (TList<TSite>);
TThisApplication = class abstract (TObject)
function Sites: TSites; virtual; abstract;
end;
Конкретный реализующий класс (объявленный в разделе реализации!) Для TThisApplication
TThisApplicationConcrete = class(TThisApplication)
class constructor ClassCreate;
strict private
FSites: TSites;
function Sites: TSites; override;
end;
class constructor TThisApplicationConcrete.ClassCreate;
begin
RegisterImplementorClass(TThisApplication, TThisApplicationConcrete);
end;
function TThisApplicationConcrete.Sites: TSites;
var
SiteList: TSites;
begin
if not Assigned(FSites) then begin
SiteList := TSites.Create; // Change to use factory
//RetrieveSites(SiteList);
FSites := SiteList;
end;
Result := FSites;
end;
Функция для получения экземпляра TThisApplication:
function ThisApplication: TThisApplication;
var
ImplementorClass: TClass;
begin
ImplementorClass := GetImplementorClass(TThisApplication);
if Assigned(ImplementorClass) then begin
Result := ImplementorClass.Create as TThisApplication;
end else begin
Result := nil;
end;
end;
В настоящее время это закодировано в отдельной функции, но оно может быть перенесено на завод.
Полный пример кода
Если кто-то захочет поэкспериментировать, у меня есть полный код моих тестовых проектов по адресу: http://www.bjsoftware.com/delphistuff/stackoverdlow/classconstructors.zip
Zip-содержимое:
- 4 проекта, использующих одни и те же исходные файлы, отличающиеся только условными определениями (поэтому в них также включены dproj)
- 4 исходных файла
- Группрой и ее дск со всеми 4 проектами
- RunTestApps.cmd для запуска всех 4 проектов
- Results.txt с выводом моего запуска RunTestApps.cmd
- WriteUp.txt с текстом этого вопроса
Пожалуйста, имейте в виду, что вам всегда нужно делать "Build All Projecs", потому что все dcu и exe идут в исходный dir, иначе вы столкнетесь с множеством ошибок и / или путаницы, потому что exe не делает то, что указывает его имя.