Схема наилегчайшего веса - Flyweight pattern
Эта статья включает в себя список общих Рекомендации, но он остается в основном непроверенным, потому что ему не хватает соответствующих встроенные цитаты.Май 2008 г.) (Узнайте, как и когда удалить этот шаблон сообщения) ( |
В компьютерное программирование, наилегчайший вес это шаблон разработки программного обеспечения. Легкий вес - это объект что сводит к минимуму объем памяти использование путем обмена как можно большим объемом данных с другими подобными объектами; это способ использовать объекты в больших количествах, когда простое повторяющееся представление потребовало бы неприемлемого объема памяти. Часто некоторые части состояния объекта могут быть общими, и обычно их хранят во внешнем структуры данных и временно передать их объектам, когда они используются.
Классическим примером использования паттерна flyweight являются структуры данных для графического представления символов в текстовый редактор. Было бы желательно иметь для каждого символа в документе глиф объект, содержащий схему шрифта, метрики шрифта и другие данные форматирования, но это будет составлять сотни или тысячи байтов для каждого символа. Вместо этого для каждого персонажа может быть ссылка к объекту легковесного глифа, который используется всеми экземплярами одного и того же символа в документе; только позиция каждого символа (в документе и / или на странице) должна храниться внутри.
Другой пример интернирование струн.
В других контекстах идея совместного использования идентичных структур данных называется хеширование.
Обзор
Легкий вес [1]шаблон дизайна - один из двадцати трех хорошо известных Шаблоны проектирования GoF в которых описывается, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые проще реализовать, изменить, протестировать и повторно использовать.
Какие проблемы может решить шаблон проектирования Легковес?[2]
- Следует эффективно поддерживать большое количество объектов.
- Следует избегать создания большого количества объектов.
Например, при представлении больших текстовых документов создание объекта для каждого символа в документе приведет к появлению огромного количества объектов, которые невозможно обработать эффективно.
Какое решение описывает шаблон проектирования Flyweight?
Определять Наилегчайший вес
объекты, которые
- хранить внутреннее (инвариантное) состояние, которым можно поделиться и
- предоставить интерфейс, через который может быть передано внешнее (вариантное) состояние.
Это позволяет клиентам (1) повторно использовать (делиться) Наилегчайший вес
объекты (вместо создания каждый раз нового объекта) и (2) переходят во внешнее состояние, когда они вызывают Наилегчайший вес
операция.
Это значительно уменьшает количество физически создаваемых объектов.
Внутреннее состояние инвариантен (не зависит от контекста) и, следовательно, может использоваться совместно (например, код символа «A» в данном наборе символов).
Внешнее состояние является вариантным (зависит от контекста) и, следовательно, не может использоваться совместно и должен передаваться (например, позиция символа «A» в текстовом документе).
См. Также схему классов и последовательности UML ниже.
История
По учебнику Паттерны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования,[3] модель наилегчайшего веса была впервые придумана и широко исследована Пол Колдер и Марк Линтон в 1990 году, чтобы эффективно обрабатывать глифовую информацию в Редактор документов WYSIWYG,[4] хотя аналогичные методы уже использовались в других системах, например, фреймворк приложения Weinand et al. (1988).[5]
Структура
Схема классов и последовательности UML
В приведенном выше UML диаграмма классов, то Клиент
class относится (1) к ЛегковесЗавод
класс для создания / обмена Наилегчайший вес
объекты и (2) к Наилегчайший вес
интерфейс для выполнения операции путем перехода во внешнее (вариантное) состояние (flyweight.operation (extrinsicState)
). В Наилегчайший вес1
класс реализует Наилегчайший вес
интерфейс и хранит внутреннее (инвариантное) состояние, которым можно поделиться.
Диаграмма последовательности показывает взаимодействия во время выполнения: Клиент
вызовы объектов getFlyweight (ключ)
на ЛегковесЗавод
который создает и возвращает Наилегчайший вес1
объект.После вызова операция (extrinsicState)
на возвращенный Наилегчайший вес1
объект, Клиент
снова звонит getFlyweight (ключ)
на ЛегковесЗавод
, которая теперь делится и возвращает уже существующие Наилегчайший вес1
объект.
Неизменность и равенство
Для обеспечения безопасного обмена между клиентами и потоками объекты-легковесы должны быть неизменный. Объекты-легковесы по определению являются объектами-ценностями. Идентичность экземпляра объекта не имеет значения; следовательно, два экземпляра Легковеса с одинаковым значением считаются равными.
Пример на C # (обратите внимание на переопределения Equals и GetHashCode, а также перегрузки операторов == и! =):
общественный учебный класс КофеФлавур { общественный КофеФлавур(нить вкус) => это.Вкус = вкус; общественный нить Вкус { получать; } общественный отменять логический Равно(Объект? объект) => Равно(это, объект); общественный статический bool Равно(КофеФлавур? оставили, КофеФлавур? верно) => Нить.Равно(оставили?.Вкус, верно?.Вкус); общественный отменять int GetHashCode() => это.Вкус.GetHashCode(); общественный статический bool оператор ==(КофеФлавур а, КофеФлавур б) => Равно(а, б); общественный статический bool оператор !=(КофеФлавур а, КофеФлавур б) => !Равно(а, б); }
Параллелизм
Особое внимание следует уделять сценариям, в которых объекты-легковесы создаются в нескольких потоках. Если список значений конечен и известен заранее, можно заранее создать экземпляры легковесов и извлечь их из контейнера в нескольких потоках без конкуренции. Если экземпляры Flyweights создаются в нескольких потоках, есть два варианта:
- Сделайте создание экземпляров Flyweight однопоточным, таким образом создавая конкуренцию и обеспечивая один экземпляр для каждого значения.
- Разрешить параллельным потокам создавать несколько экземпляров Flyweight, тем самым устраняя конкуренцию и разрешая несколько экземпляров для каждого значения. Этот вариант возможен только при соблюдении критерия равенства.
Пример на C #
с помощью System.Collections.Concurrent;с помощью System.Collections.Generic;с помощью System.Threading;общественный интерфейс ICoffeeFlavourFactory { КофеФлавур GetFlavour(нить вкус);}общественный учебный класс Уменьшенный отпечаток памяти : ICoffeeFlavourFactory { частный только чтение объект _cacheLock = новый объект(); частный только чтение IDictionary<нить, КофеФлавур> _cache = новый Словарь<нить, КофеФлавур>(); общественный КофеФлавур GetFlavour(нить вкус) { если (_cache.TryGetValue(вкус, из КофеФлавур кэшированныйКофе)) возвращаться кэшированныйКофеФлавур; вар кофе = новый КофеФлавур(вкус); ThreadPool.QueueUserWorkItem(AddFlavourToCache, кофе); возвращаться кофе; } частный пустота AddFlavourToCache(объект государственный) { вар кофе = (КофеФлавур)государственный; если (!_cache.ContainsKey(кофе.Вкус)) { замок (_cacheLock) { _cache[кофе.Вкус] = кофе; } } }}общественный учебный класс Минимум памяти : ICoffeeFlavourFactory { частный только чтение ConcurrentDictionary<нить, КофеФлавур> _cache = новый ConcurrentDictionary<нить, КофеФлавур>(); общественный КофеФлавур GetFlavour(нить вкус) { возвращаться _cache.GetOrAdd(вкус, flv => новый КофеФлавур(flv)); }}
Простая реализация
Легковес позволяет обмениваться объемными данными, общими для каждого объекта. Другими словами, если вы считаете, что одни и те же данные повторяются для каждого объекта, вы можете использовать этот шаблон, чтобы указать на один объект и, следовательно, легко сэкономить место. Здесь FlyweightPointer создает статический член Company, который используется для каждого объекта MyObject.
// Определяет объект-легковес, который повторяется.общественный учебный класс FlyWeight{ общественный нить Название компании { получать; набор; } общественный нить Компания { получать; набор; } общественный нить Вебсайт компании { получать; набор; } // Объемные данные общественный байт[] Логотип компании { получать; набор; }}общественный статический учебный класс FlyWeightPointer{ общественный статический только чтение FlyWeight Компания = новый FlyWeight { Название компании = "Abc", Компания = «XYZ», Вебсайт компании = "www.abc.com" // Загрузить CompanyLogo сюда };}общественный учебный класс MyObject{ общественный нить Имя { получать; набор; } общественный нить Компания { получать { возвращаться FlyWeightPointer.Компания.Название компании; } }}
Пример на Java
импорт java.util.ArrayList;импорт java.util.WeakHashMap;учебный класс КофеФлавур { частный окончательный Нить имя; частный статический окончательный WeakHashMap<Нить, КофеФлавур> КЭШ = новый WeakHashMap<>(); // только intern () может вызвать этот конструктор частный КофеФлавур(Нить имя) { это.имя = имя; } @Override общественный Нить нанизывать() { возвращаться имя; } общественный статический КофеФлавур стажер(Нить имя) { синхронизированный (КЭШ) { возвращаться КЭШ.computeIfAbsent(имя, КофеФлавур::новый); } } общественный статический int ароматизаторы() { синхронизированный (КЭШ) { возвращаться КЭШ.размер(); } }}@FunctionalInterfaceинтерфейс Заказ { пустота обслуживать(); статический Заказ из(Нить flavourName, int tableNumber) { КофеФлавур вкус = КофеФлавур.стажер(flavourName); возвращаться () -> Система.из.println("Обслуживание" + вкус + "на стол" + tableNumber); }}учебный класс Кофейный магазин { частный окончательный ArrayList<Заказ> заказы = новый ArrayList<>(); общественный пустота принять заказ(Нить вкус, int tableNumber) { заказы.Добавить(Заказ.из(вкус, tableNumber)); } общественный пустота служба() { заказы.для каждого(Заказ::обслуживать); }}общественный учебный класс Легковес Пример { общественный статический пустота главный(Нить[] аргументы) { Кофейный магазин магазин = новый Кофейный магазин(); магазин.принять заказ("Капучино", 2); магазин.принять заказ("Фраппе", 1); магазин.принять заказ("Эспрессо", 1); магазин.принять заказ("Фраппе", 897); магазин.принять заказ("Капучино", 97); магазин.принять заказ("Фраппе", 3); магазин.принять заказ("Эспрессо", 3); магазин.принять заказ("Капучино", 3); магазин.принять заказ("Эспрессо", 96); магазин.принять заказ("Фраппе", 552); магазин.принять заказ("Капучино", 121); магазин.принять заказ("Эспрессо", 121); магазин.служба(); Система.из.println(«Объекты CoffeeFlavor в кеше:» + КофеФлавур.ароматизаторы()); }}
Выполнение этого кода даст следующее:
Подача капучино к столу 2 Подача фраппе к столу 1 Подача эспрессо к столу 1 Подача фраппе к столу 897 Подача капучино к столу 97 Подача фраппе к столу 3 Подача эспрессо к столу 3 Подача капучино к столу 3 Подача эспрессо к столу 96 Подача эспрессо на стол 96 Подача эспрессо на стол 96 Подача эспрессо на стол 96 в кеше: 3
Пример на Python
Атрибуты могут быть определены на уровне класса, а не только для экземпляров в Python потому что классы являются первоклассными объектами в языке, то есть нет никаких ограничений на их использование, поскольку они такие же, как и любой другой объект. Экземпляры классов в новом стиле хранят данные экземпляров в специальном словаре атрибутов экземпляр .__ dict__
. По умолчанию доступ к атрибутам сначала выполняется в этом __dict__
, а затем вернуться к атрибутам класса экземпляра. [7] Таким образом, класс может быть своего рода контейнером-легковесом для своих экземпляров.
Хотя классы Python являются изменяемыми по умолчанию, неизменяемость можно эмулировать, переопределив класс __setattr__
, чтобы запретить изменение любых атрибутов Легковеса.
# Экземпляров CheeseBrand будут наилегчайшимиучебный класс СырБренд: def __в этом__(себя, марка: ул, Стоимость: плавать) -> Никто: себя.марка = марка себя.Стоимость = Стоимость себя._неизменный = Истинный # Отключает будущую атрибуцию def __setattr__(себя, имя, ценить): если getattr(себя, "_неизменный", Ложь): # Разрешить начальную атрибуцию поднимать Ошибка выполнения(«Этот объект неизменен») еще: супер().__setattr__(имя, ценить)учебный класс Сырная лавка: меню = {} # Общий контейнер для доступа к Flyweights def __в этом__(себя) -> Никто: себя.заказы = {} # контейнер для каждого экземпляра с частными атрибутами def stock_cheese(себя, марка: ул, Стоимость: плавать) -> Никто: сыр = СырБренд(марка, Стоимость) себя.меню[марка] = сыр # Общий наилегчайший вес def sell_cheese(себя, марка: ул, единицы: int) -> Никто: себя.заказы.установить по умолчанию(марка, 0) себя.заказы[марка] += единицы # Атрибут экземпляра def total_units_sold(себя): возвращаться сумма(себя.заказы.значения()) def Общая прибыль(себя): доход = 0 за марка, единицы в себя.заказы.Предметы(): доход += себя.меню[марка].Стоимость * единицы возвращаться доходмагазин1 = Сырная лавка()магазин2 = Сырная лавка()магазин1.stock_cheese("белый", 1.25)магазин1.stock_cheese("синий", 3.75)# Теперь в каждом CheeseShop в инвентаре есть "белый" и "синий"# ОДИН "белый" и "голубой" CheeseBrandмагазин1.sell_cheese("синий", 3) # Оба могут продаватьмагазин2.sell_cheese("синий", 8) # Но проданные единицы хранятся по экземплярамутверждать магазин1.total_units_sold() == 3утверждать магазин1.Общая прибыль() == 3.75 * 3утверждать магазин2.total_units_sold() == 8утверждать магазин2.Общая прибыль() == 3.75 * 8
Пример на Ruby
# Легковесный объектучебный класс Напольная лампа attr_reader :цвет #attr_reader делает атрибут цвета доступным снаружи # класса, вызвав .color в экземпляре Lamp def инициализировать(цвет) @цвет = цвет конецконецучебный класс Ветвь дерева def инициализировать(номер ветви) @номер ветви = номер ветви конец def вешать(напольная лампа) ставит "Вешать #{напольная лампа.цвет} лампа на ветке #{@номер ветви}" конецконец# Фабрика наилегчайшего весаучебный класс ЛампаЗавод def инициализировать @ лампы = {} конец def find_lamp(цвет) если @ лампы.has_key?(цвет) # если лампа уже существует, ссылаться на нее вместо создания новой напольная лампа = @ лампы[цвет] еще напольная лампа = Напольная лампа.новый(цвет) @ лампы[цвет] = напольная лампа конец напольная лампа конец def total_number_of_lamps_made @ лампы.размер конецконецучебный класс Рождественская елка def инициализировать @lamp_factory = ЛампаЗавод.новый @lamps_hung = 0 dress_up_the_tree конец def Hang_lamp(цвет, номер ветви) Ветвь дерева.новый(номер ветви).вешать(@lamp_factory.find_lamp(цвет)) @lamps_hung += 1 конец def dress_up_the_tree Hang_lamp('красный', 1) Hang_lamp('синий', 1) Hang_lamp('желтый', 1) Hang_lamp('красный', 2) Hang_lamp('синий', 2) Hang_lamp('желтый', 2) Hang_lamp('красный', 3) Hang_lamp('синий', 3) Hang_lamp('желтый', 3) Hang_lamp('красный', 4) Hang_lamp('синий', 4) Hang_lamp('желтый', 4) Hang_lamp('красный', 5) Hang_lamp('синий', 5) Hang_lamp('желтый', 5) Hang_lamp('красный', 6) Hang_lamp('синий', 6) Hang_lamp('желтый', 6) Hang_lamp('красный', 7) Hang_lamp('синий', 7) Hang_lamp('желтый', 7) ставит "Сделали #{@lamp_factory.total_number_of_lamps_made} всего лампы » ставит "Подвешенный #{@lamps_hung} всего лампы » конецконец
Пример на Scala
/*запускать как скрипт с использованием `scala flyweight.scala`ожидаемый результат: Подача CoffeeFlavour (Espresso) к столу 121 Подача CoffeeFlavour (Капучино) к столу 121 Подача CoffeeFlavour (Frappe) к столу 552 Подача CoffeeFlavour (Эспрессо) к столу 96 Подача CoffeeFlavour (Капучино) к столу 3 Подача CoffeeFlavour (Эспрессо) к столу 3 Подача CoffeeFlavour (Frappe) к столу 3 Подача CoffeeFlavour (Капучино) к столу 97 Подача CoffeeFlavour (Frappe) к столу 897 Подача CoffeeFlavour (Эспрессо) к столу 1 Подача CoffeeFlavour (Frappe) к столу 1 Подача CoffeeFlavour (Капучино) к столу 2 всего сделано объектов CoffeeFlavour: 3*// * конструктор `private` гарантирует, что только интернированные * значения типа `CoffeeFlavour` могут быть получены. * /учебный класс КофеФлавур частный (вал имя: Нить){ отменять def нанизывать = s "CoffeeFlavour ($ name)"}объект КофеФлавур { импорт scala.collection.mutable импорт scala.ref.WeakReference частный вал тайник = изменчивый.карта.пустой[Нить, ref.WeakReference[КофеФлавур]] def подать заявление(имя: Нить): КофеФлавур = синхронизированный { тайник.получать(имя) матч { дело Немного(WeakReference(вкус)) => вкус дело _ => вал новый = новый КофеФлавур(имя) тайник.положить(имя, WeakReference(новый)) новый } } def всегоКофеВкусСделано = тайник.размер}дело учебный класс Заказ(tableNumber: Int, вкус: КофеФлавур){ def обслуживать: Единица измерения = println(s "Обслуживание $ аромат к столу $ tableNumber")}объект Кофейный магазин { вар заказы = Список.пустой[Заказ] def принять заказ(flavourName: Нить, стол: Int) { вал вкус = КофеФлавур(flavourName) вал порядок = Заказ(стол, вкус) заказы = порядок :: заказы } def служба: Единица измерения = заказы.для каждого(_.обслуживать) def отчет = s "всего произведено объектов CoffeeFlavour: ${КофеФлавур.всегоКофеВкусСделано}"}Кофейный магазин.принять заказ("Капучино", 2)Кофейный магазин.принять заказ("Фраппе", 1)Кофейный магазин.принять заказ("Эспрессо", 1)Кофейный магазин.принять заказ("Фраппе", 897)Кофейный магазин.принять заказ("Капучино", 97)Кофейный магазин.принять заказ("Фраппе", 3)Кофейный магазин.принять заказ("Эспрессо", 3)Кофейный магазин.принять заказ("Капучино", 3)Кофейный магазин.принять заказ("Эспрессо", 96)Кофейный магазин.принять заказ("Фраппе", 552)Кофейный магазин.принять заказ("Капучино", 121)Кофейный магазин.принять заказ("Эспрессо", 121)Кофейный магазин.службаprintln(Кофейный магазин.отчет)
Пример в Swift
// Экземпляры CoffeeFlavour будут наилегчайшимиструктура CoffeeFlavor : CustomStringConvertible { вар вкус: Нить вар описание: Нить { вкус }}// Меню действует как фабрика и кеш для легковесных объектов CoffeeFlavourструктура Меню { частный вар ароматы: [Нить: CoffeeFlavor] = [:] мутирующий func искать(вкус: Нить) -> CoffeeFlavor { если позволять существующий = ароматы[вкус] { возвращаться существующий } позволять newFlavor = CoffeeFlavor(вкус: вкус) ароматы[вкус] = newFlavor возвращаться newFlavor }}структура Кофейный магазин { частный вар заказы: [Int: CoffeeFlavor] = [:] частный вар меню = Меню() мутирующий func принять заказ(вкус: Нить, стол: Int) { заказы[стол] = меню.искать(вкус: вкус) } func обслуживать() { за (стол, вкус) в заказы { Распечатать("Обслуживание \(вкус) к столу \(стол)") } }}вар кофейный магазин = Кофейный магазин()кофейный магазин.принять заказ(вкус: "Капучино", стол: 1)кофейный магазин.принять заказ(вкус: "Фраппе", стол: 3)кофейный магазин.принять заказ(вкус: "Эспрессо", стол: 2)кофейный магазин.принять заказ(вкус: "Фраппе", стол: 15)кофейный магазин.принять заказ(вкус: "Капучино", стол: 10)кофейный магазин.принять заказ(вкус: "Фраппе", стол: 8)кофейный магазин.принять заказ(вкус: "Эспрессо", стол: 7)кофейный магазин.принять заказ(вкус: "Капучино", стол: 4)кофейный магазин.принять заказ(вкус: "Эспрессо", стол: 9)кофейный магазин.принять заказ(вкус: "Фраппе", стол: 12)кофейный магазин.принять заказ(вкус: "Капучино", стол: 13)кофейный магазин.принять заказ(вкус: "Эспрессо", стол: 5)кофейный магазин.обслуживать()
Пример в кристалле
# Экземпляры CoffeeFlavor станут наилегчайшими весамиучебный класс CoffeeFlavor def инициализировать(new_flavor : Нить) @имя = new_flavor конец def to_s(io) io << @имя конецконец# Меню действует как фабрика и кеш для легковесных объектов CoffeeFlavorучебный класс Меню def инициализировать @ ароматизаторы = {} из Нить => CoffeeFlavor конец def искать(аромат_имя : Нить) @ ароматизаторы[аромат_имя] ||= CoffeeFlavor.новый(Flavour_name) конец def total_flavors_made @ ароматизаторы.размер конецконец# Заказ - это контекст наилегчайшего веса CoffeeFlavor.учебный класс Заказ частный добытчик table_number : Int32, вкус : CoffeeFlavor def инициализировать(@table_number, @вкус) конец def обслуживать ставит "Обслуживание #{вкус} к столу #{table_number}" конецконецучебный класс Кофейный магазин частный добытчик заказы частный добытчик меню def инициализировать @orders = [] из Заказ @меню = Меню.новый конец def принять заказ(аромат_имя : Нить, стол : Int32) вкус = меню.искать(аромат_имя) порядок = Заказ.новый(стол, вкус) @orders << порядок конец def служба заказы.каждый делать |порядок| порядок.обслуживать конец конец def отчет "Total CoffeeFlavor made: #{меню.total_flavors_made}" конецконец# Программамагазин = Кофейный магазин.новыймагазин.принять заказ(«Капучино», 2)магазин.принять заказ("Фраппе", 1)магазин.принять заказ("Эспрессо", 1)магазин.принять заказ("Фраппе", 897)магазин.принять заказ("Капучино", 97)магазин.принять заказ("Фраппе", 3)магазин.принять заказ("Эспрессо", 3)магазин.принять заказ("Капучино", 3)магазин.принять заказ("Эспрессо", 96)магазин.принять заказ("Фраппе", 552)магазин.принять заказ("Капучино", 121)магазин.принять заказ("Эспрессо", 121)магазин.службаставит магазин.отчет
Выход
Подача капучино на стол 2 сделано: 4
Пример на C ++
C ++ Стандартная библиотека шаблонов предоставляет несколько контейнеров, которые позволяют сопоставить уникальные объекты с ключом. Использование контейнеров помогает еще больше сократить использование памяти, устраняя необходимость в создании временных объектов.
#включают <iostream>#включают <map>#включают <string>// Экземпляры Tenant будут мимолетнымиучебный класс Жилец {общественный: Жилец(const стандартное::нить& имя = "") : m_name(имя) {} стандартное::нить имя() const { возвращаться m_name; }частный: стандартное::нить m_name;};// Реестр действует как фабрика и кеш для легковесных объектов клиентаучебный класс Реестр {общественный: Реестр() : арендаторы() {} Жилец findByName(const стандартное::нить& имя) { если (арендаторы.считать(имя) != 0) возвращаться арендаторы[имя]; авто newTenant = Жилец(имя); арендаторы[имя] = newTenant; возвращаться newTenant; }частный: стандартное::карта<стандартное::нить,Жилец> арендаторы;};// Квартира сопоставляет уникального арендатора с его номером комнаты.учебный класс Квартира {общественный: Квартира() : m_occupants(), m_registry() {} пустота addOccupant(const стандартное::нить& имя, int комната) { m_occupants[комната] = m_registry.findByName(имя); } пустота арендаторы() { за (авто я : m_occupants) { const int комната = я.первый; const Жилец жилец = я.второй; стандартное::cout << жилец.имя() << "занимает комнату" << комната << стандартное::конец; } }частный: стандартное::карта<int,Жилец> m_occupants; Реестр m_registry;};int главный() { Квартира квартира; квартира.addOccupant("Дэйвид", 1); квартира.addOccupant("Сара", 3); квартира.addOccupant("Джордж", 2); квартира.addOccupant("Лиза", 12); квартира.addOccupant("Майкл", 10); квартира.арендаторы(); возвращаться 0;}
Смотрите также
Рекомендации
- ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Паттерны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования. Эддисон Уэсли. стр.195ff. ISBN 978-0-201-63361-0.CS1 maint: несколько имен: список авторов (связь)
- ^ «Шаблон проектирования« Легковес »- проблема, решение и применимость». w3sDesign.com. Получено 2017-08-12.
- ^ Гамма, Эрих; Ричард Хелм; Ральф Джонсон; Джон Влиссидес (1995). Паттерны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования. Эддисон-Уэсли. стр.205–206. ISBN 978-0-201-63361-0.
- ^ Колдер, Пол Р .; Линтон, Марк А. (октябрь 1990 г.). Глифы: легковесные объекты для пользовательских интерфейсов. 3-й ежегодный ACM SIGGRAPH Симпозиум по программному обеспечению и технологиям пользовательского интерфейса. Сноуберд, штат Юта, США. С. 92–101. Дои:10.1145/97924.97935. ISBN 0-89791-410-4.
- ^ Вайнанд, Андре; Гамма, Эрих; Марти, Рудольф (1988). ET ++ - объектно-ориентированный фреймворк приложений на C ++. OOPSLA (Системы объектно-ориентированного программирования, языки и приложения). Сан-Диего, Калифорния, США. С. 46–57. CiteSeerX 10.1.1.471.8796. Дои:10.1145/62083.62089. ISBN 0-89791-284-5.
- ^ "Шаблон проектирования наилегчайшего - структура и взаимодействие". w3sDesign.com. Получено 2017-08-12.
- ^ "Модель данных §". (Онлайн) Справочник по языку Python. Фонд программного обеспечения Python. Получено 7 марта 2017.