Способ управления сложными системами был известен ещё в древности: divide et impera (разделяй и властвуй). Применительно к проектированию программных систем он заключается в том, что необходимо составлять ее из небольших относительно самостоятельных подсистем, разрабатываемых и отлаживаемых независимо от других. Другими словами основной целью декомпозиции является разделение сложной системы на более простые составляющие.
Известны два подхода к декомпозиции сложной системы: алгоритмическая и объектная.
При объектной декомпозиции проектируемая программа представляется в виде совокупности объектов, обменивающихся сообщениями.
Попробуем разобраться с основным понятием ООП - объектом.
Вообще, объект это - предмет или явление (сущность), обладающая следующими атрибутами:
определёнными свойствами (характеристиками);
способностью совершать действия;
способностью реагировать на происходящие события.
В ООП для представления объектов используется специальная структура, обеспечивающая хранение совокупности:
данных, описывающих характеристики объекта (свойства - properties),
алгоритмов его действий (методы - methods)
событий, на которые он может реагировать (события - events).
Рассмотрим в качестве примера объекта мобильный телефон.
Во-первых, он обладает рядом свойств - размеры, вес, разрешающая способность дисплея, объем телефонной книги и т.д. Можете продолжить список в соответствии со своими предпочтениями.
Во-вторых, он может совершать некоторые действия - набирать номер, звонить, посылать SMS и т.д. Для этого он использует реализуемые внутри него методы.
В-третьих, он реагирует на некоторые события - нажали цифровую клавишу - включилась подсветка дисплея и цифра появилась на экране, поступил вызов - звучит мелодия и т.д.
В обобщённом виде схема организации объекта приведена на рис. 1.2
В современной учебной литературе [1] принято выделять только три основных принципа ООП: иерархия, инкапсуляция и полиморфизм. В то же время не для практической работы не менее важно понимания и основополагающих принципов абстрагирования и модульности [2].
Рассмотрим их в том порядке, в котором они возникают при проектировании.
Итак, в результате объектной декомпозиции были выделены объекты. При этом они могут оказаться весьма сложными по своей структуре (как например тот же сотовый телефон) и воспроизвести все его свойства, методы и события не представляется возможным. А этого делать и нужно. Следует выделить наиболее существенные характеристики объекта, отличающие его от всех других видов объектов. Несущественные характеристики объектов принимать во внимание не следует, от них нужно абстрагироваться .
В результате такого подхода мы получаем не точную копию объекта реального мира, а его упрощённую модель - абстракцию.
Абстракция выделяет существенные характеристики некоторого объекта, отличающие его от всех других видов объектов и, таким образом, чётко определяет его концептуальные границы с точки зрения наблюдателя.
Следует заметить, что направление и уровень абстракции существенно зависит от того, для чего разрабатывается программа (вышеупомянутой "точки зрения наблюдателя"). Так, например, для мультимедийного каталога телефонов в качестве существенных характеристик выступают его внешний вид, массогабаритные и пользовательские характеристики, а от материала, из которого изготовлена печатная плата, и химического состава припоя лучше абстрагироваться. В то же время, если вы создаёте технологическую линию по производству печатных плат для телефонов, то ситуация изменяется на прямо противоположную.
Рис. 1.3. Абстракция фокусируется на существенных с точки зрения наблюдателя характеристиках объекта
Если в процессе объектной декомпозиции выяснилось, что некоторые объекты характеризуются одним набором характеристик, то говорят, что они принадлежат к одному классу.
Для представления абстракций объектов используется специальный определяемый программистом тип данных - класс.
Класс - это структурный тип данных, который включает описание полей данных, а также методов (процедур и функций), работающих с этими полями.
Процесс объединения данных с действиями над этими данными получил название инкапсуляции.
Суть инкапсуляции заключается в скрытии внутренней реализации объекта.
Практически это означает наличие двух частей в классе: интерфейса и реализации.
Интерфейс - это совокупность доступных извне характеристик состояния и поведения объекта. Реализация- это совокупность недоступных извне алгоритмов поведения объекта.
Принцип разделения интерфейса и реализации соответствует сути вещей: в интерфейсной части собрано все, что касается взаимодействия данного объекта с любыми другими объектами; реализация скрывает от других объектов все детали, не имеющие отношения к процессу взаимодействия объектов.
Таким образом, инкапсуляция - это процесс отделения друг от друга элементов объекта, определяющих его устройство и поведение.
Значительное упрощение в понимании сложных задач достигается за счёт образования из абстракций иерархической структуры.
Иерархия - это расположение частей или элементов целого от высшего к низшему, это упорядочивание абстракций, расположение их по уровням.
В ООП иерархия подразумевает два вида отношений:
отношения наследования
отношения включения (использования)(рис. 1.6)
Важнейшим видом иерархии является наследование.
Программист для решения определённого класса задач может строить иерархию классов, в которой (и это самой главное) каждый следующий производный класс имеет доступ к данным и действиям своих предков (суперклассов). Потомок получает в своё распоряжение все, что принадлежало его предку. Потомок может добавить новые методы, свойства и поля и изменить реализацию любого метода, но не может их уничтожить.
При отношениях включения (использования) в состав некоторого класса может в качестве поля (свойства) включаться другой класс.
Различают несколько терминов, связанных с конкретными механизмами реализации полиморфизма для различных случаев:
1. Чистый полиморфизм - используется для обозначения того, что один код функции может по-разному интерпретироваться в зависимости от типа аргументов;
2. Перегрузка (полиморфные имена функций) - используется, когда определяется несколько функций с одним именем - одно и то же имя функции может многократно использоваться в разных местах программы;
3. Переопределение (перекрытие) используется в ООП при необходимости задания различных реализаций одноимённых методов в иерархии классов; различают следующие виды полиморфизма:
1. Чистый полиморфизм - используется для обозначения того, что один код функции может по-разному интерпретироваться в зависимости от типа аргументов;
Чистый полиморфизм используется в языках высокого уровня абстракции, например, в языке Lisp или Smalltalk;
2. Перегрузка (полиморфные имена функций) - используется, когда определяется несколько функций с одним именем - одно и то же имя функции может многократно использоваться в разных местах программы;
выбор нужной функции может определяться типами аргументов, областью видимости (внутри модуля, файла, класса и т. д.); если выбор определяется типом аргументов, то перегрузка называется параметрической; например, язык C++ позволяет разработчику выполнять параметрическую перегрузку функций как вне классов, так и внутри;
3. Переопределение (перекрытие) используется в ООП при необходимости задания различных реализаций одноимённых методов в иерархии классов; различают следующие виды полиморфизма:
а) простой полиморфизм - конкретный метод определяется типом объекта при компиляции программы (раннее связывание); одноимённые методы при использовании простого полиморфизма называют статическими полиморфными;
б) сложный полиморфизм (полиморфные объекты) - конкретный метод определяется типом объекта при выполнении программы (позднее связывание); одноимённые методы при использовании сложного полиморфизма называют виртуальными полиморфными;
Связи между модулями - это их представление друг о друге.
Правильное разделение программы на модули является почти такой же сложной задачей, как выбор правильного набора абстракций. Модули играют роль физических контейнеров, в которые помещаются определения классов и объекты при логическом построении системы. Для описания небольших систем допустимо описание всех классов помещать в один модуль. Однако для большинства программ лучшим решением будет сгруппировать в отдельный модуль логически связанные классы и объекты, оставив открытыми те элементы, которые совершенно необходимо видеть другим модулям.
Таким образом, модульность - это свойство системы, которая была разложена на внутренне связные, но слабо связанные между собой модули.
Мы рассмотрели теоретические основы объектно-ориентированного подхода к разработке программ.
Следует особо подчеркнуть, что ООП является методологией проектирования и общей концепцией программирования, а не особенностью какого-либо языка. Существует множество объектно-ориентированных языков программирования (Lisp, Smalltalk, C++, Java, PHP, Ada, Delphi (ранее - Object Pascal)), каждый из которых реализует концепцию ООП по-своему, со своим синтаксисом, типизацией и другими, присущему конкретному языку особенностями.