Sign in Join
Wiki copytemporary

Templates/ru: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Templates/ru

OldNewLine
1+## Вступление
2+
3+> **Note:** Шаблоны, представленные на этой странице, больше не нужны, потому что поддержка [[Generics]] в FPC полная и достаточно удобная для использования.
4+
5+*Templates* (шаблоны) - это простой механизм для решения проблемы написания дублирующего кода для реализации общего класса для конкретных типов в строго типизированном языке. Используется в основном для базовых (необъектных) типов; для объектов наследования от одного базового класса TObject обычно используется для построения общих классов. В продвинутых языках шаблоны заменяются нативно-языковой реализацией, так называемыми "дженериками". Собственная реализация дженериков в FPC 2.4.0 еще не является ни полной, ни практически используемой. Пока это не так, могут быть использованы шаблоны.
6+
7+**Плюсы:**
8+
9+- Безопасность типов - устраняет раздражение при типизации и ошибках, которые оно часто вызывает
10+- Лучшее обслуживание - универсальный код класса пишется только один раз и используется для всех специализированных классов
11+- Лучшее повторное использование кода - можно создать хороший набор повторно используемых классов-дженериков
12+- Лучшая эффективность, масштабируемость - типы классов-дженериков могут быть лучше приспособлены к конкретным потребностям. Например, на 16-битной машине можно использовать специализированный TList с 16-битным индексным типом SmallInt.
13+
14+**Минусы:**
15+
16+- Более сложны
17+- Большой код - каждый специализированный класс компилируется отдельно и создает дополнительный код
18+
19+## Использование
20+
21+Классическим примером базового класса-дженерика является список элементов. Free Pascal в настоящее время предлагает три способа использования списков:
22+
23+- **array of** - классическая структура динамического массива, которая может содержать динамические элементы одного типа. Элементы доступны по прямой адресации: SomeArray\[Index\]. Функции SetLength и Length используются для обработки размера.
24+- **TList** - объектно-ориентированный способ, которым класс обрабатывает все операции со списками. Реализация в LCL сохраняет совместимость с VCL, где TList содержит список указателей. Если требуется список другого типа элемента, необходимо скопировать и переписать полностью новый класс или использовать приведение типов в каждом месте, где есть ссылка на элемент списка. Типизация не эффективна, например, `Pointer(Byte)`, и небезопасна для типа: `Pointer(Int64)`. Индекс TList имеет тип Integer. Если требуется индекс `Int64` или `SmallInt`, класс необходимо скопировать и переписать.
25+- **TCollection** - это более общее и безопасное, но более сложное решение для хранения списка предметов. При таком подходе программист должен создать класс элементов, который наследуется от TCollectionItem, и установить для этого вновь созданного типа класса параметр конструктора TCollection.
26+- **native generics** - пока нет полной реализации. Отсутствует поддержка ссылки на общий тип, наследования, ограничений.
27+- **templates** - использует включение кода, чтобы обойти отсутствие нативной поддержки. Можно использовать наследование и ссылки на общие типы. Ограничения задаются операторами и другими функциями в общем классе.
28+
29+Шаблоны пытаются решить проблему, используя концепцию параметризованных включаемых файлов. Универсальный модуль записывается только один раз как шаблон с использованием универсальных (не указанных) типов в разделах интерфейса и реализации. Затем в процессе специализации используется шаблон класса, а общие параметры шаблона заменяются определенными типами для создания нового специализированного класса.
30+
31+*GenericList.inc:*
32+
33+``` pascal
34+{$IFDEF INTERFACE}
35+ // TGList<T> = class
36+ TGList = class
37+ Items: array of T;
38+ procedure Add(Item: T);
39+ end;
40+{$UNDEF INTERFACE}
41+{$ENDIF}
42+
43+{$IFDEF IMPLEMENTATION}
44+procedure TGList.Add(Item: T);
45+begin
46+ SetLength(Items, Length(Items) + 1);
47+ Items[Length(Items) - 1] := Item;
48+end;
49+{$UNDEF IMPLEMENTATION}
50+{$ENDIF}
51+```
52+
53+По мере специализации ранее созданные файлы шаблонов включаются в новый модуль.
54+
55+*ListInteger.pas:*
56+
57+``` pascal
58+unit ListInteger;
59+{$mode ObjFPC}
60+interface
61+
62+uses
63+ Classes;
64+
65+type
66+ T = Integer; // T указывается для какого-то точного типа
67+{$DEFINE INTERFACE}
68+{$INCLUDE 'GenericList.inc'}
69+
70+type
71+ // TListInteger<Integer> = class
72+ TListInteger = class(TGList)
73+ // Дополнительные поля и методы могут быть добавлены здесь
74+ end;
75+
76+implementation
77+
78+{$DEFINE IMPLEMENTATION}
79+{$INCLUDE 'GenericList.inc'}
80+
81+end.
82+```
83+
84+Наконец, у нас есть новый специализированный модуль, названный ListInteger, и мы можем использовать наш новый специализированный тип в некотором коде.
85+
86+``` pascal
87+program GenericTest;
88+{$mode ObjFPC}
89+uses
90+ ListInteger;
91+var
92+ List: TListInteger;
93+begin
94+ try
95+ List := TListInteger.Create;
96+ List.Add(1);
97+ List.Add(2);
98+ List.Add(3);
99+ finally
100+ List.Free;
101+ end;
102+end.
103+```
104+
105+### Наследование
106+
107+Классы-дженерики могут наследоваться друг от друга так, как это делают классы-недженерики. Реализация немного сложна, так как для каждого модуля может быть определен только один специализированный тип определенного класса. Чтобы иметь возможность указать класс-дженерик, который наследуется от другого класса-дженерика, следует сохранить некоторую схему именования.
108+
109+Предположим, что мы хотим создать расширенный список дженериков TAdvancedList, у которого есть поле Capacity.
110+
111+Определение базового класса-дженерика:
112+
113+*GenericList.inc:*
114+
115+``` pascal
116+{$IFDEF INTERFACE}
117+ // TGList<TListIndex, TListItem> = class
118+ TGList = class
119+ Items: array[TListIndex] of TListItem;
120+ procedure Add(Item: TListItem);
121+ end;
122+{$UNDEF INTERFACE}
123+{$ENDIF}
124+
125+{$IFDEF IMPLEMENTATION}
126+procedure TGList.Add(Item: TListItem);
127+begin
128+ SetLength(Items, Length(Items) + 1);
129+ Items[Length(Items) - 1] := Item;
130+end;
131+{$UNDEF IMPLEMENTATION}
132+{$ENDIF}
133+```
134+
135+Определение для расширенного класса-дженерика:
136+
137+*GenericAdvancedList.inc:*
138+
139+``` pascal
140+{$IFDEF INTERFACE}
141+ TListIndex = TAdvancedListIndex;
142+ TListItem = TAdvancedListItem;
143+{$DEFINE INTERFACE}
144+{$INCLUDE 'GenericList.inc'}
145+ // TGAdvancedList<TAdvancedListIndex, TAdvancedListItem> = class(TGList)
146+ TGAdvancedList = class
147+ Capacity: TAdvancedListIndex;
148+ end;
149+{$UNDEF INTERFACE}
150+{$ENDIF}
151+
152+{$IFDEF IMPLEMENTATION}
153+{$INCLUDE 'GenericList.inc'}
154+{$UNDEF IMPLEMENTATION}
155+{$ENDIF}
156+```
157+
158+Пример без некоторой специализации:
159+
160+*AdvancedListInteger.pas:*
161+
162+``` pascal
163+unit AdvancedListInteger;
164+
165+interface
166+
167+uses
168+ Classes;
169+
170+type
171+ TAdvancedListIndex = Integer; // указан некоторый точный тип
172+ TAdvancedListItem = Integer; // указан некоторый точный тип
173+{$DEFINE INTERFACE}
174+{$INCLUDE 'GenericAdvancedList.inc'}
175+
176+type
177+ // TAdvancedListInteger<Integer, Integer> = class
178+ TAdvancedListInteger = class(TGAdvancedList)
179+ // Дополнительные поля и методы могут быть добавлены здесь
180+ end;
181+
182+implementation
183+
184+{$DEFINE IMPLEMENTATION}
185+{$INCLUDE 'GenericAdvancedList.inc'}
186+
187+end.
188+```
189+
190+Теперь у нас есть специализированный класс из класса-дженерика, который наследуется от другого класса-дженерика.
191+
192+### Ограничения
193+
194+Хорошо иметь класс, который, например, работает как контейнер для любого другого типа. Класс-контейнер не должен ничего знать о содержащемся классе. Но некоторые классы могут извлечь пользу из знания того, что можно сделать с помощью закрытого класса. В реализации нативных дженериков есть возможность ограничить группу типов, которые можно использовать в качестве параметров типа класса-дженерика. В шаблонах такой функции нет, но ограничения выполняются просто путем использования типа-дженерика в классе-дженерике. Если тип-дженерик используется для сложения и вычитания, то могут использоваться только те типы, которые поддерживают эти операции. Строка также может быть объединена с помощью оператора плюс. Но если используется умножение, то допускаются только числовые типы. Если тип-джернерик используется в качестве индекса массива, то могут использоваться только порядковые типы. Если некоторые методы, например, `Free`, используется над типом-дженериком, тогда ограничение включает все классы. Поэтому в шаблонах операции, выполняемые с дженерик-типами, ограничивают набор используемых типов для специализации.
195+
196+**Теоретически ограничиваемые группы:**
197+
198+- порядковые типы
199+- типы с плавающей точкой
200+- записи (records)
201+- массивы
202+- наборы данных (sets)
203+- интерфейсы
204+- процедуры, функции, конструкторы
205+- классы со всеми потомками
206+
207+## Классы-дженерики
208+
209+Есть много классов, которые могут извлечь выгоду из существования дженериков:
210+
211+- TList - упорядоченный список элементов, методы Add, Delete, Insert, Move, Exchange, Clear, Sort
212+- TDictionary - список пар ключ-значение
213+- TPair - Ключ и значение для использования в TDictionary
214+- TStack - Структура [LIFO](https://ru.wikipedia.org/wiki/FIFO_%D0%B8_LIFO), Push и Pop (реализация стека (последний пришел, первый вышел))
215+- TQueue - Структура [FIFO](https://ru.wikipedia.org/wiki/%D0%9E%D1%87%D0%B5%D1%80%D0%B5%D0%B4%D1%8C_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)), Enqueue и Dequeue (реализация очереди (первый пришел, первый вышел))
216+- TRange или TInterval - структура двух значений, Расстояние
217+- TSet - набор элементов, Add, Remove, Intersection, Complement, Union, Product
218+- TStream - разделяется на TInputStream и TOutputStream
219+- TTree - иерархическая структура с узлами
220+- TMatrix - многомерный список
221+- TBitmap - расширенный TMatrix с графически ориентированными методами
222+- TGraph
223+- TPoint - может быть 1D, 2D, 3D или многомерным
224+- TVector
225+- TComplexNumber
226+
227+There are some interesting specialized types which can replace non-generic types: Есть несколько интересных специализированных типов, которые могут заменить типы-не-дженерики:
228+
229+| Specialized generic class | Similar data type |
230+|----------------------------------------|------------------------|
231+| TList\<Char\> | string |
232+| TList\<WideChar\> | WideString |
233+| TList\<Byte\> | array of Byte |
234+| TList\<string\> | Classes.TStrings |
235+| TList\<Pointer\> | Classes.TList |
236+| TList\<Boolean\> | Classes.TBits |
237+| TList\<TObject\> | Contnrs.TObjectList |
238+| TList\<TComponent\> | Contnrs.TComponentList |
239+| TList\<TClass\> | Contnrs.TClassList |
240+| TStack\<Pointer\> | Contnrs.TStack |
241+| TStack\<TObject\> | Contnrs.TObjectStack |
242+| TQueue\<Pointer\> | Contnrs.TQueue |
243+| TQueue\<TObject\> | Contnrs.TObjectQueue |
244+| TStream\<Byte\> | Classes.TMemoryStream |
245+| TDictionary\<TPair\<string, string\>\> | Classes.TStringList |
246+| TList\<TMethod\> | LCLProc.TMethodList |
247+| TTree\<TTreeNode\> | ComCtrls.TTreeView |
248+| TPoint\<Integer\> | Classes.TPoint |
249+| TPoint\<SmallInt\> | Classes.TSmallPoint |
250+| TRectangle\<Integer\> | Classes.TRect |
251+
252+## Существующие библиотеки дженериков
253+
254+Каждый программист может создать собственную библиотеку универсальных классов.
255+
256+**Некоторые готовые к использованию классы:**
257+
258+- **TemplateGenerics** - Пакет Lazarus из нескольких экспериментальных классов-дженериков. В дополнение к параметризованному типу значения списка он также предлагает параметризованный тип индекса. Последний пакет можно скачать из репозитория SVN с помощью клиента SVN.
259+
260+ svn co http://svn.zdechov.net/svn/PascalClassLibrary/Generics/TemplateGenerics/ TemplateGenerics
261+
262+- **rtl-generics package** - разработка Maciej Izak, которая входит в состав исходного кода fpc, начиная с версии 3.1.1 +, и находится в корневом каталоге компилятора в директории *\packages\rtl-generics*. Актуальную версию библиотеки можно найти на [github'е](https://github.com/maciej-izak/generics.collections)
263+
264+## См. также
265+
266+- [[Generics/ru|Generics]]
267+
268+## Внешние ссылки
269+
270+- [Object Pascal (Delphi) Templates](http://www.dummzeuch.de/delphi/object_pascal_templates/english.html)
271+- [Templates in Object Pascal](http://edn.embarcadero.com/article/27603)
272+- [Template (programming)](http://en.wikipedia.org/wiki/Template_%28programming%29) - general description and priciples
273+- [Generics with Delphi 2009 Win32](http://sjrd.developpez.com/delphi/tutoriel/generics/)
274+
275+---
276+*From the Free Pascal wiki, page "Templates/ru", as it stood on 2020-06-04 after a change by Zoltanleo, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history