Гради Буч - Объектно-ориентированный анализ и проектирование с примерами приложений на С++, страница 13
Описание файла
PDF-файл из архива "Гради Буч - Объектно-ориентированный анализ и проектирование с примерами приложений на С++", который расположен в категории "". Всё это находится в предмете "объектно-ориентированный анализ и проектирование" из 7 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 13 страницы из PDF
Кроме того заметьте, что операция establish описана какvirtual для того, чтобы подклассы могли ее переопределять.В открытую (public) часть описания вынесены конструктор и деструктор объекта(определяющие процедуры его порождения и уничтожения), две процедуры модификации(очистка всего плана clear и определение элементов плана establish) и два селектора-определителясостояния (функции name и desiredCondition). Мы опустили в описании закрытую частькласса, заменив ее многоточием, поскольку сейчас нам важны внешние ответственности, а невнутреннее представление класса.ИнкапсуляцияЧто это значит? Хотя мы описывали нашу абстракцию GrowingPlan как сопоставлениедействий моментам времени, она не обязательно должна быть реализована буквально как таблицаданных.
Действительно, клиенту нет никакого дела до реализации класса, который егообслуживает, до тех пор, пока тот соблюдает свои обязательства. На самом деле, абстракцияобъекта всегда предшествует его реализации. А после того, как решение о реализации принято,оно должно трактоваться как секрет абстракции, скрытый от большинства клиентов. Как мудрозамечает Ингалс: «Никакая часть сложной системы не должна зависеть от внутреннего устройствакакой-либо другой части» [50]. В то время, как абстракция «помогает людям думать о том, что ониделают», инкапсуляция «позволяет легко перестраивать программы» [51].Абстракция и инкапсуляция дополняют друг друга: абстрагирование направлено нанаблюдаемое поведение объекта, а инкапсуляция занимается внутренним устройством. Чаще всегоинкапсуляция выполняется посредством скрытия информации, то есть маскировкой всехвнутренних деталей, не влияющих на внешнее поведение.
Обычно скрываются и внутренняяструктура объекта и реализация его методов.Инкапсуляция, таким образом, определяет четкие границы между различнымиабстракциями. Возьмем для примера структуру растения: чтобы понять на верхнем уровнедействие фотосинтеза, вполне допустимо игнорировать такие подробности, как функции корнейрастения или химию клеточных стенок.
Аналогичным образом при проектировании базы данныхпринято писать программы так, чтобы они не зависели от физического представления данных;вместо этого сосредотачиваются на схеме, отражающей логическое строение данных [52]. В обоихслучаях объекты защищены от деталей реализации объектов более низкого уровня.Дисков прямо утверждает, что «абстракция будет работать только вместе с инкапсуляцией» [53]. Практически это означает наличие двух частей в классе: интерфейса иреализации. Интерфейс отражает внешнее поведение объекта, описывая абстракцию поведениявсех объектов данного класса.
Внутренняя реализация описывает представление этой абстракции имеханизмы достижения желаемого поведения объекта. Принцип разделения интерфейса иреализации соответствует сути вещей: в интерфейсной части собрано все, что касаетсявзаимодействия данного объекта с любыми другими объектами; реализация скрывает от другихобъектов все детали, не имеющие отношения к процессу взаимодействия объектов. Бритон иПарнас назвали такие детали «тайнами абстракции» [54].Инкапсуляция скрывает детали реализации объектаИтак, инкапсуляцию можно определить следующим образом:Инкапсуляция — это процесс отделения друг от друга элементов объекта, определяющихего устройство и поведение; инкапсуляция служит для того, чтобы изолировать контрактныеобязательства абстракции от их реализации.Примеры инкапсуляции.
Вернемся к примеру гидропонного тепличного хозяйства. Ещеодной из ключевых абстракций данной предметной области является нагреватель,поддерживающий заданную температуру в помещении. Нагреватель является абстракцией низкогоуровня, поэтому можно ограничиться всего тремя действиями с этим объектом: включение,выключение и запрос состояния. Нагреватель не должен отвечать за поддержание температуры,это будет поведением более высокого уровня, совместно реализуемым нагревателем, датчикомтемпературы и еще одним объектом. Мы говорим о поведении более высокого уровня, потому чтооно основывается на простом поведении нагревателя и датчика, добавляя к ним кое-что еще, аименно гистерезис (или запаздывание), благодаря которому можно обойтись без частыхвключений и выключении нагревателя в состояниях, близких к граничным. Приняв такое решениео разделении ответственности, мы делаем каждую абстракцию более цельной.Как всегда, начнем с типов.// Булевский типenum Boolean {FALSE, TRUE};В дополнение к трем предложенным выше операциям, нужны обычные мета-операциисоздания и уничтожения объекта (конструктор и деструктор).
Поскольку в системе может бытьнесколько нагревателей, мы будем при создании каждого из них сообщать ему место, где онустановлен, как мы делали это с классом датчиков температуры TemperatureSensor. Итак, воткласс Heater для абстрактных нагревателей, написанный на C++:class Heater {public:Heater(Location);~Heater();void turnOn();void tum0ff();Boolean is0n() const;private:};Вот и все, что посторонним надо знать о классе Heater. Внутренность класса это совсемдругое дело. Предположим, проектировщики аппаратуры решили разместить управляющиекомпьютеры вне теплицы (где слишком жарко и влажно), и соединить их с датчиками иисполнительными устройствами с помощью последовательных интерфейсов.
Разумно ожидать,что нагреватели будут коммутироваться с помощью блока реле, а оно будет управлятьсякомандами, поступающими через последовательный интерфейс. Скажем, для включениянагревателя передается текстовое имя команды, номер места нагревателя и еще одно число,используемое как сигнал включения нагревателя.Вот класс, выражающий абстрактный последовательный порт.class SerialPort {public:SerialPort();~SerialPort();void write(char*);void write(int);static SerialPort ports[10];private:};Экземпляры этого класса будут настоящими последовательными портами, в которыеможно выводить строки и числа.Добавим еще три параметра в класс Heater.class Heater {public:…protected:const Location repLocation;Boolean repIsOn;SerialPort* repPort;};Эти параметры repLocation, repIsOn, repPort образуют его инкапсулированноесостояние.
Правила C++ таковы, что при компиляции программы, если клиент попытаетсяобратиться к этим параметрам напрямую, будет выдано сообщение об ошибке.Определим теперь реализации всех операций этого класса.Heater::Heater(Location 1):repLocation(1),repIsOn(FALSE),repPort(&SerialPort::ports[l]) {}Heater::Heater() {}void Heater::turnOn(){if (!repls0n) {repPort->write("*");repPort->write(repLocation);repPort->write(1);repIsOn = TRUE;}}void Heater::turn0ff(){if (repIsOn) {repPort->write("*");repPort->write(repLocation);repPort->write(0);repIsOn = FALSE;}}Boolean Heater::is0n() const{return repIsOn;}Такой стиль реализации типичен для хорошо структурированных объектноориентированных систем: классы записываются экономно, поскольку их специализацияосуществляется через подклассы.Предположим, что по какой-либо причине изменилась архитектура аппаратных средствсистемы и вместо последовательного порта управление должно осуществляться черезфиксированную область памяти.
Нет необходимости изменять интерфейсную часть класса —достаточно переписать реализацию. Согласно правилам C++, после этого придетсяперекомпилировать измененный класс, но не другие объекты, если только они не зависят отвременных и пространственных характеристик прежнего кода (что крайне нежелательно исовершенно не нужно).Обратимся теперь к реализации класса GrowingPlan. Как было сказано, это, в сущности,временной график действий.
Вероятно, лучшей реализацией его был бы словарь пар времядействие с открытой хеш-таблицей. Нет смысла запоминать действия час за часом, онипроисходят не так часто, а в промежутках между ними система может интерполировать ходпроцесса.Инкапсуляция скроет от посторонних взглядов два секрета: то, что в действительностиграфик использует открытую хеш-таблицу, и то, что промежуточные значения интерполируются.Клиенты вольны думать, что они получают данные из почасового массива значений параметров.Разумная инкапсуляция локализует те особенности проекта, которые могут подвергнутьсяизменениям. По мере развития системы разработчики могут решить, что какие-то операциивыполняются несколько дольше, чем допустимо, а какие-то объекты занимают больше памяти,чем приемлемо.
В таких ситуациях часто изменяют внутреннее представление объекта, чтобыреализовать более эффективные алгоритмы или оптимизировать алгоритм по критерию памяти,заменяя хранение данных вычислением. Важным преимуществом ограничения доступа являетсявозможность внесения изменений в объект без изменения других объектов.В идеальном случае попытки обращения к данным, закрытым для доступа, должнывыявляться во время компиляции программы. Вопрос реализации этих условий для конкретныхязыков программирования является предметом постоянных обсуждений.
Так, Smalltalkобеспечивает защиту от прямого доступа к экземплярам другого класса, обнаруживая такиепопытки во время компиляции. В тоже время Object Pascal не инкапсулирует представлениекласса, так что ничто в этом языке не предохраняет клиента от прямых ссылок на внутренние полядругого объекта. Язык CLOS занимает в этом вопросе промежуточную позицию, возлагая всеобязанности по ограничению доступа на программиста. В этом языке все слоты могут сопровождаться атрибутами :reader, :writer и : accessor, разрешающими соответственно чтение,запись или полный доступ к данным (то есть и чтение, и запись). При отсутствии атрибутов слотполностью инкапсулирован. По соглашению, признание того, что некоторая величина хранится вслоте, рассматривается как нарушение абстракции, так что хороший стиль программирования наCLOS требует, чтобы при публикации интерфейса класса, документировались бы только именаего функций, а тот факт, что слот имеет функции полного доступа, должен скрываться [55].