Глянь мой новый курс по Git! Привет! Глянь мой новый курс по Git! Привет! Глянь мой новый курс по Git на GitByBit.com! Привет! Хочешь круто подтянуть Git? Глянь мой новый курс на GitByBit.com!

Дублирование видимых данных

Также известен как: Duplicate Observed Data

Проблема

Данные предметной области программы хранятся в классах, отвечающих за пользовательский интерфейс (GUI).

Решение

Имеет смысл выделить данные предметной области в отдельные классы и, таким образом, обеспечить связь и синхронизацию между классом предметной области и GUI.

До
Duplicate Observed Data - Before
После
Duplicate Observed Data - After

Причины рефакторинга

Вы хотите иметь несколько видов интерфейса для одних и тех же данных (например, у вас есть приложение не только для десктопа, но также для телефонов и планшетов). В этом случае вам будет очень сложно избежать большого количества ошибок и дублирования кода, если вы не разделите GUI и предметную область.

Достоинства

  • Вы разделяете ответственность между классами бизнес-логики и представления (принцип единственной обязанности), что упрощает читабельность и понимание программы в целом.

  • Если потребуется добавить новый вид интерфейса, вам нужно будет создать новые классы представления, при этом код бизнес-логики трогать нет никакой нужды (принцип открытости/закрытости).

  • Над бизнес-логикой и пользовательскими интерфейсами теперь могут работать разные люди.

Когда нельзя применить

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

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

Порядок рефакторинга

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

  2. В обработчиках событий класса GUI используйте сеттеры для установки новых значений полей. Это даст возможность передавать новые значения в связанный объект предметной области.

  3. Создайте класс предметной области и скопируйте в него необходимые поля из класса GUI. Для всех этих полей создайте геттеры и сеттеры.

  4. Примените паттерн Наблюдатель к этим двум классам:

    • В классе предметной области создайте массив для хранения объектов наблюдателей (объектов GUI), а также методы их регистрации, удаления и оповещения.

    • В классе GUI создайте поле для хранения ссылки на объект предметной области, а также метод update(), который будет реагировать на изменения в этом объекте и обновлять значения полей в классе GUI. Обратите внимание, в методе обновления значения должны устанавливаться напрямую, чтобы избежать рекурсии.

    • В конструкторе класса GUI создайте экземпляр класса предметной области и сохраните его в созданном поле. Зарегистрируйте объект GUI как наблюдатель в объекте предметной области.

    • В сеттерах полей класса предметной области вызывайте метод оповещения наблюдателя (т.е. метод обновления в классе GUI), чтобы передать новые значения в пользовательский интерфейс.

    • Измените сеттеры полей класса GUI так, чтобы они теперь устанавливали новые значения в объекте предметной области, причём напрямую. Будьте внимательны, если значения будут устанавливаться через сеттер класса предметной области, это приведёт к бесконечной рекурсии.