Применение Закона Деметры таким образом, что это не улучшает дизайн

Предположим, у нас есть следующий класс Boy, который пытается договориться о свидании с Girl, анализируя ее расписание (пример на Java):

public class Boy {

  public boolean tryArrangeDate(Girl girl, Date time) {
    boolean success = true;
    for (GirlRoutine routine : girl.getSchedule()) {
      if (routine.happensAt(time)) {
        success = false;
        break;
      }
    }
    return success;
  }

}

Метод Boy.tryArrangeDate() явно нарушает Закон Деметры из-за вызова routine.happensAt(). Один из способов решить эту проблему — перенести анализ расписания непосредственно в Girl и избежать зависимости от GirlRoutine. Вероятно, это было бы одним из лучших решений в данном случае. RFC для класса Boy будет сокращен.

Но предположим, что мы выбираем другое направление для устранения нарушений Закона Деметры и меняем код таким образом:

public class Boy {

  public boolean tryArrangeDate(Girl girl, Date time) {
    return isFree(girl.getSchedule(), time);
  }

  private boolean isFree(List<GirlRoutine> schedule, Date time) {
    boolean free = true;
    for (GirlRoutine routine : schedule) {
      if (happensAt(routine, time)) {
        free = false;
        break;
      }
    }
    return free;
  }

  private boolean happensAt(GirlRoutine routine, Date time) {
    return routine.happensAt(time);
  }

}

Добавлены два частных метода, которые просто делегируют вызовы Girl и ее расписанию/процедурам.

Каждый метод, взятый по отдельности, кажется, не нарушает Закон Деметры (для простоты будем рассматривать извлечение элемента из коллекции как примитивную операцию без вызова метода). Но в целом мы не уменьшили RFC для этого класса, не улучшили связность и фактически увеличили WMC. Принцип Расскажи, не спрашивай не сохраняется. Итак, закон Деметры удовлетворен, но дизайн все еще хлипкий.

Вопрос: Правда ли, что (формально) второй фрагмент кода не нарушает Закон Деметры?

ПРИМЕЧАНИЕ. Цель вопроса НЕ в том, чтобы найти альтернативные решения, а в том, чтобы подтвердить/опровергнуть, что решение соответствует Закону Деметры


person Volodymyr Tsukur    schedule 07.09.2013    source источник
comment
Я не понимаю, как скрытие доступа к свойствам за вызовами функций в том же классе Boy что-то меняет. Вы воспринимаете вещи слишком буквально. Если (и это теоретически, если) вы хотите улучшить эту сторону вещей, вы должны переместить метод isFree в класс Girl, чтобы tryArrangeDate стал return girl.isFree(date), как вы упомянули.   -  person Jon    schedule 07.09.2013
comment
@Jon Введение isFree в класс Girl - это нормальный способ обработки вещей. Это мне понятно и это описано в формулировке вопроса. Все, что я хочу, это проверить, нарушает ли второй фрагмент кода Закон Деметры или нет, вот и все!   -  person Volodymyr Tsukur    schedule 07.09.2013
comment
Немного изменил вопрос, чтобы сделать его более понятным.   -  person Volodymyr Tsukur    schedule 07.09.2013


Ответы (1)


Обновить

Поскольку вы просто спрашиваете, нарушает ли второй Закон Деметры, то да, я бы сказал, что это так. Независимо от того, как вы рефакторите tryArrangeDate, это не меняет того факта, что вызывается метод из GirlRoutine. Когда в Законе Деметры упоминается «любой метод объекта», я понимаю, что он относится к методам, доступным другим объектам, методам, открытым для «внешнего мира». Приватные методы во втором фрагменте кода являются просто вспомогательными методами для tryArrangeDate.

Изначально

Класс Girl является «экспертом», когда дело касается его собственных GirlRoutine. Насколько это возможно, вы должны скрывать это от кого-либо еще. Помните принцип говори, не спрашивай.

Возможно, вы можете сделать:

Response response = girl.askOut(boy, date);

Не читайте это так, как будто экземпляр Girl спрашивает экземпляр Boy; Эмпирическое правило, которому я следую (в большинстве случаев), заключается в том, что объект, из которого вы вызываете метод, является прямым объектом действия, которое представляет метод. В этом случае непосредственным объектом askOut является экземпляр Girl.

Класс Response может иметь информацию о том, как все прошло, или это может быть что-то простое, например:

enum Response { ACCEPTED, REJECTED }

Таким образом, экземпляр Girl имеет все необходимое, чтобы принять или отклонить запрос экземпляра Boy.

person Psycho Punch    schedule 07.09.2013
comment
Спасибо за ваш ответ. Второй фрагмент кода в вопросе нарушает [скажи, не спрашивай][pragprog.com/articles /скажи-не спрашивай] принцип. Это кристально ясно. Мой вопрос заключается в том, нарушил ли этот фрагмент кода LoD или нет. - person Volodymyr Tsukur; 07.09.2013
comment
Хорошо, я обновил свой ответ. Причина, по которой я упомянул принцип «скажи, не спрашивай», заключается в том, что они идут рука об руку, как указано в ссылке, которую я предоставил. - person Psycho Punch; 08.09.2013