Templates/ru: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Templates/ru
| Old | New | Line | |
|---|---|---|---|
| 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).* |
