Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

GRASP: принцип Protected Variations.




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

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

Термин «интерфейс» используется для обозначения способа обеспечения доступа и не сводится к понятиям интерфейса Java или COM.

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

Основные механизмы шаблона PV

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

Базовые механизмы защиты от вариаций. Инкапсуляция данных, интерфейсы, полиморфизм, перенаправление — все эти принципы реализуются в рамках шаблона PV.

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

Поиск служб. Поиск служб подразумевает использование служб именования (например, JDNI для Java или Naming Service для CORBA) или средств получения доступа к службе (например,Jini для Java или UDDI для веб-сервисов). Клиенты в этом случае защищены от изменения расположения служб: они могут использовать стандартный интерфейс поиска служб. Это — специальный случай проектирования на основе данных.

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

Рефлексивное проектирование (проектирование на метауровне). Примером реализации этого подхода является использование java.beans.Introspector для получения объекта BeanInfo, запрашивающего имя метода, реализующего свойство компонента Х и вызвающего метод invoke. При этом система остается защищенной от влияния логики изменения кода внешних компонентов за счет использования служб метаязыка. Это проектное решение является частным случаем проектирования на основе данных.

Унифицированный доступ. Некоторые языки, такие как Ada, Eiffel и C#, поддерживают синтаксические конструкции, при которых доступ к полям и методам классов осуществляется одинаково. Например, запись circle.Radius может означать и вызов метода get_Radius() (т. е. метод чтения значения свойства), и прямое обращение к открытому полю Radius, в зависимости от определения класса. Тогда открытые поля можно преобразовывать в свойства без изменения кода клиентских объектов.

Стандартные языки. Официальные стандарты языков (например, SQL) тоже обеспечивают выполнение шаблона PV.

Сокрытие структуры

Частным случаем шаблона PV является шаблон Don't Talk to Strangers (Не разговаривайте с незнакомцами) или Law of Demeter (закон Деметры).

Этот шаблон позволяет обеспечить устойчивость системы к изменению структуры объектов (это — довольно типичная точка неустойчивости).

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

§ объекту this (т. е. объекту разрешается вызывать свои методы);

§ параметру выполняемого метода;

§ атрибуту (полю) объекта this;

§ элементу коллекции, являющейся атрибутом объекта this;

§ объекту, созданному внутри метода.

Все эти объекты — это прямые объекты или «знакомые», а все остальные — непрямые или «незнакомцы». Закон Деметры предписывает объекту общаться только со «знакомыми» и не разговаривать с «незнакомцами», что позволяет избежать связывания с непрямыми объектами.

Пример. В общем виде ситуацию можно представить следующим образом:

 

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

Для выполнения закона Деметры прямым объектам могу понадобиться новые операции, которые выступают в роли дополнительных операций, позволяющих избежать «разговоров с незнакомцами». Эти операции обеспечивают получение требуемой информации, оставляя «за кадром» способ ее получения. Например:

}

Код класса-клиента изменится следующим образом:

Закону Деметры не всегда нужно следовать. Все зависит от самой структура объектов. В стандартных библиотеках (например, в Java API или .NET Framework BCL) структурные связи между классами объектов довольно устойчивы. В почти готовых системах структура объектов тоже довольно устойчива. Однако, на ранних итерациях выполнения проекта объектная структура еще не стабильна.

Когда не следует применять шаблон PV. Следует определить два типа особых точек.

§ Точка вариации (variation point) — точка ветвления в существующей на данный момент системе или в требованиях к ней, например, необходимость поддержки нескольких интерфейсов для системы вычисления налоговых платежей.

§ Точка эволюции (evolution point) — предполагаемая точка ветвления, которая может возникнуть в будущем, однако не определяемая существующими требованиями.

Шаблон PV применяется и точкам вариации, и к точкам эволюции. Но иногда для реализации точек эволюции требуется гораздо больше усилий, чем для реализации простого проектного решения без учета этих точек. В таком случае лучше остановиться на неробастном решении и модифицировать его по мере необходимости (в случае реальных, а не предполагаемых изменений).

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

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

решение, следует хорошо взвесить все «за» и «против».

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

Преимущества

§ легкость добавления новых расширений и вариаций;

§ возможность добавления новых реализаций, не затрагивая клиента;

§ слабое связывание;

§ минимизация влияния изменений.



GRASP: принцип Controller.

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

Системное событие (system event) — это событие, генерируемое внешним исполнителем в процессе его взаимодействия с системой. Системные события связаны с системными операциями (system operation), т. е. операциями, выполняемыми системой в ответ на системные события.

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

Контроллер (controller) — это объект, не относящийся к интерфейсу пользователя и отвечающий за обработку системных событий. Контроллер определяет методы для выполнения системных операций.

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

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

§ класс представляет сценарий некоторого прецедента, в рамках которого выполняется обработка всех системных событий, и обычно называется <Прецедент>Наndlеr, <Прецедент>Coordinator или <Прецедент>Session (контроллер прецедента или контроллер сеанса).

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

Неформально, сеанс — это экземпляр взаимодействия с исполнителем. Сеансы могут иметь произвольную длину, но зачастую организованы в рамках прецедента (сеансы прецедента).

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

Рис. 11. Некоторые системные операции для приложения NextGen (на

этом рисунке вся система представлена в виде класса, что

допустимо, а иногда и полезно)

 

Пример. В приложении NextGen существует несколько системных операций (рис. 11).

В процессе анализа поведения системы системные операции относятся к классу System. Однако это не означает, что их выполняет программный класс System. Более того, обязанности по выполнению системных операций обычно возлагаются на класс-контроллер.

Какой класс должен выступать в роли контроллера для системных событий типа enterItem или makeNewReturn (рис. 12)?

 

Рис. 12. Кто является контроллером для события enterItem?

Согласно шаблону Controller, возможны следующие варианты:

§ класс, представляющий всю систему в целом, устройство или подсистему (Register, POSSystem);

§ класс, представляющий получателя или искусственный обработчик всех системных событий некоторого сценария прецедента (ProcessSaleSession / ProcessSaleHandler или ProcessReturnsSession / ProcessReturnsHandler).

Замечание. В предметной области POS-систем объект Register (который также называют терминалом) представляет собой специализированное утройство с соответствующим программным обеспечением.

В терминах диаграммы взаимодействий должен использоваться один из вариантов, представленных на рис. 13.

Рис. 13. Варианты контроллеров

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

Системные операции, идентифицированные в процессе анализа поведения системы, на этапе проектирования присваиваются одному или нескольким классам контроллеров (рис. 14).

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

Во всех случаях при использовании объектно-ориентированного подхода для обработки внешних событий применяются контроллеры. Шаблон Controller обеспечивает наиболее типичные проектные решения для этого случая. Как видно из рис. 12, контроллер — это своеобразный вид интерфейса между уровнями предметной области и графического представления.

Рис. 14. Распределение системных операций

Чтобы обеспечить возможность поддержки информации о состоянии прецедента, для обработки всех системных событий в рамках одного прецедента должен использоваться один и тот же класс контроллера. Такая информация может понадобиться, например, для идентификации момента нарушения последовательности системных событий (например, выполнение операции makePayment перед выполнением операции endSale). Для различных прецедентов можно использовать разные контроллеры.

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

Первым типом контроллеров является внешний контроллер (façade controller), представляющий всю «систему», устройство или подсистему. Основная идея сводится к выбору некоторого класса, имя которого охватывает все слои приложения. Этот класс обеспечивает главную точку вызова всех служб из интерфейса пользователя и обращения к остальным слоям. Этот класс может представлять физический объект: например систему (Register), телекоммуникационный переключатель (TelecommSwitch, Phone) или устройство (Robot); всю программную часть системы: например POSSystem, или любые другие понятия, выбранные разработчиком для представления системы в целом.

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

Если применяется контроллер прецедента (use case controller), то для каждого прецедента должен существовать отдельный контроллер. Заметим, что это не объект из предметной области, а искусственная конструкция, поддерживающая жизнедеятельность системы (ср. с шаблоном Pure Fabrication). Например, если в системе NextGen используются прецеденты «Оформление продажи» и «Возврат товара», то в ней могут быть реализованы классы ProcessSaleHandler (Обработчик продажи) и ProcessReturnsHandler (Обработчик возвратов).

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

Важным следствием применения шаблона Controller является вынесение обязанностей по выполнению системных событий за пределы уровня представления и внешнего интерфейса объектов (например, объектов окон или аплетов). Другими словами, системные операции, отражающие процессы в предметной области, должны обрабатываться на уровне логики приложения или реализации объектов, а не на уровне интерфейса.

Преимущества

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

§ Контроль состояния прецедента. Иногда необходимо удостовериться, что системные операции выполняются в некоторой определенной последовательности. Например, необходимо гарантировать, чтобы операция makePayment выполнялась только после операции endSale, для чего необходимо накапливать информацию о последовательности событий. Для этой цели удобноиспользовать контроллер, особенно контроллер прецедента.

Проблемы и решения

Плохо спроектированный класс контроллера имеет низкую степень зацепления: он выполняет слишком много обязанностей и является несфокусированным. Такой контроллер называется раздутым (bloated controller). Признаки раздутого контроллера таковы:

§ в системе имеется единственный класс контроллера, получающий все системные сообщения, которых поступает слишком много (такая ситуация зачастую возникает при использовании внешнего контроллера);

§ контроллер сам выполняет все задачи, не делегируя обязанности другим классам. Обычно это приводит к нарушению основных принципов шаблонов Information Expert и High Cohesion;

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

Существует несколько рецептов для устранения проблемы раздутого контроллера.

1) Добавить несколько контроллеров. Помимо внешних контроллеров, можно использовать контроллеры прецедентов. Например, в системе, имеющей много системных событий (скажем, в системе резервирования авиабилетов), можно использовать следующие контроллеры: MakeReservationHandler (обработчик события резервирования), ManageSchedulesHandler (менеджер расписания), ManageFaresHandler (менеджер тарифов).

2) Перепроектировать контроллер таким образом, чтобы он делегировал обязанности по выполнению системных операций другим объектам.










Последнее изменение этой страницы: 2018-05-27; просмотров: 200.

stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда...