Temporal Coupling Between Method Calls

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

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

class Foo {
  public List<String> names() {
    List<String> list = new LinkedList();
    Foo.append(list, "Jeff");
    Foo.append(list, "Walter");
    return list;
  }
  private static void append(
    List<String> list, String item) {
    list.add(item.toLowerCase());
  }
}

Что вы об этом думаете? Я считаю, что понятно, что делает функция names() — она создает список имен. Чтобы избежать дублирования, существует вспомогательная процедура append(), которая преобразует элемент в нижний регистр и добавляет его в список.

Это плохой дизайн.

Это процедурный дизайн, и между строками метода names() существует временная связь.

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

Идеальное решение для метода with() создало бы новый экземпляр List, заполнило бы его с помощью addAll(list), затем добавило бы элемент с помощью add(item) и, наконец, возвратило бы его. Это было бы идеально неизменяемым, но медленным.

Итак, что не так с этим:

Выглядит абсолютно чисто, не так ли? Создайте список, добавьте в него два элемента и верните его. Да, сейчас он чистый - на данный момент. Потому что мы помним, что делает append(). Через несколько месяцев, когда мы вернемся к этому коду, он будет выглядеть так:

Теперь понятно, что append() фактически добавляет "Jeff" в list? Что произойдет, если я удалю эту строку? Это повлияет на возвращаемый результат в последней строке? Я не знаю. Мне нужно проверить тело метода append(), чтобы убедиться.

Как насчет сначала возвращать list и затем вызывать append()? Вот что возможно сделает “рефакторинг” в нашем коде:

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

Никто. Нигде. Это называется временной связью.

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

Наоборот, в этом дизайне нет никакого “порядка”:

Он просто возвращает список, который формируется несколькими вызовами метода with(). Это одна строка вместо четырех.

Как уже обсуждалось ранее, идеальный метод в ООП должен содержать только один оператор, и этим оператором является return.

То же самое верно и для проверки. Например, этот код некорректен:

В то время как этот гораздо лучше:

See the difference?

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

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-28 at 14:53

sixnines availability badge   GitHub stars