Уточнение ключевых абстракций. Определив кандидатов на роли ключевых абстракций, мы должны оценить их по критериям, описанным в предыдущих главах. По словам Страуструпа "программист должен задаваться вопросами: Как создаются объекты класса? Как можно копировать и/или уничтожать объекты данного класса? Какие операции могут быть выполнены над этим объектом? Если ответы на эти вопросы туманны, то, возможно, общая концепция не ясна и лучше сесть и подумать еще раз, чем бросаться программировать" [53].
Определив новые абстракции, мы должны найти их место в контексте уже существующих классов и объектов. Не стоит пытаться делать это строго сверху вниз или снизу вверх. Халберт и О'Брайен утверждают, что "нет особой необходимости строить иерархию классов, начиная с самого верхнего класса, и потом дополнять ее подклассами. Чаще вы создаете несколько независимых иерархий, осознаете их общие черты и выделяете один или несколько суперклассов. Требуется несколько проходов вверх и вниз по иерархии, чтобы создать программный проект" [54]. Это не карт-бланш на хакерство, а всего лишь наблюдение, основанное на опыте и подтверждающее тот факт, что объектно-ориентированное проектирование - процесс последовательных приближений. Сходное наблюдение делает Страуструп: "Наиболее частые реорганизации в иерархии классов - это сведение совпадающих частей двух классов в один и разделение класса на два новых" [55].
Трудно сразу расположить классы и объекты на правильных уровнях абстракции. Иногда, найдя важный класс, мы можем передвинуть его вверх в иерархии классов, тем самым увеличивая степень повторности использования кода. Это называется продвижением класса [56]. Аналогично, можем прийти к выводу, что класс слишком обобщен, и это затрудняет наследование: происходит семантический разрыв или конфликт зернистости [57]. В обоих случаях мы пытаемся выявить зацепление или недостаточную связность абстракций и смягчить конфликт.
Программисты часто легкомысленно относятся к правильному наименованию классов и объектов, но на самом деле очень важно отразить в обозначении классов и объектов сущность описываемых ими предметов. Программы необходимо писать тщательно, как художественную литературу, дума я и о читателях, и о компьютере [58]. При идентификации одного только объекта вам нужно придумать имена: для него, для его класса и для модуля, в котором класс объявлен. Умножьте на тысячу объектов и сотни классов, и вы поймете, как остра проблема.
Мы предлагаем следующие правила:
• Объекты следует называть существительными: theSensor или shape.
• Классы следует называть обобщенными существительными: Sensors, Shapes.
• Операции-модификаторы следует называть активными глаголами: Draw, moveLeft.
• У операций-селекторов в имя должен включаться запрос или форма глагола "to be": extentOf, isOpen.
• Подчеркивание и использование заглавных букв - на ваше усмотрение, постарайтесь лишь не противоречить сами себе.
Идентификация механизмов
Как найти механизмы? В предыдущем обсуждении мы называли механизмами структуры, посредством которых объекты взаимодействуют друг с другом и ведут себя так, как требуется. Так же как при разработке класса фактически определяется поведение отдельных объектов, так же и механизмы служат для задания поведения совокупности объектов. Таким образом, механизмы представляют шаблоны поведения.
Рассмотрим требование, предъявляемое к автомобилю: нажатие на акселератор должно приводить к увеличению оборотов двигателя, а отпускание акселератора - к их уменьшению. Как это происходит, водителю совершенно безразлично. Может быть использован любой механизм, обеспечивающий нужное поведение, и его выбор - дело вкуса разработчика. Например, допустимо любое из предложенных ниже инженерных решений:
• Механическая связь между акселератором и карбюратором (обычное решение).
• Под педалью ставится датчик давления, который соединяется с компьютером, управляющим карбюратором (механизм управления по проводам).
• Карбюратора нет. Бак с горючим находится на крыше автомобиля и топливо свободно течет в двигатель. Поток топлива регулируется зажимом на трубке. Нажатие на педаль акселератора ослабляет зажим (очень дешево).
Какую именно реализацию выберет разработчик, зависит от таких параметров, как стоимость, надежность, технологичность и т.д.
Подобно тому, как было бы недопустимой невежливостью со стороны клиента нарушать правила пользования сервером, также и выход за пределы правил и ограничений поведения, заданных механизмом, социально неприемлем. Водитель был бы удивлен, если бы, нажав на педаль акселератора, он увидел зажегшиеся фары.