Гради Буч - Объектно-ориентированный анализ и проектирование с примерами приложений на С++ (1158635), страница 25
Текст из файла (страница 25)
Если же активный объект имеетсвязь с пассивным, возможны следующие три подхода к синхронизации:• Последовательный - семантика пассивного объекта обеспечивается вприсутствии только одного активного процесса.• Защищенный - семантика пассивного объекта обеспечивается вприсутствии многих потоков управления, но активные клиенты должныдоговориться и обеспечить взаимное исключение.• Синхронный - семантика пассивного объекта обеспечивается вприсутствии многих потоков управления; взаимное исключение обеспечиваетсервер.Все объекты, описанные в этой главе, были последовательными. Вглаве 9 мы рассмотрим остальные варианты более подробно.АгрегацияСемантика.
В то время, как связи обозначают равноправные или"клиент-серверные" отношения между объектами, агрегация описываетотношения целого и части, приводящие к соответствующей иерархииобъектов, причем, идя от целого (агрегата), мы можем придти к его частям(атрибутам). В этом смысле агрегация - специализированный частный случайассоциации. На рис.
3-3 объект rampControllerРис. 3-3. Агрегацияимеет связь с объектом growingRamp и атрибут h класса Heater(нагреватель). В данном случае rampController - целое, a h- его часть.Другими словами, h - часть состояния rampController. Исходя изrampController, можно найти соответствующий нагреватель. Однако по hнельзя найти содержащий его объект (называемый также его контейнером),если только сведения о нем не являются случайно частью состояния h.Агрегация может означать физическое вхождение одного объекта вдругой, но не обязательно. Самолет состоит из крыльев, двигателей, шасси ипрочих частей. С другой стороны, отношения акционера с его акциями - этоагрегация, которая не предусматривает физического включения.
Акционермонопольно владеет своими акциями, но они в него не входят физически. Это,несомненно, отношение агрегации, но скорее концептуальное, чем физическоепо своей природе.Выбирая одно из двух - связь или агрегацию - надо иметь в видуследующее. Агрегация иногда предпочтительнее, поскольку позволяет скрытьчасти в целом. Иногда наоборот предпочтительнее связи, поскольку онислабее и менее ограничительны. Принимая решение, надо взвесить все.Объект, являющийся атрибутом другого объекта (агрегата), имеетсвязь со своим агрегатом. Через эту связь агрегат может посылать емусообщения.Пример. Добавим в спецификацию класса TemperatureControllerописание нагревателя:Heater h;После этого каждый объект TemperatureController будет иметьсвой нагреватель. В соответствии с нашим определением класса Heater впредыдущей главе мы должны инициализировать нагреватель при созданиинового контроллера, так как сам этот класс не предусматривает конструкторапо умолчанию.
Мы могли бы определить конструктор классаTemperatureController следующим образом:TemperatureController::TemperatureController(Location 1) :h(1) {}3.3. Природа классовЧто такое класс?Понятия класса и объекта настолько тесно связаны, что невозможноговорить об объекте безотносительно к его классу.
Однако существует важноеразличие этихКласс представляет набор объектов, которые обладают общей структуройи одинаковым поведениемдвух понятий. В то время как объект обозначает конкретную сущность,определенную во времени и в пространстве, класс определяет лишьабстракцию существенного в объекте. Таким образом, можно говорить оклассе "Млекопитающие", который включает характеристики, общие для всехмлекопитающих. Для указания на конкретного представителя млекопитающихнеобходимо сказать "это - млекопитающее" или "то - млекопитающее".В общепонятных терминах можно дать следующее определениекласса: "группа, множество или вид с общими свойствами или общимсвойством, разновидностями, отличиями по качеству, возможностями илиусловиями" [17]. В контексте объектно-ориентированного анализа дадимследующее определение класса:Класс - это некое множество объектов, имеющих общую структуру иобщее поведение.Любой конкретный объект является просто экземпляром класса.
Чтоже не является классом? Объект не является классом, хотя в дальнейшем мыувидим, что класс может быть объектом. Объекты, не связанные общностьюструктуры и поведения, нельзя объединить в класс, так как по определениюони не связаны между собой ничем, кроме того, что все они объекты.Важно отметить, что классы, как их понимают в большинствесуществующих языков программирования, необходимы, но не достаточны длядекомпозиции сложных систем. Некоторые абстракции так сложны, что немогут быть выражены в терминах простого описания класса. Например, надостаточно высоком уровне абстракции графический интерфейс пользователя,база данных или система учета как целое, это явные объекты, но не классы.10Лучше считать их некими совокупно-стями (кластерами) сотрудничающихклассов.
Страуструп называет такие кластеры компонентами [18]. Мы же, попричинам, которые будут объяснены в главе 5, называем такие кластерыкатегориями классов.Интерфейс и реализацияМейер [19] и Снайдерс [20] высказали идею контрактногопрограммирования:большие задачи надо разделить на много маленьких и перепоручить ихмелким субподрядчикам. Нигде эта идея не проявляет себя так ярко, как впроектировании классов.По своей природе, класс - это генеральный контракт междуабстракцией и всеми ее клиентами. Выразителем обязательств класса служитего интерфейс, причем в языках с сильной типизацией потенциальныенарушения контракта можно обнаружить уже на стадии компиляции.Идея контрактного программирования приводит нас к разграничениювнешнего облика, то есть интерфейса, и внутреннего устройства класса,реализации.
Главное в интерфейсе - объявление операций, поддерживаемыхэкземплярами класса. К нему можно добавить объявления других классов,переменных, констант и исключительных ситуаций, уточняющих абстракцию,которую класс должен выражать. Напротив, реализация класса никому, кроменего самого, не интересна. По большей части реализация состоит вопределении операций, объявленных в интерфейсе класса.Мы можем разделить интерфейс класса на три части:• открытую (public) - видимую всем клиентам;• защищенную (protected) - видимую самому классу, его подклассам идрузьям (friends);• закрытую (private) - видимую только самому классу и его друзьям.Разные языки программирования предусматривают различныекомбинации этих частей. Разработчик может задать права доступа к той илииной части класса, определив тем самым зону видимости клиента.В частности, в C++ все три перечисленных уровня доступаопределяются явно.
В дополнение к этому есть еще и механизм друзей, спомощью которого посторонним классам можно предоставить привилегиювидеть закрытую и защищенную области класса. Тем самым нарушаетсяинкапсуляция, поэтому, как и в жизни, друзей надо выбирать осторожно. ВAda объявления могут быть сделаны закрытыми или открытыми. В Smalltalkвсе переменные - закрыты, а методы - открыты. В Object Pascal все поля иоперации открыты, то есть никакой инкапсуляции нет.
В CLOS обобщенныефункции открыты, а слоты могут быть закрытыми, хотя узнать их значениявсе равно можно.Состояние объекта задается в его классе через определения константили переменных, помещаемые в его защищенной или закрытой части. Темсамым они инкапсулированы, и их изменения не влияют на клиентов.Внимательный читатель может спросить, почему же представлениеобъекта определяется в интерфейсной части класса, а не в его реализации.Причины чисто практические: в противном случае понадобились бы объектноориентированные процессоры или очень хитроумные компиляторы.
Когдакомпилятор обрабатывает объявление объекта, например, такое:10Можно попытаться выразить такие абстракции одним классом, но повторнойиспользуемости и возможности наследования не получится. Иметь громоздкийинтерфейс - плохая практика, так как большинство клиентов использует только малуюего часть. Более того, изменение одной части этого гигантского интерфейса требуетобновления каждого из клиентов, независимо от того, затронуло ли его это изменениепо сути. Вложенность классов не устраняет этих проблем, а только откладывает их.Displayltem item1;он должен знать, сколько отвести под него памяти.
Если бы этаинформация содержалась в реализации класса, нам пришлось бы написать ееполностью, прежде, чем мы смогли бы задействовать его клиентов. То есть,весь смысл отделения интерфейса от реализации был бы потерян.Константы и переменные, составляющие представление класса,известны под разными именами. В Smalltalk их называют переменныеэкземпляра, в Object Pascal - поля, в C++ - члены класса, а в CLOS - слоты. Мычасто будем использовать эти термины как синонимы.Жизненный цикл классаВ поведении простых классов можно разобраться, изучая операции ихоткрытой части. Однако поведение более интересных классов (такое какперемещение объекта класса DisplayItem или составление расписания дляэкземпляра класса TemperatureController) включает взаимодействиеразных операций, входящих в класс.
Как уже говорилось выше, объекты такихклассов действуют как маленькие машины, части которых взаимодействуютдруг с другом, и так как все такие объекты имеют одно и то же поведение,можно использовать класс для описания их общей семантики, упорядоченнойпо времени и событиям. Как будет показано в главе 5, мы можем описыватьдинамику поведения объектов, используя модель конечного автомата.3.4. Отношения между классамиТипы отношенийРассмотрим сходства и различия между следующими классами: цветы,маргаритки, красные розы, желтые розы, лепестки и божьи коровки. Мыможем заметить следующее:• Маргаритка - цветок.• Роза - (другой) цветок.• Красная и желтая розы - розы.• Лепесток является частью обоих видов цветов.• Божьи коровки питаются вредителями, поражающими некоторыецветы.Из этого простого примера следует, что классы, как и объекты, несуществуют изолированно.