Модель наилегчайшего веса
![]() | Эта статья включает список общих ссылок , но в ней отсутствуют достаточные соответствующие встроенные цитаты . ( Май 2008 г. ) |

В компьютерном программировании шаблон «легковес» проектирования программного обеспечения относится к объекту , который минимизирует использование памяти за счет совместного использования некоторых своих данных с другими аналогичными объектами. Шаблон «Легкий вес» — один из двадцати трёх известных шаблонов проектирования GoF . [1] Эти шаблоны способствуют гибкому объектно-ориентированному проектированию программного обеспечения, которое легче внедрять, изменять, тестировать и повторно использовать.
В других контекстах идея совместного использования структур данных называется хешированием .
Этот термин был впервые придуман, а идея широко исследована Полом Колдером и Марком Линтоном в 1990 году. [2] для эффективной обработки информации о глифах в редакторе документов WYSIWYG . [3] Однако подобные методы уже использовались в других системах еще в 1988 году. [4]
Обзор
[ редактировать ]Шаблон «приспособленец» полезен при работе с большим количеством объектов, которые имеют общие простые повторяющиеся элементы, которые использовали бы большой объем памяти, если бы они были встроены индивидуально. Обычно общие данные хранятся во внешних структурах данных и временно передаются объектам, когда они используются.
Классическим примером являются структуры данных, используемые для представления символов в текстовом процессоре . Проще говоря, каждый символ в документе может иметь объект- глиф , содержащий контур шрифта, метрики шрифта и другие данные форматирования. Однако при этом для каждого символа будут использоваться сотни или тысячи байт памяти. Вместо этого каждый символ может иметь ссылку на объект-глиф, общий для каждого экземпляра одного и того же символа в документе. Таким образом, внутри необходимо хранить только позицию каждого символа.
В результате легковесные объекты могут: [5]
- хранить внутреннее состояние, которое является инвариантным, контекстно-независимым и доступным для совместного использования (например, код символа «A» в заданном наборе символов)
- предоставить интерфейс для передачи внешнего состояния, которое является вариантом, зависит от контекста и не может быть общим (например, положение символа «A» в текстовом документе)
Клиенты могут повторно использовать Flyweight
объекты и при необходимости переходить во внешнее состояние, уменьшая количество физически созданных объектов.
Структура
[ редактировать ]
На приведенной выше UML диаграмме классов показано:
- тот
Client
класс, который использует шаблон легковеса - тот
FlyweightFactory
класс, который создает и разделяетFlyweight
объекты - тот
Flyweight
интерфейс , который принимает внешнее состояние и выполняет операцию - тот
Flyweight1
класс, который реализуетFlyweight
и сохраняет внутреннее состояние
Диаграмма последовательности показывает следующие взаимодействия во время выполнения :
- The
Client
вызовы объектовgetFlyweight(key)
наFlyweightFactory
, который возвращаетFlyweight1
объект. - После звонка
operation(extrinsicState)
на возвращенииFlyweight1
объект,Client
снова звонитgetFlyweight(key)
наFlyweightFactory
. - The
FlyweightFactory
возвращает уже существующийFlyweight1
объект.
Детали реализации
[ редактировать ]Существует несколько способов реализации шаблона наилегчайшего веса. Одним из примеров является изменчивость: могут ли измениться объекты, хранящие внешнее состояние легковеса.
Неизменяемые объекты легко доступны для совместного использования, но при каждом изменении состояния требуется создавать новые внешние объекты. Напротив, изменяемые объекты могут иметь общее состояние. Изменяемость позволяет лучше повторно использовать объекты за счет кэширования и повторной инициализации старых, неиспользуемых объектов. Совместное использование обычно нецелесообразно, когда состояние сильно варьируется.
Другие основные проблемы включают извлечение (как конечный клиент получает доступ к «легкому весу»), кэширование и параллелизм .
Поиск
[ редактировать ]Фабричный фасадом интерфейс для создания или повторного использования легковесных объектов часто является сложной базовой системы. Например, интерфейс фабрики обычно реализуется как синглтон , обеспечивающий глобальный доступ для создания приспособилок.
Вообще говоря, алгоритм поиска начинается с запроса нового объекта через интерфейс фабрики.
Запрос обычно перенаправляется в соответствующий кеш в зависимости от типа объекта. Если запрос выполнен объектом в кэше, он может быть повторно инициализирован и возвращен. В противном случае создается экземпляр нового объекта. Если объект разделен на несколько внешних подкомпонентов, они будут объединены перед возвратом объекта.
Кэширование
[ редактировать ]Существует два способа кэширования объектов-легковесов: поддерживаемый и необслуживаемый.
Объекты с сильно изменчивым состоянием можно кэшировать с помощью структуры FIFO . Эта структура сохраняет неиспользуемые объекты в кеше без необходимости поиска в кеше.
Напротив, необслуживаемые кеши требуют меньше предварительных затрат: объекты для кешей инициализируются массово во время компиляции или запуска. Как только объекты заполняют кэш, алгоритм извлечения объектов может иметь больше накладных расходов, чем операции push/pop поддерживаемого кэша.
При извлечении внешних объектов с неизменяемым состоянием нужно просто найти в кеше объект с желаемым состоянием. Если такой объект не найден, необходимо инициализировать объект с таким состоянием. При извлечении внешних объектов с изменяемым состоянием в кеше необходимо искать неиспользуемый объект для повторной инициализации, если используемый объект не найден. Если неиспользуемого объекта нет, необходимо создать экземпляр нового объекта и добавить его в кэш.
Отдельные кэши могут использоваться для каждого уникального подкласса внешнего объекта. Несколько кешей можно оптимизировать отдельно, связывая с каждым кешем уникальный алгоритм поиска. Эту систему кэширования объектов можно инкапсулировать с помощью шаблона цепочки ответственности , который способствует слабой связи между компонентами.
Параллелизм
[ редактировать ]Особое внимание следует принимать во внимание, когда объекты-легковесы создаются в нескольких потоках. Если список значений конечен и известен заранее, экземпляры легковесов могут быть созданы заранее и извлечены из контейнера в нескольких потоках без каких-либо конфликтов. Если легковесы создаются в нескольких потоках, есть два варианта:
- Сделайте создание легковесного экземпляра однопоточным, что приведет к возникновению конфликтов и обеспечению одного экземпляра на каждое значение.
- Разрешите параллельным потокам создавать несколько экземпляров-легковесов, тем самым устраняя конфликты и позволяя использовать несколько экземпляров для каждого значения.
Чтобы обеспечить безопасное совместное использование между клиентами и потоками, объекты-легковесы можно превратить в объекты неизменяемых значений , где два экземпляра считаются равными, если их значения равны.
В этом примере из C# 9 используются записи [7] чтобы создать объект значения, представляющий вкусы кофе:
public record CoffeeFlavours(string flavour);
Пример на С#
[ редактировать ]В этом примере каждый экземпляр MyObject
класс использует Pointer
класс для предоставления данных.
// Defines Flyweight object that repeats itself.
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>
// Instances of Tenant will be the Flyweights
class Tenant {
public:
Tenant(const std::string& name = "") : m_name(name) {}
std::string name() const {
return m_name;
}
private:
std::string m_name;
};
// Registry acts as a factory and cache for Tenant flyweight objects
class Registry {
public:
Registry() : tenants() {}
Tenant& findByName(const std::string& name) {
if (!tenants.contains(name)) {
tenants[name] = Tenant{name};
}
return tenants[name];
}
private:
std::map<std::string, Tenant> tenants;
};
// Apartment maps a unique tenant to their room number.
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() << " occupies room " << room << std::endl;
}
}
private:
std::map<int, Tenant*> m_occupants;
Registry m_registry;
};
int main() {
Apartment apartment;
apartment.addOccupant("David", 1);
apartment.addOccupant("Sarah", 3);
apartment.addOccupant("George", 2);
apartment.addOccupant("Lisa", 12);
apartment.addOccupant("Michael", 10);
apartment.tenants();
return 0;
}
Пример на PHP
[ редактировать ]<?php
class CoffeeFlavour {
private static array $CACHE = [];
private function __construct(private string $name) {}
public static function intern(string $name): self {
self::$CACHE[$name] ??= new self($name);
return self::$CACHE[$name];
}
public static function flavoursInCache(): int {
return count(self::$CACHE);
}
public function __toString(): string {
return $this->name;
}
}
class Order {
private function __construct(
private CoffeeFlavour $flavour,
private int $tableNumber
) {}
public static function create(string $flavourName, int $tableNumber): self {
$flavour = CoffeeFlavour::intern($flavourName);
return new self($flavour, $tableNumber);
}
public function __toString(): string {
return "Serving {$this->flavour} to table {$this->tableNumber}";
}
}
class CoffeeShop {
private array $orders = [];
public function takeOrder(string $flavour, int $tableNumber) {
$this->orders[] = Order::create($flavour, $tableNumber);
}
public function service() {
print(implode(PHP_EOL, $this->orders).PHP_EOL);
}
}
$shop = new CoffeeShop();
$shop->takeOrder("Cappuccino", 2);
$shop->takeOrder("Frappe", 1);
$shop->takeOrder("Espresso", 1);
$shop->takeOrder("Frappe", 897);
$shop->takeOrder("Cappuccino", 97);
$shop->takeOrder("Frappe", 3);
$shop->takeOrder("Espresso", 3);
$shop->takeOrder("Cappuccino", 3);
$shop->takeOrder("Espresso", 96);
$shop->takeOrder("Frappe", 552);
$shop->takeOrder("Cappuccino", 121);
$shop->takeOrder("Espresso", 121);
$shop->service();
print("CoffeeFlavor objects in cache: ".CoffeeFlavour::flavoursInCache().PHP_EOL);
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Эддисон Уэсли. стр. 195 и далее . ISBN 978-0-201-63361-0 .
{{cite book}}
: CS1 maint: несколько имен: список авторов ( ссылка ) - ^ Гамма, Эрих ; Ричард Хелм ; Ральф Джонсон ; Джон Влиссидес (1995). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Аддисон-Уэсли . стр. 205–206 . ISBN 978-0-201-63361-0 .
- ^ Колдер, Пол Р.; Линтон, Марк А. (октябрь 1990 г.). «Глифы: легковесные объекты для пользовательских интерфейсов». Материалы 3-го ежегодного симпозиума ACM SIGGRAPH по программному обеспечению и технологиям пользовательского интерфейса - UIST '90 . 3-й ежегодный симпозиум ACM SIGGRAPH по программному обеспечению и технологиям пользовательского интерфейса. Сноуберд, Юта, США. стр. 92–101. дои : 10.1145/97924.97935 . ISBN 0-89791-410-4 .
- ^ Вайнанд, Эндрю; Гамма, Эрих; Марти, Рудольф (1988). ET++ — объектно-ориентированная платформа приложений на C++ . OOPSLA (Системы, языки и приложения объектно-ориентированного программирования). Сан-Диего, Калифорния, США. стр. 100-1 46–57. CiteSeerX 10.1.1.471.8796 . дои : 10.1145/62083.62089 . ISBN 0-89791-284-5 .
- ^ «Реализация шаблонов-легковесов в Java» . Разработчик.com . 28 января 2019 г. Проверено 12 июня 2021 г.
- ^ «Шаблон проектирования «Легкий вес» — структура и сотрудничество» . w3sDesign.com . Проверено 12 августа 2017 г.
- ^ БиллВагнер. «Записи — справочник по C#» . docs.microsoft.com . Проверено 12 июня 2021 г.