Книга: Объектно-ориентированный анализ и проектирование с примерами приложений на С++
4.3. Ключевые абстракции и механизмы
4.3. Ключевые абстракции и механизмы
Ключевые абстракции
Поиск и выбор ключевых абстракций. Ключевая абстракция - это класс или объект, который входит в словарь проблемной области. Самая главная ценность ключевых абстракций заключена в том, что они определяют границы нашей проблемы: выделяют то, что входит в нашу систему и поэтому важно для нас, и устраняют лишнее. Задача выделения таких абстракций специфична для проблемной области. Как утверждает Голдберг, "правильный выбор объектов зависит от назначения приложения и степени детальности обрабатываемой информации" [51].
Как мы уже отмечали, определение ключевых абстракций включает в себя два процесса: открытие и изобретение. Мы открываем абстракции, слушая специалистов по предметной области: если эксперт про нее говорит, то эта абстракция обычно действительно важна [52]. Изобретая, мы создаем новые классы и объекты, не обязательно являющиеся частью предметной области, но полезные при проектировании или реализации системы. Например, пользователь банкомата говорит "счет, снять, положить"; эти термины - часть словаря предметной области. Разработчик системы использует их, но добавляет свои, такие, как база данных, диспетчер экрана, список, очередь и так далее. Эти ключевые абстракции созданы уже не предметной областью, а проектированием.
Наиболее мощный способ выделения ключевых абстракций - сводить задачу к уже известным классам и объектам. Как будет показано ниже в главе 6, при отсутствии таких повторно используемых абстракций мы рекомендуем пользоваться сценариями, чтобы вести процесс идентификации классов и объектов.
Уточнение ключевых абстракций. Определив кандидатов на роли ключевых абстракций, мы должны оценить их по критериям, описанным в предыдущих главах. По словам Страуструпа "программист должен задаваться вопросами: Как создаются объекты класса? Как можно копировать и/или уничтожать объекты данного класса? Какие операции могут быть выполнены над этим объектом? Если ответы на эти вопросы туманны, то, возможно, общая концепция не ясна и лучше сесть и подумать еще раз, чем бросаться программировать" [53].
Определив новые абстракции, мы должны найти их место в контексте уже существующих классов и объектов. Не стоит пытаться делать это строго сверху вниз или снизу вверх. Халберт и О'Брайен утверждают, что "нет особой необходимости строить иерархию классов, начиная с самого верхнего класса, и потом дополнять ее подклассами. Чаще вы создаете несколько независимых иерархий, осознаете их общие черты и выделяете один или несколько суперклассов. Требуется несколько проходов вверх и вниз по иерархии, чтобы создать программный проект" [54]. Это не карт-бланш на хакерство, а всего лишь наблюдение, основанное на опыте и подтверждающее тот факт, что объектно-ориентированное проектирование - процесс последовательных приближений. Сходное наблюдение делает Страуструп: "Наиболее частые реорганизации в иерархии классов - это сведение совпадающих частей двух классов в один и разделение класса на два новых" [55].
Классы и объекты должны быть на надлежащем уровне абстракции: не слишком высоко и не слишком низко.
Трудно сразу расположить классы и объекты на правильных уровнях абстракции. Иногда, найдя важный класс, мы можем передвинуть его вверх в иерархии классов, тем самым увеличивая степень повторности использования кода. Это называется продвижением класса [56]. Аналогично, можем прийти к выводу, что класс слишком обобщен, и это затрудняет наследование: происходит семантический разрыв или конфликт зернистости [57]. В обоих случаях мы пытаемся выявить зацепление или недостаточную связность абстракций и смягчить конфликт.
Программисты часто легкомысленно относятся к правильному наименованию классов и объектов, но на самом деле очень важно отразить в обозначении классов и объектов сущность описываемых ими предметов. Программы необходимо писать тщательно, как художественную литературу, дума я и о читателях, и о компьютере [58]. При идентификации одного только объекта вам нужно придумать имена: для него, для его класса и для модуля, в котором класс объявлен. Умножьте на тысячу объектов и сотни классов, и вы поймете, как остра проблема.
Мы предлагаем следующие правила:
• Объекты следует называть существительными: theSensor или shape.
• Классы следует называть обобщенными существительными: Sensors, Shapes.
• Операции-модификаторы следует называть активными глаголами: Draw, moveLeft.
• У операций-селекторов в имя должен включаться запрос или форма глагола "to be": extentOf, isOpen.
• Подчеркивание и использование заглавных букв - на ваше усмотрение, постарайтесь лишь не противоречить сами себе.
Идентификация механизмов
Как найти механизмы? В предыдущем обсуждении мы называли механизмами структуры, посредством которых объекты взаимодействуют друг с другом и ведут себя так, как требуется. Так же как при разработке класса фактически определяется поведение отдельных объектов, так же и механизмы служат для задания поведения совокупности объектов. Таким образом, механизмы представляют шаблоны поведения.
Рассмотрим требование, предъявляемое к автомобилю: нажатие на акселератор должно приводить к увеличению оборотов двигателя, а отпускание акселератора - к их уменьшению. Как это происходит, водителю совершенно безразлично. Может быть использован любой механизм, обеспечивающий нужное поведение, и его выбор - дело вкуса разработчика. Например, допустимо любое из предложенных ниже инженерных решений:
• Механическая связь между акселератором и карбюратором (обычное решение).
• Под педалью ставится датчик давления, который соединяется с компьютером, управляющим карбюратором (механизм управления по проводам).
• Карбюратора нет. Бак с горючим находится на крыше автомобиля и топливо свободно течет в двигатель. Поток топлива регулируется зажимом на трубке. Нажатие на педаль акселератора ослабляет зажим (очень дешево).
Какую именно реализацию выберет разработчик, зависит от таких параметров, как стоимость, надежность, технологичность и т.д.
Подобно тому, как было бы недопустимой невежливостью со стороны клиента нарушать правила пользования сервером, также и выход за пределы правил и ограничений поведения, заданных механизмом, социально неприемлем. Водитель был бы удивлен, если бы, нажав на педаль акселератора, он увидел зажегшиеся фары.
Ключевые абстракции определяют словарь проблемной области, механизмы определяют суть проекта. В процессе проектирования разработчик должен придумать не только начинку классов, но и то, как объекты этих классов будут взаимодействовать друг с другом. Но конкретный механизм взаимодействия все равно придется разложить на методы классов. В итоге протокол класса будет отражать поведение его объектов и работу механизмов, в которых они участвуют.
Механизмы, таким образом, представляют собой стратегические решения в проектировании, подобно проектированию структуры классов. С другой стороны, проектирование интерфейса какого-то одного класса - это скорее тактическое решение. Стратегические решения должны быть выполнены явно, иначе у нас получится неорганизованная толпа объектов, кидающихся выполнять работу, расталкивая друг друга. В наиболее элегантных, стройных и быстрых программах воплощены тщательно разработанные механизмы.
Механизмы суть средства, с помощью которых объекты взаимодействуют друг с другом для достижения необходимого поведения более высокого уровня.
Механизмы представляют только один из шаблонов, которые мы находим в структурированных системах. Так, на нижнем конце своеобразной биологической пирамиды находятся идиомы. Это обороты, специфические для языков программирования или программистских культур, и отражающие общепринятые способы выражаться [Определяющей характеристикой идиомы является то, что ее игнорирование или нарушение влечет немедленные социальные последствия: вы превращаетесь в йеху или, еще хуже, в чужака, не заслуживающего уважения]. Например, в CLOS не принято использовать подчеркивание в именах функций или переменных, хотя в Ada это дело обычное [59]. Изучая язык, приходится учить его идиомы, которые обычно передаются в форме фольклора. Однако, как отметил Коплейн, идиомы играют важную роль в кодификации шаблонов низкого уровня. Он заметил, что "многие общепрограммистские действия идиоматичны" и поэтому распознание таких идиом позволяет "использовать конструкции C++ для выражения функциональности вне самого этого языка с сохранением иллюзии, что они являются частью языка" [60].
Место на верху пирамиды занимают среды разработки. Среда разработки - это собрание классов, предназначенных для определенной прикладной ситуации. Среда дает готовые классы, механизмы и услуги, которыми можно сразу пользоваться или приспосабливать для своих нужд.
Если идиомы составляют часть программистской культуры, то среды разработки обычно - коммерческий продукт. Например, Apple MacApp и его преемник Bedrock - среды, написанные на C++ и предназначенные для построения приложений со стандартным интерфейсом пользователя Macintosh. Аналогичную роль для Windows играют Microsoft Foundation Classes и ObjectWindows корпорации Borland.
Примеры механизмов. Рассмотрим механизм рисования, применяемый обычно в графических интерфейсах пользователя. Для того, чтобы представить какой-либо рисунок на экране, необходимы несколько объектов: окно, вид, модель, которую надо показать, и, наконец, клиент, который знает, когда надо нарисовать модель, но не знает, как это сделать. Сначала клиент дает окну команду нарисовать себя. Так как окно может включать в себя ряд видов, оно в свою очередь приказывает каждому из них нарисовать себя. Каждый вид посылает сообщение своей модели нарисовать себя, в результате чего и появляется изображение на экране. В этом механизме модель полностью отделена от окна и вида, в котором она представлена: виды могут посылать сообщения моделям, но модели не могут посылать сообщения видам. Smalltalk использует вариант этого механизма, названный парадигмой Model-View-Controller, модель-вид-контроллер (MVC) [61].
Механизмы, таким образом, представляют уровень повторного использования в проектировании, более высокий, чем повторное использование индивидуальных классов. MVC, например, является основой интерфейса пользователя в языке Smalltalk. Эта парадигма в свою очередь строится на базе механизма зависимостей, который вложен в поведение базового класса языка Smalltalk (класса object) и часто используется библиотекой классов языка Smalltalk.
Примеры механизмов можно найти во многих системах. Структуру операционной системы, например, можно описать на высоком уровне абстракции по тем механизмам, которые используются для диспетчеризации программ. Система может быть монолитной (как MS-DOS), иметь ядро (UNIX) или представлять собой иерархию процессов (операционная система THE) [62]. В системах искусственного интеллекта использованы разнообразные механизмы принятия решений. Одним из наиболее распространенных является механизм рабочей области, в которую каждый индивидуальный источник знаний независимо заносит свои сведения. В таком механизме не существует центрального контроля, но любое изменение в рабочей области может явиться толчком для выработки системой нового пути решения поставленной задачи [63]. Коад похожим образом выявил ряд общих механизмов в объектно-ориентированных системах, включая шаблоны временных ассоциаций, протоколирование событий и широковещательную рассылку сообщений [64]. Во всех случаях эти механизмы проявляются не как индивидуальные классы, а как структуры сотрудничающих классов.
На этом завершается наше изучение классификации и понятий, являющихся основой объектно-ориентированного проектирования. Следующие три главы посвящены самому методу, в частности системе обозначений, процессу проектирования и рассмотрению практических примеров.
- Ключевые слова
- 1.2.1. Ключевые слова и идентификаторы
- Новые ключевые слова
- Ключевые моменты больших проектов
- Раздел 1 Лояльность: определение и ключевые факторы
- Ключевые понятия
- Ключевые положения
- Ключевые факторы успешного отбора каналов коммуникаций
- Ключевые свойства форм
- Ключевые показатели работы категорийного менеджера
- КЛЮЧЕВЫЕ СЛОВА: auto, extern, static, register
- Глава 4 Классификация