fbpx

Blog

Применяем паттерны проектирования в Android. Flyweight

Тема паттернов проектирования будет актуальная всегда, потому что писать масштабируемый и читаемый код нужно всегда, независимо под какую платформу вы проектируете. Поэтому в этом посте мы на примере рассмотрим паттерн проектирования Flyweight, он же Легковес или Приспособленец. Итак для чего используется этот паттерн?

Легковес — это структурный паттерн проектирования, который позволяет вместить бóльшее количество объектов в отведённую оперативную память. Легковес экономит память, разделяя общее состояние объектов между собой, вместо хранения одинаковых данных в каждом объекте.

Пример

Рассмотрим мобильное приложение для поиска недвижимости. Скорее всего в такого типа приложении один из экранов для поиска будет состоять из карты и множества маркеров, обозначающих квартиру или дом. Если таких объектов будет очень много, например, больше 100 000 то, учитывая ограниченные ресурсы мобильного телефона, приложение может начать подтормаживать, из-за нехватки оперативной памяти.

Решение

В этом случае нам может помочь

  • Кластеризация
  • Использование паттерна Flyweight. 

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

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

Помните, в начале поста был вопрос, что будет в случае сравнения двух чисел 127?

@Test
public void compareIntegersTest() {
    Integer a = 127;
    Integer b = 127;
    assertEquals(true, a == b);
}

В этом случае сравнение вернет результат true и тест будет пройден. Однако в случае сравнения 128 результат будет false и тест провалится.

@Test
public void compareIntegersTest() {
       Integer a = 128;
       Integer b = 128;
       assertEquals(true, a == b);
}

Все дело в том, что используя == мы сравниваем ссылки по которым эти объекты хранятся. Когда мы пишем Integer a = 127 мы создаем Integer (ссылочный тип). Получается что сравнивая a == b мы сравниваем ссылки двух объектов. Но почему-то для 127 результат будет true, а для 128 уже false. Все дело в статичном методе valueOf() который вызывается при инициализации переменной. Этот метод, как раз оптимизирует количество объектов в памяти, используя IntegerCache, и достаёт уже ранее созданные объекты из памяти если они есть. Как раз это и есть пример использования паттерна Flyweight в реальном мире. Тоже самое касается и StringPool, ShortCache, LongCache, CharacterCache

Шаги реализации

  1. Разделите поля класса, который станет легковесом, на две части:
    • внутреннее состояние: значения этих полей одинаковы для большого числа объектов;
    • внешнее состояние (контекст): значения полей уникальны для каждого объекта.
  2. Оставьте поля внутреннего состояния в классе, но убедитесь, что их значения неизменяемы. Эти поля должны инициализироваться только через конструктор.
  3. Превратите поля внешнего состояния в параметры методов, где эти поля использовались. Затем удалите поля из класса.
  4. Создайте фабрику, которая будет кешировать и повторно отдавать уже созданные объекты. Клиент должен запрашивать из этой фабрики легковеса с определённым внутренним состоянием, а не создавать его напрямую.
  5. Клиент должен хранить или вычислять значения внешнего состояния (контекст) и передавать его в методы объекта легковеса.

Преимущества и недостатки

Плюсы

  • Экономит оперативную память.

Минусы

  • Усложняет код программы из-за введения множества дополнительных классов.
  • Расходует процессорное время на поиск/вычисление контекста.

Ну и что?

Итак, мы выяснили, что паттерн Легковес может использоваться при разработке мобильных приложений под Android OS для экономии памяти путем выделения общего состояния, не выделяя при этом память под каждый одинаковый объект. Как видите в Java этот паттерн тоже активно используется, и теперь вы сможете ответить на вопрос на собеседовании, что будет при сравнении двух чисел 127. Подписывайтесь на телеграм-канал и группу вконтакте для получения полезных материалов и статей.

Пример проекта

Тут сможете посмотреть имплементацию паттерна https://github.com/iluwatar/java-design-patterns/tree/master/flyweight