Подготовительные серии были тут - http://18delphi.blogspot.com/2013/07/1.html
Теперь хочется рассказать о практике создания "шаблонных" контейнеров в "стиле STL".
ПОКА - БЕЗ "настоящих" Generic'ов (http://18delphi.blogspot.ru/2013/03/generic-generic.html). Но и их - ТОЖЕ можно использовать. Но ПОКА - есть МНОЖЕСТВО НЕЗАКРЫТЫХ ошибок, которые Embarcadero почему-то не спешит закрывать. Минус ребятам. Что сказать. Даже мою ошибку (http://18delphi.blogspot.com/2013/05/resolved.html http://18delphi.blogspot.com/2013/05/xe4.html) - "типа исправили", но не закрыли. Хотя там - "копейки". Если я своим умом - всё правильно понимаю...
Одна ремарка. Несмотря на то, что в STL подобные контейнеры называются vector - я решил сохранить преемственность с Delphi и назвал их List.
Итак. Как обычно.
Модель:
Код:
List.imp.pas:
Теперь хочется рассказать о практике создания "шаблонных" контейнеров в "стиле STL".
ПОКА - БЕЗ "настоящих" Generic'ов (http://18delphi.blogspot.ru/2013/03/generic-generic.html). Но и их - ТОЖЕ можно использовать. Но ПОКА - есть МНОЖЕСТВО НЕЗАКРЫТЫХ ошибок, которые Embarcadero почему-то не спешит закрывать. Минус ребятам. Что сказать. Даже мою ошибку (http://18delphi.blogspot.com/2013/05/resolved.html http://18delphi.blogspot.com/2013/05/xe4.html) - "типа исправили", но не закрыли. Хотя там - "копейки". Если я своим умом - всё правильно понимаю...
Одна ремарка. Несмотря на то, что в STL подобные контейнеры называются vector - я решил сохранить преемственность с Delphi и назвал их List.
Итак. Как обычно.
Модель:
List.imp.pas:
{$IfNDef List_imp}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Библиотека "SandBox"
// Модуль: "List.imp.pas"
// Родные Delphi интерфейсы (.pas)
// Generated from UML model, root element: Impurity::Class Shared Delphi Sand Box::SandBox::STLLike::List
//
// Абстрактный список значений
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{$Define List_imp}
PItemType = ^_ItemType_;
const
{ Sizes }
cItemSize = SizeOf(_ItemType_);
type
IndexType = System.Integer;
_List_ = {mixin} class(_List_Parent_)
{* Абстрактный список значений }
private
// private fields
f_Data : Tl3PtrLoc;
f_Count : IndexType;
{* Поле для свойства Count}
private
// private methods
procedure ReAllocList(aNewCapacity: IndexType);
procedure CheckIndex(anIndex: IndexType); // can raise EListError
{* проверяет валидность индекса и поднимает исключение, если он неправильный }
function ItemSlot(anIndex: IndexType): PItemType;
function ExpandSize(aTargetSize: IndexType): Cardinal;
procedure CheckSetItem(anIndex: IndexType); // can raise EListError
{* Проверяет валидность индекса при вставке }
protected
// property methods
procedure pm_SetCount(aValue: IndexType);
function pm_GetCapacity: IndexType;
procedure pm_SetCapacity(aValue: IndexType);
function pm_GetItems(anIndex: IndexType): _ItemType_;
procedure pm_SetItems(anIndex: IndexType; const aValue: _ItemType_);
protected
// overridden protected methods
procedure Cleanup; override;
{* Функция очистки полей объекта. }
public
// public properties
property Count: IndexType
read f_Count
write pm_SetCount;
property Capacity: IndexType
read pm_GetCapacity
write pm_SetCapacity;
property Items[anIndex: IndexType]: _ItemType_
read pm_GetItems
write pm_SetItems;
default;
{* Элементы списка. }
end;//_List_
{$Else List_imp}
// start class _List_
procedure _List_.ReAllocList(aNewCapacity: IndexType);
//#UC START# *51DEB8770017_51DEB07E03E4_var*
var
l_Cap : Integer;
l_Cnt : Integer;
//#UC END# *51DEB8770017_51DEB07E03E4_var*
begin
//#UC START# *51DEB8770017_51DEB07E03E4_impl*
f_Data.SetSize(aNewCapacity * cItemSize);
l_Cap := Self.Capacity;
Assert(l_Cap >= aNewCapacity);
l_Cnt := f_Count;
if (l_Cap > l_Cnt) then
System.FillChar(ItemSlot(l_Cnt)^, (l_Cap - l_Cnt) * cItemSize, 0);
//#UC END# *51DEB8770017_51DEB07E03E4_impl*
end;//_List_.ReAllocList
procedure _List_.CheckIndex(anIndex: IndexType); // can raise EListError
//#UC START# *51DEB95E00BD_51DEB07E03E4_var*
procedure _Error;
begin
raise EListError.CreateFmt(SListIndexError + ' from (%d)', [anIndex, f_Count])
end;
//#UC END# *51DEB95E00BD_51DEB07E03E4_var*
begin
//#UC START# *51DEB95E00BD_51DEB07E03E4_impl*
if (anIndex < 0) or (anIndex >= f_Count) then
_Error;
//#UC END# *51DEB95E00BD_51DEB07E03E4_impl*
end;//_List_.CheckIndex
function _List_.ItemSlot(anIndex: IndexType): PItemType;
//#UC START# *51DEBE2D008A_51DEB07E03E4_var*
//#UC END# *51DEBE2D008A_51DEB07E03E4_var*
begin
//#UC START# *51DEBE2D008A_51DEB07E03E4_impl*
Result := PItemType(f_Data.AsPointer + anIndex * cItemSize);
assert(Result <> nil);
//#UC END# *51DEBE2D008A_51DEB07E03E4_impl*
end;//_List_.ItemSlot
function _List_.ExpandSize(aTargetSize: IndexType): Cardinal;
//#UC START# *51DEC11F0058_51DEB07E03E4_var*
const
cIncrArray : array [0..3] of Integer = (64 * 1024, 1024, 128, 4);
cMaxForTwice : Integer = 1 * 1024 * 1024;
var
I : Integer;
//#UC END# *51DEC11F0058_51DEB07E03E4_var*
begin
//#UC START# *51DEC11F0058_51DEB07E03E4_impl*
Assert(aTargetSize > 0);
Result := aTargetSize;
if (Result > cMaxForTwice) then
// большие массивы не удваиваем а подравниваем под 1мб
Result := (aTargetSize div cMaxForTwice + 1) * cMaxForTwice
else
begin
for I := 0 to High(cIncrArray) do
if (aTargetSize > cIncrArray[I]) then
begin
Result := (aTargetSize div cIncrArray[I]) * cIncrArray[I] * 2;
Break;
end;//aTargetSize > cIncrArray[I]
end;//Result > cMaxForTwic
//#UC END# *51DEC11F0058_51DEB07E03E4_impl*
end;//_List_.ExpandSize
procedure _List_.CheckSetItem(anIndex: IndexType); // can raise EListError
//#UC START# *51DECAA8035E_51DEB07E03E4_var*
//#UC END# *51DECAA8035E_51DEB07E03E4_var*
begin
//#UC START# *51DECAA8035E_51DEB07E03E4_impl*
CheckIndex(anIndex);
//#UC END# *51DECAA8035E_51DEB07E03E4_impl*
end;//_List_.CheckSetItem
procedure _List_.pm_SetCount(aValue: IndexType);
//#UC START# *51DEB1ED0017_51DEB07E03E4set_var*
procedure SayBadCount(aNewCount: LongInt);
begin
raise EListError.CreateFmt(sListIndexError, [aNewCount]);
end;
var
l_Ptr : PItemType;
{$IfNDef l3Items_IsUnrefcounted}
l_Index : Integer;
{$EndIf l3Items_IsUnrefcounted}
//#UC END# *51DEB1ED0017_51DEB07E03E4set_var*
begin
//#UC START# *51DEB1ED0017_51DEB07E03E4set_impl*
if (aValue < 0) then
SayBadCount(aValue);
if (aValue < f_Count) then
begin
l_Ptr := ItemSlot(aValue);
{$IfDef l3Items_IsUnrefcounted}
System.FillChar(l_Ptr^, (f_Count - 1 - aValue) * cItemSize, 0);
{$Else l3Items_IsUnrefcounted}
for l_Index := aValue to f_Count - 1 do
begin
FreeItem(l_Ptr^);
Inc(PMem(l_Ptr), cItemSize);
end;//for i
{$EndIf l3Items_IsUnrefcounted}
end//aValue < f_Count
else
if (aValue > Self.Capacity) then
ReAllocList(ExpandSize(aValue));
if (f_Count < aValue) then
System.FillChar(ItemSlot(f_Count)^, (aValue - f_Count) * cItemSize, 0);
f_Count := aValue;
//#UC END# *51DEB1ED0017_51DEB07E03E4set_impl*
end;//_List_.pm_SetCount
function _List_.pm_GetCapacity: IndexType;
//#UC START# *51DEB20E0130_51DEB07E03E4get_var*
//#UC END# *51DEB20E0130_51DEB07E03E4get_var*
begin
//#UC START# *51DEB20E0130_51DEB07E03E4get_impl*
Result := f_Data.GetSize div cItemSize;
//#UC END# *51DEB20E0130_51DEB07E03E4get_impl*
end;//_List_.pm_GetCapacity
procedure _List_.pm_SetCapacity(aValue: IndexType);
//#UC START# *51DEB20E0130_51DEB07E03E4set_var*
procedure SayBadCap(aNewCapacity: IndexType);
begin
raise EListError.CreateFmt(sListIndexError, [aNewCapacity]);
end;
//#UC END# *51DEB20E0130_51DEB07E03E4set_var*
begin
//#UC START# *51DEB20E0130_51DEB07E03E4set_impl*
if (aValue < 0) then
SayBadCap(aValue);
if (pm_GetCapacity <> aValue) then
begin
{ If the list is shrinking, then update _Count for the smaller size. }
if (aValue < f_Count) then
Count := aValue;
ReAllocList(aValue);
end;//GetCapacity(Self) <> aValue
//#UC END# *51DEB20E0130_51DEB07E03E4set_impl*
end;//_List_.pm_SetCapacity
function _List_.pm_GetItems(anIndex: IndexType): _ItemType_;
//#UC START# *51DECA1202C5_51DEB07E03E4get_var*
//#UC END# *51DECA1202C5_51DEB07E03E4get_var*
begin
//#UC START# *51DECA1202C5_51DEB07E03E4get_impl*
CheckIndex(anIndex);
Result := ItemSlot(anIndex)^;
//#UC END# *51DECA1202C5_51DEB07E03E4get_impl*
end;//_List_.pm_GetItems
procedure _List_.pm_SetItems(anIndex: IndexType; const aValue: _ItemType_);
//#UC START# *51DECA1202C5_51DEB07E03E4set_var*
{$IfNDef l3Items_IsAtomic}
var
l_P : PItemType;
{$EndIf l3Items_IsAtomic}
//#UC END# *51DECA1202C5_51DEB07E03E4set_var*
begin
//#UC START# *51DECA1202C5_51DEB07E03E4set_impl*
CheckSetItem(anIndex);
{$IfDef l3Items_IsAtomic}
PItemType(ItemSlot(anIndex))^ := aValue;
{$Else l3Items_IsAtomic}
l_P := PItemType(ItemSlot(anIndex));
if not IsSame(l_P^, aValue) then
begin
FreeItem(l_P^);
FillItem(l_P^, aValue);
end;//not IsSame(l_P^, anItem)
{$EndIf l3Items_IsAtomic}
//#UC END# *51DECA1202C5_51DEB07E03E4set_impl*
end;//_List_.pm_SetItems
procedure _List_.Cleanup;
//#UC START# *479731C50290_51DEB07E03E4_var*
//#UC END# *479731C50290_51DEB07E03E4_var*
begin
//#UC START# *479731C50290_51DEB07E03E4_impl*
Count := 0;
f_Data.SetSize(0);
inherited;
//#UC END# *479731C50290_51DEB07E03E4_impl*
end;//_List_.Cleanup
{$EndIf List_imp}
UnrefcountedListPrim.imp.pas:
{$IfNDef UnrefcountedListPrim_imp}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Библиотека "SandBox"
// Модуль: "UnrefcountedListPrim.imp.pas"
// Родные Delphi интерфейсы (.pas)
// Generated from UML model, root element: Impurity::Class Shared Delphi Sand Box::SandBox::STLLike::UnrefcountedListPrim
//
// Список значений без какого то бы ни было подсчёта ссылок
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{$Define UnrefcountedListPrim_imp}
{$Define l3Items_IsUnrefcounted}
_List_Parent_ = _UnrefcountedListPrim_Parent_;
{$Include List.imp.pas}
_UnrefcountedListPrim_ = {mixin} class(_List_)
{* Список значений без какого то бы ни было подсчёта ссылок }
end;//_UnrefcountedListPrim_
{$Else UnrefcountedListPrim_imp}
// start class _UnrefcountedListPrim_
function IsSame(const A: _ItemType_;
const B: _ItemType_): Boolean;
//#UC START# *51DECB820261_51DED02E0163_var*
//#UC END# *51DECB820261_51DED02E0163_var*
begin
//#UC START# *51DECB820261_51DED02E0163_impl*
Result := (A = B);
//#UC END# *51DECB820261_51DED02E0163_impl*
end;//IsSame
type _List_R_ = _UnrefcountedListPrim_;
{$Include List.imp.pas}
{$EndIf UnrefcountedListPrim_imp}
AtomicList.imp.pas:
{$IfNDef AtomicList_imp}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Библиотека "SandBox"
// Модуль: "AtomicList.imp.pas"
// Родные Delphi интерфейсы (.pas)
// Generated from UML model, root element: Impurity::Class Shared Delphi Sand Box::SandBox::STLLike::AtomicList
//
// Список атомарных значений
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{$Define AtomicList_imp}
{$Define l3Items_IsAtomic}
_UnrefcountedListPrim_Parent_ = _AtomicList_Parent_;
{$Include UnrefcountedListPrim.imp.pas}
_AtomicList_ = {mixin} class(_UnrefcountedListPrim_)
{* Список атомарных значений }
end;//_AtomicList_
{$Else AtomicList_imp}
// start class _AtomicList_
procedure FillItem(var thePlace: _ItemType_;
const aFrom: _ItemType_); forward;
procedure FreeItem(var thePlace: _ItemType_);
//#UC START# *51DEC20B01D7_51DED48301D9_var*
//#UC END# *51DEC20B01D7_51DED48301D9_var*
begin
//#UC START# *51DEC20B01D7_51DED48301D9_impl*
thePlace := _ItemType_(0);
//#UC END# *51DEC20B01D7_51DED48301D9_impl*
end;//FreeItem
procedure FillItem(var thePlace: _ItemType_;
const aFrom: _ItemType_);
//#UC START# *51DECB440087_51DED48301D9_var*
//#UC END# *51DECB440087_51DED48301D9_var*
begin
//#UC START# *51DECB440087_51DED48301D9_impl*
thePlace := aFrom;
//#UC END# *51DECB440087_51DED48301D9_impl*
end;//FillItem
type _UnrefcountedListPrim_R_ = _AtomicList_;
{$Include UnrefcountedListPrim.imp.pas}
{$EndIf AtomicList_imp}
Код тут - http://sourceforge.net/p/rumtmarc/code-0/19/tree/trunk/Blogger/SandBox и http://sourceforge.net/p/rumtmarc/code-0/19/tree/trunk/Blogger/SandBoxTest

NameRec:
ОтветитьУдалитьВызывает уважение.
Особенно, если учесть, что код сгенерирован из UML-диаграммы.
1. Правильно ли я понимаю, что ваш (Вашей организации) инструмент UML-моделирования поддерживает возможность при определении методов в классе на UML-модели сразу же указывать код реализации?
На эту мысль наводят пары #UC START# - #UC END#, ну и группировка локальных процедур в #UC START# *..._var* - #UC END# *..._var*.
Или, реализация методов лежит отдельно от UML-диаграммы?
2. Относительно реализации контейнеров. Вы не смотрели SDL/DeCAL - библиотеке, о которой рассказывал (http://gurin.tomsknet.ru/delphidecal.html) Уважаемый мною Сергей Гурин?
"Вызывает уважение."
УдалитьСпасибо :-)
"Или, реализация методов лежит отдельно от UML-диаграммы?"
И да и нет :-) В данном случае "пары #UC START# - #UC END#" - это как раз "маркеры" - "вот сюда впишите код".
"Относительно реализации контейнеров. Вы не смотрели SDL/DeCAL"
УдалитьСтыдно признаться. Но. Я о ней - не знал.
Посмотрю внимательно.
Но вот это мне знакомо:
TObject0 = class
public
I: Integer;
constructor Create(aI: Integer);
{$IFDEF TTUSEPOOL}
class function NewInstance : TObject; override;
procedure FreeInstance; override;
{$ENDIF}
end;
TTUSEPOOL !!!!!
-- я САМ такое тоже придумал :-)
«В данном случае "пары #UC START# - #UC END#" - это как раз "маркеры" - "вот сюда впишите код".»
Удалить-- Таким образом, из UML генерируется структура класса, GUID в #UC START# *...*, как я понимаю, идентификатор метода в UML-редакторе? Вероятно, назначается автоматически...
Тэги #UC START# - #UC END# используются при генерации кода из UML-модели при её (модели) изменении для того, чтобы не потерять существующую реализацию. Я правильно понимаю?
«TTUSEPOOL !!!!!
-- я САМ такое тоже придумал :-)»
-- Да, там есть условно-поключаемая реализация хранения служебных структур в своём собственном пуле RAM. Мы отключаем - FastMM работает достаточно эффективно...
Кстати, есть порт DeCAL к XE-версиям Delphi.
"как я понимаю, идентификатор метода в UML-редакторе? Вероятно, назначается автоматически..."
УдалитьИменно. Автоматически.
"Тэги #UC START# - #UC END# используются при генерации кода из UML-модели при её (модели) изменении для того, чтобы не потерять существующую реализацию. Я правильно понимаю?"
Всё правильно.
Одна ремарка. Бывает (если это описано в шаблоне генерации), что код - тоже "сам" генерируется. Тогда как раз он тегами - не окружается.
"Да, там есть условно-поключаемая реализация хранения служебных структур в своём собственном пуле RAM"
Знакомо :-)
"Мы отключаем - FastMM работает достаточно эффективно..."
Я знаю :-) FastMM - хорош.
"Кстати, есть порт DeCAL к XE-версиям Delphi."
Спасибо. Обязательно посмотрю. А что там с 64-битами? Не знаете случайно?
NameRec:
Удалить«Одна ремарка. Бывает (если это описано в шаблоне генерации), что код - тоже "сам" генерируется. Тогда как раз он тегами - не окружается.»
-- На шаблоны я обратил внимание.
Как я понимаю, это ваш (Вашей организации) собственный DSL.
Пока могу только догадываться, в каких случаях требуются шаблоны и чем мотивировано их появление. Вероятно, это вызвано желанием определить специальные способы трансляции отношений (зависимость, агрегирование) для разных UML-диаграм. Надеюсь, Вы найдёте время раскрыть эту тему.
«"Да, там есть условно-поключаемая реализация хранения служебных структур в своём собственном пуле RAM"
Знакомо :-)»
-- Да, такое решение относительно часто встречается. Другое дело, я никогда не находил в нём особенной пользы, поскольку работать оно начинает на большом количестве данных, т.е. в случаях, когда уже уместно применить простую СУБД (например, SQLite).
«"Мы отключаем - FastMM работает достаточно эффективно..."
Я знаю :-) FastMM - хорош.»
-- Он кстати, доступен со времён Delphi 7, если не ошибаюсь...
«"Кстати, есть порт DeCAL к XE-версиям Delphi."
Спасибо. Обязательно посмотрю. А что там с 64-битами? Не знаете случайно?»
-- Здесь - не уверен. Хотя модуль небольшой, "хоккея" там практически нет :-) Это не какой-нибудь code injection :-)
Я не думаю, что XE-реализацию будет сложно портировать на 64-разряда, хотя и утверждать не возьмусь, поскольку соответствующего опыта не имею...
"Как я понимаю, это ваш (Вашей организации) собственный DSL.
УдалитьПока могу только догадываться, в каких случаях требуются шаблоны и чем мотивировано их появление. Вероятно, это вызвано желанием определить специальные способы трансляции отношений (зависимость, агрегирование) для разных UML-диаграм. Надеюсь, Вы найдёте время раскрыть эту тему."
Именно так. Надеюсь - найду.
"Да, такое решение относительно часто встречается. Другое дело, я никогда не находил в нём особенной пользы, поскольку работать оно начинает на большом количестве данных, т.е. в случаях, когда уже уместно применить простую СУБД (например, SQLite)."
У меня была реальная необходимость.
"Он кстати, доступен со времён Delphi 7, если не ошибаюсь..."
Тогда я его к сожалению не использовал "промышленно".
"Здесь - не уверен. Хотя модуль небольшой, "хоккея" там практически нет :-) Это не какой-нибудь code injection :-)
Я не думаю, что XE-реализацию будет сложно портировать на 64-разряда, хотя и утверждать не возьмусь, поскольку соответствующего опыта не имею..."
Я вот как раз и надеюсь на "отсутствие хоккея" и что всё будет хорошо. Другое дело, что у меня СОБСТВЕННАЯ инфраструктура (контейнеров в частности) - посему не факт, что удастся быстро и безболезненно "скрестить ежа с ужом".
"собственный DSL"
УдалитьПока одно могу сказать, что его можно править в процессе редактирования самой прикладной модели. Через UML.
Заводить новые стереотипы и отношения между ними. Видоизменять шаблоны генерации.
Т.е. DSL - "растёт" вместе с прикладными проектами.
Т.е. "по ходу пьесы" - можно выделять "шаблонные решения". Что я активно и делаю.
"Это не какой-нибудь code injection"
УдалитьВы про "вызов локальных функций"? :-)
NameRec:
Удалить«"...Надеюсь, Вы найдёте время раскрыть эту тему."
Именно так. Надеюсь - найду.»
-- Было бы очень интересно.
Хотелось бы также услышать от Вас причины, приведшие к необходимости разработки собственного DSL для кодогенерации. Этот DSL показался мне излишне криптованым, понятно, что XML"многословен", но IMHO он и знаком большему количеству людей (что снижает "порог входа") и, на мой взгляд, несколько более структурирован.
Кроме того, в случае, можно было бы попробовать некий "симбиоз" XML-описания с LUA/Python императивом.
Возможно, это всё выглядело бы не столь кратко, но возможно, чуть более понятно.
Хотя, говорить это меня подталкивает, скорее, личный опыт, нежели точное осознание задач, решаемых кодогенерацией.
На эту тему хотелось бы получить Ваши комментарии.
«"Да, такое решение относительно часто встречается. Другое дело, я никогда не находил в нём особенной пользы, поскольку работать оно начинает на большом количестве данных, т.е. в случаях, когда уже уместно применить простую СУБД (например, SQLite)."
У меня была реальная необходимость.»
-- Если не секрет, поделитесь хотя бы типажом задачи.
Интересно уловить ситуации, когда потребуются, скажем, массивы (или списки) долиной порядка 100 000 элементов.
«Другое дело, что у меня СОБСТВЕННАЯ инфраструктура (контейнеров в частности) - посему не факт, что удастся быстро и безболезненно "скрестить ежа с ужом".»
-- Это-то как раз, понятно :-)
«"собственный DSL"
Пока одно могу сказать, что его можно править в процессе редактирования самой прикладной модели. Через UML.»
-- Вы говорите о шаблонах, которые используются для кодогенерации и обеспечивают раскрытие специфики конкретной диаграммы? Иля я Вас неправильно понял?
«Заводить новые стереотипы и отношения между ними. Видоизменять шаблоны генерации.»
-- Тема стереотипов и их отображения в код, IMHO самая существенная часть Вашей методологии. Было бы очень интересно, если бы Вы осветили эту тему.
«Т.е. DSL - "растёт" вместе с прикладными проектами.»
-- Вероятно, под DSL Вы понимаете сейчас чуть ли не саму совокупность UML-диаграм, описывающих конкретный проект. Иля я понял Вас неверно?
«Т.е. "по ходу пьесы" - можно выделять "шаблонные решения". Что я активно и делаю.»
-- Не ошибусь ли я, если предположу, что:
1. "Шаблонные решения" привязаны к конкретной диаграмме или, возможно, к семейству диаграмм?
2. Необходимость в выделении "шаблонных решений" вызывается потребностью конкретизировать отношения между элементами диаграмм? Если так, то какие отношения Вы находите необходимым специализировать шаблонами?
«"Это не какой-нибудь code injection"
Вы про "вызов локальных функций"? :-)»
-- Нет :-)
Про code injection я упомянул как о некоей разновидности "шаманства", грозящей сложностями с адаптацией при смене версии Delphi или, уж тем более, разрядности платформы.
К распространённому (в своё время) примеру такого "шаманства" я отношу реализацию итераторов в Turbo Vision, от которой нам проще было отказаться, чем адаптировать её для 32-разрядного Delphi.
""Шаблонные решения" привязаны к конкретной диаграмме или, возможно, к семейству диаграмм?"
УдалитьШаблонные решения привязываются к стереотипу.
"примеру такого "шаманства" я отношу реализацию итераторов в Turbo Vision"
ОтветитьУдалитьhttp://18delphi.blogspot.ru/2013/03/blog-post_5929.html
;-)
NameRec: Разумеется, моё упоминание итераторов Turbo Vision было неслучайным ;-)
Удалить