Uploaded by allochka.storozheva

GRASP

advertisement
GRASP
GRASP (GeneralResponsibilityAssignmentSoftwarePatterns) — шаблоны
проектирования, используемые для решения общих задач по назначению
обязанностей классам и объектам.
Известно девять GRAPS шаблонов, изначально описанных в
книге КрейгаЛармана «Применение UML и шаблонов проектирования». GRAPS
паттерны не имеют выраженной структуры, четкой области применения и
конкретной решаемой проблемы, а лишь представляют собой обобщенные
подходы/рекомендации/принципы, используемые при проектировании дизайна
системы. Рассмотрим характеристики основных GRASP шаблонов.
GRASP состоит из 9 шаблонов:

Creator

Controller

PureFabrication (доп)

InformationExpert

HighCohesion

Indirection(доп)

LowCoupling

Polymorphism(доп)

ProtectedVariations(доп)
Основные паттерны
Информационный эксперт (InformationExpert)
Шаблон информационный эксперт является базовым и в то же время самым
очевидным из девяти.
Информационный эксперт описывает основополагающие принципы назначения
обязанностей классам и объектам. Согласно описанию, информационным
экспертом (объектом наделенным некоторыми обязанностями) является объект,
обладающий максимумом информацией, необходимой для выполнения
назначенных обязанностей.
Применение шаблона информационный эксперт повышает связность модулей и
не противоречит свойству инкапсуляции.
Создатель (Creator)
Creator или Создатель — суть ответственности такого объекта в том, что
он создает другие объекты. Сразу напрашивается аналогия с абстрактной
фабрикой.
По сути шаблон проектирования Абстрактная фабрика (создание объектов
концентрируется в отдельном классе) это альтернатива создателя.
Но есть ряд моментов, которые должны выполняться, когда мы наделяем объект
ответственностью создателя:





Создатель содержит или агрегирует создаваемые объекты;
Создатель использует создаваемые объекты ;
Создатель знает, как проинициализировать создаваемый объект ;
Создатель записывает создаваемые объекты
Создатель имеет данные инициализации для A
Возвращаясь к примеру с Customer'ом, рассмотрим следующий .NET код:
Controller
Обязанности по обработке входящих системных сообщений необходимо
делегировать специальному объекту Controller'у.
Controller — это объект, который отвечает за обработку системных событий, и при
этом не относится к интерфейсу пользователя. Controller определяет методы для
выполнения системных операций.
Идея шаблона состоит в том, чтобы не размазывать этот сложный код по всему
приложению. Такой объект как Controller будет превращать многопоточный вход в
один поток. Код бизнес — логики будет однопоточным. Шаблон не описывает то,
как Conroller будет превращать многопоточный вход в один поток, но он должен
это так или иначе делать (ставить запросы в очередь, создавать свою копию
объекта бизнес — логики для каждого потока).
Supervising Controller
В этом варианте MVP(Model-View-Presenter (MVP) — шаблон проектирования,
производный от MVC, который используется в основном для построения
пользовательского интерфейса) представление знает о модели и отвечает за
связывание данных с отображением. Это делает общение между презентером и
View более лаконичным, но в ущерб тестируемости взаимодействия ViewPresenter.
Пример Supervising Controller:
LowCoupling
Низкая связанность, отвечает за то, чтобы объекты в системе знали друг о друге как
можно меньше. Ведь чем меньше объект знает о других объектах, тем больше будет
изолировано и тем меньше правок необходимо будет делать, если в системе что-то
поменяется.
На наших диаграммах все хорошо. Blog ничего не знает о Comment, а степени
связанности у каждого класса составляют всего лишь единицу. В более сложных
системах связей бывает гораздо больше, и шаблон LowCoupling позволяет избежать
следующих проблем:

При изменении в связанных классах, необходимо делать локальные изменения в
данном классе

Понимание каждого класса в отдельности усложняется и требует изучения всех
связанных классов

Повторное использование становится невозможным из-за того, что перетянув
часть системы, необходимо тянуть почти всю систему.
Это пример слабой связи. Здесь мы продемонстрируем, как добиться слабой связи,
применяя механизм внедрения зависимостей. Реализация слабой связи позволяет
начать путешествие с любым классом, который реализовал интерфейс Vehicle.
Шаг 1. Интерфейс автомобиля, позволяющий реализовать слабую связь.
interface Vehicle {
public void move();
}
Шаг 2: Класс Car реализует интерфейс Vehicle .
class Car implements Vehicle {
@Override
public void move() {
System.out.println("Car is moving");
}
}
Шаг 3: Класс Bike реализует интерфейс Vehicle .
class Bike implements Vehicle {
@Override
public void move() {
System.out.println("Bike is moving");
}
}
Шаг 4: Теперь создайте класс Traveler, который содержит ссылку на интерфейс
Vehicle .
class Traveler {
private Vehicle v;
public Vehicle getV() {
return v;
}
public void setV(Vehicle v) {
this.v = v;
}
public void startJourney() {
v.move();
}
}
Шаг 5: Тестовый класс для примера слабой связи - Traveler - пример слабой связи.
public static void main(String[] args) {
Traveler traveler = new Traveler();
traveler.setV(new Car()); // Inject Car dependency
traveler.startJourney(); // start journey by Car
traveler.setV(new Bike()); // Inject Bike dependency
traveler.startJourney(); // Start journey by Bike
}
HighCohesion
Высокая степень зацепления — так переводится название шаблона. Это противовес
предыдущего шаблона. Зацепление — процесс взаимодействия класса с другими
классами системы и область ответственности за действия.
Принцип HighCohesion говорит о том, что класс должен стараться выполнять как можно
меньше не специфичных для него задач.
Имеет смысл создать 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 представляют из себя два связанных между собой
паттерна, рассматривать которые имеет смысл только вместе. Их суть можно
объединить следующим образом: система должна состоять и слабо связанных
классов, которые содержать связанную бизнес — логику. Соблюдение этих
принципов позволяет удобно переиспользовать созданные классы, не теряя
понимания об их зоне ответственности.
Доп. Паттерны
Чистая выдумка (PureFabrication)
PureFabrication или чистая выдумка, или чистое синтезирование. Здесь суть
в выдуманном объекте. Аналогом может быть шаблон Service (сервис)
в парадигме DDD.
Какую проблему решает PureFabrication?



Уменьшает зацепление ( LowCoupling);
Повышает связанность (HighCohesion) ;
Упрощает повторное использование кода.
Давайте рассмотрим пример и все станет на свои места.
К примеру у вас есть объект Customer и следую шаблону информационный
эксперт вы наделили его логикой которую мы показывали выше, как вы
реализуете сохранение Customera в БД?
Так вот следуя PureFabrication принципу, мы создадим Сервис или репозиторий
(место, где хранятся и поддерживаются какие-либо данные) который будет доставать и
сохранять такой объект в базу данных.
Посредник (Indirection)
Indirection или посредник. Можно столкнуться с таким вопросом: «Как определить
ответственность объекта и избежать сильной связанности между объектами, даже
если один класс нуждается в функционале (сервисах), который предоставляет
другой класс?»
Для этого можно присвоить обязанности по обеспечению связи между
компонентами или службами промежуточному объекту.
Если переводить на русский язык, то паттерн подразумевает следующее: любой
объект в коде необходимо вызывать через его интерфейс (тот самый
промежуточный объект).
Такое решение можно сделать с помощью GoF паттерна медиатор
мы можем переписать этот код с помощью mediator для связки между объектами:
Полиморфизм (Polymorphism)
Полиморфизм позволяет реализовывать одноименные публичные методы,
позволяя различным классам выполнять различные действия при одном и том же
вызове.
Необходимо обрабатывать различные варианты поведения на основании типа,
допуская замену частей системы.
Предлагается распределить обязанности между классами с использованием
полиморфных операций, оставив каждой внешней системе свой интерфейс. В
качестве примера можно привести стандартизованные библиотеки, либо
конфигурацию приложения путем подключения тех или иных плагинов для разных
заказчиков под их нужды.
Злоупотребление полиморфизмом приводит к переусложнению кода и в общем
случае не приветствуется.
Принцип полиморфизма является основополагающим в ООП. В этом контексте
принцип тесно связан с GoF паттерном strategy. Это самый яркий пример
реализации полиморфизма.
Устойчивость к изменениям (ProtectedVariations)
Необходимо спроектировать систему так, чтобы изменение одних ее элементов
не влияло на другие. В качестве решения предлагается идентифицировать точки
возмоджных изменений или неустойчивости и распределить обязанности таким
образом, чтобы обеспечить устойчивую работу системы.
По мнению многих это самый важный принцип который косвенно связан с
остальными принципами GRASP. В настоящее время одним из наиболее важных
показателей качества кода является простота изменений. Как архитекторы и
программисты, мы должны быть готовы к постоянно меняющимся требованиям.
Это не является «nicetohave» атрибутом - это «must-have» в любом приложении и
наша обязанность как программистов и архитекторов нашей системы это
обеспечить.
Вывод
Шаблоны GRASP состоят из 8 паттернов:
1. Information Expert — информацию обрабатываем там, где она содержится.
2. Creator — создаем объекты там, где они нужны.
3. Controller — выносим логику многопоточности в отдельный класс или
компонент.
4. Low Coupling 5) High Cohesion — проектируем классы с однородной бизнеслогикой и минимальным количеством связей между собой.
5. Polymorphism — различные варианты поведения системы при
необходимости оформляем в виде полиморфных вызовов.
6. Pure Fabrication — не стесняемся создавать классы, не имеющие аналог в
предметной области, если это необходимо для соблюдения Low Coupling и
High Cohesion.
7. Indirection — любой класс вызываем через его интерфейс.
8. Protected Variations — применяя все вышесказанное, получаем устойчивый
к изменениям код
https://habr.com/ru/post/38323/
https://habr.com/ru/company/otus/blog/521476/
https://habr.com/ru/post/92570/
https://bool.dev/blog/detail/grasp-printsipy#polymorphism
https://habr.com/ru/company/otus/blog/507600/
https://habr.com/ru/company/otus/blog/505852/
https://intellect.icu/printsipy-patterny-grasp-7919
https://www.sourcecodeexamples.net/2018/06/low-coupling-grasp-pattern.html
Download