This article includes a list of general references, but it lacks sufficient corresponding inline citations. (May 2008) |
В компьютерном программировании шаблон проектирования программного обеспечения «приспособленец» относится к объекту , который минимизирует использование памяти , разделяя некоторые из своих данных с другими подобными объектами. Шаблон проектирования «приспособленец» является одним из двадцати трех известных шаблонов проектирования GoF . [1] Эти шаблоны способствуют гибкому объектно-ориентированному проектированию программного обеспечения, которое проще внедрять, изменять, тестировать и повторно использовать.
В других контекстах идея совместного использования структур данных называется хэш-консингом .
Термин был впервые введен в обиход, а идея широко исследована Полом Колдером и Марком Линтоном в 1990 году [2] для эффективной обработки информации о глифах в редакторе документов WYSIWYG . [3] Однако подобные методы уже использовались в других системах еще в 1988 году. [4]
Шаблон flyweight полезен при работе с большим количеством объектов, которые совместно используют простые повторяющиеся элементы, которые использовали бы большой объем памяти, если бы были встроены по отдельности. Обычно общие данные хранятся во внешних структурах данных и временно передаются объектам, когда они используются.
Классическим примером являются структуры данных, используемые для представления символов в текстовом процессоре . Наивно, каждый символ в документе может иметь объект глифа , содержащий его контур шрифта, метрики шрифта и другие данные форматирования. Однако это будет использовать сотни или тысячи байт памяти для каждого символа. Вместо этого каждый символ может иметь ссылку на объект глифа, общий для каждого экземпляра одного и того же символа в документе. Таким образом, внутри необходимо хранить только положение каждого символа.
В результате объекты-легковесы могут: [5]
Клиенты могут повторно использовать Flyweight
объекты и передавать внешнее состояние по мере необходимости, что сокращает количество физически создаваемых объектов.
Приведенная выше диаграмма классов UML показывает:
Client
, который использует шаблон «легковеса»FlyweightFactory
, который создает и разделяет Flyweight
объектыFlyweight
который принимает внешнее состояние и выполняет операциюFlyweight1
, который реализует Flyweight
и хранит внутреннее состояниеДиаграмма последовательности показывает следующие взаимодействия во время выполнения :
Client
вызывает getFlyweight(key)
метод FlyweightFactory
, который возвращает Flyweight1
объект.operation(extrinsicState)
возвращенного Flyweight1
объекта Client
снова getFlyweight(key)
вызывается FlyweightFactory
.FlyweightFactory
уже существующий Flyweight1
объект.Существует несколько способов реализации шаблона приспособленца. Одним из примеров является изменчивость: могут ли изменяться объекты, хранящие внешнее состояние приспособленца.
Неизменяемые объекты легко совместно используются, но требуют создания новых внешних объектов всякий раз, когда происходит изменение состояния. Напротив, изменяемые объекты могут совместно использовать состояние. Изменяемость позволяет улучшить повторное использование объектов посредством кэширования и повторной инициализации старых, неиспользуемых объектов. Совместное использование обычно нецелесообразно, когда состояние сильно изменчиво.
Другие основные проблемы включают извлечение (как конечный клиент получает доступ к приспособленцу), кэширование и параллелизм .
Интерфейс фабрики для создания или повторного использования объектов-приспособленцев часто является фасадом для сложной базовой системы. Например, интерфейс фабрики обычно реализуется как синглтон для предоставления глобального доступа для создания приспособленцев.
В общем случае алгоритм поиска начинается с запроса нового объекта через интерфейс фабрики.
Запрос обычно пересылается в соответствующий кэш в зависимости от типа объекта. Если запрос выполнен объектом в кэше, он может быть повторно инициализирован и возвращен. В противном случае создается новый объект. Если объект разделен на несколько внешних подкомпонентов, они будут объединены перед возвратом объекта.
Существует два способа кэширования объектов-приспособленцев: поддерживаемые и неподдерживаемые кэши.
Объекты с сильно изменчивым состоянием могут кэшироваться с помощью структуры FIFO . Эта структура сохраняет неиспользуемые объекты в кэше, без необходимости поиска в кэше.
Напротив, необслуживаемые кэши имеют меньшие начальные накладные расходы: объекты для кэшей инициализируются оптом во время компиляции или запуска. После того, как объекты заполняют кэш, алгоритм извлечения объектов может иметь больше связанных накладных расходов, чем операции push/pop поддерживаемого кэша.
При извлечении внешних объектов с неизменяемым состоянием необходимо просто выполнить поиск в кэше объекта с желаемым состоянием. Если такой объект не найден, необходимо инициализировать объект с таким состоянием. При извлечении внешних объектов с изменяемым состоянием необходимо выполнить поиск в кэше неиспользуемого объекта для повторной инициализации, если используемый объект не найден. Если неиспользуемый объект отсутствует, необходимо создать новый объект и добавить его в кэш.
Отдельные кэши могут использоваться для каждого уникального подкласса внешнего объекта. Несколько кэшей могут быть оптимизированы отдельно, связывая уникальный алгоритм поиска с каждым кэшем. Эта система кэширования объектов может быть инкапсулирована с шаблоном цепочки ответственности , который способствует слабой связи между компонентами.
Особое внимание следует уделить случаям, когда объекты-приспособленцы создаются в нескольких потоках. Если список значений конечен и известен заранее, приспособленцы могут быть созданы заранее и извлечены из контейнера в нескольких потоках без конкуренции. Если приспособленцы создаются в нескольких потоках, есть два варианта:
Чтобы обеспечить безопасное совместное использование данных клиентами и потоками, объекты-приспособлены можно преобразовать в объекты с неизменяемыми значениями , где два экземпляра считаются равными, если их значения равны.
В этом примере из C# 9 записи [7] используются для создания объекта-значения, представляющего вкусы кофе:
публичная запись CoffeeFlavours ( стручковый ароматизатор );
В этом примере каждый экземпляр класса MyObject
использует Pointer
класс для предоставления данных.
// Определяет объект Flyweight, который повторяет себя. public class Flyweight { public string Name { get ; set ; } public string Location { get ; set ; } public string Website { get ; set ; } public byte [] Logo { get ; set ; } } public static class Pointer { public static readonly Flyweight Company = new Flyweight { "Abc" , "XYZ" , "www.example.com" }; } public class MyObject { public string Name { get ; set ; } public string Company = > Pointer.Company.Name ; }
Библиотека стандартных шаблонов C++ предоставляет несколько контейнеров, которые позволяют сопоставлять уникальные объекты с ключом. Использование контейнеров помогает еще больше сократить использование памяти, устраняя необходимость создания временных объектов.
#include <iostream> #include <map> #include <string> // Экземпляры Tenant будут классом Flyweights Tenant { public : Tenant ( const std :: string & name = "" ) : m_name ( name ) {} std :: string name () const { return m_name ; } private : std :: string m_name ; }; // Реестр действует как фабрика и кэш для объектов-приспособленцев Tenant class Registry { public : Registry () : tenants () {} Арендатор & findByName ( const std :: string & name ) { if ( ! tenants.contains ( name )) { tenants [ name ] = Tenant { name } ; } return tenants [ name ]; } private : std :: map < std :: string , Tenant > tenants ; } ; // Квартира сопоставляет уникального арендатора с номером его комнаты. class Apartment { public : Apartment () : m_occupants (), m_registry () {} void addOccupant ( const std :: string & name , int room ) { m_occupants [ room ] = & m_registry.findByName ( name ) ; } void tenants () { for ( const auto & i : m_occupants ) { const int & room = i . first ; const auto & tenant = i . second ; std :: cout << tenant -> name () << " занимает комнату " << room << std :: endl ; } } private : std :: map < int , Tenant *> m_occupants ; Registry m_registry ; }; int main () { Квартира квартира ; квартира . addOccupant ( "Дэвид" , 1 ); квартира . addOccupant ( "Сара" , 3 ); квартира . addOccupant ( "Джордж" , 2 ); квартира . addOccupant ( "Лиза" , 12 ); квартира . addOccupant ( "Майкл" , 10 ); квартира . арендаторы (); вернуть 0 ; }
<?phpкласс CoffeeFlavour { частный статический массив $CACHE = []; частная функция __construct ( частная строка $name ) {} публичная статическая функция intern ( string $name ) : self { self :: $CACHE [ $name ] ??= new self ( $name ); return self :: $CACHE [ $name ]; } публичная статическая функция flavorsInCache () : int { return count ( self :: $CACHE ); } публичная функция __toString () : string { return $this -> name ; }}класс Заказать { частная функция __construct ( частный CoffeeFlavour $flavour , частный int $tableNumber ) {} публичная статическая функция create ( string $flavourName , int $tableNumber ) : self { $flavour = CoffeeFlavour :: intern ( $flavourName ); return new self ( $flavour , $tableNumber ); } public function __toString () : string { return "Подача { $this -> flavor } на стол { $this -> tableNumber } " ; } }класс Кофешоп { частный массив $orders = []; public function takeOrder ( string $flavour , int $tableNumber ) { $this -> orders [] = Order :: create ( $flavour , $tableNumber ); } публичная функция service () { print ( implode ( PHP_EOL , $ this -> orders ) .PHP_EOL ); } } $shop = new CoffeeShop (); $shop -> takeOrder ( "Капучино" , 2 ); $shop -> takeOrder ( "Фраппе" , 1 ); $shop -> takeOrder ( "Эспрессо" , 1 ); $shop - > takeOrder ( "Фраппе" , 897 ); $shop -> takeOrder ( "Капучино" , 97 ); $shop - > takeOrder ( "Фраппе" , 3 ); $shop -> takeOrder ( "Эспрессо" , 3 ); $shop -> takeOrder ( "Капучино" , 3 ); $shop -> takeOrder ( "Эспрессо" , 96 ); $shop -> takeOrder ( "Фраппе" , 552 ); $shop -> takeOrder ( "Капучино" , 121 ); $shop -> takeOrder ( "Эспрессо" , 121 ); $shop -> service (); print ( "Объекты CoffeeFlavor в кэше: " . CoffeeFlavour :: flavorsInCache () . PHP_EOL );
{{cite book}}
: CS1 maint: multiple names: authors list (link)