Абстрагирование и обобщение




Для выявления свойств, присущих классу объектов, используются обобщение и абстрагирование.

Обобщение — это мысленное выделение, фиксирование каких-нибудь общих существенных свойств, принадлежащих только данному классу предметов или отношений.

Абстрагирование — это мысленное отвлечение, отделение общих, существенных свойств, выделенных в результате обобщения, от прочих несущественных или необщих свойств рассматриваемых предметов или отношений и отбрасывание (в рамках нашего изучения) последних.

Абстракция — это такие существенные характеристики некоторого объекта, которые отличают его от всех других видов объектов и, таким образом, четко определяют особенности данного объекта с точки зрения дальнейшего рассмотрения и анализа.

В программировании абстракция — это класс объектов. Обобщение и абстрагирование применяются для формирования абстракции.

Абстракция, как понятие, имеет содержание и объем. Содержание понятия — это совокупность свойств, мыслимых в понятии. Объем понятия — это совокупность объектов, мыслимых в понятии. Класс объектов чаще всего описывается как содержание понятия (иногда класс объектов задается перечислением всех объектов, т.е. объемом понятия). Для формирования содержания понятия рассматривают объем понятия и изучают каждый составляющий объем понятия объект.

Абстракция реального предмета существенно зависит от решаемой задачи: в одном случае нас будет интересовать форма предмета, в другом - вес, в третьем - материалы, из которых он сделан, в четвертом - закон движения предмета и т. д.

Если поставлена задача определения вида графического файла, то главной характеристикой файла будет его сигнатура (идентификационная последовательность символов, присущая только объектам определенного вида. Например, для файла *.png сигнатура состоит из цифр 137 80 78 71 13 10 26 10, для *.gif — символов G I F, для *.bmp — символов BM).

Если же задача заключается в отображении файла существенными будут: размер по вертикали, размер по горизонтали, цветовая палитра и т.п.

Абстракция концентрирует внимание на внешних особенностях объекта, позволяя отделить основное в поведении объекта от его реализации.

 

По мнению Ингалса: «Для построения системы должен использоваться минимальный набор неизменяемых компонент; сами компоненты должны быть по возможности стандартизованы и связаны единым способом построения». Применительно к OOП такими компонентами являются классы и объекты, отражающие ключевые абстракции системы, а единство построения (каркас) обеспечивается соответствующими механизмами реализации.

Опыт показывает, что процесс выделения классов и объектов является последовательно-итеративным. В действительности, за исключением самых простых задач, с первого раза не удается окончательно выделить и описать классы. Очевидно, такой процесс связан с дополнительными затратами на перекомпиляцию, согласование и внесение изменений в проект системы. Очень важно, следовательно, с самого начала по возможности приблизиться к правильным решениям, чтобы сократить число последующих шагов приближения к истине. Для оценки качества классов и объектов, выделяемых в системе, можно предложить следующие пять критериев:

- взаимозависимость;

- связность;

- достаточность;

- полнота;

- простота (безызбыточность).

Термин «взаимозависимость» заимствован из структурного проектирования, но в более вольном толковании он используется и в OOП. Стивене, Майерс и Константин определяют его так: «степень глубины связей между отдельными модулями. Фрагменты системы, сильно зависимые от других, гораздо сложнее воспринимать, заменять и модифицировать. Для улучшения качества системы следует по возможности избегать сильной зависимости между отдельными модулями». Пример правильного подхода к проблеме взаимозависимости приведен Пэйдж-Джонсом в виде модульной стереосистемы, где усилитель мощности размещен в конструкции колонки громкоговорителей.

Понятие связности также заимствовано из структурного проектирования. Связность — это степень взаимодействия между элементами отдельного модуля (а для OOП еще и отдельного класса или объекта). Наименее желательной является связность по случайному принципу, когда в одном классе или модуле собираются совершенно независимые абстракции. Для примера можно вообразить класс, соединяющий абстракции собак и космических аппаратов. Наиболее желательной является функциональная связность, при которой все элементы класса или модуля тесно взаимодействуют в достижении определенной цели. Так, например, класс «собака» будет функционально связанным, если он описывает поведение собаки, собаки в целом и ничего, кроме собаки.

К идеям взаимозависимости и связности тесно примыкают понятия достаточности, полноты и простоты. Под достаточностью подразумевается наличие в классе или модуле программы всего необходимого для реализации логичного и эффективного поведения. Иначе говоря, компоненты должны быть полностью пригодны к использованию. Для примера рассмотрим класс «множество». Операция удаления элемента из множества в этом классе, очевидно, необходима, но будет ошибкой не включить в этот класс и операцию добавления элемента. Нарушение требования достаточности обнаруживается очень быстро, как только создается класс-пользователь такой абстракции. Под полнотой подразумевается наличие в интерфейсной части класса всех необходимых характеристик абстракции. Идея достаточности предъявляет к интерфейсу минимальные требования, а идея полноты охватывает все существенные аспекты абстракции. Полнотой характеризуется такой класс или модуль, интерфейс которого гарантирует все необходимое для взаимодействия с пользователями. Полнота является субъективным фактором, и разработчики часто ей злоупотребляют, вынося на верхний уровень такие операции, которые можно реализовать на более низком уровне. Из этого вытекает требование простоты (минимальной необходимости). Простыми являются только такие операции, которые обеспечивают реализацию эффективного действия абстракции. Так, в примере с «множеством» операция добавления элемента является примитивной, а операция добавления четырех элементов не будет примитивной, так как эффективно реализуется через операцию добавление одного элемента. Конечно, эффективность тоже субъективный фактор. Операции прямого доступа к структуре данных являются примитивными по определению. Операции, которые можно свести к нескольким примитивным, но при этом затрачиваются значительные вычислительные ресурсы, также являются потенциально кандидатами на включение в разряд примитивных.

Для просмотрщика графических файлов были выделены следующие объекты: Менеджер, Диалог выбора файла, Транслятор, Конфигуратор, Область отображения, Файл и Изображение.

Определять существенные свойства для объектов будем по следующему алгоритму:

- формулируем назначение объекта, которое он выполняет в рамках поставленной задачи;

- сравниваем объект с другими объектами, входящими в объем понятия, соответствующего объекту, и выделяем существенные свойства;

- пытаемся обеспечить минимальную взаимозависимость между классами объектов, максимальную связность внутри каждого класса объектов, достаточность, полноту и простоту.

-

Объект «Файл» предназначен для того, чтобы хранить содержимое файла и информацию о файле. Объем понятия «Файл» включает в себя файлы различного типа: текстовые файлы, исполняемые файлы, звуковые файлы, видеофайлы, графические файлы и т.п. Если для каждого из типов файлов указать свойства, используемые для работы с файлом, то получим следующий список:

- название файла;

- длина файла;

- тип файла;

- дата создания файла;

- буфер для содержимого;

- конструктор;

- деструктор;

- операция открытия файла;

- операция чтения содержимого файла;

- операция чтения атрибутов файла;

- операция инициализации объекта класса «Файл».

Объект «Изображение» служит для хранения изображения в форме, удобной для отображения, и информации об изображении. Объем понятия «Изображение» задается такими объектами, как картинка, чертеж, карта и т.п. Существенными для всех этих объектов будут:

- высота;

- ширина;

- палитра;

- разрешение (количество точек на дюйм);

- конструктор;

- деструктор.

Объект «Диалог выбора файла» предназначен для работы с файлами. В его обязанности входит распознавание графических файлов, отображение списка графических файлов, выбор следующего файла для загрузки, создание и использование объекта «Файл». Объем понятия «Диалог выбора файла» включает в себя диалоговые окна Windows различного типа: диалог открытия файла, диалог сохранения файла, проводник и т.п. Из всего этого следует, что существенными для объекта будут свойства:

- фильтр файлов;

- список файлов;

- кнопка подтверждения выбора;

- кнопка отмены;

- указатель текущего каталога;

- конструктор;

- деструктор.

Объект «Конфигуратор» играет роль посредника между реестром и просмотрщиком. Он хранит в себе извлеченные из реестра параметры и режим отображения. Объем понятия «Конфигуратор» включает в себя: редактор реестра, диалог настройки шрифта, диалог настройки параметров абзаца и т.п. Существенными для этого объекта являются свойства:

- список параметров;

- указатель текущего параметра;

- списки возможных значений параметров;

- конструктор;

- деструктор.

Объект «Транслятор» используется для перевода содержимого файла в форму, удобную для отображения. Объем понятия «Транслятор» охватывает: кодировщик, преобразователь, переводчик и т.п. Существенными для такого объекта являются свойства:

- алгоритм преобразования;

- операция получения содержимого файла;

- операция записи изображения;

- конструктор;

- деструктор.

Объект «Область отображения» предназначен для вывода изображения на экран дисплея. Объем понятия «Область отображения» включает в себя: канву, холст, полотно и т.п. Существенными для такого объекта являются свойства:

- высота области;

- ширина области;

- параметры отображения;

- конструктор;

- деструктор;

- операция рисования изображения.

Объект «Менеджер» выполняет функции управляющего элемента, посредством которого пользователь может управлять просмотрщиком. Объем понятия «Менеджер» состоит из таких объектов, как: управляющий объект, диспетчер, распредитель и т.п.

Существенными для такого объекта являются свойства:

- набор команд;

- конструктор;

- деструктор;

 

Ограничение доступа

Созданию абстракции какого-либо объекта должны предшествовать определенные решения о способе ее реализации. Выбранный способ реализации должен быть скрыт и защищен для большинства объектов-пользователей (обращающихся к данной абстракции). Как справедливо отметил Ингалс, «никакая часть сложной системы не должна находиться в зависимости от подробностей внутреннего устройства других частей системы». Ограничение доступа позволяет вносить в программу изменения, сохраняя ее надежность и минимизируя затраты на этот процесс.

Абстрагирование и ограничение доступа являются взаимодополняющими операциями: абстрагирование фокусирует внимание на внешних особенностях объекта, а ограничение доступа — или иначе защита информации — не позволяет объектам-пользователям различать внутреннее устройство объекта. Ограничение доступа, таким образом, определяет явные барьеры между различными абстракциями. Возьмем для примера структуру растения: чтобы понять на верхнем уровне действие фотосинтеза, вполне допустимо игнорировать такие подробности, как корни растения или метахондрии клеток. Аналогичным образом при проектировании баз данных программисты не обращают внимание на физический смысл данных, а сосредотачиваются на схеме, отражающей логическое строение данных.

В обоих случаях объекты верхнего уровня абстракции не связаны прямо с подробностями их реализации на низком уровне. Лисков считает что «для «работы» абстракции, доступ к ее внутренней структуре должен быть ограничен». Практически это означает наличие двух частей в описании класса: интерфейса и реализации. Интерфейс отражает внешнее проявление объекта, создавая абстракцию поведения всех объектов данного класса. Внутренняя реализация описывает механизмы достижения желаемого поведения объекта. Принцип такого различения интерфейса и реализации соответствует разделению по сути: в интерфейсной части собрано все, что касается взаимодействия данного объекта с любыми другими объектами; реализация скрывает от других объектов все детали, не имеющие отношения к процессу взаимодействия объектов.

Итак, понятие ограничения доступа можно определить следующим образом:

Ограничение доступа — это процесс защиты отдельных элементов объекта, не затрагивающий существенных характеристик объекта как целого.

На практике осуществляется защита как структуры объекта, так и реализации его методов.

Предположим, что по какой-либо причине изменилась архитектура аппаратных средств системы и вместо последовательного порта управление должно осуществляться через область памяти. В такой ситуации нет необходимости изменять интерфейсную часть объекта, а достаточно переписать реализацию. При правильном использовании ограничение доступа позволяет локализовать те особенности проекта, которые подвержены изменениям. По мере развития системы может оказаться, что какие-то операции выполняются несколько дольше, чем допустимо, а какие-то объекты занимают больше памяти, чем приемлемо. В таких ситуациях часто изменяют структуру объекта, чтобы реализовать более эффективные алгоритмы или оптимизировать объемы используемой памяти. Важным преимуществом ограничения доступа является возможность внесения изменений в объект без изменения других объектов, связанных с данными.

В идеальном случае попытки обращения к данным, закрытым для доступа, должны выявляться во время компиляции программы. Вопрос реализации этих условий для конкретных языков программирования является предметом постоянных обсуждений.

 

Модульность

Понятие модульности. По мнению Майерса, «Разделение программы на фрагменты позволяет частично уменьшить ее сложность. Однако гораздо важнее тот факт, что разделение программы улучшает проработку ее частей. Эти части весьма ценны для исчерпывающего понимания программы в целом». В некоторых языках программирования модульность не реализована, и классы составляют лишь физическую основу декомпозиции. В других языках модульность является элементом конструкции и позволяет осуществлять на ее основе проектные решения. В таких языках классы и объекты составляют логическую структуру системы; эти абстракции организуются в модули, образуя физическую структуру системы. Это свойство становится особенно полезным, когда система состоит из многих сотен классов.

По Лискову, «модульность — это разделение программы на раздельно компилируемые фрагменты, имеющие между собой средства сообщения». Можно использовать также и определение Парнаса: «Связи между модулями — это предположения о возможности их использования». В большинстве языков, поддерживающих принцип модульности как самостоятельную концепцию, также реализуется разделение интерфейсной части и реализации. Таким образом, модульность и ограничение доступа идут неотделимо друг от друга.

Правильное разделение программы на модули является почти такой же сложной задачей, как выбор правильного набора абстракций. Абсолютно прав Зелковиц, утверждая: «поскольку в начале работы над проектом решения могут быть неясными, декомпозиция на модули может вызвать затруднения. Для хорошо известных приложений (например, создание компиляторов) этот процесс можно стандартизовать, но для новых задач (военные системы или управление космическими аппаратами) задача может быть очень трудной».

Модули выполняют роль физических контейнеров, в которые помещаются определения классов и объектов при логическом проектировании системы. Это такая же ситуация, которая возникает у проектировщиков бортовых компьютеров. Логика электронного оборудования может быть построена на основе элементарных вентилей типа не, и-не, или-не, но можно и объединить группы таких вентилей в стандартные интегральные схемы, например, серий 7400, 7402 или 7404. Отсутствие стандартизованных фрагментов дает программисту гораздо большую степень свободы — как если бы электронщик имел в своем распоряжении средства создания кремниевых кристаллов.

Для небольших задач допустимо описание каждого класса и объекта в отдельном модуле. В других, наиболее тривиальных случаях, в один модуль лучше собрать все логически связанные классы и объекты и оставить незащищенными все те элементы, которые должны быть видимы для всей программы. Однако такой подход применяется в исключительных случаях. Рассмотрим, например, задачу, которая выполняется на многопроцессорном оборудовании и требует для координации механизма передачи сообщений. В больших системах вполне обычным является наличие нескольких сотен и даже тысяч видов сообщений. Было бы наивным определение каждого класса сообщений в отдельном модуле. При этом не только возникает сложная ситуация с документированием, но чрезвычайно труден для пользователя даже просто поиск нужных фрагментов описания. При внесении в проект изменений потребуется модифицировать и перекомпилировать сотни модулей. Отсюда можно понять, что защита информации может обернуться и обратной стороной. Деление программы на модули бессистемным образом гораздо хуже, чем отсутствие модульности вообще.

В традиционном структурном проектировании модульность связывается в первую очередь с логической группировкой подпрограмм на основе принципов взаимной связи и общей логики. В объектно-ориентированном программировании ситуация несколько иная: необходимо физически разделить классы и объекты, составляющие логическую структуру проекта и явно отличающиеся от подпрограмм.

Модульность — это свойство системы, связанное с возможностью декомпозиции ее на ряд тесно связанных модулей.

Таким образом, принципы абстрагирования, ограничения доступа и модульности являются взаимодополняющими. Объект определяет явные границы определенной абстракции, а ограничение доступа и модульность создают барьеры между абстракциями.

В процессе разделения системы на модули могут быть полезными два правила. Первое состоит в следующем: поскольку модули служат в качестве элементарных и неделимых блоков программы, которые могут использоваться в системе многократно, распределение классов и объектов должно создавать для этого максимальные удобства. Второе правило вытекает из того факта, что многие компиляторы создают отдельный сегмент кода для каждого модуля. Поэтому размер модуля должен быть соответственно ограничен. Объявление функций в модуле может оказать большое влияние на возможность их вызова из другого модуля, так как это связано с разделением памяти на страницы. Большое количество вызовов между сегментами загружает кэш-память и снижает характеристики всей системы.

 

Внутреннее строение (реализация) классов и объектов разрабатывается только после завершения проектирования их внешнего облика. При этом необходимо принять два проектных решения: выбрать способ представления класса или объекта и способ размещения их в модуле.

Представление классов и объектов почти всегда связано с ограничением доступа к элементам абстракции. Это позволяет вносить изменения (например, перераспределение памяти и временных ресурсов) без нарушения функциональных связей с другими классами и объектами. Вирт считает, что «выбор способа представления является нелегкой задачей и не определяется только техническими возможностями. Он всегда должен рассматриваться с точки зрения операций над данными». Рассмотрим, например, класс, соответствующий совокупности планов полета самолетов. Как его нужно оптимизировать — по эффективности поиска или по времени включения в план и удаления из него? Поскольку невозможно реализовать и то и другое одновременно, нужно сделать выбор на основе знаний и характера задачи. Не всегда удается сделать такой выбор и тогда создается семейство классов с одинаковым интерфейсом, поведение которых зависит от направления оптимизации.

Одним из наиболее трудных решений является выбор между возможностью вычисления элементов состояния объекта и их хранением в виде поля данных. Рассмотрим, например, класс «корпус» с соответствующим ему методом «объем». Этот метод возвращает значение объема объекта. В структуре объекта хранятся данные о высоте конуса и радиусе основания в виде отдельных полей. Следует ли еще создать поле данных для значения объема или следует вычислять его по мере необходимости с помощью метода «объем»? Если мы хотим получать значение объема максимально быстро, нужно создавать соответствующее поле данных. Если важнее экономия памяти, лучше вычислить это значение. Оптимальный способ представления объекта всегда определяется характером решаемой задачи. В любом случае этот выбор не должен зависеть от внешних особенностей (интерфейса) класса, наоборот, такой выбор не должен сказываться на отношениях с объектами-пользователями.

Аналогичный вопрос возникает при выборе места для декларирования классов и объектов в программном модуле. Решение о месте декларирования классов и объектов в языках является компромиссом требований «видимости» и защиты информации. В общем случае модули должны быть функционально связанными и зависимыми. При этом следует учитывать ряд нетехнических факторов, таких, как повторное использование, документирование, требования секретности. Проектирование модулей — не менее простой процесс, чем проектирование классов и объектов. О проблеме защиты информации Парнас, Клементе и Вейс говорят следующее: «Реализация этого принципа не всегда является очевидной. Необходимо минимизировать стоимость программных средств (в целом за время эксплуатации) и оценить вероятность внесения изменений. Такая оценка исходит из практического опыта и знания предметной области, включая технологию программирования и аппаратную реализацию».

Класс объектов «Файл» должен быть полностью открыт. Ко всем его полям необходим доступ. Поэтому все свойства этого класса располагаются в интерфейсной части.

Класс объектов «Изображение» также должен быть полностью открыт. Ко всем его полям необходим доступ. Поэтому все свойства этого класса располагаются в интерфейсной части.

В классе объектов «Диалог выбора файла» должны быть скрыты такие свойства как фильтр файлов, список файлов и указатель текущего каталога.

Класс объектов «Конфигуратор» должен быть полностью открыт. Ко всем его полям необходим доступ. Поэтому все свойства этого класса располагаются в интерфейсной части.

Класс объектов «Транслятор» скрывает такое свойства, как алгоритм преобразования.

Класс объектов «Область отображения» должен скрывать все свойства, кроме конструктора и деструктора.

Класс объектов «Менеджер» должен быть полностью видим пользователю. Поэтому все его свойства должны быть общедоступны и, следовательно, располагаться в интерфейсной части.



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2020-06-05 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: