Применение ЗАКОНА DEMETER с фасадным рисунком

В основных навыках для гибкого разработчика, в потребностях и интерфейсе возможностей, глава 12, я пытаюсь понять основное решение, предложенное для задачи применения ЗАКОНА DEMETER, которое автор упомянул к концу этого глава.

Чтобы сделать рассказ коротким.

Начнем со следующего случая:

public class City {
  public string name{};
  public City twinCity{};
  public Street[] streets{};
}
public class Street {
  public string name{};
  public House[] houses{};
}
public class House {
  public int number{};
  public Color color{};
}

Model of a City Grid

Если автор утверждает, что:

Модели, подобные этому, побуждают нас раскрывать, а не инкапсулировать. Если ваш код имеет ссылку на конкретный экземпляр города, скажем, тот, который карты Сиэтла, и вы хотели, чтобы цвет дома в 1374 году Улица, то вы можете сделать что-то вроде следующего:

public Foo() {
  Color c = Seattle.streets()["Main"].
    houses()[1374].
    color();
}

Проблема, если это делается как общая практика, заключается в том, что система везде разрабатывает зависимости, а изменение любой части этого модель может влиять вверх и вниз по цепочке этих зависимостей. Вот где Закон Деметры, в котором говорится: "Не разговаривай с чужих". Это формализовано в объектных системах, как Закон Деметры для функций/методов. Способ M объекта O может вызывать методы следующих видов объектов:

  • Os
  • Параметры Ms
  • Любые объекты, созданные в M
  • Объекты прямого компонента Os
  • Любые глобальные переменные, доступные O

и предположим, что при применении ЗАКОНА DEMTER мы должны стремиться к чему-то вроде

public Foo() {
   Color c = Seattle.ColorOfHouseInStreet("Main",1374);
}

и быстро предупреждает следующее:

Хотя это, казалось бы, было бы разумной политикой изначально, она может быстро выйти из-под контроля, поскольку можно ожидать, что интерфейс любого объекта чтобы обеспечить буквально все, к чему это относится. Эти интерфейсы имеют тенденцию раздувается со временем, и на самом деле, похоже, почти нет конца количество общедоступных методов, которые может оказать конкретное стекло.

Затем после быстрого обхода объяснения проблемы связи и зависимости, где он упоминает о важности разделения клиента и его обслуживания интерфейсом службы и, возможно, кроме того, отделяя клиентский интерфейс "потребности" от "интерфейс возможностей обслуживания" с использованием адаптера как нечто идеальное, но не обязательно практическое;

Ideal decoupling

он предлагает, чтобы исправить эту проблему, мы могли бы объединить ЗАКОН DEMETER и разделение потребностей/возможностей с использованием шаблона фасада, как показано ниже:

Из исходной зависимости

Violation of the law of demeter

При применении закона деметатора и разделения интерфейса потребностей/возможностей мы должны сначала получить:

Too complex

Но, учитывая, что это просто непрактично, особенно с точки зрения насмешек, мы могли бы сделать что-то более простое с фасадом:

LoD needs/Capability separationMock

Проблема в том, что я просто не вижу, как это точно решает проблему не нарушать Закон Деметры. Я думаю, что он поддерживает закон между первоначальным клиентом и оригинальным сервисом. Но это просто переместило нарушение в FACADE.

Ответ 1

Я бы сказал, что из ваших описаний он просто представил компонент черного ящика вокруг функций Cirty/Street/House в форме CityMapFacade. Кроме того, этот компонент придерживается общедоступного интерфейса в виде интерфейса IClientNeeds. Такая инкапсуляция черного ящика защищает и делает проверяемым более широкое приложение, потому что сложность отношений City/Street/House не раскрывается. Кажется, что то, что было когда-то старым, снова является новым:) Такая методология в стиле "черного ящика" (из-за отсутствия лучшего термина) существует уже давно. Он фокусируется на создании многоразовых, инкапсулированных компонентов функциональности с четко определенным интерфейсом. Клиент не заботится о реализации, пока выполняется публичный контракт. Компонент сменяется, доступен для обновления, макет. Внутри компонента сложность также уменьшается, поскольку она отвечает только за изолированное подмножество приложения. Как правило, он будет полностью проверен на единицу, чтобы гарантировать, что он правильно реализует свой публичный контракт/интерфейс. На мой взгляд, автор углубил более глубокое обсуждение этого всего под ковриком, чтобы упростить свое объяснение.

Ответ 2

Я предполагаю, что существует "неправильное общение" между вами и автором.

Шаг 1: у вас есть это:

needs interfaces

Надеюсь, вы увидите, что на данный момент нарушения LoD не существует: каждый конкретный класс зависит от абстракции его потребностей и, таким образом, вызывает вызовы только этого ограниченного интерфейса; плюс зависимость от объекта (например, City для CityCapabilityAdapter), что разрешено LoD. Таким образом, до сих пор никаких нарушений.


Шаг 2: у вас есть следующее:

needs facade

Я думаю, автор здесь означает, что CityMapFacade концептуально зависит от всех трех объектов, но физически не работает с ними напрямую. Вместо этого он работает с ними с помощью инструментов LoD. например фасад может зависеть от тех адаптеров, которые уже были представлены ранее. Итак, опять же, здесь нет нарушения LoD.

Чтобы прояснить эту интерпретацию, обратите внимание на абстрактные вещи на диаграмме, они выделены курсивом (UML 2.0). Таким образом, CityMapFacade является интерфейсом, который отвечает за решение собственных проблем LoD. И это можно решить с помощью уже продемонстрированных фактов (например, адаптеров). Конкретное решение на диаграмме не показано, это просто говорит об абстракциях. И нет никаких шансов спорить о нарушении LoD в конкретной реализации фасада, поскольку автор уже продемонстрировал, что у него есть инструменты для обхода нарушения путем введения адаптеров.

Ответ 3

Я не знаю книгу, которую вы читали, и когда я впервые прочитал ваш вопрос, я подумал, что это "LoD сошел с ума". И я понимаю, что вы спрашиваете о конкретной реализации этого фасада без нарушения LoD.

Позвольте мне начать с моей первой мысли об этом: Если мне нужно знать свойство "цвет" объекта "Дом", я думаю, что абсолютно нормально просить объект "Дом" о его собственности. Конечно, нам не нужно обсуждать, что это действительно плохая идея, чтобы спуститься по цепочке из города по улице в дома из-за зависимостей, которые вы получаете.

Я бы просто реализовал метод getHouse как

public class City {
  public House getHouse(street, number) {...}
}

Этот метод может просто обнаружить адресный объект Street и спросить об этом доме с заданным номером, который не будет нарушать LoD.

Но, конечно, вы получите следующий код:

public Foo() {
  Color c = Seattle.getHouse("Main", 1374).color();
}

который снова нарушает LoD, если мы берем его буквально.

Но так или иначе, я бы это реализовал, если это единственный момент, когда мне нужен цвет дома, так как я не вижу никаких преимуществ в создании ServiceFacades для одного использования. Но если это необходимо более одного раза, и вы действительно не хотите ломать LoD, это довольно легко сделать, но мне нужно было второе представление, чтобы это увидеть. Реализация вашего фасада будет выглядеть следующим образом:

public class CityMapService
{
  public Color getColorOfHouse(City city, String nameOfStreet, int number)
  {
    Street street = city.getStreet(nameOfStreet);
    return getColorOfHouse(street, number);
  }

  public Color getColorOfHouse(Street street, int number)
  {
    House house = street.getHouse(number);
    return getColorOf(house);
  }

  public Color getColorOf(House house)
  {
    return house.getColor();
  }
}

Никакой метод не прерывает LoD.

Имеет ли смысл это делать? Я бы сказал, да. Внутренняя структура каждого объекта не раскрывается, а "более крупная" структура того, как соединяются города, улицы и дома, скрыта за вашим фасадом. И если какая-либо деталь этого изменения вам, скорее всего, понадобится изменить только один метод. И насмешка все еще довольно проста и прямолинейна.