вторник, 23 мая 2017 г.

Временная связанность

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


Взгляните на этот пример.

Вот этот код:
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());
  }
}
Что ты об этом думаешь? Полагаю, что понятно, что делает name () - создание списка имен. Чтобы избежать дублирования, существует дополнительная процедура append (), которая преобразует элемент в нижний регистр и добавляет его в список.

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

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

Позвольте мне сначала показать вам лучший (хотя и не лучший!) Дизайн, а затем я попытаюсь объяснить его преимущества:
class Foo {
  public List<String> names() {
    return Foo.with(
      Foo.with(
        new LinkedList(),
        "Jeff"
      ),
      "Walter"
    );
  }
  private static List<String> with(
    List<String> list, String item) {
    list.add(item.toLowerCase());
    return list;
  }
}
Идеальная конструкция для метода with () создаст новый экземпляр List, заполнит его через addAll (список), затем добавит (item) к нему и, наконец, вернется. Это было бы непреложно, но медленно.

Итак, что случилось с этим:
List<String> list = new LinkedList();
Foo.append(list, "Jeff");
Foo.append(list, "Walter");
return list;
Он выглядит совершенно чистым, не так ли? Создайте экземпляр списка, добавьте к нему два элемента и верните его. Да, сейчас чисто. Потому что мы помним, что делает append (). Через несколько месяцев мы вернемся к этому коду, и он будет выглядеть следующим образом:
List<String> list = new LinkedList();
// 10 more lines here
Foo.append(list, "Jeff");
Foo.append(list, "Walter");
// 10 more lines here
return list;
Теперь понятно, что append () добавляет в список «Jeff»? Что произойдет, если я удалю эту строку? Это повлияет на результат, возвращаемый в последней строке? Я не знаю. Мне нужно проверить тело метода append (), чтобы убедиться в этом.

Кроме того, как насчет возвращения списка первым и вызова append ()? Вот что может сделать «рефакторинг» для нашего кода:
List<String> list = new LinkedList();
if (/* something */) {
  return list;
}
// 10 more lines here
Foo.append(list, "Walter");
Foo.append(list, "Jeff");
// 10 more lines here
return list;
Прежде всего, мы возвращаем список слишком рано, когда он не готов. Но кто-нибудь сказал мне, что эти два вызова append () должны произойти до возврата списка? Во-вторых, мы изменили порядок вызовов append (). Опять кто-то сказал мне, что важно называть их в этом конкретном порядке?

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

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

Напротив, в этом проекте нет никакого «порядка»:
return Foo.with(
  Foo.with(
    new LinkedList(),
    "Jeff"
  ),
  "Walter"
);
Он просто возвращает список, который строится с помощью нескольких вызовов метода with (). Это одна строка вместо четырех.

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

То же самое относится и к валидации. Например, этот код плохой:
list.add("Jeff");
Foo.checkIfListStillHasSpace(list);
list.add("Walter");
Хотя это намного лучше:
list.add("Jeff");
Foo.withEnoughSpace(list).add("Walter");
видите разницу?

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

Комментариев нет:

Отправить комментарий