Сайт о телевидении

Сайт о телевидении

» » Шаблоны проектирования GRASP. Паттерны GRASP

Шаблоны проектирования GRASP. Паттерны GRASP

GRASP (General Responsibility Assignment Software Patterns) - паттерны проектирования, используемые для решения общих задач по назначению обязанностей классам и объектам .

Известно девять GRAPS паттернов, изначально описанных в книге Крейга Лармана «Применение UML и шаблонов проектирования». В отличие от рассмотренных ранее паттернов GoF, GRAPS паттерны не имеют выраженной структуры, четкой области применения и конкретной решаемой проблемы, а представляют собой обобщенные подходы, используемые при проектировании дизайна системы. Формально их можно отнести к принятой в GoF классификации на порождающие, структурные и паттерны поведения.

Для более детального понимания GRASP паттернов приведем примеры их использования из предметной области морских грузоперевозок.

Головной офис компании состоит из трех отделов: отдел по работе с клиентами, отдел диспетчеризации рейсов, отдел комплектации рейсов. Менеджеры из отдела по работе с клиентами оформляют договоры на доставку груза из пункта А в пункт В. Диспетчеры из отдела диспетчеризации рейсов отслеживают положение судов. Администраторы из отдела комплектации рейсов формируют грузовые рейсы на основании договоров с клиентами.

Информационный эксперт (Information Expert) -

структурный паттерн

Паттерн Информационный эксперт является базовым и в то же время самым очевидным из девяти. Информационный эксперт описывает основополагающие принципы назначения обязанностей классам и объектам. Согласно описанию Информационным экспертом (объектом, наделенным некоторыми обязанностями) является объект, обладающий максимумом информации, необходимой для выполнения назначенных обязанностей.

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

Низкая связанность (Low Coupling) - структурный -

и Высокое зацепление (High Cohesion) - паттерн поведения

Реализует основополагающий принцип объектно-ориентированного проектирования. Считается, что любая спроектированная система должна удовлетворять принципам низкой связности и высокого зацепления модулей. Соответствие данным паттернам позволяет легко модифицировать и сопровождать программный код, а также повышает степень его повторного использования. Мера связности модулей определяется количеством информации, которой располагает один модуль о природе другого. В свою очередь, мера зацепления модуля определяется степенью сфокусированности его обязанностей. Существуют методологии, согласно которым меры связности и зацепления можно оценить по шкале от 1 до 10 для конкретного случая.

Примером хорошего дизайна системы может служить набор утилит GNU Binutils для Linux, где каждая утилита (если ее рассматривать как модуль) выполняет лишь минимальные обязанности (высокое зацепление) и почти ничего не знает о природе других утилит (низкая связность), в связи с чем может быть легко заменена на аналог в некотором варианте использования.

Устойчивый к изменениям (Protected Variantions) - структурный

Проблема модификации системы наиболее актуальна в условиях динамически изменяющихся требований. Зачастую удается выделить так называемые точки неустойчивости системы, которые наиболее часто будут подвержены изменению/модификации. Тогда сущность паттерна Устойчивый к изменениям заключается в устранении точек неустойчивости путем определения их в качестве интерфейсов и реализации для них различных вариантов поведения.

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

Контроллер (Controller) - паттерн поведения

Контроллер отвечает за обработку входных системных событий, делегируя обязанности по их обработке компетентным классам. В общем случае Контроллер реализует один или несколько вариантов использования.

Известно понятие внешнего Контроллера (Front Controller), который представляет всю систему в целом (агрегирует весь функционал системы в одном классе).

Примером Контроллера является класс Administrator, реализующий вариант использования системы администраторов из отдела комплектации рейсов. Использование Контроллеров позволяет отделить логику от представления, тем самым повышая возможность повторного использования кода.

Полиморфизм (Polymorphism) - паттерн поведения

Паттерн Полиморфизм позволяет обрабатывать альтернативные варианты поведения на основе типа. При этом альтернативные реализации приводятся к обобщенному интерфейсу. Рассмотрим интеграцию системы с внешними компонентами расчета тарифов на перевозку груза. Классы Loca/Tarificator и WorldWideTarificator являются Адаптерами к соответствующим внешним компонентам. Применение паттерна Полиморфизм позволяет в будущем легко модифицировать систему.

Чистая выдумка (Pure Fabrication) - паттерн поведения

Существует понятие модели программирования по предметной области, согласно которой каждой сущности из предметной области соответствует один или более классов программной среды. При этом обязанности взаимодействия сущностей, как правило, накладываются на них самих. Такой подход имеет очевидный недостаток - высокая связность модулей системы. Паттерн Чистая выдумка позволяет решить данную проблему путем введения в программную среду дополнительного класса (не отражающего реальной сущности из предметной области) и наделения его требуемыми обязанностями. Например, обязанности по сохранению информации о клиентах судоходной компании можно назначить выдуманному классу ClientsSaver. Применение паттерна Чистая выдумка позволяет дизайну системы соответствовать принципам низкой связности и высокого зацепления.

Перенаправление (Indirection) - паттерн поведения

Паттерн Перенаправление реализует низкую связность между классами путем назначения обязанностей по их взаимодействию дополнительному объекту-посреднику. Примером данного паттерна может служить класс ClientsSaver (см. Чистая выдумка), который является промежуточным слоем между сущностями клиентов и хранилищем, в котором они будут сохранены. Кроме того, контроллер из триады МУС является посредником между данными их их представлением.

Шаблоны проектирования GRASP

GRASP (англ. General Responsibility Assignment Software Patterns (общие образцы распределения обязанностей)) - паттерны, используемые в объектно-ориентированном проектировании для решения общих задач по назначению обязанностей классам и объектам .

В книге Крейга Лармана «Применение UML и шаблонов проектирования» описано 9 таких образцов. Каждый из них помогает решить некоторую проблему, возникающую при объектно-ориентированном анализе, и которая возникает практически в любом проекте по разработке программного обеспечения. Таким образом, GRASP-паттерны - это хорошо документированные, стандартизированные и проверенные временем принципы объектно-ориентированного анализа, а не попытка привнести что-то принципиально новое.

Каталог паттернов

Ниже следует краткая характеристика девяти известных паттернов.

Information Expert (Информационный эксперт)

Шаблон Information Expert определяет базовый принцип назначения обязанностей. Он утверждает, что обязанности должны быть назначены объекту, который владеет максимумом необходимой информации для выполнения обязанности. Такой объект называется информационным экспертом . Возможно, этот шаблон является самым очевидным из девяти, но вместе с тем и самым важным.

Если дизайн не удовлетворяет этому принципу, то при программировании получается спагетти-код , в котором очень трудно разбираться. Локализация обязанностей позволяет повысить уровень инкапсуляции и уменьшить уровень связанности . Кроме читабельности кода повышается уровень готовности компонент к повторному использованию.

Creator (Производитель)

Паттерн Creator решает, кто должен создавать объект. Фактически, это применение шаблона Information Expert к проблеме создания объектов. Более конкретно, нужно назначить классу B обязанность создавать экземпляры класса A, если выполняется как можно больше из следующих условий:

  • Класс B содержит (contains) или агрегирует (aggregate) объекты A.
  • Класс B записывает (records) экземпляры объектов A.
  • Класс B активно использует (closely uses) объекты A
  • Класс B обладает данными инициализации (has the initializing data) для объектов A.

Альтернативой производителю является образец Фабрика . В этом случае создание объектов концентрируется в отдельном классе.

Controller (Контроллер)

Контроллер берёт на себя ответственность за выполнение операций, приходящих от пользователя и часто выполняет сценарий одного или нескольких вариантов использования (например, один контроллер может обрабатывать сценарии создания и удаления пользователя). Как правило, контроллер не выполняет работу самостоятельно, а делегирует обязанности компетентным объектам.

Иногда класс-контроллер представляет всю систему в целом, корневой объект, устройство или важную подсистему (внешний контроллер ).

Low Coupling (Слабая связанность)

Low Coupling - это оценочный образец (или принцип), который устанавливает следующие свойства:

  • малое число зависимостей между классами (подсистемами)
  • слабая зависимость одного класса (подсистемы) от изменений в другом классе (подсистеме)
  • высокая степень повторного использования подсистем

High Cohesion (Сильное сцепление)

High Cohesion - это также оценочный образец (или принцип), который, в отличие от предыдущего, задаёт свойство сильного сцепления внутри подсистем (поэтому никакого противоречия с предыдущим паттерном нет - наоборот, они дополняют друг друга). Классы (подсистемы) таким образом получаются сфокусированными, управляемыми и понятными.

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

Polymorphism (Полиморфизм)

Полиморфизм позволяет обрабатывать альтернативные варианты поведения на основе типа и заменять подключаемые компоненты системы. Обязанности распределяются для различных вариантов поведения с помощью полиморфных операций для этого класса. Все альтернативные реализации приводятся к общему интерфейсу.

Пример: Интеграция разрабатываемой системы с различными внешними системами учета налогов. Используются локальные программные объекты, обеспечивающие адаптацию (Адаптеры), при отправке сообщения к такому объекту выполняется обращение к внешней системе с использованием ее собственного программного интерфейса. Использование полиморфизма здесь оправдано для возможной адаптации к различным внешним системам.

Pure Fabrication (Чистая выдумка)

Чистая выдумка - это класс, не отражающий никакого реального объекта предметной области, но специально придуманный для усиления связности, ослабления связанности или увеличения степени повторного использования. Pure Fabrication отражает концепцию сервисов в модели Программирование от предметной области.

Indirection (Посредник)

Образец Indirection поддерживает слабую связанность (и возможность повторного использования) путём назначения обязанностей посредника между ними промежуточному объекту. Пример: образец

Protected Variations (Сокрытие реализации)

Образец Protected Variations защищает элементы от изменения других элементов (объектов или подсистем) с помощью вынесения взаимодействия в фиксированный

Шаблоны объектно-ориентированного проектирования GRASP February 8th, 2009

Current Mood: creative

На одном собеседовании меня спросили: "знаете ли Вы, что такой GRASP?". К своему стыду, я даже понятия не имел, что эта аббревиатура означает. Потом, все же, человек, проводивший интервью сознался, что этот вопрос был со звездочкой и ответ на него был не обязателен. И действительно, у каждого специалиста свои тараканы в голове и разобраться с каждой имеющейся аббревиатуре почти нереально и все же, после того, как я все-таки посмотрел, что такое GRASP, меня охватил еще больший стыд, как оказалось название GRASP объединяет фундаментальные принципы, положенные в основу объектно-ориентированного проектирования. Принципы эти на интуитивном уровне известны каждому разработчику, хоть раз прикасавшемуся к ООП, однако их систематическое именование и повторное использование в виде шаблонов оставалось за рамками трансцендентного процесса проектирования ПО. Настала пора снять завесу тайны и назвать вещи своими именами.

GRASP - General Responsibility Assignment Software Patterns (основные шаблоны распределения обязанностей в программном обеспечении). Это методический подход к объектному проектированию. Эти шаблоны называют также шаблонами распределения обязанностей. Под "шаблоном" в данном контексте, да и в любом контексте, связанном с разработкой ПО, понимается именованная пара "проблема/решение", содержащая рекомендации для применения в различных конкретных ситуациях. Шаблоны GRASP относятся к этапу проектирования и отвечают за взаимосвязь объектов в системе. GRASP состоит из 5 основных и 4 дополнительных шаблонов.

Основные шаблоны:

    Information Expert

Дополнительные шаблоны:

    Pure Fabrication

  • Protected Variations

Information Expert

Шаблон решает проблему распределения обязанностей между объектами в объектно-ориентированной системе. Под обязанностью в контексте GRASP понимается некое действие (функция) объекта. Т.о. Information Expert дает рекомендации касательно того, какие функции должен выполнять тот или иной объект. А решение очень простое и понятное без доказательств: назначать обязанность следует информационному эксперту - классу, у которого имеется информация, требуемая для выполнения обязанности. Конечно, не всегда бывает так, что вся необходимая информация заключена в одном классе (это, кстати, идет в противоречие с паттерном High Cohesion, о котором позже), чаще она распределена между различными классами, тогда каждый из этих классов будет являться частичным экспертом, т.е. будет предоставлять только доступную ему информацию, а при общем взаимодействии будет решена общая задача.

Шаблон Creator решает проблему о том, кто должен создавать экземпляры новых классов. Решение состоит в назначении классу B обязанностей создавать экземпляры класса A, если выполняется одно из условий:

    Класс B агрегирует (aggregate) объекты A

    Класс B содержит (contains) объекты A

    Класс B записывает (records) экземпляры объектов A

    Класс B активно использует (closely uses) объекты A

    Класс B обладает данными инициализации (has the initializing data), которые будут передаваться объектам A при их создании (т.е. при создании объектов А класс В является экспертом)

Смысл паттерна снова интуитивно понятен, каждый разработчик использовал его, руководствуясь здравой логикой, сам того не осознавая. Тем не менее, за кажущейся простотой скрывается масса преимуществ: поддерживается шаблон Low Coupling, снижаются затрат на сопровождение и обеспечивается возможности повторного использования созданных компонентов в дальнейшем.

Controller

Шаблон сontroller решает давнюю проблему разделения интерфейса и логики в интерактивном приложении. Это не что иное, как C из аббревиатуры MVC. Этот шаблон отвечает за то, к кому именно должны обращаться вызовы из V (View), и кому C должен делегировать запросы на выполнение (какая модель M должна их обработать). Если обобщить назначение сontroller, то он должен отвечать за обработку входных системных сообщений. Под системными сообщениями здесь понимаются события высокого уровня, генерируемые внешним исполнителем. Чтобы решить поставленную проблему необходимо делегировать обязанности обработки системных сообщений классу, удовлетворяющему одному из следующих условий:

    Класс представляет всю систему в целом, устройство или подсистему (внешний контроллер)

    Класс представляет сценарий некоторого прецедента, в рамках которого выполняется обработка всех системных событий, и обычно называется <Прецедент>Handler, <Прецедент>Coordinator или <Прецедент>Session (контроллер прецедента или контроллер сеанса). Для всех системных событий в рамках одного сценария прецедента используется один и тот же класс-контроллер. Неформально, сеанс - это экземпляр взаимодействия с исполнителем. Сеансы могут иметь произвольную длину, но зачастую организованы в рамках одного прецедента (сеанса прецедента).

Как бы заумно не звучало все вышеперечисленное, на самом деле все предельно просто: контроллер это своеобразный вид интерфейса между уровнями предметной области и графического представления. Первым типом контроллеров является внешний контроллер (facade controller), представляющий всю систему, устройство или подсистему. Если применяется контроллер прецедента (use case controller), то для каждого прецедента должен существовать отдельный контроллер. Не объект предметной области, а искусственная конструкция.

Low Coupling

Шаблон решает проблему связности. Связность можно определить как количество точек соприкосновения классов между собой. Известно, что чем ниже связность классов, тем меньше их взаимовлияние, тем выше возможность повторного использования. А рекомендация здесь простая: распределять обязанности между классами надо таким образом, чтобы уменьшить связность.

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

Высокая степень связывания сама по себе не является проблемой. Проблемой является жесткое связывание с неустойчивыми в некотором отношении элементами.

High Cohesion

Шаблон решает общую проблему управления сложностью за счет регулирования степени зацепления классов. Разница между связностью и зацеплением очень тонка, и понять ее необходимо на более глубоком интуитивном уровне. Зацепление (cohesion) (или более точно, функциональное зацепление) - это мера связанности и сфокусированности обязанностей класса. Считается, что элемент обладает высокой степенью зацепления, если его обязанности тесно связаны между собой и он не выполняет огромных объемов работы. Класс с низкой степенью зацепления выполняет много разнородных функций или несвязанных между собой обязанностей.

High Cohesion твердит, что класс должен стараться выполнять как можно меньше не специфичных для него задач, и иметь вполне определенную область применения. Только с опытом приходит понимание балансировки между High Cohesion и Low Coupling. считается что связывание и зацепление это инь и янь проектирования ПО. Некорректное связывание порождает неправильное зацепление и наоборот.

Pure Fabrication

Формулировки и мотивации дополнительных шаблонов GRASP не так очевидны. И без примеров здесь трудно ориентироваться. Проблемой, решаемой Pure Fabrication, является обеспечение реализации шаблонов High Cohesion и Low Coupling или других принципов проектирования, если шаблон Expert (например) не обеспечивает подходящего решения. Решением может быть введение служебного класса, не представляющего понятия конкретной предметной области, однако имеющего высокую степень зацепления.

Например, классу X необходимо сохранять информацию в реляционной базе данных. Согласно шаблону Information Expert, эту обязанность можно присвоить самому классу X. Однако следует принять во внимание, что Данная задача требует достаточно большого числа специализированных операций, поэтому класс X будет иметь низкую степень зацепления, класс X будет связан с интерфейсом БД, что повысит связность, кроме того задача записи в БД является довольно общей, поэтому необходимо обеспечить повторное использование кода. Естественным решением данной проблемы является создание нового класса, ответственного за сохранение объектов некоторого вида на постоянном носителе, например в реляционной базе данных. Его можно назвать PersistentStorage. Это чисто синтетический объект, что полностью соответствует Pure Fabrication. В итоге решаются задачи зацепления, связности и повторного использования.

Indirection

Шаблон решает проблему прямой связности. Решением является введение промежуточного объекта для обеспечения связи между другими компонентами или службами, которые не связаны между собой напрямую.

Пример, рассмотренный выше, с введением класса PersistentStorage является также и примером шаблона Indirection, т.к. дополнительный класс будет являться промежуточным звеном между БД и классом X. Примерами специализированных вариантов Indirection являются шаблоны Adapter, Facade, Observer (см. шаблоны Gang of Four). Целью введения промежуточного звена является обеспечение слабой связности за счет отделения друг от друга различных компонентов.

Polymorphism

Принцип полиморфизма является основополагающим в ООП. В данном контексте шаблон Polymorphism решает проблему обработки альтернативных вариантов поведения на основе типа. Решать эту проблему стоит с использование полиморфных операций, а не с помощью проверки типа и условной логики. Кроме того, при помощи полиморфизма легко создавать подключаемые компоненты. В итоге получаем следующие преимущества:

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

    Новые вариации можно вводить без модификации клиентской части приложения.

Protected Variations

Основная проблема, решаемая шаблоном Protected Variations: как спроектировать объекты, подсистемы и систему, чтобы изменение этих элементов не оказывало нежелательного влияния на другие элементы? необходимо идентифицировать точки возможных вариаций или неустойчивости; распределить обязанности таким образом, чтобы обеспечить устойчивый интерфейс. Шаблон PV описывает ключевой принцип, на основе которого реализуются механизмы и шаблоны программирования и проектирования с целью обеспечения гибкости и защиты системы от влияния изменений внешних систем.

Про принципы SOLID в сети есть много информации. В каких-то местах – она заумная до ужаса, в каких-то – описано понятным человеческим языком. Почему-то, в последнее время я не могу терпеть слишком заумных объяснений. На поверку дня убеждаюсь, что человек, который действительно знает о чем говорит, всегда может объяснить вещи “человеческим” или более понятным языком, чем принято в кандидатских работах.
Но про принципы GRASP написано немного, а многое из того что написано – отравляет понимание своей заумностью. Конечно вопрос не из простых. Но и сложность тоже не космическая.
Итак, пункт первый – зачем эти принципы?
Объектно-ориентированное программирование (ООП) – я бы сказал, что это набор основных концепций (абстракция, инкапсуляция, наследование, полиморфизм), конструкций (классы, методы) и принципов. ООП – это модель, обобщенная модель части предметной или объектной области для программирования окружающего. Это программирование по обобщенной модели, выражение объектной модели в терминах языка программирования. И модель эта, как и любая другая модель, - осознано ограничена.
Так вот, громадной частью ООП является набор принципов. И тут вроде бы все ясно. Только вот принципов много. И разные авторы выделяют как основные иногда схожие, а иногда и разные принципы, и что удивительно – все правильные.
Моделировать – не сложно. Только проблема в том что мы все таки имеем дело с моделью. А когда объекты (сущности) уже созданы, тогда вступает в игру именно программирование и проектирование. Объекты должны взаимодействовать и при этом модель должна быть гибкой и простой. Вот тут нам и помогают принципы.
GRASP – это набор принципов по версии такого эксперта как Крэг Ларман , который написал о них в своей книге - Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development

Кстати, книга есть в переводе на русский . Книга как и книга GoF(Gang of four) заняла свое место в истории и соответственно принципы GRASP, которым посвящена малая часть книги тоже.
Возможно, они не так популярны как скажем принципы SOLID, скорее всего, мне кажется, потому что они определенны более обобщенными. Эти принципы более абстрактные чем шаблоны GoF или SOLID.
Итак принципы GRASP, точнее сказать не принципы, а шаблоны в оригинале. General Responsibility Assignment Software Patterns – это можно перевести так – паттерны распределения ответственности. Суть в том, что это не строгие паттерны как у GoF, это скорее тот смысл, которым мы наделяем объекты. Так что принципы распределения общей ответственности подходит больше чем паттерны. И поэтому название статьи не шаблоны GRASP как было бы идеологически верно, а все таки принципы GRASP.
GRASP выделяет следующие принципы-шаблоны:

  • Information Expert (Информационные эксперт)
  • Creator (Создатель)
  • Controller (Контроллер)
  • Low Coupling (Слабая связанность)
  • High Cohesion (Высокая сцепленность)
  • Pure Fabrication (Чистая выдумка или чистое синтезирование)
  • Indirection (Посредник)
  • Protected Variations (Сокрытие реализации или защищенные изменения)
  • Polymorphism(Полиморфизм)
Теперь давайте рассмотрим каждый из них по порядку.
Information Expert
Информационный эксперт или просто эксперт – это скорее ответственность. Экспертом может быть любой класс. Тут даже дело не в проектировании, а в осведомленности. Зачем нам нужен информационный эксперт? Затем, что если объект владеет всей нужной информацией для какой-то операции или функционала, то значить и этот объект будет выполнять либо делегировать выполнение этой операции.
Итак рассмотрим пример. Есть некая система продаж. И есть класс Sale (продажа). Нам необходимо посчитать общую сумму продаж. Тогда кто будет считать общую сумму по продажам? Конечно же класс – Sales, потому что именно он обладает всей информацией необходимой для этого.
Creator
Creator или Создатель – суть ответственности такого объекта в том, что он создает другие объекты. Сразу напрашивается аналогия с фабриками. Так оно и есть. Фабрики тоже имеют именно ответственность – Создатель.
Но есть ряд моментов, которые должны выполнятся, когда мы наделяем объект ответственность создатель:
1. Создатель содержит или агрегирует создаваемые объекты
2. Создатель использует создаваемые объекты
3. Создатель знает как проинициализировать создаваемый объект
4. Создатель записывает создаваемые объекты (эту штуку я до конце не понял на самом деле)
Controller
Уже где-то слышали, не правда ли? Controller или Контролер – это объект-прослойка между UI логикой и предметной (бизнес) логикой приложения. Создаем контроллер так чтобы все вызовы от UI перенаправлялись именно ему и соответственно все данные UI тоже получает через него.
Напоминает MVC, MVP? Так и есть. Это по сути Presenter из MVP и контроллер из MVC. Разница между MVC и MVP есть, но это касается только направлений вызовов, ну и это тему естественно другой беседы.
Итак, котроллер отвечает на такой вопрос: “Как UI должен взаимодействовать с доменной логикой приложения?” или просто “Как взаимодействовать с системой?”. Это чем то напоминает фасад. Фасад тоже предоставляет облегченный доступ к целой подсистеме объектов. Так и тут контроллер для UI своего рода фасад которые предоставляет доступ к целой подсистеме бизнес логики.
Low Coupling
Тоже известная штука. Low Coupling или Слабая связанность. Если объекты в приложении сильно связанны то любой изменение приводит к изменениям во всех связанных объектах. А это неудобно и порождает баги. Вот по-этому везде пишут что необходимо чтобы код был слабо связан и зависел от абстракций.
Например если наш класс Sale реализует интерфейс ISale и другие объекты зависят именно от ISale, т.е. от абстракции, то когда мы захотим внести изменения касательно Sale – нам нужно будет всего лишь подменить реализацию.
Low Coupling встречается и в SOLID принципах в виде – Dependency Injection. Сейчас можно часто услышать такой принцип. Но суть остается прежней: “Программируйте на основе абстракций (интерфейс, абстрактный класс и т.п.), а не реализаций ”.
High Cohesion
High Cohesionили высокая сцепленность – это соотносится к слабой связанности, они идут в паре и одно всегда приводит к другому. Это как инь и янь, всегда вместе. Дело в том что наши классы когда мы их задумываем имеют какую-то одну ответственность (Single resposibility principle), например Sale(продажа) обладает всеми ответственностями которые касаются продаж, например как мы уже говорили вычисление общей суммы – Total. Но давайте представим что мы совершили оплошность и привнесли в Sale еще такую ответственность как Payment (платеж). Что получится? Получится что одни члены класса которые касаются Sale буду между собой достаточно тесно связанны, и также членные класса которые оперируют с Payment между собой будут тесно связаны, но в целом сцепленность класса SaleAndPayment будет низкой, так как по сути мы имеем дело с двумя обособленными частями в одном целом. И резонно будет провести рефакторинг и разделить класс SaleAndPayment на Sale и Payment, которые внутри будут тесно связанны или по другому сцеплены.
Так что высокая сцепленность это как мера того что мы не нарушаем single resposibility principle. Вернее сказать, выскоая сцепленность получается в результате соблюдения такого приципа из SOLID как single resposibility principle (SRP).
Основной вопрос на который дает ответ высокая сцепленность – “Как поддерживать объекты сфокусированными на одной ответственности, понятными, управляемыми и как побочный эффект иметь слабо связанный код?”. Их разделять. Подробнее это описано в 17 главе книги Лармана.
Pure Fabrication
Pure Fabrication или чистая выдумка или чистое синтезирование. Суть в выдуманном объекте. Такой себе принцип-хак. Но без него никак. Аналогом может быть шаблон Service(сервис) в парадигме DDD.
Иногда, сталкиваемся с таким вопросом: “Какой объект наделить ответственностью, но принципы информационный эксперт, высокая сцепленность не выполняются или не подходят?”. Использовать синтетический класс который обеспечивает высокую сцепленность. Тут без примера точно не разобраться.
Итак – ситуация. Какой класс должен сохранять наш объект Sale в базу данных? Если подчиняется принципу “информационный эксперт”, то Sale, но наделив его такой ответственностью мы получаем слабую сцепленность внутри него. Тогда можно найти выход, создав синтетическую сущность – SaleDao или SaleRepository, которая будет сильно сцеплена внутри и будет иметь единую ответственность – сохранять Sale в базу.
Так как мы выдумали этот объект а не спроектировали с предметной области, то и он подчиняется принципу “чистая выдумка”.
Indirection
Indirection или посредник. Можно столкнутся с таким вопросом: “Как определить ответственность объекта и избежать сильной связанности между объектами, даже если один класс нуждается в функционале (сервисах), который предоставляет другой класс?” Необходимо наделить ответственностью объект посредник.
Например возвратимся опять же MVC. UI логике на самом деле нужен не контроллер, а модель, доменная логика. Но мы не хотим? чтобы UI логика была сильно связанна с моделью, и возможно в UI мы хотим получать данные и работать с разной предметной логикой. А связывать UI слой с бизнес логикой было бы глупо, потому что получим код который будет сложный для изменений и поддержки. Выход – вводим контроллер как посредника между View и Model.
Так что распределяйте ответственности своим объектам ответственно (с умом).
Protected Variations
Protected Variations или сокрытие реализации или защищенные изменения. Как спроектировать объекты, чтобы изменения в объекте или объекта не затрагивали других? Как избежать ситуации когда меняя код объекта придется вносить изменения в множество других объектов системы?
Кажется мы такое обсуждали уже. И пришли к выводу что нужно использовать low coupling или dependency injection. Именно! Но суть в принципа немного в другом. Суть в том чтобы определить “точки изменений” и зафиксировать их в абстракции (интерфейсе). “Точки изменений” – не что иное как наши объекты, которые могут меняться.
То есть суть в принципа, чтобы определить места в системе, где поведение может изменится и выделить абстракцию, на основе которой и будет происходить дальнейшее программирование с использованием этого объекта.
Все это делается для того чтобы обеспечить устойчивость интерфейса. Если будет много изменений связанных с объектов, он, в таком ключе, считается не устойчивым и тогда нужно выносить его в абстракцию от которой будем зависеть, либо распределять обязанности и ответственность в код иным образом
Polymorphism
Polymorphismили полиморфизм. Тоже знакомо, не так ли? Так вот это об том же полиморфизме , который мы знаем из ООП. Если заметить то достаточно много паттернов GoF, да и вообще паттернов, построено на полиморфизме. Что он дает?Он дает возможность трактовать однообразно разные объекты с одинаковым интерфейсом (спецификацией). Давайте вспомним такие паттерны как Strategy, Chain of Resposibility, Command… – их много. И все по своей суть основываются на полиморфизме.
Полиморфизм решает проблему обработки альтернативных вариантов поведения на основе типа. Тут яркий пример это шаблон GoF – Strategy (Стратегия).
Например, для реализации гибкого функционала для шифрования можно определить интерфейс IEncryptionAlgorithm с методом Encrypt, и объект создатель, который вернет IEncryptionAlgorithm, создав внутри себя актуальную реализацию этого интерфейса.
Явно или неясно, пользуясь другими принципами или шаблонами – мы тоже часто используем принципы или шаблоны GRASP при разработке и проектировании. Многие принципы пересекаются, просто возможно разные авторы по разному смещают акценты, но на удивление все принципы и шаблоны верны – и SOLID, и GRASP и классические GoF паттерны и многие другие. И все их следует использовать с умом и балансировать ними применяя совместно с какими-то хаками под свою систему, что позволяет разрабатывать действительно красивые, устойчивые и гибкие архитектуры и приложения.
Принципы GRASP хоть и упоминаются реже других все же занимают свое место и играют важную роль именно в распределении обязанностей между объектами и заставляют нас думать еще и в таком ключе, а не просто подгонять шаблоны как кирпичи разной формы чтобы выстроить пазл архитектуры.
Принципы GRASP не создают четкую структуру, которой мы должны следовать в коде. Это больше распределение ролей и ответственностей между объектами, а также свойства, которыми эти объекты должны обладать для того чтобы исполнять свои роли.

GRASP (General Responsibility Assignment Software Patterns - общие образцы распределения обязанностей) - паттерны, используемые в объектно-ориентированном проектировании для решения общих задач по назначению обязанностей классам и объектам.

GRASP паттерны не имеют выраженной структуры, четкой области применения и конкретной решаемой проблемы, а лишь представляют собой обобщенные подходы/рекомендации/принципы, используемые при проектировании дизайна системы.

Information Expert (Информационный эксперт)

Шаблон Information Expert определяет базовый принцип назначения обязанностей. Он утверждает, что обязанности должны быть назначены объекту, который владеет максимумом необходимой информации для выполнения обязанности. Такой объект называется информационным экспертом . Возможно, этот шаблон является самым очевидным из девяти, но вместе с тем и самым важным.

Если дизайн не удовлетворяет этому принципу, то при программировании получается спагетти-код , в котором очень трудно разбираться. Локализация обязанностей позволяет повысить уровень инкапсуляции и уменьшить уровень связанности . Кроме читабельности кода повышается уровень готовности компонент к повторному использованию.

Creator (Создатель)

Шаблон Creator решает, кто должен создавать объект. Фактически, это применение шаблона Information Expert к проблеме создания объектов. Более конкретно, нужно назначить классу B обязанность создавать экземпляры класса A, если выполняется как можно больше из следующих условий:

  • Класс B содержит или агрегирует объекты A.
  • Класс B записывает экземпляры объектов A.
  • Класс B активно использует объекты A
  • Класс B обладает данными инициализации для объектов A.

Альтернативой создателю является шаблон проектирования Фабрика . В этом случае создание объектов концентрируется в отдельном классе.

Controller (Контроллер)

Контроллер берёт на себя ответственность за выполнение операций, приходящих от пользователя и часто выполняет сценарий одного или нескольких вариантов использования (например, один контроллер может обрабатывать сценарии создания и удаления пользователя). Как правило, контроллер не выполняет работу самостоятельно, а делегирует обязанности компетентным объектам.

Иногда класс-контроллер представляет всю систему в целом, корневой объект, устройство или важную подсистему (внешний контроллер ).

Использование контроллеров позволяет отделить логику от представления, тем самым повышая возможность повторного использования кода.

Low Coupling (Слабая связанность)

Low Coupling - это принцип, который позволяет распределить обязанности между объектами таким образом, чтобы степень связанности между системами оставалась низкой. Степень связанности (coupling) - это мера, определяющая, насколько жестко один элемент связан с другими элементами, либо каким количеством данных о других элементах он обладает. Элемент с низкой степенью связанности (или слабым связыванием) зависит от не очень большого числа других элементов и имеет следующие свойства:

  • Малое число зависимостей между классами (подсистемами).
  • Слабая зависимость одного класса (подсистемы) от изменений в другом классе (подсистеме).
  • Высокая степень повторного использования подсистем.

Low Coupling позволяет избежать следующих проблем:

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

High Cohesion (Сильное зацепление)

High Cohesion - это принцип, который задаёт свойство сильного зацепления внутри подсистемы. Классы (подсистемы) таким образом получаются сфокусированными, управляемыми и понятными. Зацепление (cohesion) (или более точно, функциональное зацепление) - это мера связанности и сфокусированности обязанностей класса. Считается что объект (подсистема) обладает высокой степенью зацепления, если его обязанности тесно связаны между собой и он не выполняет огромных объемов работы.

Класс с низкой степенью зацепления выполняет много разнородных функций или несвязанных между собой обязанностей. Такие классы создавать нежелательно, поскольку они приводят к возникновению следующих проблем:

  • Трудность понимания.
  • Сложность при повторном использовании.
  • Сложность поддержки.
  • Ненадежность, постоянная подверженность изменениям.

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

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

Polymorphism (Полиморфизм)

Полиморфизм позволяет обрабатывать альтернативные варианты поведения на основе типа и заменять подключаемые компоненты системы. Обязанности распределяются для различных вариантов поведения с помощью полиморфных операций для этого класса. Все альтернативные реализации приводятся к общему интерфейсу.

Пример: Интеграция разрабатываемой системы с различными внешними системами учета налогов. Используются локальные программные объекты, обеспечивающие адаптацию (Адаптеры). При отправке сообщения к такому объекту выполняется обращение к внешней системе с использованием ее собственного программного интерфейса. Использование полиморфизма здесь оправдано для возможной адаптации к различным внешним системам.

Pure Fabrication (Чистая выдумка)

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

Существует понятие модели программирования по предметной области, согласно которой, каждой сущности из предметной области соответствует один или более классов программной среды. При этом, обязанности взаимодействия сущностей, как правило накладываются на них самих. Такой подход имеет очевидный недостаток - высокая связность модулей системы. Шаблон чистая выдумка позволяет решить данную проблему, путем введения в программную среду дополнительного класса (не отражающего реальной сущности из предметной области) и наделение его требуемыми обязанностями.

Indirection (Посредник)

Indirection поддерживает слабую связанность (и возможность повторного использования) путём назначения обязанностей посредника между ними промежуточному объекту. Пример: Model-View-Controller , который позволяет ослабить связь между данными и их представлением с помощью введения класса-посредника (контроллер), отвечающего за поведение.

Protected Variations (Сокрытие реализации)

Шаблон Protected Variations защищает элементы от изменения других элементов (объектов или подсистем) с помощью вынесения взаимодействия в фиксированный интерфейс . Всё взаимодействие между элементами должно происходить через него. Поведение может варьироваться лишь с помощью создания другой реализации интерфейса.

Advertisements