Гради Буч - Объектно-ориентированный анализ и проектирование с примерами приложений на С++ (1158635), страница 22
Текст из файла (страница 22)
Концепция побочного действияпозволяет уточнить наше определение состояния:Состояние объекта представляет суммарный результат егоповедения.Наиболее интересны те объекты, состояние которых не статично: ихсостояние изменяется и запрашивается операциями.Примеры. Опишем на языке C++ класс Queue (очередь):class Queue {public:Queue();Queue(const Queue&);virtual ~Queue();virtual Queue& operator=(const Queue&);virtual int operator== (const Queue&) const;int operator!=(const Queue&) const;virtual void clear();virtual void append(const void*);virtual void pop();virtual void remove(int at);virtual int length() const;virtual int isEmpty() const;virtual const void* front() const;virtual int location(const void*);protected:…};В определении класса используется обычная для С идиома ссылки наданные неопределенного типа с помощью void*, благодаря чему в очередьможно вставлять объекты разных классов.
Эта техника не безопасна - клиентдолжен ясно понимать, с каким (какого класса) объектом он имеет дело.Кроме того, при использовании void* очередь не "владеет" объектами,которые в нее помещены. Деструктор ~Queue () уничтожает очередь, но не ееучастников. В следующем разделе мы рассмотрим параметризованные типы,которые помогают справляться с такими проблемами.Так как определение Queue задает класс, а не объект, мы должныобъявить экземпляры класса, с которыми могут работать клиенты:Queue а, b, с, d;Мы можем выполнять операции над объектами:а.append(&deb);а.append(&karen);a.
append (&denise);b = a;а.pop();Теперь очередь а содержит двух сотрудников (первой стоит karen), аочередь b - троих (первой стоит deb). Таким образом, очереди имеютопределенное состояние, которое влияет на их будущее поведение - например,одну очередь можно безопасно продвинуть (pop) еще два раза, а вторую - три.Операции. Операция - это услуга, которую класс может предоставитьсвоим клиентам. На практике типичный клиент совершает над объектамиоперации пяти видов.4 Ниже приведены три наиболее распространенныеоперации:• МодификаторОперация, которая изменяет состояние объекта.• СелекторОперация, считывающая состояние объекта, ноне меняющая состояния.• ИтераторОперация, позволяющая организовать доступ ковсем частям объекта в строго определенной последовательности.Поскольку логика этих операций весьма различна, полезно выбратьтакой стиль программирования, который учитывает эти различия в кодепрограммы.
В нашей спецификации класса Queue мы вначале перечислили всемодификаторы (функции-члены без спецификаторов const - clear, append, pop,remove), а потом все селекторы (функции со спецификаторами const - length,isEmpty, front и location). Позднее в главе 9, следуя нашему стилю, мыопределим отдельный класс, который действует как агент, отвечающий заитеративный просмотр очередей.Две операции являются универсальными; они обеспечиваютинфраструктуру, необходимую для создания и уничтожения экземпляровкласса:• КонструкторОперация создания объекта и/или егоинициализации.• ДеструкторОперация, освобождающая состояние объектаи/или разрушающая сам объект.В языке C++ конструктор и деструктор составляют часть описаниякласса, тогда как в Smalltalk и CLOS эти операторы определены в протоколеметакласса (то есть класса класса).В чисто объектно-ориентированных языках, таких как Smalltalk,операции могут быть только методами, так как процедуры и функции внеклассов в этом языке определять не допускается.
Напротив, в языках ObjectPascal, C++, CLOS и Ada допускается описывать операции как независимые отобъектов подпрограммы. В C++ они называются функциями-нечленами; мыже будем здесь называть их свободными подпрограммами. Свободныеподпрограммы - это процедуры и функции, которые выполняют рольопераций высокого уровня над объектом или объектами одного или разныхклассов. Свободные процедуры группируются в соответствии с классами, длякоторых они создаются.
Это дает основание называть такие пакеты процедурутилитами класса. Например, для определенного выше класса Queue можнонаписать следующую свободную процедуру:void copyUntilFound(Queue& from. Queue& to, void* item){while ((!from.isEmpty()) && (from.front() != item)){to.append(from.front()) ;from.pop();}}Смысл в том, что содержимое одной очереди переходит в другую дотех пор, пока в голове первой очереди не окажется заданный объект. Этооперация высокого уровня; она строится на операциях-примитивах классаQueue.В C++ (и Smalltalk) принято собирать все логически связанныесвободные подпрограммы и объявлять их частью некоторого класса, неимеющего состояния.
Все такие функции будут статическими.4Липпман предложил несколько иную классификацию: функции управления,функции реализации, вспомогательные функции (все виды модификаторов) ифункции доступа (эквивалентные селекторам) [7].Таким образом, можно утверждать, что все методы - операции, но невсе операции - методы: некоторые из них представляют собой свободныеподпрограммы. Мы склонны использовать только методы, хотя, как будетпоказано в следующем разделе, иногда трудно удержаться от искушения,особенно если операция по своей природе выполняется над несколькимиобъектами разных классов и нет никаких причин объявить ее операциейименно одного класса, а не другого.Роли и ответственности.
Совокупность всех методов и свободныхпроцедур, относящихся к конкретному объекту, образует протокол этогообъекта. Протокол, таким образом, определяет поведение объекта,охватывающее все его статические и динамические аспекты. В самыхнетривиальных абстракциях полезно подразделять протокол на частныеаспекты поведения, которые мы будет называть ролями. Адаме говорит, чтороль - это маска, которую носит объект [8]; она определяет контрактабстракции с ее клиентами.Объединяя наши определения состояния и поведения объекта, ВирфсБрок вводит понятие ответственности. "Ответственности объекта имеют двестороны - знания, которые объект поддерживает, и действия, которые объектможет исполнить. Они выражают смысл его предназначения и место всистеме.
Ответственность понимается как совокупность всех услуг и всехконтрактных обязательств объекта" [9]. Таким образом можно сказать, чтосостояние и поведение объекта определяют исполняемые им роли, а те, в своюочередь, необходимы для выполнения ответственности данной абстракции.Действительно большинство интересных объектов исполняют в своейжизни разные роли, например [10]:• Банковский счет может быть в хорошем или плохом состоянии (двероли), и от этой роли зависит, что произойдет при попытке снятия с негоденег.• Для фондового брокера пакет акций - это товар, который можнопокупать или продавать, а для юриста это знак обладания определеннымиправами.• В течении дня одна и та же персона может играть роль матери, врача,садовника и кинокритика.Роли банковского счета являются динамическими ивзаимоисключающими. Роли пакета акций слегка перекрываются, но каждаяиз них зависит от того, что клиент с ними делает.
В случае персоны ролидинамически изменяются каждую минуту.Как мы увидим в главах 4 и 6, мы часто начинаем наш анализ сперечисления разных ролей, которые может играть объект. Во времяпроектирования мы выделяем эти роли, вводя конкретные операции,выполняющие ответственности каж-дой роли.Объекты как автоматы. Наличие внутреннего состояния объектовозначает, что порядок выполнения операций имеет существенное значение.Это наводит на мысль представить объект в качестве маленькой независимоймашины [II]. Действительно, для ряда объектов такой временной порядокнастолько существен, что наилучшим способом их формального описаниябудет конечный автомат. В главе 5 мы введем обозначения для описанияиерархических конечных автоматов, которые можно использовать длявыражения соответствующей семантики.Продолжая аналогию с машинами, можно сказать, что объекты могутбыть активными и пассивными.
Активный объект имеет свой потокуправления, а пассивный - нет. Активный объект в общем случае автономен,то есть он может проявлять свое поведение без воздействия со стороны другихобъектов. Пассивный объект, напротив, может изменять свое состояние толькопод воздействием других объектов. Таким образом, активные объектысистемы - источники управляющих воздействий. Если система имеетнесколько потоков управления, то и активных объектов может бытьнесколько. В последовательных системах обычно в каждый момент временисуществует только один активный объект, например, главное окно, диспетчеркоторого ловит и обрабатывает все сообщения. В таком случае остальныеобъекты пассивны: их поведение проявляется, когда к ним обращаетсяактивный объект.
В других видах последовательных архитектур (системыобработки транзакций) нет явного центра активности, и управлениераспределено среди пассивных объектов системы.ИдентичностьСемантика. Хошафян и Коуплэнд предложили следующееопределение:"Идентичность - это такое свойство объекта, которое отличаетего от всех других объектов" [12].Они отмечают, что "в большинстве языков программирования иуправления базами данных для различения временных объектов их именуют,тем самым путая адресуемость и идентичность. Большинство баз данныхразличают постоянные объекты по ключевому атрибуту, тем самым смешиваяидентичность и значение данных".