Как стать автором
Обновить
886.72
OTUS
Цифровые навыки от ведущих экспертов

Шаблоны GRASP: Low Coupling (низкая связанность) и High Cohesion (высокое зацепление)

Время на прочтение4 мин
Количество просмотров47K
И снова здравствуйте. Меня зовут Владислав Родин. В настоящее время я являюсь руководителем курса «Архитектор высоких нагрузок» в OTUS, а также преподаю на курсах, посвященных архитектуре ПО.

Специально к старту нового набора на курс «Архитектура и шаблоны проектирования» я продолжаю серию своих публикаций про шаблоны GRASP.




Введение


Описанные в книге Craig'а Larman'а «Applying UML and patterns, 3rd edition», GRASP'овские паттерны являются обобщением GoF'овских паттернов, а также непосредственным следствием принципов ООП. Они дополняют недостающую ступеньку в логической лестнице, которая позволяет получить GoF'овские паттерны из принципов ООП. Шаблоны GRASP являются скорее не паттернами проектирования (как GoF'овские), а фундаментальными принципами распределения ответственности между классами. Они, как показывает практика, не обладают особой популярностью, однако анализ спроектированных классов с использованием полного набора GRASP'овских паттернов является необходимым условием написания хорошего кода.

Полный список шаблонов GRASP состоит из 9 элементов:

  • Information Expert
  • Creator
  • Controller
  • Low Coupling
  • High Cohesion
  • Polymorphism
  • Pure Fabrication
  • Indirection
  • Protected Variations

В прошлый раз мы обсудили принцип Creator. Сейчас я предлагаю рассмотреть два принципа GRASP, которые имеет смысл рассматривать только в паре, потому что рассмотрение их по отдельности в пределе приводит к явно плохому коду. Эти принципы могут рассматриваться не только в контексте микропроектирования, но и при проектировании, например, микросервисов. Ниже поговорим о Low Coupling и High Cohesion.

Low Coupling


Формулировка


Необходимо распределить ответственности между классами так, чтобы обеспечить минимальную связанность.

Пример нарушения


Самым ярким примером нарушения этого принципа, с моей точки зрения, является циклическая зависимость (да-да, то, за что обычно отрывают руки на code review):

public class A {
    private int a;
    private B b;
   
    public A(int a) {
        this.a = a;
        this.b = new B(this);
    }
}

public class B {
    private A a;
    
    public B(A a) {
        this.a = a;
    }
}

На UML — диаграмме классов для такой системы можно будет увидеть как зависимость класса A на класс B, так и зависимость класса B на класс A. Почему это плохо? Дело в том, что мы не можем отдать класс A без класса B, также как и класс B без класса A: их нельзя переиспользовать по — отдельности, только вместе. Чем меньше связей между классами — тем лучше, вот о чем говорит нам принцип Low Coupling. Если вспомнить предыдущие разобранные нами принципы: Information Expert и Creator, то можно вспомнить, что соблюдение этих принципов приводит к уменьшению количества ненужных связей между классами.

High Cohesion


Формулировка


Если возвести Low Coupling в абсолют, то достаточно быстро можно прийти к тому, чтобы разместить всю функциональность в одном единственном классе. В таком случае связей не будет вообще, но все при этом понимают, что что-то тут явно не так, ведь в этот класс попадет совершенно несвязанная между собой бизнес — логика. Принцип High Cohesion говорит нам следующее: классы должны содержать связанную бизнес — логику.

Пример нарушения


Давайте рассмотрим класс, представляющий из себя данные с какого — либо счетчика:

@AllArgsConstructor
public class Data {
    private int temperature;
    private int time;
   
   private int calculateTimeDifference(int time) {
      return this.time - time; 
  }

  private int calculateTemperatureDifference(int temperature) {
      return this.temperature - temperature; 
  }
}

Представленный класс содержит бизнес — логику по работе как с температурой, так и со временем. Они не имеют ничего общего, за исключением того, что собираются с одного датчика. Если мы захотим переиспользовать бизнес — логику, связанную по работе только с температурой, то осуществить это легко не получится. Если проводить параллели с принципами SOLID, то класс нарушает SRP: через него проходят две оси изменения.

Кстати говоря, наличие префиксов в названиях часто говорит о том, что принцип High Cohesion нарушается: программист, пишущий этот код, сам понимал, что он работает с двумя классами, с двумя разными контекстами. Чтобы не запутаться, было принято решение о добавлении префиксов.

Пример соблюдения


Имеет смысл создать 2 класса: один для температуры, другой для времени:

@AllArgsConstructor
public class Data {
    private TemperatureData temperatureData;
    private TimeData timeData;
   
   public Data(int time, int temperature) {
       this.temperatureData = new TemperatureData(temperature);
       this.timeData = new TimeData(time);
   }

   // тут логика по работе как со временем, так и с температурой

}

@AllArgsConstructor
public class TimeData {
    private int time;

    private int calculateTimeDifference(int time) {
      return this.time - time; 
  }
}

@AllArgsConstructor
public class TemperatureData {
    private int temperature;

    private int calculateTemperatureDifference(int temperature) {
      return this.temperature - temperature; 
  }
}

Таким образом, бизнес-логика в каждом из классов является «сильно зацепленной», эти классы легко переиспользовать, образуя любые комбинации.

Вывод


Low Coupling и High Cohesion представляют из себя два связанных между собой паттерна, рассматривать которые имеет смысл только вместе. Их суть можно объединить следующим образом: система должна состоять и слабо связанных классов, которые содержать связанную бизнес — логику. Соблюдение этих принципов позволяет удобно переиспользовать созданные классы, не теряя понимания об их зоне ответственности.



Фабричный метод и абстрактная фабрика


Теги:
Хабы:
Всего голосов 4: ↑4 и ↓0+4
Комментарии0

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS