Подтвердить что ты не робот

Как соблюдать Закон Деметры?

Всякий раз, когда я вижу статьи о Законе Деметры, автор никогда, кажется, не дает убедительного примера того, как подчиняться этому закону. Они все объясняют, что это такое, и показывают пример нарушения закона, но это легко.

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

Скажем, у меня есть класс с этими свойствами:

public class Band {

    private Singer singer;
    private Drummer drummer;
    private Guitarist guitarist;
}

Я где-то в программе, и у меня есть экземпляр этого класса Band, и я хочу, чтобы имя гитариста, что я обычно вижу, это что-то вроде:

guitaristName = band.getGuitarist().getName();

Это не кажется слишком плохим, потому что он не слишком глубоко в цепи, но закон Деметры говорит, что, возможно, это должно быть сделано следующим образом:

guitaristName = band.getGuitaristName();

и мой класс Band имеет метод:

public String getGuitaristName() {
    return guitarist.getName();
}

Это как вы должны подчиняться закону?

Спасибо.

4b9b3361

Ответ 1

Вы не применяете LoD на соответствующем уровне: оба Band и Guitarist должны считаться частью одного и того же модуля, и дилемма, которую вы имеете, должна определяться на основе максимального удобства.

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

Здесь вы действительно видите, что этот принцип действует примерно так: вы используете AsyncHttpClient, который представляет собой абстракцию, построенную поверх Netty, которая представляет собой абстракцию, построенную поверх Java NIO. Теперь, если AsyncHttpClient API заставил вас в каком-то месте напрямую управлять объектом Java NIO, чей API намного более сырой и имеет понятия, совершенно чуждые AsyncHttpClient, это было бы примером нарушения LoD.

Ответ 2

Я думаю, что идея закона заключается в том, что, как говорит Дэнкрумм, обеспечить людям доступ к объектам на соответствующем уровне.

Представьте себе, что наш класс Band моделирует стойку офиса группы. Задача состоит в том, чтобы выступать в качестве PA для членов группы и иметь дело с любым, кто хочет взаимодействовать с группой.

Теперь позвольте сказать, что у нас был промоутер тура от PR-компании, которая хочет собрать некоторые PR-материалы для своего следующего тура. Мы могли бы моделировать его классом:

class TourPromoter {

  public String makePosterText(Band band) {
    String guitaristsName =  band.getGuitarist().getName();
    String drummersName = band.getDrummer().getName();
    String singersName = band.getSinger().getName();
    StringBuilder posterText = new StringBuilder();

    posterText.append(band.getName()
    posterText.append(" featuring: ");
    posterText.append(guitaristsName);
    posterText.append(", ");
    posterText.append(singersName);
    posterText.append(", ")
    posterText.append(drummersName);
    posterText.append(", ")
    posterText.append("Tickets £50.");

    return posterText.toString();
  }

}

В реальной жизни это эквивалент промоутера тура, который звонит в офис и говорит:

  • Промоутер тура: Могу ли я поговорить с вашим гитаристом?

  • Регистратор: Хорошо, я заставлю его за вас.

  • Гитарист: Привет, это гитарист

  • Tour Promter: Привет, я собираю ваш последний плакат. Просто хотел проверить ваше имя?

  • Гитарист: Это Джимми Пейдж

  • Организатор тура: Отлично, спасибо. О, ты мог бы получить меня своим барабанщиком?...

Достаточно сказать, что ПА будет уволен довольно быстро. Большинство разумных людей спрашивали: "Разве вы не могли обработать этот телефонный звонок для нас?"

У нас может быть метод getGuitaristsName(), который технически будет выполнять Закон Деметры, но мы по-прежнему просим наш класс TourPromoter запомнить детали о группе - то есть у них есть гитарист, тогда как эта информация должен действительно принадлежать самой группе.

Чтобы убедиться, что мы внедряем методы разумным образом, нам нужно взглянуть на то, что искатель тура действительно ищет, т.е. имена всех участников группы. Если мы моделируем этот метод в коде, это дает нам большую гибкость для внесения изменений в Band позже, без необходимости даже касаться TourPromoter:

public class Band {
    private Singer singer;
    private Drummer drummer;
    private Guitarist guitarist;

    public String[] getMembers() {
        return {singer.getName(), drummer.getName(), guitarist.getName()};
    }  
}

public class TourPromoter {
    public String makePosterText(Band band) {
        StringBuilder posterText = new StringBuilder();

        posterText.append(band.getName());
        posterText.append(" featuring: "); 
        for(String member: band.getMembers()) {
            posterText.append(member);
            posterText.append(", ");
        }
        posterText.append("Tickets: £50");

        return posterText.toString();
    }    
}

Если теперь добавить басиста или KeyboardPlayer, только класс группы должен знать разницу, и промоутер тура не нуждается в изменении. Это означает, что теперь мы также соблюдаем принцип Single Responsibility Principle - т.е. Наш метод makePosterText() должен измениться только в том случае, если мы изменим формат нашего плаката, а не если группа изменится.

Я не думаю, что Закон Деметры скажет вам, какой метод вам нужно вытащить, чтобы наилучшим образом выполнить принцип (например, getMembers(), а не getGuitaristsName()), и таким образом, я думайте, что вы правы - это показывает вам, когда все сломано, но не обязательно, как их исправить. Однако, имея в виду LoD, это означает, что вы следите за нарушениями, которые затем могут быть исправлены с помощью комбинации других принципов проектирования, таких как SRP.

Ответ 3

Закон Деметры сам по себе не представляет методологии для хорошего спроса. В лучшем случае это полезная эвристика для выявления потенциально проблемных областей кода. Отклонения от закона можно считать " запах кода

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

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

a.x.getFoo()

к

a.getXFoo()

следует букве закона, не следуя духу (подтверждения для @TedHopp для этого момента). Вместо этого вы должны следить за тем, что пользователи пытаются сделать, когда они пробивают абстракцию a, чтобы получить x foo и соответствующим образом определить этот метод. Это может быть не тривиальная задача, а то, почему нам платят большие деньги:)

Ответ 4

У меня была бы группа, отвечающая на вопрос, кто играет на этом инструменте?

public String whoIsPlaying(String instrument);

Тогда, если вам нужно имя гитариста, вы бы сказали:

band.whoIsPlaying("guitar");

Ничто не мешает вам пинговать и другие инструменты.