Объектно-ориентированный анализ и проектирование

Лекция №1-2

Тема: Объектно-ориентированный подход в программировании

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

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

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

При объектно-ориентированном подходе программа представляет собой описание объектов, их свойств (или атрибутов), классов (или совокупностей), отношений между ними, способов их взаимодействия и операций над объектами (или методов).

К наиболее важным инструментальным средствам ООП относятся:

• абстрагирование;

• инкапсуляция;

• наследование;

• полиморфизм;

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

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

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

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

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

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

С точки зрения объектно-ориентированного программирования, класс – это определенный пользователем тип, а объект – это экземпляр какого-либо класса.

Здесь можно провести аналогию с переменными C/С++.

Есть тип данных, например int. Можно объявить несколько переменных данного типа

inti1, i2, i3;

и каждой из них присвоить некоторое значение

i1 = 3; i2 = 7; i3 = i2 + i1;

Инкапсуляция

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

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

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

Наследование

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

Преимущество наследования в том, что не нужно изобретать велосипед заново.

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

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

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

Следует различать отношениями «является» (IS-A)) и «содержит» (HAS-A). «Является» — это наследование. При отношении «является» объект типа производного класса является также объектом типа базового класса. Можно рассматривать объекты базового и производного классов как подобные; их общность выражается в атрибутах и функциях базового класса. Все объекты любых классов, открыто порожденных общим базовым классом, могут рассматриваться как объекты этого базового класса.

«Содержит» — это композиция. При отношении «содержит» объект класса включает один или более объектов других классов в качестве своих элементов.

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

Полиморфизм

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

Объектно-ориентированный анализ и проектирование

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

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

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

Жизненный цикл

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

· Анализа предметной области и формулировки требований к программе

· Проектирования структуры программы

· Реализации программы в кодах (собственно программирования)

· Внедрения программы

· Сопровождения программы

· Отказа от использования программы

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

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

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

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

Методология ООАП тесно связана с концепцией автоматизированной разработки программного обеспечения (ComputerAidedSoftwareEngineering, CASE). Любое CASE-средство реализует ту или иную графическую нотацию. Одной из таких нотаций является унифицированный язык моделирования (UnifiedModellingLanguage, UML).

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

Атрибуты класса

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

<квантор видимости><имя атрибута>[кратность]: <тип атрибута> = <исходное значение>{строка-свойство}

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

Символ "+" обозначает атрибут с областью видимости типа общедоступный (public).

Символ "#" обозначает атрибут с областью видимости типа защищенный (protected).

И, наконец, знак "-" обозначает атрибут с областью видимости типа закрытый (private).

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

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

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

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

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

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

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

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

Операции класса

В третьей сверху секции прямоугольника записываются операции или методы класса. Операция (operation) представляет собой некоторый сервис, предоставляемый каждым

экземпляром класса по определенному требованию. Совокупность операций характеризует функциональный аспект поведения класса. Каждой операции класса соответствует отдельная строка:

<квантор видимости><имя операции>(список параметров): <выражение типа возвращаемого значения>{строка-свойство}

Для квантора видимости и имени операции справедливо все то же что и для квантора видимости атрибута и имени атрибута.

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

<вид параметра><имя параметра>:<выражение типа>=<значение параметра по умолчанию>.

Здесь вид параметра - одно из ключевых слов in, out или inout со значением in по умолчанию. Имя параметра это идентификатор соответствующего формального параметра. Выражение типа является зависимой от конкретного языка программирования спецификацией типа возвращаемого значения. Вид значения по умолчанию зависит от конкретного языка программирования. То же касается и типа возвращаемого значения. Двоеточие и выражение типа возвращаемого значения могут быть опущены, если операция не возвращает никакого значения.

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

Операция, которая не может изменять состояние системы и, соответственно, не имеет никакого побочного эффекта, обозначается строкой-свойством "{запрос}" ("{query}"). В

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

Описание классов с помощью C#

Как было сказано ранее, в программировании класс – это пользовательский тип данных.

Для определения классов в C# используется ключевое слово class. После него следует имя класса, которое должно быть уникальным в пределах пространства имен (позже). Требования к именам классов C# те же, что и к именам переменных в С/С++. Могут содержать латинские буквы (большие и малые), цифры и знак подчеркивания, не могут начинаться с цифры. Язык C# регистрозависимый. Для имен классов, переменных, параметров и т.д. не принято использовать венгерскую нотацию. Имя класс рекомендуется начинать с заглавной буквы, если имя состоит из нескольких частей, с большой буквы начинается каждая из них. MyMegaClass. Описание класса заключается в операторные скобки. Оно состоит из определения полей и описания методов. Каждый из этих компонентов (поле или метод) обязательно начинается с модификатора области видимости. Выделяют следующие модификаторы области видимости:

public (открытый) – компонент доступен отовсюду, в том числе из других классов и из других сборок;

protected (защищенный) – компонент доступен из класса, которому он принадлежит и из классов, производных от данного;

private (закрытый) – компонент доступен только из класса, которому он принадлежит;

internal (внутренний) – компонент доступен только из классов, принадлежащих сборке, в которой определен данный класс (в том числе и самому классу).

После модификатора области видимости следует описание компонента. Для данных это:

· его тип (стандартный или пользовательский);

· имя, уникальное в пределах класса;

· опционально, начальное значение, которое поле получит при создании объекта класса (в С++ такая возможность отсутствует и данные инициализируются только с помощью конструктора).

Сразу отметим, что эти же модификаторы доступа справедливы и для методов класса.

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

Без дополнительных объявлений каждый компонент относится к конкретному экземпляру. В C# существует возможность объявить компоненты, которые будут иметь отношение ко всему классу (ко всем его объектам в равной степени – хотя такое утверждение и не совсем верно). Такие компоненты называются статическими. Для их объявления используется ключевое слово static.

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

Статические методы могут быть вызваны без создания объектов класса. При этом для их вызова вместо имени объекта используется имя класса. Статические данные могут получать доступ только к статическим данным класса (объяснение этому в теме про указатель this). Обращаться к статическим данным можно из статических и нестатических методов класса. Доступ из внешних модулей к статическим данным происходит через имя класса.

Способы достижения инкапсуляции в C#

· Аналогично С++. Для каждого закрытого (защищенного) данного создается пара методов. Один из них может установить значение закрытой переменной, второй вернуть его во внешнюю программу. Метод для получения информации называется accessor, метод для изменения информации – mutator.

· Не совсем инкапсуляция, но служит для защиты данных класса. Использование полей с модификатором readonly. Значение такого поля можно изменить при инициализации или в конструкторе класса.

Можно описать статические поля для чтения. По сути, они будут аналогичны полям, определенным с модификатором const. Различие здесь только одно: константа заменяется своим значением в процессе компиляции, а статическое поля «только для чтения» - на этапе выполнения программы.

· С помощью свойств класса (property) – с наибольшей степени соответствующий концепции ООП способ. Свойства являются объединением данных и методов в одно целое. Внешне при обращении к свойству синтаксис совпадает с обращением к полю. На самом деле свойство в общем случае состоит из двух скрытых внутренних методов. Преимущества свойств заключается в том, что вместо использования двух отдельных методов, можно использовать одно единственное свойство и работать с ним как с открытой переменной-компонентом класса.

Свойство C# описывается как обычная переменная, но после имени переменной идут операторные скобки в которых могут быть два блока:

· блок доступа (get), в котором обязательно должен быть оператор return.

· блок изменения (set), в котором может использоваться ключевое слово value, которое представляет собой правую часть выражения изменения значения свойства.

Для внутреннего представления свойства реально создаются два метода get_Name() и set_Name() с соответствующими сигнатурами. (проверить, что это действительно так в л.р. №2).

Можно создавать свойства только для чтения (с одним блоком get), только для записи (с одним блоком set). Также свойства могут быть статическими (справедливо все то же что и для статических методов).

Конструкторы классов

Конструктор это специальный метод класса, который вызывается автоматически при создании объекта класса. ВС#, как и в C++ имя конструктора совпадает с именем класса. Конструктор сам по себе не создает объект класса, он только настраивает его начальное состояние. В C#, в отличии от C++, если поля класса не инициализировать явно, они будут инициализированы значениями по умолчанию (числовые данные обычно нулями).

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

Конструктор это метод класса, следовательно, для него возможно наличие перегруженных вариантов в том количестве, в котором это требуется логикой программы. К конструкторам применимы модификаторы доступа. Можно, например, запретить создание объектов класса (хотя для этого есть способ лучше – абстрактные классы).

КЛАССЫ

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

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

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

Полный синтаксис такой:

[атрибуты] [модификаторы-прав-доступа] classидентификатор [базовый-класс]

{

// Объявление переменных экземпляров.

доступ тип переменная!;

доступ тип переменная2;

//. . .

доступ тип переменнаяN;

// Объявление методов

доступ тип_возврата метод1 (параметры) {

// тело метода

}

доступ тип_возврата метод2{параметры) {

// тело метода

}

доступ тип_возврата методN {параметры) {

// тело метода

}

}

Здесь элемент доступ означает спецификатор доступа (например, public), который определяет, как к этому члену можно получить доступ.

Определение класса в языке С# не надо заканчивать точкой с запятой. Тем не менее, если ее поставить, программа будет компилироваться.

В языке С# все происходит в рамках какого-нибудь класса.

Классы состоят из членов и могут включать следующие объекты:

· константы,

· события,

· поля,

· финализаторы,

· индексаторы,

· конструкторы экземпляров,

· методы,

· объявления вложенных типов,

· операторы,

· свойства,

· статические конструкторы.

Наши рекомендации