Что такое объектная декомпозиция
Понятие объектной декомпозиции
Объектная декомпозиция
Объектной декомпозицией называют процесс представления предметной области задачи в виде совокупности функциональных элементов – объектов, обменивающихся в процессе выполнения программы входными воздействиями – сообщениями.
Каждый выделяемый объект предметной области должен уметь выполнять некоторые действия, зависящие от полученных сообщений и параметров самого объекта.
Совокупность значений параметров объекта называют его состоянием, а совокупность реакций на получаемые сообщения – поведением. Параметры состояния и элементы поведения объектов определяются условием задачи.
В процессе решения задачи объект, получив некоторое сообщение, выполняет заранее определенные действия, например, может изменить собственное состояние, выполнить некоторые вычисления, нарисовать окно или график и, в свою очередь, сформировать сообщения другим объектам. Таким образом, процессом решения задачи управляет последовательность сообщений. Передавая эти сообщения от объекта к объекту, система выполняет необходимые действия.
Различие процедурной и объектной декомпозиции предметной области задачи рассмотрим на примере разработки программы исследования элементарных функций, приведенной в примере 4.
Пример 5. Разработать программу исследования элементарных функций, которая для функций y=sin x, y=cos x, y=tg x, y=ln x, y=e x выполняет следующие действия на заданном отрезке:
· строит таблицу значений функции с заданным шагом;
· определяет корни функции;
· определяет максимум и минимум функции.
В основе объектной декомпозиции также лежит граф состояний интерфейса (см. рисунки 6–7). Будем считать, что каждое состояние интерфейса – это состояние некоторого функционального элемента системы, т. е. объекта. Состояний интерфейса пять, соответственно получаем пять объектов. Назовем эти объекты следующим образом: Главное меню, Меню операций, Табулятор, Определитель корней, Определитель экстремумов. Эти объекты передают управление друг другу, генерируя сообщение Активизировать.
Кроме этого можно выделить еще один объект Функция, который должен обеспечивать вычисление выбранной функции по заданному аргументу. Номер функции сообщается данному объекту Главным меню, после того, как пользователь осуществит выбор.
Результат объектной декомпозиции изображают в виде диаграммы объектов (см. рисунок 6).
Согласно определению схемы структурной, как схемы, отражающей состав программного обеспечения и взаимодействие по управлению его частей, диаграмма объектов является также схемой структурной сравнительно небольшой программы при объектной декомпозиции. Для сложных программных систем схема структурная отражает взаимодействие между более сложными частями программы – подсистемами, и обычно выглядит так же, как при структурной декомпозиции.
Полная характеристика объекта включает идентифицирующее условное имя, а также перечень и описание параметров состояния и аспектов поведения.
Так состояние объекта Функция характеризуется единственным параметром: номером функции, который передает ему Главное меню. А поведение включает реакции на два типа сообщений: получив номер функции, объект должен сохранить его, изменив свое состояние, а получив запрос на вычисление значения функции, сопровождающийся определенным значением аргумента – вернуть значение функции в заданной точке.
Таким образом, при выполнении объектной декомпозиции определяют и описывают множество объектов предметной области и множество сообщений, которое формирует и получает каждый объект.
Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет
Основы объектно-ориентированной технологии программирования
Сложность, с которой должен справляться программист, лежит в самой природе вещей и явлений. Борьба со сложностью и трудоемкостью лежит в основе развития технологий программирования. Она управляет и направляет развитие языков программирования. В зависимости от используемой технологии программист может моделировать более или менее сложные процессы и явления реального мира.
В функциональном программировании используется алгоритмическая декомпозиция. Сложные или повторяющиеся действия в программе выделяются в подпрограммы, а механизм вызова подпрограмм в языках программирования, позволяет их многократно использовать. Таким образом, программа представляет собой цепочку вызовов подпрограмм, которые поэтапно решают поставленную задачу. Этот подход подход долгое время использовался при решении небольших вычислительных задач.
Развитие компьютерной техники, усложнение потребностей человека, требуют либо решения задач предельной сложности, либо более полного решения, либо более быстрого решения в реальном времени. Необходимо более полно представлять картину моделируемого явления, более надежно контролировать процесс работы программы. Этого можно достичь, если решение задачи осуществляется в терминах наиболее близких к предметной области. Наиболее эффективные разультаты дает на сегодняшний день объектно-ориентированная декомпозиция: явление, процесс представляется в виде совокупности объектов, каждый из которых является абстракцией реальных предметов, явлений, процессов. Программа в данном случае представляет собой «жизнь» взаимодействующих между собой объектов, которые благодаря этому взаимодействию поэтапно решают поставленную задачу. Объектно-ориентированный подход позволяет решать более широкий круг задач, моделировать более сложные процессы и явления (как показывает практика из любой области человеческой деятельности.
Объектно-ориентированная парадигма
Объектно-ориентированный анализ выполняется на основе определенного набора принципов:
Абстракция
«Люди развили чрезвычайно эффективную технологию преодоления сложности преодоления сложности. Мы абстрагируемся от нее. Будучи не в состоянии полностью воссоздать сложный объект, мы просто игнорируем не слишком важные детали и, таким образом, имеем дело с обобщенной идеализированной моделью объекта». Вулф.
ОБЪЕКТ =
= набор характеристик (описывающих свойства предметов реального мира) +
+ набор правил поведения (реакция на воздействие)
Когда выявлена сущность (смысл, семантика) объекта в модели, он должен быть назван уникальным именем. В зависимости от моделируемой области реального мира по своей сущности могут быть выявлены следующие виды объектов:
Таким образом, в процессе анализа задачи, программист (аналитик) выявляет объекты, которые будут использоваться для ее решения. Дает объектам имена, снабжает краткими описаниями для классификации.
Каков бы ни был объект его поведение определяется состоянием. В зависимости от состояния объект может по разному реагировать на те или иные события произошедшие в моделируемом мире, подчиняться различным правилам или линиям поведения, физическим законам и т.д. Состояние, в котором находится объект называется текущим. В процессе жизни объекты проходят различные стадии, но не хаотично, а в определенном порядке.
По аналогии с естественным языком:
ОБЪЕКТ (существительное) =
АТРИБУТЫ (прилагательные) +
+ ПОВЕДЕНИЕ (глаголы только в поаелительном наклонении: сделай это! сделай то!)
Поведение описывается набором действий (операций), которые можно совершить над объектом или сам объект может совершить. Что происходит: чтение-запись, выполнение любых вычислений, порождение событий или реакция на те или иные события происходящие в моделируемой системе. Можно выделить следующие группы действий (операций):
Этот принцип помогает отделить друг от друга атрибуты и методы объекта, определяющие его внутреннее устройство и внешне видимое поведение (с помощью закрытых (private) и открытых (public) членов). Специальный класс видимости protected играет важную роль при наследовании: если к некоторым private-членам класса необходимо получать доступ из классов наследников, то они должны быть помечены как protected (защищенные).
Модули компилируются отдельно, но при сборке между ними легко могут быть установлены связи. Это позволяет собирать испольняемые файлы из модулей (уже готовых или только что откомпилированных). Инкрементная компиляция.
Со временем практическое программирование позволит накопить большое количество разнообразных модулей, из которых программист может быстро собирать в проект и исполняемую программу. Отдельные модули уже протестированы и отлажены многократным использованием в различных проектах. Не нужно одно и то же рарабатывать по несколько раз. Кроме своих модулей, программист может использовать модули своих коллег или различных библиотек.
Т.о. модульность способствует сокращению времени компиляции и повторному использованию кода, работе в команде. Модульность дополняет абстракцию и инкапсуляцию в процессе практической реализации.
Алгоритмическая и объектно-ориентированная декомпозиция
Алгоритмическая и объектно-ориентированная декомпозиция
При разработке сложной программной системы необходимо разделять ее на более простые подсистемы, каждую из которых можно проектировать и программировать самостоятельно, если необходимо также подвергая разделению на части. Такой процесс называется декомпозицией. Существует (в первом приближении) два вида декомпозиции:
· алгоритмическая или структурная;
· объектно-ориентированная (далее ОО).
Алгоритмическая декомпозиция основана на разделении алгоритма решения задачи на подзадачи (по принципу некоторой промежуточной завершенности, повторяемости некоторых шагов). Неформально говоря, это рассмотрение предметной области с упором на глаголы.
Алгоритмическая декомпозиция и сейчас вполне успешно применима в некоторых классах задач, но для большинства других задач считается устаревшей и не приводит к созданию качественных продуктов, соответствующих поставленному заданию.
ОО декомпозиция основана на выделении сущностей предметной области, их характеристик и возможных операциях с ними. Неформально говоря, это рассмотрение предметной области с упором на существительные.
Но правильно построенные предложения состоят и из существительных и из глаголов. И ОО декомпозиция не исключают друг друга с алгоритмической, вопрос лишь в том какая доминирует при начальных разбиениях системы на подсистемы.
ОО программирование (ООП) вносит в разработку ПО:
3. Повторное использование
4. Удобство сопровождения
5. Способность к расширению
6. Упрощение выпуска новых версий
Ключевыми понятиями ООП являются понятия класса и объекта, реализующие в программировании философские понятия общего и частного. Не претендуя на определение, класс — это шаблон для создания объектов, определяющий их способ представления в памяти компьютера и поведение (то есть реакцию на внешние воздействия). Объекты также будем называть экземплярами класса.
В ООП при написании классов выделяют две стороны:
Интерфейс – декларативная часть класса, описывает «внешнему миру» абстракцию класса, его облик, в определенном смысле контракт – «обязуюсь делать то-то и то-то». В большинстве ОО языков программирования выделяют как минимум следующие три уровня интерфейса:
· Public (общедоступный, открытый) – элементы класса, доступные всем, кто использует класс и/или его объекты
· Protected (защищенный) – элементы класса, доступные в классах производных (наследующих) от данного.
· Private (закрытый) – элементы класса, доступные только изнутри класса, а так же из некоторых особо выделенных классов и функций (в разных ОО языках существуют разные способы указания таких классов и функций).
Реализация – императивная (процедурная) часть класса, не видна (в идеале, и не существенна) для остального мира, «то-то и то-то делаю так-то и так-то»
Перейдем к классу, как синтаксическому элементу конкретного ОО-языка – Delphi. Итак, прежде всего класс – это тип данных. Так же как и другие типы, классы описываются в разделах type программ и модулей, причем могут быть как интерфейсной (interface), так и реализационной (implementation) части модуля. Однако, описание классов в реализационной части модуля – явление малораспространенное.
Полное описание (интерфейсная часть) класса выглядит следующим образом:
= class [abstract | sealed] [( )]
где – правильный идентификатор, а – имя класса, описание которого доступно в данной точке программы (то есть он описан или предварительно описан выше по тексту модуля или находится в интерфейсной части используемого модуля). Если указан класс-предок, но не описано ни одного элемента в классе, то ключевое слово end можно опустить. Такое описание называется кратким описанием класса. Необязательное ключевое слово abstract говорит о том, что класс абстрактный, то есть не предназначен для того, чтобы его объекты создавались и использовались в программе. Необязательное ключевое слово sealed говорит о том, что класс опечатан, то есть не может иметь наследников. Класс не может быть одновременно абстрактным и опечатанным.
Интерфейс класса состоит из произвольного количества произвольно чередующихся, в том числе с повторениями, секций (областей доступности или видимости):
public, protected, private, strict protected, strict private, published и automated
Значение первых трех областей видимости совпадает с указанным выше. Для секции private способ указания функций и классов, имеющих доступ к этим элементам — размещение в одном модуле с классом. Последние две по области видимости приравнены к public, но имеют некоторую дополнительную чисто техническую нагрузку. Кроме того, применение ключевого слова automated признано устаревшим и оставлено только для обратной совместимости с предыдущими версиями, оно рассматриваться в курсе не будет. Ключевое слово published будет рассмотрено в теме «VCL». Действие атрибута распространяется на элементы класса, описанные по тексту после него до момента появления другого атрибута или конца объявления класса. В начале описания класса может присутствовать секция, не имеющая явно указанного атрибута видимости, она в различных случаях расценивается или как public, или как published (этот нюанс будет рассмотрен в теме «VCL»). В отношении атрибутов strict protected, strict private в справочной документации сказано, что они ведут себя соответственно так же как protected и private, но игнорируют фактор размещения в одном модуле и допускают обращение методов экземпляра только к элементам этого же экземпляра, однако на практике их поведение оказывается сложнее (см. пример далее по тексту).
Существует понятие предварительного описания класса, которое необходимо, если два класса в одном модуле так или иначе ссылаются друг на друга. Так как язык Pascal, а значит и Delphi, последовательно исповедует принцип «прежде чем что-то использовать, нужно это описать», возникает невозможность дать настоящее (полное или краткое) корректное определение этим классам. В таком случае один из классов (или оба) предварительно (до того как на них ссылаются) описываются следующим образом:
Даже если у класса предполагается наличие предка – он не указывается, иначе определение будет воспринято как краткая форма настоящего определения класса, а не как предварительное описание. Естественно, предварительное описание требует последующего полного или краткого описания в том же файле.
Различают следующие виды элементов класса:
· обычное поле (поле экземпляра)
· обычный метод (метод экземпляра)
· обычные методы класса
· статические методы класса
· обычное свойство (свойство экземпляра)
Обычные поля, обычные методы и обычные свойства будем совокупно называть обычными элементами класса. Поля класса, методы класса и свойства класса будем совокупно называть классовыми элементами класса.
В пределах каждой секции видимости все виды элементов могут произвольно чередоваться, однако, для удобства чтения обычно их располагают блоками.
Обычное поле – это физически хранимый в экземплярах класса элемент данных. Набор полей и их значений отражает состояние объекта. Поля полностью относятся к декларативной части класса и аналогичны полям типа записи (record). Поля описываются следующим образом:
– правильный уникальный в пределах класса идентификатор,
– доступный в данной точке файла тип (то есть. тип, описанный ранее по тексту или описанный в интерфейсной части используемого модуля или встроенный тип Delphi), кроме файловых типов.
Ключевое слово var не обязательно, кроме случая, когда данный элемент не первый в текущей секции видимости и элемент описанный непосредственно перед ним, не является обычным полем.
Поле класса не хранится в каждом экземпляре класса, но разделяется между всеми экземплярами этого класса. Поля описываются следующим образом:
Слова class var можно пропускать, если предшествующий элемент в текущей секции видимости также является полем класса.
Обычный метод – описанная внутри класса и связанная с экземплярами класса (то есть имеющая доступ ко всем элементам класса) процедура или функция. Совокупность методов описанных в классе (а так же и унаследованных) отражает поведение экземпляров класса.
Методы классифицируются на:
- Конструкторы (создают в памяти и/или инициализируют состояние объекта); Деструкторы (освобождают память и/или состояние объекта); Модификаторы (изменяют состояние объекта); Селекторы (возвращают некоторую характеристику состояния); Итераторы (организуют доступ к частям объекта в строго определенном порядке).
Методы – единственный вид элементов класса, у которых присутствует как декларативная (объявление), так и императивная (определение, реализация) часть. Декларативная часть находится внутри объявления класса.
Заголовки объявления методов последних трех типов по структуре не отличаются от заголовков объявлений обычных процедур и функций (за исключением специфического набора директив). Объявление конструкторов начинается с ключевого слова constructor, а объявление деструкторов с ключевого слова destructor, после которых следует имя и список параметров. Несмотря на то, что при объявлении конструктора не указывается тип возвращаемого значения, конструктор является функцией. Возвращаемое значение – экземпляр того класса, в котором описан конструктор. Деструктор и по форме описания и по использованию является процедурой.
Существует два способа вызова конструктора – через имя класса или через имя объекта. Между этими способами есть существенная разница: при вызове через имя класса происходит выделение памяти под объект и инициализация объекта (в том числе и тот код, что находится в реализации конструктора). При вызове через имя объекта подразумевается, что под объект уже была ранее выделена память и производится только инициализация объекта (в том числе и тот код, что находится в реализации конструктора). Хорошая практика программирования: все ресурсы, захваченные конструктором, должны быть освобождены деструктором.
Директивы должны следовать именно в указанном порядке, разделяются «;» и присутствуют только в описании метода (но не в его реализации):
reintroduce; overload; binding; calling convention; abstract; warning;
где binding – одно из: virtual, dynamic, или override; calling convention – одно из: register, pascal, cdecl, stdcall, или safecall; и warning – любое подмножество из: platform, deprecated и library.
Все они применяются в особых случаях и могут отсутствовать. Некоторые из них будут рассмотрены позднее в темах «Полиморфизм. Статические, динамические и виртуальные методы. Перегрузка методов».
Также среди методов можно выделить так называемые обработчики сообщений, они имеют особые правила описания параметров и директивы, их рассмотрение выходит за рамки курса.
Реализация методов всегда располагается в разделе implementation модуля, вне зависимости от того, где находится его объявление. От заголовка реализации обычной процедуры или функции заголовок реализации метода отличается наличием имени класса перед именем метода, отделенным точкой.
Всем обычным методам в качестве неявного параметра (то есть нигде не описанного) передается ссылка на сам объект, для которого вызван метод. Этот параметр называется Self. Собственно, явное его упоминание для доступа к элементам класса не обязательно, хотя возможно. Но его использование необходимо либо для разрешения конфликта имен локальной переменной метода и элемента класса, либо когда методу нужно передать объект в качестве параметра куда-либо вовне класса.
В противоположность обычным методам, методы класса – методы, не оперирующие с обычными элементами класса. Их объявление начинается с ключевого слова class (заголовок реализационной части – тоже). Методы класса бывают обычными и статическими. В обычных методах класса неявный параметр Self присутствует, но носит другой смысл нежели в обычных методах экземпляра – это класс, для которого вызван метод класса (не тоже самое что класс, где метод описан — описан он может быть и в предке). Через этот параметр можно обращаться к другим классовым элементам класса, а так же к конструкторам (так как они занимают особое, промежуточное положение между обычными методами и методами класса). Статические методы класса не имеют неявного параметра Self, но имеют доступ к классовым элементам того же класса, где они объявлены. Для описания статического метода класса нужно указать директиву static в объявлении метода.
Обычное свойство – это логически присутствующий в экземпляре класса элемент данных, с точки зрения внутренней реализации оно может однозначно соответствовать физическому хранилищу (полю), или нет (то есть быть представлено методом). Для внешнего пользователя свойства очень похожи на обычные поля.
property [[ ]]: [index ] ; [default;]
– корректный уникальный в пределах класса идентификатор,
– произвольный тип (кроме файловых),
– список, перечисленных через точку с запятой, пар
:
Перед именем индексирующей переменной допускается модификатор const. В справочной документации по Delphi сказано, что в качестве индексирующего типа может выступать любой тип, это, вообще говоря, неверно (файловые типы не могут), однако, например, строки или записи могут быть индексирующими переменными. После объявления индексированного свойства может быть указана через точку с запятой директива default (не путать со спецификатором default). Директива default позволяет индексировать сам экземпляр класса для доступа к свойству (то есть свойство используется «по умолчанию» при попытке индексации объекта). В классе может быть несколько свойств по умолчанию, если у них существенно различается список индексов.
Модификатор index позволяет нескольким свойствам разделять один метод доступа. Такие разделяемые методы должны принимать дополнительный параметр типа Integer (как если бы это был индекс свойства). При обращении к конкретному свойству в качестве этого параметра будет присылаться константа, обозначенная после ключевого слова index. Для методов read это должен быть последний параметр, для методов write – предпоследний (последний – устанавливаемое значение свойства).
– последовательность из спецификаторов read, write, stored, default или nodefault и implements, где хотя бы один из спецификаторов read или write обязан присутствовать. Спецификаторы stored, default или nodefault будут рассмотрены в теме «VCL». Спецификатор implements относится к теме «Интерфейсы» и его рассмотрение выходит за рамки курса.
Спецификатор read указывает способ считывания значения из свойства. Для неиндексированных свойств может быть указано имя поля, описанного ранее в этом классе (или унаследованного и доступного), имеющее тот же тип что и свойство, или имя метода, описанного ранее в этом классе (или унаследованного и доступного), не принимающего параметров и возвращающего значение того же типа что и свойство. Для индексированных свойств обязательно должен быть указано имя метода, описанного ранее в этом классе (или унаследованного и доступного), принимающего в качестве параметров значения всех тех же типов, что указаны в списке индексов свойства и возвращающего значение того же типа что и свойство.
Спецификатор write указывает способ записи значения в свойство. Для неиндексированных свойств может быть указано имя поля, описанного ранее в этом классе (или унаследованного и доступного), имеющее тот же тип что и свойство, или имя метода, описанного ранее в этом классе (или унаследованного и доступного), принимающего в качестве параметра одно значение того же типа что и свойство и не возвращающего никакого значения (то есть процедура). Для индексированных свойств обязательно должно быть указано имя метода, описанного ранее в этом классе (или унаследованного и доступного), принимающего в качестве параметров значения всех тех же типов, что указаны в списке индексов свойства, плюс еще один параметр того же типа что и свойство, и не возвращающего никакого значения (то есть процедура).
Хорошей практикой программирования считается заключение всех полей в секциях private или protected, а для тех из них, к которым нужен доступ внешних пользователей, описывать соответствующие свойства.
Свойства, для которых указан только спецификатор read, можно только читать, а в свойства, для которых указан только спецификатор write, можно только записывать значения.
Свойства класса — это логические элементы данных, не связанные с экземплярами, они могут ссылаться на поля класса или статические методы класса. Их объявление должно начинаться с ключевых слов class property. Они не могут быть опубликованы, к ним неприменимы спецификаторы хранения.
Объект или экземпляр класса (в терминах Delphi) – это переменная типа класс. Соответственно, объявление объекта (а если быть точным, ссылки на объект) может располагаться в разделе var (любом – глобальном или локальном) или в пределах объявления другого класса или записи. Очень важно помнить, переменная-объект – это всего лишь адрес памяти. Мало объявить переменную. Прежде чем использовать объект, его нужно создать (то есть выделить память и инициализировать) при помощи конструктора. Из этого логически вытекает, что операция присваивания не копирует объект, а лишь адрес, то есть после ее выполнения две переменные указывают на один и тот же объект.
O1 := TSomeClass. Create;
O2 := O1; // обе переменные указывают на один и тот же объект в памяти
Доступ к элементам класса извне класса осуществляется через оператор «точка» (.).
Для всех элементов:
Для классовых элементов также возможна (и является желательной) формой обращения:
Доступ к элементам класса из обычных методов, описанных в нем (для того же объекта, для которого вызван метод), осуществляется просто по имени элемента.