Flyweight паттерн

В данной заметке я опишу структурный паттерн “Легковес” или “Приспособленец” (Flyweight)
Данный паттерн относится к группе Структурных шаблонов.

Рассмотрим пример работы паттерна ниже:

Зачем он нужен? Для экономии оперативной памяти. Соглашусь что во времена повсеместного использования Java (которое потребляет cpu и память просто так), это уже и не так уж важно, однако использовать стоит.
На приведенном выше примере выводится только 40 объектов, но если поднять их количество до 120000, то потребление памяти увеличится соответствующе.
Посмотрим на потребление памяти без использования паттерна flyweight в браузере Chromium:

Без использования паттерна потребление памяти составляет ~300 мегабайт.

Теперь добавим в приложение паттерн и посмотрим потребление памяти:

С использованием паттерна потребление памяти составляет ~200 мегабайт, таким образом мы сэкономили 100 мегабайт памяти в тестовом приложении, в серьезных проектах разница может быть гораздо больше.

Как работает?

В приведенном выше примере мы отрисовываем 40 котиков или для наглядности 120 тысяч. Каждый котик загружается в память в виде png изображения, далее в большинстве рендеров оно конвертируется в битовую карту для отрисовки (фактически bmp), делается это для скорости, так как сжатый png очень долго отрисовывается. Без использования паттерна мы загружаем 120 тысяч картинок котиков в оперативную память и рисуем, а вот при использовании паттерна “легковес” мы загружаем в память одного котика и рисуем его 120 тысяч раз с разной позицией и прозрачностью. Вся магия состоит в том, что координаты и прозрачность мы реализуем отдельно от изображения кота, при отрисовке рендер берет всего одного котика и использует объект с координатами и прозрачностью для корректной отрисовки.

Как выглядит в коде?

Ниже приведены примеры для языка Rise

Без использования паттерна:


Картинка кота загружается для каждого объекта в цикле отдельно – catImage.

С использованием паттерна:

Одна картинка кота используется 120 тысячами объектов.

Где используется?

Используется в GUI фреймворках, например у Apple в системе “переиспользования” (reuse) ячеек таблиц UITableViewCell, чем поднимают порог вхождения для новичков которые не знают про этот паттерн. Также повсеместно используется в разработке игр.

Исходный код

https://gitlab.com/demensdeum/patterns/

Источники

https://refactoring.guru/ru/design-patterns/flyweight
http://gameprogrammingpatterns.com/flyweight.html

Хороший, плохой, мерзкий синглтон

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


Wolf in sheep’s clothing by SarahRichterArt

История происходит в альтернативной вселенной, все совпадения случайны…

Погладь кота на дому с Cat@Home

У каждого человека иногда в жизни возникает непреодолимое желание погладить кота. Аналитики всего мира пророчат что первый стартап создавший приложение по доставке и аренде котиков станет крайне популярным, в недалекой перспективе будет куплен компанией Moogle за триллионы долларов. Вскоре так и происходит – парень из Тюмени создает приложение Cat@Home, и вскоре становится триллиардером, компания Moogle получает себе новый источник прибыли, а миллионы застрессованых людей получают возможность заказать кота на дом для дальнейшего глаженья и успокоения.

Атака клонов

Крайне богатый дантист из Мурманска Алексей Голобородько, впечатлившись статьей про Cat@Home из Фorbes, решает что тоже хочет быть астрономически богатым. Для достижения этой цели, через своих друзей, он находит компанию из Голдфилда – Wakeboard DevPops которая оказывает услуги по разработке ПО, он заказывает разработку клона Cat@Home у них.

Команда победителей

Проект называют Fur&Pure, поручают талантливой команде разработчиков из 20 человек; далее сосредоточимся на группе мобильной разработки из 5 человек. Каждый член команды получает свою часть работы, вооружившись agile-ом и скрамом, команда завершает разработку в срок (за полгода), без багов, релизит приложение в iStore, где ее оценивают 100.000 пользователей на 5, много комментариев о том как прекрасно приложение, как прекрасен сервис (Альтернативная вселенная как-никак). Коты выглажены, приложение выпущено, вроде-бы все идет хорошо. Однако компания Moogle не торопится покупать стартап за триллионы долларов, потому что в Cat@Home уже появились не только коты но и собаки.

Собака лает, караван идет

Владелец приложения решает что пора добавить в приложение собак, обращается за оценкой в компанию и получает примерно минимум полгода на добавление собак в приложение. Фактически приложение будет написано с нуля снова. За это время Moogle добавит в приложение змей, пауков и морских свинок, а Fur&Pur получит только собак.
Почему так получилось? Во всем виновато отсутствие гибкой архитектуры приложения, одним из самых распространенных факторов является антипаттерн проектирования Singleton.

А что такого?

Для того чтобы заказать кота на дом, потребителю нужно создать заявку и отправить ее в офис, где в офисе ее обработают и пришлют курьера с котом, курьер уже получит оплату за услугу.
Один из программистов решает создать класс “ЗаявкаНаКота” с необходимыми полями, выносит этот класс в глобальное пространство приложения через синглтон. Зачем он это делает? Для экономии времени (копеечная экономия получаса), ведь проще вынести заявку в общий доступ, чем продумывать архитектуру приложения и использовать dependency injection. Дальше остальные разработчики подхватывают этот глобальный объект и привязывают свои классы к нему. Например все экраны сами обращаются к глобальному объекту “ЗаявкаНаКота” и показывают данные по заявке. В итоге такое монолитное приложение тестируется и сдается в релиз.
Все вроде хорошо, но вдруг появляется заказчик с требованием добавить в приложение заявки на собак. Команда судорожно начинает оценивать сколько компонентов в системе затронет данное изменение. По окончанию анализа оказывается что нужно переделать от 60 до 90% кода, чтобы научить приложение принимать в глобальном объекте-синглтоне не только “ЗаявкуНаКота” но и “ЗаявкуНаСобаку”, оценивать добавление остальных животных на данном этапе уже бесполезно, справиться хотя бы с двумя.

Как не допустить синглтон

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

Источники

https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons
http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/
https://blog.ndepend.com/singleton-pattern-costs/