Модель наилегчайшего веса

Software design pattern for objects
Скриншот пакета Writer LibreOffice.
Текстовые редакторы, такие как LibreOffice Writer , часто используют шаблон «Приспособленец».

В компьютерном программировании шаблон проектирования программного обеспечения «приспособленец» относится к объекту , который минимизирует использование памяти , разделяя некоторые из своих данных с другими подобными объектами. Шаблон проектирования «приспособленец» является одним из двадцати трех известных шаблонов проектирования GoF . [1] Эти шаблоны способствуют гибкому объектно-ориентированному проектированию программного обеспечения, которое проще внедрять, изменять, тестировать и повторно использовать.

В других контекстах идея совместного использования структур данных называется хэш-консингом .

Термин был впервые введен в обиход, а идея широко исследована Полом Колдером и Марком Линтоном в 1990 году [2] для эффективной обработки информации о глифах в редакторе документов WYSIWYG . [3] Однако подобные методы уже использовались в других системах еще в 1988 году. [4]

Обзор

Шаблон flyweight полезен при работе с большим количеством объектов, которые совместно используют простые повторяющиеся элементы, которые использовали бы большой объем памяти, если бы были встроены по отдельности. Обычно общие данные хранятся во внешних структурах данных и временно передаются объектам, когда они используются.

Классическим примером являются структуры данных, используемые для представления символов в текстовом процессоре . Наивно, каждый символ в документе может иметь объект глифа , содержащий его контур шрифта, метрики шрифта и другие данные форматирования. Однако это будет использовать сотни или тысячи байт памяти для каждого символа. Вместо этого каждый символ может иметь ссылку на объект глифа, общий для каждого экземпляра одного и того же символа в документе. Таким образом, внутри необходимо хранить только положение каждого символа.

В результате объекты-легковесы могут: [5]

  • хранить внутреннее состояние, которое является инвариантным, независящим от контекста и разделяемым (например, код символа «A» в заданном наборе символов)
  • предоставить интерфейс для передачи внешнего состояния, которое является вариантным, контекстно-зависимым и не может использоваться совместно (например, позиция символа «А» в текстовом документе)

Клиенты могут повторно использовать Flyweightобъекты и передавать внешнее состояние по мере необходимости, что сокращает количество физически создаваемых объектов.

Структура

Пример класса UML и диаграммы последовательности для шаблона проектирования Flyweight. [6]

Приведенная выше диаграмма классов UML показывает:

  • класс Client, который использует шаблон «легковеса»
  • класс FlyweightFactory, который создает и разделяет Flyweightобъекты
  • интерфейс , Flyweight который принимает внешнее состояние и выполняет операцию
  • класс Flyweight1, который реализует Flyweightи хранит внутреннее состояние

Диаграмма последовательности показывает следующие взаимодействия во время выполнения :

  1. Объект Clientвызывает getFlyweight(key)метод FlyweightFactory, который возвращает Flyweight1объект.
  2. После вызова operation(extrinsicState)возвращенного Flyweight1объекта Clientснова getFlyweight(key)вызывается FlyweightFactory.
  3. Возвращает FlyweightFactoryуже существующий Flyweight1объект.

Подробности реализации

Существует несколько способов реализации шаблона приспособленца. Одним из примеров является изменчивость: могут ли изменяться объекты, хранящие внешнее состояние приспособленца.

Неизменяемые объекты легко совместно используются, но требуют создания новых внешних объектов всякий раз, когда происходит изменение состояния. Напротив, изменяемые объекты могут совместно использовать состояние. Изменяемость позволяет улучшить повторное использование объектов посредством кэширования и повторной инициализации старых, неиспользуемых объектов. Совместное использование обычно нецелесообразно, когда состояние сильно изменчиво.

Другие основные проблемы включают извлечение (как конечный клиент получает доступ к приспособленцу), кэширование и параллелизм .

Извлечение

Интерфейс фабрики для создания или повторного использования объектов-приспособленцев часто является фасадом для сложной базовой системы. Например, интерфейс фабрики обычно реализуется как синглтон для предоставления глобального доступа для создания приспособленцев.

В общем случае алгоритм поиска начинается с запроса нового объекта через интерфейс фабрики.

Запрос обычно пересылается в соответствующий кэш в зависимости от типа объекта. Если запрос выполнен объектом в кэше, он может быть повторно инициализирован и возвращен. В противном случае создается новый объект. Если объект разделен на несколько внешних подкомпонентов, они будут объединены перед возвратом объекта.

Кэширование

Существует два способа кэширования объектов-приспособленцев: поддерживаемые и неподдерживаемые кэши.

Объекты с сильно изменчивым состоянием могут кэшироваться с помощью структуры FIFO . Эта структура сохраняет неиспользуемые объекты в кэше, без необходимости поиска в кэше.

Напротив, необслуживаемые кэши имеют меньшие начальные накладные расходы: объекты для кэшей инициализируются оптом во время компиляции или запуска. После того, как объекты заполняют кэш, алгоритм извлечения объектов может иметь больше связанных накладных расходов, чем операции push/pop поддерживаемого кэша.

При извлечении внешних объектов с неизменяемым состоянием необходимо просто выполнить поиск в кэше объекта с желаемым состоянием. Если такой объект не найден, необходимо инициализировать объект с таким состоянием. При извлечении внешних объектов с изменяемым состоянием необходимо выполнить поиск в кэше неиспользуемого объекта для повторной инициализации, если используемый объект не найден. Если неиспользуемый объект отсутствует, необходимо создать новый объект и добавить его в кэш.

Отдельные кэши могут использоваться для каждого уникального подкласса внешнего объекта. Несколько кэшей могут быть оптимизированы отдельно, связывая уникальный алгоритм поиска с каждым кэшем. Эта система кэширования объектов может быть инкапсулирована с шаблоном цепочки ответственности , который способствует слабой связи между компонентами.

Параллелизм

Особое внимание следует уделить случаям, когда объекты-приспособленцы создаются в нескольких потоках. Если список значений конечен и известен заранее, приспособленцы могут быть созданы заранее и извлечены из контейнера в нескольких потоках без конкуренции. Если приспособленцы создаются в нескольких потоках, есть два варианта:

  1. Сделайте создание экземпляров-приспособленцев однопоточным, тем самым создавая конкуренцию и гарантируя, что каждому значению будет соответствовать один экземпляр.
  2. Разрешить параллельным потокам создавать несколько легковесных экземпляров, тем самым устраняя конкуренцию и допуская несколько экземпляров для одного значения.

Чтобы обеспечить безопасное совместное использование данных клиентами и потоками, объекты-приспособлены можно преобразовать в объекты с неизменяемыми значениями , где два экземпляра считаются равными, если их значения равны.

В этом примере из 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

<?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 );

Смотрите также

Ссылки

  1. ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Шаблоны проектирования: элементы повторно используемого объектно-ориентированного программного обеспечения. Эддисон Уэсли. стр. 195 и далее. ISBN 978-0-201-63361-0.{{cite book}}: CS1 maint: multiple names: authors list (link)
  2. ^ Гамма, Эрих ; Ричард Хелм ; Ральф Джонсон ; Джон Влиссидес (1995). Шаблоны проектирования: элементы повторно используемого объектно-ориентированного программного обеспечения . Addison-Wesley . стр. 205–206. ISBN 978-0-201-63361-0.
  3. ^ Колдер, Пол Р.; Линтон, Марк А. (октябрь 1990 г.). "Глифы: объекты-приспособления для пользовательских интерфейсов". Труды 3-го ежегодного симпозиума ACM SIGGRAPH по программному обеспечению и технологиям пользовательского интерфейса - UIST '90 . 3-й ежегодный симпозиум ACM SIGGRAPH по программному обеспечению и технологиям пользовательского интерфейса. Сноуберд, Юта, США. стр. 92–101. doi :10.1145/97924.97935. ISBN 0-89791-410-4.
  4. ^ Вайнанд, Андре; Гамма, Эрих; Марти, Рудольф (1988). ET++ — объектно-ориентированная платформа приложений на C++ . OOPSLA (Системы объектно-ориентированного программирования, языки и приложения). Сан-Диего, Калифорния, США. стр. 46–57. CiteSeerX 10.1.1.471.8796 . дои : 10.1145/62083.62089. ISBN  0-89791-284-5.
  5. ^ "Реализация шаблонов Flyweight в Java". Developer.com . 2019-01-28 . Получено 2021-06-12 .
  6. ^ "Шаблон проектирования Flyweight - Структура и сотрудничество". w3sDesign.com . Получено 2017-08-12 .
  7. ^ Билл Вагнер. "Записи - Справочник по C#". docs.microsoft.com . Получено 12.06.2021 .
Retrieved from "https://en.wikipedia.org/w/index.php?title=Flyweight_pattern&oldid=1250195786"