Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

Применение объектно-ориентированного подхода при проектировании информационной системы (Основные понятия)

Содержание:

ВВЕДЕНИЕ

В настоящее время иногда всё ещё раздаются голоса по поводу того, что объектно-ориентированное программирование (ООП) это не есть что-то необходимое и даже не есть что-то полезно. Часто это из-за того, что у авторов нет чёткого понимания о том, что же такое ООП, в чём его суть и где те самые удобства, которые оно даёт. Здесь мы рассмотрим достаточно яркие примеры, иллюстрирующие пользу ООП и то, как, зачем и почему оно появилось.

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

ГЛАВА 1. ИСТОРИЯ И ТЕРМИН ОБЪЕКТНО-ОРИЕНТИРОВАННОГО ПОДХОДА ПРОГРАММИРОВАНИЯ

Раздел 1. Основные понятия

Для начала рассмотрим основные используемые термины:

Объектно-ориентированное программирование - это методика программирования, основывающаяся на представлении программы в виде комплекса объектов, каждая единица которого является частью определённого класса, которые в свою очередь образуют иерархию наследования. (Определение Гради Буча)

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

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

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

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

Полиморфизм подтипов Полиморфизм подтипов (в ООП называемый просто «полиморфизмом») — свойство системы, позволяющее использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.

Шаблон проектирования или паттерн (в программировании) - повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста.

Раздел 2. История развития

История развития объектно-ориентированного программирования берет свое начало в конце 60-х годов. Первым объектно-ориентированным языком был язык программирования Simula, созданным сотрудниками Норвежского вычислительного центра (Осло) Кристеном Нюгором и Оле-Йоханом Далем для моделирования сложных систем.

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

Одновременно готовились две версии Симулы. Первая, Симула I, формировалась по контракту с подразделением Univac корпорации Sperry Rand для машины UNIVAC 1107 (обошедшейся NCC в 990 тыс. долл.). Американские заказчики желали видеть этот язык фортраноподобным, от чего, впрочем, авторы категорически отказались; в результате прародителем Симулы стал Алгол 60. Последний был выбран благодаря блочной архитектуре, хорошим средствам сокрытия данных, а также вследствие высокой популярности в европейских научных центрах. А работы над вторым вариантом спонсировал NCC.

Пожалуй, первым компьютерным решением, воплотившим в себе объектный подход, стал программно-аппаратный графический Планшет (Sketchpad: A Man-Machine Graphical Communications System), использовавший оборудование DEC PDP. Его в 1963 г. разработал 25-летний Иван Сазерленд, помогавший в создании симуляторов вертолетов военному научному агентству DARPA, а затем занявшийся в Массачусетском технологическом институте докторской диссертацией и имевший удовольствие общения с Клодом Шенноном. С помощью светового пера и системы выпадающих меню пользователь Планшета мог рисовать различные несложные изображения на аналоговом дисплее, перемещать их и точно располагать в определенных позициях экрана, а также хранить. В качестве понятия класса Сазерленд использовал определение "мастер", разделяя описание и реально существующий на экране объект (экземпляр "мастера").

Первая всемирная конференция по объектно-ориентированным системам программирования прошла в Портленде в 1986г. Возможно, именно прозвучавшие на ней доклады оказали стимулирующее влияние на Уильяма Аткинсона, инженера Apple, который через год после этого спроектировал систему HyperCard, прообраз современных визуальных сред быстрой разработки. Эффективность новой технологии оказалась столь высокой, что уже в 1989 г. одиннадцать компаний, среди которых были 3Com, American Airlines, Canon, Data General, Hewlett-Packard, Philips Telecommunications, Sun Microsystems и Unisys, основали группу OMG (Object Management Group), призванную формировать индустриальные стандарты на объектное программирование и упрощать интеграцию приложений с помощью универсальных кросс-платформных технологий. Эта группа первым делом приступила к выработке единого стандарта компонентной модели CORBA (Common Object Request Broker Architecture) – набора спецификаций, определяющих способы объектно-ориентированного взаимодействия компонентов промежуточного уровня в гетерогенных средах без привязки к конкретным языкам программирования. С самого начала CORBA нацеливалась на поддержку крупных, индустриальных проектов, и этот подход со временем себя полностью оправдал. Сегодня нет другого столь же распространенного независимого стандарта, поддерживающего самые разные ОС и объектные модели.

Симула традиционно не считается объектно-ориентированным языком в каноническом смысле этого слова, в частности, создатель языка Smalltalk Алан Кэй имел в виду под этим термином семантику акторов, впервые реализованную в языке Плэнер Карла Хьюитта, а не расширение алголоподобных языков «объектной» нотацией.

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

Тем не менее, этот язык активно использовался в образовательном процессе в высших учебных заведениях, особенно в Скандинавии; о влияние Симулы на разработку им C++ отмечал его автор — Страуструп (в том числе в книге «Дизайн и эволюция C++»).

Несмотря на многомиллионные вложения, сделанные в 1970-1980 годах коммерческими компаниями и государственными структурами разных стран в универсальные языки программирования (такие, как Алгол, PL/1, Си) и языки логического программирования (прежде всего Prolog), самой распространенной в мире программной технологией остается ООП. Наиболее известным событием нового тысячелетия в этой сфере стал быстро набравший популярность язык программирования C#. Его можно смело считать лучшим на сегодня объектно-ориентированным средством создания графических приложений (формально – для разных платформ, фактически же – только для .NET, так как без среды разработки ценность C# равна нулю). Правда, лучшим C# можно назвать среди небольшого числа коммерческих альтернатив (Java, С++ и, вероятно, Delphi). Если же сравнить его возможности со средствами SmallTalk, то окажется, что ряд сильных идей ООП, заложенных в SmallTalk продвинутыми инженерными умами, оказался в C# упущенным.

В ближайшее десятилетие развитие ООП будет проходить под влиянием трех концепций: Microsoft .NET (прежде всего .NET Framework и ее подмножества и соответственно реализации C#), Java (все входящие в это понятие технологии) и CORBA. Причем важнейшей особенностью CORBA останется независимость от ОС и языка программирования CORBA-компонентов. Методология MDA станет основной объединяющей платформой моделирования для всех этих технологий, прежде всего в силу своей независимости от конкретного разработчика. Немаловажна и объявленная сквозная поддержка MDA во всех средствах разработки "Швейцарии в мире ПО" – корпорации Borland – и гиганта IBM (особенно учитывая недавнюю покупку Rational Software и развитие проекта Eclipse). Что касается "программирования в малом" (programming in small), то в этой области продолжится совершенствование шаблонов проектирования (высокоуровневого объектного программирования), их внедрение в среды разработки и дальнейшее сочетание шаблонного подхода с активно развивающимся языком моделирования UML, последняя версия которого 2.0 появится со дня на день.

Хочется также надеяться, что со временем в коммерческих оболочках создания ПО на популярных языках программирования все же будут реализованы возможности ООП, предложенные еще 25 лет назад авторами SmallTalk.

Сегодня существует достаточное количество объектно-ориентированных языков программирования, наиболее популярными из которых в настоящее время являются C++, Delphi, Java, Visual Basic, Flash. Но, кроме того, многие языки, которые принято причислять к процедурной парадигме, тоже обладают свойствами ООП, имея возможность работать с объектами. Так, объектно-ориентированное программирование в C — это большой раздел программирования на данном языке, то же самое касается ООП в python и многих других структурных языках.

ГЛАВА 2. ОСНОВНЫЕ ПРИНЦИПЫ ОБЪЕКТНО ОРИЕНТИРОВАННОГО ПРОГРАММИРОВАНИЯ

Принципы объектно-ориентированного программирования помимо обработки событий – это инкапсуляция, наследование, подклассы и полиморфизм. Они особенно полезны и необходимы при разработке тиражируемых и простых в сопровождении приложений.

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

Раздел 1. Принцип наследования ООП и подклассы

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

Наследование есть свойство объектов порождать своих потомков. Объект-потомок автоматически наследует от родителя все поля и методы, может дополнять объекты новыми полями и заменять (перекрывать) методы родителя или дополнять их.

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

Последовательное проведение в жизнь принципа «наследуй и изменяй» хорошо согласуется с поэтапным подходом к разработке крупных программных проектов и во многом стимулирует такой подход.

Раздел 2. Полиморфизм объектно-ориентированного программирования

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

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

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

Раздел 3. Инкапсуляция

Инкапсуляция есть объединение в единое целое данных и алгоритмов обработки этих данных. В рамках ООП данные называются полями объекта, а алгоритмы - объектными методами.

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

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

ГЛАВА 3. ШАБЛОНЫ ПРОЕКТИРОВАНИЯ

Раздел 1. Основная информация

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

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

Хотя легкое изменение кода под известный шаблон может упростить понимание кода, по мнению Стива Макконнелла, с применением шаблонов могут быть связаны две сложности. Во-первых, слепое следование некоторому выбранному шаблону может привести к усложнению программы. Во-вторых, у разработчика может возникнуть желание попробовать некоторый шаблон в деле без особых оснований.

Многие шаблоны проектирования в объектно-ориентированном проектировании можно рассматривать как идиоматическое воспроизведение элементов функциональных языков. Питер Норвиг утверждает, что 16 из 23 шаблонов, описанных в книге «Банды четырёх», в динамически-типизируемых языках реализуются существенно проще, чем в С++, либо оказываются незаметны. Пол Грэхэм считает саму идею шаблонов проектирования — антипаттерном, сигналом о том, что система не обладает достаточным уровнем абстракции, и необходима её тщательная переработка. Нетрудно видеть, что само определение шаблона как «готового решения, но не прямого обращения к библиотеке» по сути означает отказ от повторного использования в пользу дублирования. Это, очевидно, может быть неизбежным для сложных систем при использовании языков, не поддерживающих комбинаторы и полиморфизм типов, и это в принципе может быть исключено в языках, обладающих свойством гомоиконичности (хотя и не обязательно эффективно), так как любой шаблон может быть реализован в виде исполнимого кода.

Раздел 2. Типы шаблонов проектирования

На текущий момент существует великое множество шаблонов проектирования. Рассмотрим чуть более подробно несколько из них.

Итак, для начала кратко ознакомимся с классификацией шаблонов по типам:

  1. Основные шаблоны:

1.1 Шаблон делегирования: Объект внешне выражает некоторое поведение, но в реальности передаёт ответственность за выполнение этого поведения связанному объекту.

1.2 Шаблон функционального дизайна: Гарантирует, что каждый модуль компьютерной программы имеет только одну обязанность и исполняет её с минимумом побочных эффектов на другие части программы.

1.3 Неизменяемый интерфейс: Создание неизменяемого объекта.

1.4 Интерфейс: Общий метод для структурирования компьютерных программ для того, чтобы их было проще понять.

1.5 Интерфейс-маркер: В качестве атрибута (как пометки объектной сущности) применяется наличие или отсутствие реализации интерфейса-маркера. В современных языках программирования вместо этого могут применяться атрибуты или аннотации.

1.6 Контейнер свойств: Позволяет добавлять дополнительные свойства для класса в контейнер (внутри класса), вместо расширения класса новыми свойствами.

1.7 Канал событий: Расширяет шаблон Publish/Subscribe, создавая централизованный канал для событий. Использует объект-представитель для подписки и объект-представитель для публикации события в канале. Представитель существует отдельно от реального издателя или подписчика. Подписчик может получать опубликованные события от более чем одного объекта, даже если он зарегистрирован только на одном канале.

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

2.1 Абстрактная фабрика: Класс, который представляет собой интерфейс для создания компонентов системы.

2.2 Строитель: Класс, который представляет собой интерфейс для создания сложного объекта.

2.3 Фабричный метод: Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать.

2.4 Отложенная инициализация: Объект, инициализируемый во время первого обращения к нему.

2.5 Мультитон: Гарантирует, что класс имеет поименованные экземпляры объекта и обеспечивает глобальную точку доступа к ним.

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

2.7 Прототип: Определяет интерфейс создания объекта через клонирование другого объекта вместо создания через конструктор.

2.8 Одиночка: Класс, который может иметь только один экземпляр.

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

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

3.2 Мост: Структура, позволяющая изменять интерфейс обращения и интерфейс реализации класса независимо.

3.3 Компоновщик: Объект, который объединяет в себе объекты, подобные ему самому.

3.4 Декоратор: Класс, расширяющий функциональность другого класса без использования наследования.

3.5 Фасад: Объект, который абстрагирует работу с несколькими классами, объединяя их в единое целое.

3.6 Единая точка входа: Обеспечивает унифицированный интерфейс для интерфейсов в подсистеме. Front Controller определяет высокоуровневый интерфейс, упрощающий использование подсистемы.

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

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

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

4.1 Цепочка обязанностей: Предназначен для организации в системе уровней ответственности.

4.2 Команда: Представляет действие. Объект команды заключает в себе само действие и его параметры.

4.3 Интерпретатор: Решает часто встречающуюся, но подверженную изменениям, задачу.

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

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

4.6 Хранитель: Позволяет не нарушая инкапсуляцию зафиксировать и сохранить внутренние состояния объекта так, чтобы позднее восстановить его в этих состояниях.

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

4.8 Слуга: Используется для обеспечения общей функциональности группе классов.

4.9 Спецификация: Служит для связывания бизнес-логики.

4.10 Состояние: Используется в тех случаях, когда во время выполнения программы объект должен менять своё поведение в зависимости от своего состояния.

4.11 Стратегия: Предназначен для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости.

4.12 Шаблонный метод: Определяет основу алгоритма и позволяет наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.

4.13 Посетитель: Описывает операцию, которая выполняется над объектами других классов. При изменении класса Visitor нет необходимости изменять обслуживаемые классы.

4.14 Одноразовый посетитель: Оптимизирует реализацию шаблона посетитель, который инициализируется, единожды используется, и затем удаляется.

4.15 Иерархический посетитель: Предоставляет способ обхода всех вершин иерархической структуры данных (напр. древовидной).

Как видим, шаблонов проектирования достаточно большое количество. Но, чтобы увидеть их применение на практике, приведу пример использования шаблона Одиночка.

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

Глобальный «одинокий» объект — именно объект (log().put("Test");), а не набор процедур, не привязанных ни к какому объекту (logPut("Test");) — бывает нужен:

  • если используется существующая объектно-ориентированная библиотека и ей нужен объект, унаследованный от определённого класса/интерфейса;
  • если есть шансы, что один объект когда-нибудь превратится в несколько;
  • если интерфейс объекта (например, игрового мира) слишком сложен и не стоит засорять основное пространство имён большим количеством функций;
  • если, в зависимости от каких-нибудь условий и настроек, создаётся один из нескольких объектов. Например, в зависимости от того, ведётся лог или нет, создаётся настоящий объект, пишущий в файл, или «заглушка», ничего не делающая.

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

  • Если объект нужен уже при инициализации, он может быть затребован раньше, чем будет создан.
  • Бывает, что объект нужен не всегда. В таком случае его создание можно пропустить. Особенно это важно, если одиночек (например, диалоговых окон) много — тогда пользователь быстро получит интерфейс, а окна будут создаваться по одному, не мешая работе пользователя.

Пример шаблона, написанный на языках программирования Python, С++ и Delphi привожу в приложении 1.

ГЛАВА 4. ПРЕИМУЩЕСТВА ОБЪЕКТНО-ОРИЕНТИРОВАННОГО ПОДХОДА

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

- описание системы в виде объектов больше соответствует содержательному смыслу предметной области. Например, при использовании структурного подхода БД должна удовлетворять требованиям нормализации, в соответствии с которыми данные по одному и тому же объекту (сущности из реального мира) могут храниться в нескольких таблицах;

- сущности реального мира, как правило, обладают поведением, что в объектно-ориентированном проектировании отражается с помощью определения методов класса. В структурном подходе данные (атрибуты) и алгоритмы (методы) существуют отдельно друг от друга;

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

o адаптации системы к изменению существующих или появлению новых требований;

o сопровождения системы на разных стадиях жизненного цикла;

o повторного использования компонентов;

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

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

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

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

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

Локализация кода и данных улучшает наглядность и удобство сопровождения программного обеспечения.

Инкапсуляция информации защищает наиболее критичные данные от несанкционированного доступа.

ООП дает возможность создавать расширяемые системы (extensible systems). Это одно из самых значительных достоинств ООП и именно оно отличает данный подход от традиционных методов программирования. Расширяемость (extensibility) означает, что существующую систему можно заставить работать с новыми компонентами, причем без внесения в нее каких-либо изменений. Компоненты могут быть добавлены на этапе выполнения.

Расширение типа (type extension) и вытекающий из него полиморфизм переменных оказываются полезными преимущественно в следующих ситуациях.

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

Изменение поведения во время выполнения. На этапе выполнения один объект может быть заменен другим. Это может привести к изменению алгоритма, в котором используется данный объект.

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

Доведение полуфабрикатов. Компоненты нет надобности подстраивать под определенное приложение. Их можно сохранять в библиотеке в виде полуфабрикатов (semifinished products) и расширять по мере необходимости до различных законченных продуктов.

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

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

Мы сокращаем время на разработку, которое с выгодой может быть отдано другим проектам.

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

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

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

ГЛАВА 5. НЕДОСТАТКИ ОБЪЕКТНО-ОРИЕНТИРОВАННОГО ПОДХОДА

Объектно-ориентированное программирование требует знания четырех вещей.

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

2. Многоразовое использование требует от программиста познакомиться с большими библиотеками классов. А это может оказаться сложнее, чем даже изучение нового языка программирования. Библиотека классов фактически представляет собой виртуальный язык, который может включать в себя сотни типов и тысячи операций. В языке Smalltalk, к примеру, до того, как перейти к практическому программированию, нужно изучить значительную часть его библиотеки классов. А это тоже требует времени.

3. Проектирование классов — задача куда более сложная, чем их использование. Проектирование класса, как и проектирование языка, требует большого опыта. Это итеративный процесс, где приходится учиться на своих же ошибках.

4. Очень трудно изучать классы, не имея возможности их «пощупать». Только с приобретением мало-мальского опыта можно уверенно себя чувствовать при работе с использованием ООП.

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

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

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

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

Методы, как правило, короче процедур, поскольку они осуществляют только одну операцию над данными. Зато количество методов намного выше. Короткие методы обладают тем преимуществом, что в них легче разбираться, неудобство же их связано с тем, что код для обработки сообщения иногда «размазан» по многим маленьким методам.

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

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

Часто можно слышать, что ООП является неэффективным. Как же дело обстоит в действительности? Мы должны четко проводить грань между неэффективностью на этапе выполнения, неэффектиностью в смысле распределения памяти и неэффективностью, связанной с излишней универсализацией.

Неэффективность на этапе выполнения. В языках типа Smalltalk сообщения интерпретируются во время выполнения программы путем осуществления поиска их в одной или нескольких таблицах и за счет выбора подходящего метода. Конечно, это медленный процесс. И даже при использовании наилучших методов оптимизации Smalltalk-программы в десять раз медленнее оптимизированных C-программ [Cha92].

В гибридных языках типа Oberon-2, Object Pascal и C++ посылка сообщения приводит лишь к вызову через указатель процедурной переменной. На некоторых машинах сообщения выполняются лишь на 10% медленнее, чем обычные процедурные вызовы. И поскольку сообщения встречаются в программе гораздо реже других операций, их воздействие на время выполнения влияния практически не оказывает.

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

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

Излишняя универсальность. Неэффективность может также означать, что программа имеет ненужные возможности. В библиотечном классе часто содержится больше методов, чем это реально необходимо. А поскольку лишние методы не могут быть удалены, то они становятся мертвым грузом. Это не воздействует на время выполнения, но влияет на возрастание размера кода.

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

Другой подход — дать возможность компоновщику удалять лишние методы.

Такие интеллектуальные компоновщики уже доступны для различных языков и операционных систем.

Oberon избрал третий путь избавления от излишней универсальности.

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

Таким образом, нельзя утверждать, что ООП вообще неэффективно.

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

ЗАКЛЮЧЕНИЕ

Выживет ли объектно-ориентированное программирование, или оно лишь модное поветрие, которое скоро исчезнет?

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

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

СПИСОК ИСПОЛЬЗОВАННОЙ ЛИТЕРАТУРЫ

  1. C.Бобровский «История объектно-ориентированного программирования», PC Week/RE № 28 от 05.08.2003 г., стр. 10, и № 29 от 12.08.2003 г., стр. 20.
  2. Гради Буч. Объектно-ориентированный анализ и проектирование с примерами приложений на С++. — Бином, 1998
  3. Bjarne Stroustrup. The Design and Evolution of C++
  4. Тимоти Бадд. Объектно-ориентированное программирование в действии
  5. Гради Буч, Роберт А. Максимчук, Майкл У. Энгл, Бобби Дж. Янг, Джим Коналлен, Келли А. Хьюстон. Объектно-ориентированный анализ и проектирование с примерами приложений. — 3-е издание.
  6. Бенджамин Пирс. Типы в языках программирования
  7. Иан Грэхем. Объектно-ориентированные методы. Принципы и практика
  8. Антони Синтес. Освой самостоятельно объектно-ориентированное программирование за 21 день
  9. Зандстра М. PHP. Объекты, шаблоны и методики программирования. — 5-е изд.. — СПб.: «Диалектика», 2019. — С. 736
  10. Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж. Приемы объектно-ориентированного проектирования. Паттерны проектирования = PHP Objects, Patterns and Practice, Third Edition. — 3-е издание. — М.: «Вильямс», 2015. — С. 368.
  11. Стив Макконнелл. Совершенный код = Code complete. — СПб.: Питер, 2005. — С. 896.
  12. Марк Гранд. Шаблоны проектирования в JAVA. Каталог популярных шаблонов проектирования, проиллюстрированных при помощи UML = Patterns in Java, Volume 1. A Catalog of Reusable Design Patterns Illustrated with UML. — М.: «Новое знание», 2004. — С. 560.

ПРИЛОЖЕНИЕ 1

Пример использования шаблона Одиночки на языке Python на Метаклассах:

class MetaSingleton(type):

_instances = {}

def __call__(cls, *args, **kwargs):

if cls not in cls._instances:

cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs)

return cls._instances[cls]

class MyClass(metaclass=MetaSingleton):

Пример использования шаблона Одиночки на языке С++

class OnlyOne

{

public:

static OnlyOne& Instance()

{

static OnlyOne theSingleInstance;

return theSingleInstance;

}

private:

OnlyOne(){}

OnlyOne(const OnlyOne& root) = delete;

OnlyOne& operator=(const OnlyOne&) = delete;

};

Пример использования шаблона Одиночки на языке Delphi:

type

TSingleton = class

strict private

class var

Instance: TSingleton;

public

class function NewInstance: TObject; override;

end;

class function TSingleton.NewInstance: TObject;

begin

if not Assigned(Instance) then

Instance := TSingleton(inherited NewInstance);

Result := Instance;

end;