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