Занимаюсь последнее время перетряхиванием наших библиотек на предмет исправления зависимостей.
В библиотеках несколько миллионов строк кода и несколько тысяч модулей.
Библиотеки используются в нескольких десятках программных проектов. Над которыми трудится достаточно большой коллектив программистов.
И это не "пустые разговоры" ни о чём.
У нас за долгое время работы было написано:
1. Собственный текстовый редактор на уровне Word 97.
2. Собственная реализация IStorage. Эффективнее, чем у MS.
3. Собственный полнотекстовый индексатор.
4. Расширения к DUnit.
5. Собственная скриптовая машина (FORTH-like) на уровне Python.
6. Собственная реализация тестирования GUI-сценариев - на "естественном языке".
7. Собственный рендеринг RTF-like документов для iOS.
8. Собственная реализация SAX и DOM парадигм.
9. Собственная кодогенерация из UML.
10. Собственная реализация MVC-like фреймворка.
Зависимости показывает лишь рисование всего этого хозяйства на UML с последующей валидацией связей и циклов. А также кодогенерацией.
И уже не раз я применил нехитрый приём "инверсии зависимостей".
Который мне крайне нравится.
И я настолько вошёл во вкус, что даже сделал отдельный шаблон кодогенерации всего этого хозяйства.
Приведу пример. БЕЗ шаблона кодогенерации. В БАЗОВЫХ примитивах.
Выглядит примерно так:
Было:
A call B.method
A было связано с B,
стало:
A call C.method
B implements C.method
A связано с C.
B связано с C.
A про B теперь ничего не знает.
B про A - тоже ничего не знает.
A знает про C.
B знает про C.
С выполняет роль "сервиса" с возможностью подмены поведения.
Диаграмма:
Код:
Точка инъекции:
Собственно инъекция:
И вообще говоря подобную технику применяет Embarcadero в FMX.
У них это называется сервисы (Services).
Ну с полноценным Service-Locator'ом.
Ну а в НЕБАЗОВЫХ примитивах это выглядит ПРОЩЕ. Но об этом я напишу в следующий раз. Если будет интерес.
Для любителей "придираться к пробелам и запятым" - ПОДЧЕРКНУ, что это всего лишь - ИДЕЯ, а не руководство к действию.
Ну и понятное дело - ссылку на Spring For Delphi - я уже приводил.
И ещё коллега писал на ту же тему - Коллега написал. Пример Dependency Injection.
И ещё:
Ссылка. Критический взгляд на принцип инверсии зависимостей
Ссылка. Принцип инверсии зависимости
И сходная тема была поднята тут - О тестах и специально оборудованных "контрольных точках"
P.S. Мы за последние несколько недель сделали несколько десятков сервисов. И очень сильно развязали зависимости между библиотеками.
P.P.S. Продолжение - http://programmingmindstream.blogspot.ru/2015/03/blog-post_73.html
В библиотеках несколько миллионов строк кода и несколько тысяч модулей.
Библиотеки используются в нескольких десятках программных проектов. Над которыми трудится достаточно большой коллектив программистов.
И это не "пустые разговоры" ни о чём.
У нас за долгое время работы было написано:
1. Собственный текстовый редактор на уровне Word 97.
2. Собственная реализация IStorage. Эффективнее, чем у MS.
3. Собственный полнотекстовый индексатор.
4. Расширения к DUnit.
5. Собственная скриптовая машина (FORTH-like) на уровне Python.
6. Собственная реализация тестирования GUI-сценариев - на "естественном языке".
7. Собственный рендеринг RTF-like документов для iOS.
8. Собственная реализация SAX и DOM парадигм.
9. Собственная кодогенерация из UML.
10. Собственная реализация MVC-like фреймворка.
Зависимости показывает лишь рисование всего этого хозяйства на UML с последующей валидацией связей и циклов. А также кодогенерацией.
И уже не раз я применил нехитрый приём "инверсии зависимостей".
Который мне крайне нравится.
И я настолько вошёл во вкус, что даже сделал отдельный шаблон кодогенерации всего этого хозяйства.
Приведу пример. БЕЗ шаблона кодогенерации. В БАЗОВЫХ примитивах.
Выглядит примерно так:
Было:
A call B.method
A было связано с B,
стало:
A call C.method
B implements C.method
A связано с C.
B связано с C.
A про B теперь ничего не знает.
B про A - тоже ничего не знает.
A знает про C.
B знает про C.
С выполняет роль "сервиса" с возможностью подмены поведения.
Диаграмма:
Код:
Точка инъекции:
unit l3DispatcherHelper;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Библиотека "L3$AFW"
// Модуль: "w:/common/components/rtl/Garant/L3/l3DispatcherHelper.pas"
// Родные Delphi интерфейсы (.pas)
// Generated from UML model, root element: SimpleClass::Class Shared Delphi Low Level::L3$AFW::VCMHelpers::Tl3DispatcherHelper
//
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ! Полностью генерируется с модели. Править руками - нельзя. !
{$Include ..\L3\l3Define.inc}
interface
uses
l3ProtoObject
;
(*
Ml3DispatcherHelper = PureMixIn
procedure ClearHistory;
end;//Ml3DispatcherHelper
*)
type
Il3DispatcherHelper = interface(IUnknown)
['{41B8F325-9AFB-447E-B3E7-2C433912BC2A}']
// Ml3DispatcherHelper
procedure ClearHistory;
end;//Il3DispatcherHelper
Tl3DispatcherHelper = class(Tl3ProtoObject)
private
// private fields
f_Alien : Il3DispatcherHelper;
public
// realized methods
procedure ClearHistory;
protected
// overridden protected methods
procedure ClearFields; override;
{* Сигнатура метода ClearFields }
public
// public methods
procedure SetAlienHelper(const anAlien: Il3DispatcherHelper);
class function Exists: Boolean;
{* Проверяет создан экземпляр синглетона или нет }
public
// singleton factory method
class function Instance: Tl3DispatcherHelper;
{- возвращает экземпляр синглетона. }
end;//Tl3DispatcherHelper
implementation
uses
l3Base {a}
;
// start class Tl3DispatcherHelper
var g_Tl3DispatcherHelper : Tl3DispatcherHelper = nil;
procedure Tl3DispatcherHelperFree;
begin
l3Free(g_Tl3DispatcherHelper);
end;
class function Tl3DispatcherHelper.Instance: Tl3DispatcherHelper;
begin
if (g_Tl3DispatcherHelper = nil) then
begin
l3System.AddExitProc(Tl3DispatcherHelperFree);
g_Tl3DispatcherHelper := Create;
end;
Result := g_Tl3DispatcherHelper;
end;
procedure Tl3DispatcherHelper.SetAlienHelper(const anAlien: Il3DispatcherHelper);
//#UC START# *5501A41602E7_5501A3AE02AA_var*
//#UC END# *5501A41602E7_5501A3AE02AA_var*
begin
//#UC START# *5501A41602E7_5501A3AE02AA_impl*
Assert(f_Alien = nil);
f_Alien := anAlien;
//#UC END# *5501A41602E7_5501A3AE02AA_impl*
end;//Tl3DispatcherHelper.SetAlienHelper
class function Tl3DispatcherHelper.Exists: Boolean;
{-}
begin
Result := g_Tl3DispatcherHelper <> nil;
end;//Tl3DispatcherHelper.Exists
procedure Tl3DispatcherHelper.ClearHistory;
//#UC START# *5501A435019E_5501A3AE02AA_var*
//#UC END# *5501A435019E_5501A3AE02AA_var*
begin
//#UC START# *5501A435019E_5501A3AE02AA_impl*
if (f_Alien <> nil) then
f_Alien.ClearHistory;
//#UC END# *5501A435019E_5501A3AE02AA_impl*
end;//Tl3DispatcherHelper.ClearHistory
procedure Tl3DispatcherHelper.ClearFields;
{-}
begin
f_Alien := nil;
inherited;
end;//Tl3DispatcherHelper.ClearFields
end.
Собственно инъекция:
TvcmDispatcherHelper = class(Tl3ProtoObject, Il3DispatcherHelper)
public
// realized methods
procedure ClearHistory;
public
// public methods
class function Exists: Boolean;
{* Проверяет создан экземпляр синглетона или нет }
public
// singleton factory method
class function Instance: TvcmDispatcherHelper;
{- возвращает экземпляр синглетона. }
end;//TvcmDispatcherHelper
...
// start class TvcmDispatcherHelper
var g_TvcmDispatcherHelper : TvcmDispatcherHelper = nil;
procedure TvcmDispatcherHelperFree;
begin
l3Free(g_TvcmDispatcherHelper);
end;
class function TvcmDispatcherHelper.Instance: TvcmDispatcherHelper;
begin
if (g_TvcmDispatcherHelper = nil) then
begin
l3System.AddExitProc(TvcmDispatcherHelperFree);
g_TvcmDispatcherHelper := Create;
end;
Result := g_TvcmDispatcherHelper;
end;
class function TvcmDispatcherHelper.Exists: Boolean;
{-}
begin
Result := g_TvcmDispatcherHelper <> nil;
end;//TvcmDispatcherHelper.Exists
procedure TvcmDispatcherHelper.ClearHistory;
//#UC START# *5501A435019E_5501A60D002E_var*
//#UC END# *5501A435019E_5501A60D002E_var*
begin
//#UC START# *5501A435019E_5501A60D002E_impl*
if (vcmDispatcher <> nil) then
if (vcmDispatcher.History <> nil) then
vcmDispatcher.History.Clear(false);
//#UC END# *5501A435019E_5501A60D002E_impl*
end;//TvcmDispatcherHelper.ClearHistory
...
Tl3DispatcherHelper.Instance.SetAlienHelper(TvcmDispatcherHelper.Instance);
...
И вообще говоря подобную технику применяет Embarcadero в FMX.
У них это называется сервисы (Services).
Ну с полноценным Service-Locator'ом.
Ну а в НЕБАЗОВЫХ примитивах это выглядит ПРОЩЕ. Но об этом я напишу в следующий раз. Если будет интерес.
Для любителей "придираться к пробелам и запятым" - ПОДЧЕРКНУ, что это всего лишь - ИДЕЯ, а не руководство к действию.
Ну и понятное дело - ссылку на Spring For Delphi - я уже приводил.
И ещё коллега писал на ту же тему - Коллега написал. Пример Dependency Injection.
И ещё:
Ссылка. Критический взгляд на принцип инверсии зависимостей
Ссылка. Принцип инверсии зависимости
И сходная тема была поднята тут - О тестах и специально оборудованных "контрольных точках"
P.S. Мы за последние несколько недель сделали несколько десятков сервисов. И очень сильно развязали зависимости между библиотеками.
P.P.S. Продолжение - http://programmingmindstream.blogspot.ru/2015/03/blog-post_73.html


Комментариев нет:
Отправить комментарий