Программная инженерия.Часть II. Учебное пособие
.pdf
// Целая часть числа
Private double re;
// Мнимая часть числа
Private double im;
// Конструктор с инициализацией public Complex(double i_re, double i_im)
{
re = i_re; im = i_im;
}
// Сложение комплексных чисел
// parComplex1 – Первое комплексное число // parComplex2 – Второе комплексное число
public static Complex operator+(Complex parComplex1, Complex parComplex2)
{
return new Complex(parComplex1.re + parComplex2.re, parComplex1.im + parComplex2.im);
}
}
Листинг 15.10. Использование класса комплексного числа
Complex complex1 = new Complex (1,1); Complex complex2 = new Complex (2,2); Complex complex3 = complex1 + complex2;
Следует особо отметить, что одна из наиболее распространенных ошибок заключается в попытке делать сокрытие реализации только ради сокрытия. Целями, достойными усилий, являются: достижение предельной локализации изменений при их необходимости, прогнозируемость изменений (какие изменения в коде надо сделать для заданного изменения функциональности), прогнозируемость последствий изменений.
Полиморфизм – взаимозаменяемость объектов с одинаковым интерфейсом. Язык программирования поддерживает полиморфизм, если классы с одинаковой спецификацией могут иметь различную реализацию, например, реализация класса может быть изменена в процессе наследования. Кратко смысл полиморфизма можно выразить фразой: «один интерфейс, множество методов».
Полиморфизм позволяет писать более абстрактные программы и повышать коэффициент повторного использования кода. Общие свойства объектов объединяются в систему, которую могут называть по-разному: интерфейс, класс. Общность имеет внешнее и внутреннее выражение.
Внешне общность проявляется как одинаковый набор методов с оди-
наковыми именами и сигнатурами (типами аргументов и результатов).
II Часть | пособие Учебное
61
ПРОГРАММНАЯ ИНЖЕНЕРИЯ
Внутренняя общность есть одинаковая функциональность методов. Ее можно описать интуитивно или выразить в виде строгих законов, правил, которым должны подчиняться методы.
Возможность приписывать разную функциональность одному методу (функции, операции) называется перегрузкой метода (листинг 15.11).
В приведенном примере листинга 15.11 класс «Operation» определяет виртуальный метод, который подразумевает какое-то арифметическое вычисление с использованием двух переменных и возвращение результата. Класс «Add» является наследником «Operaion» и перегружает метод «Calculate» как операцию сложения двух переменных. Класс «Sub» также является наследником «Operation» и перегружает метод «Calculate» как операцию вычитания.
Листинг 15.11. Пример перегрузки метода для арифметической операции
// Класс для определения произвольной арифметической операции
// с использованием двух переменных
Class Operation
{
//Вычисление произвольной операции с участием двух переменных public virtual float Calculate(float parArgument1, float
}
parArgument2);
// Сложение двух переменных
Class Add : Operation
{
// Сложение с использованием двух переменных public override float Calculate(float parArgument1, float parArgument2)
{
}
}
Return parArgument1 + parArgument2;
//Сложение двух переменных
Class Sub : Operation
{
//Сложение с использованием двух переменных
Public override float Calculate(float parArgument1, Float parArgument2)
{
return parArgument1 – parArgument2;
}
}
62
В листинге 15.12 представлен простейший пример использования полиморфизма, когда объявляется локальная переменная «operation» с типом родительского класса «Operation» и ей присваивается экземпляр
класса «Add». Данное присвоение разрешается, что является одним из свойств наследования: переменным с типом родительского класса
можно присваивать экземпляры классов-наследников, ноне наоборот. В результате вызова метода «Calculate» переменная «result1» примет значение 2.0f, так как в данный момент переменная «operation» является эк-
земпляром класса «Add», где вычисление определено как сложение. Далее переменной «operation» присваивается значение экземпляра «Sub» и вызов метода «Calculate» возвратит значение 0.0f, как результат вызова метода из класса «Sub».
Листинг 15.12. Простейший пример использования полимор-
физма
Operation operation = new Add();
float result1 = operation. Calculate(1.0f, 1.0f); operation = new Sub();
float result2 = operation. Calculate(1.0f, 1.0f);
Конструктор – специальный метод в объектно ориентированном программировании, служащий для инициализации объекта при его создании (например, выделения памяти). В языках программирования С++, C# или Java конструктором класса называется функция, имеющая то же имя, что и сам класс, и не возвращающая никакого значения. Можно сказать, что конструктором называется тот метод класса, который вызывается автоматически при создании экземпляра класса. В зависимости от варианта объявления различают следующие виды конструктора (листинг 15.13):
–конструктор по умолчанию – конструктор, не имеющий обязательных аргументов; в отсутствие явно заданного конструктора по умолчанию его код генерируется компилятором автоматически при компиляции;
–конструктор копирования – конструктор, аргументом которого является ссылка на объект того же класса; используется для создания экземпляра класса со значениями полей аналогичными копируемому объекту;
–обычный конструктор – объявляется с произвольным количеством параметров, необходимых для инициализации класса.
Листинг 15.13. Пример объявления конструкторов различного вида
// Класс комплексного числа
Class Complex
{
Double re; Double im;
II Часть | пособие Учебное
63
ПРОГРАММНАЯ ИНЖЕНЕРИЯ
// Конструктор по умолчанию
Public Complex()
{
}
re = 0.0; im = 0.0;
// Конструктор копирования public Complex(Complex obj)
{
}
// Конструктор с инициализацией (обычный конструктор) public Complex(double i_re, double i_im)
{
}
}
Деструктор – специальный метод класса, служащий для деинициализации объекта (например, освобождения памяти). Имя деструктора должно совпадать с именем класса и иметь префикс~. У класса может быть только один деструктор. Деструктор не имеет модификатора доступа и параметров (листинг 15.14).
Листинг 15.14. Пример объявления конструкторов различного вида
// Класс комплексного числа class Complex
{
ouble re; double im;
// Конструктор с инициализацией (обычный конструктор) public Complex(double i_re, double i_im)
{
}
// Деструктор
~Complex()
{
}
}
64
Виртуальный метод – метод / функция класса, который может быть переопределен в классах-наследниках так, что конкретная реализация метода будет определяться во время исполнения (листинг 15.15).
Листинг 15.15. Пример объявления виртуальных и невиртуаль-
ных функций class Ancestor
{
public virtual void function1 ()
{
Console.WriteLine(“Ancestor.function1()”);
}
public void function2 ()
{
Console.WriteLine(“Ancestor.function2()”);
}
}
class Descendant: Ancestor
{
public override void function1 ()
{
Console. WriteLine (“Descendant.function1()”);
}
public void function2 ()
{
Console. WriteLine (“Descendant.function2()”);
}
}
...
Descendant descendant = new Descendant (); Ancestor ancestor = descendant;
descendant. Function1(); descendant. Function2(); ancestor. Function1(); ancestor. Function2();
Программисту не обязательно знать тип объекта для работы с ним через виртуальные методы, достаточно лишь знать, что объект принадлежит классу или наследнику класса, в котором метод объявлен. Базовый класс может и не предоставлять реализации виртуального метода, а только декларировать его существование. Такие методы без реализации называются «чисто виртуальными» (pure virtual) или «абстрактными». Класс, содержащий хотя бы один такой метод, тоже будет абстрактным.
II Часть | пособие Учебное
65
Объект такого класса создать нельзя (в некоторых языках допускается, но вызов абстрактного метода приведет к ошибке). Наследники абстрактного класса должны предоставить реализацию для всех его абстрактных
методов, иначе они, в свою очередь, будут абстрактными классами.
В примере листинга 15.15 класс «Ancestor» определяет две функции, одна из них виртуальная, другая – нет. Класс «Descendant» переопреде-
ляет обе функции, но одинаковое обращение к функциям дает разные результаты. Результат, получаемый на выходе, представлен листингом 15.16.
Листинг 15.16. Пример вызовы виртуальных и невиртуальных
функций
Descendant. Function1
Descendant. Function2
Descendant. Function1 Ancestor. Function2
В случае виртуальной функции для определения реализации функции используется информация о типе объекта и вызывается «правильная» реализация, независимо от типа указателя. При вызове невиртуаль-
ной функции компилятор руководствуется типом переменной, поэтому вызываются две разные реализации «function2()», несмотря на то что ис-
пользуется один и тот же объект.
Вывод
Мы рассмотрели основные понятия концепции объектно ориентированного программирования. Система состоит из объектов. Объекты некоторым образом взаимодействуют между собой. Каждый объект характеризуется своим состоянием и поведением. Состояние объекта задается значением полей данных. Поведение объекта задается методами.
Вопросы для самопроверки
1. Дайте определение объектно ориентированного программирования. Какие основные понятия положены в основу объектно ориентированного программирования.
ИНЖЕНЕРИЯ |
2. Какими тезисами можно кратко выразить концепции объектно |
|
ориентированного программирования? |
||
|
||
|
3. Дайте определения наследования, инкапсуляции, полиморфизма. |
|
|
Поясните смысл их использования. |
|
ПРОГРАММНАЯ |
4. Дайте определение конструктора класса. Какие основные виды |
|
конструкторов бывают? Дайте определение деструктора. |
||
|
||
|
5. Дайте определение виртуальной функции. Чем переопределение |
|
|
виртуальной функции в дочернем классе отличается от переопределе- |
|
|
ния невиртуальной функции? |
|
|
|
|
|
|
66
Литература
1.Программная инженерия: учебник / В. А. Антипов и др.; под ред.
Б.Г. Трусова. М.: Академия, 2014. 282 с.
2.Соммервилл И. Инженерия программного обеспечения. 6-е изд. / пер. с англ. М.: Изд. дом «Вильямс», 2002. 624 с.
3.Гецци К., Джазаейри М., Мандриоли Д. Основы инженерии программного обеспечения. 2-е изд. / пер. с англ. СПб.: БХВ-Петербург, 2005. 832 с.
Тема 16. ШАБЛОНЫ ПРОЕКТИРОВАНИЯ
План
16.1.Описание шаблонов проектирования.
16.2.Принципы работы с шаблонами проектирования.
16.3.Основные типы шаблонов.
16.4.Программные средства.
16.1. Описание шаблонов проектирования
Шаблоны проектирования – это эффективные способы решения характерных задач проектирования, которые проявляются как при проектировании, так и при конструировании. Шаблон не является законченным образцом проекта, который может быть прямо преобразован в код, это описание или образец того, как решить определенную задачу, и чтобы это решение можно было использовать в различных ситуациях. Объектно ориентированные шаблоны определяют отношения и взаимодействия между классами или объектами без определения того, какие конечные классы или объекты приложения будут использоваться. Алгоритмы не рассматриваются как шаблоны, так как они решают задачи вычисления, а не проектирования.
Любой шаблон описывает определенную задачу, которая снова и снова возникает в некоторой работе, а также принцип ее решения, причем таким образом, что это решение можно было использовать сколько угодно раз.
В общем случае шаблон состоит из четырех основных элементов.
1. Имя. Сославшись на имя, можно сразу описать проблему проектирования, ее решения и их последствия. Присваивание шаблонам имен позволяет проектировать на более высоком уровне абстракции. С помощью словаря шаблонов можно вести обсуждение с коллегами, упоминать шаблоны в документации, в тонкостях представлять дизайн систе-
II Часть | пособие Учебное
67
ПРОГРАММНАЯ ИНЖЕНЕРИЯ
мы. Нахождение хороших имен было одной из самых трудных задач при составлении каталога шаблонов.
2.Задача. Описание того, когда следует применять шаблон. Необходимо сформулировать задачу и ее контекст. Может описываться конкретная проблема проектирования, например, способ представления алгоритмов в виде объектов. Иногда отмечается, какие структуры классов или объектов свидетельствуют о негибком дизайне. Также может включаться перечень условий, при выполнении которых имеет смысл применять данный шаблон.
3.Решение. Описание элементов дизайна, отношений между ними, функций каждого элемента. Не имеется в виду конкретный дизайн или реализация, поскольку шаблон применяется в самых разных ситуациях. Дается абстрактное описание задачи проектирования и того, как она может быть решена с помощью некоторого весьма обобщенного сочетания элементов (классов и объектов).
4.Результаты. Они являются следствием применения шаблона и возможных компромиссов. Зачастую при описании проектных решений
опоследствиях не упоминают, а знать о них необходимо, чтобы можно было сделать выбор между различными вариантами данного шаблона, оценив их преимущества и недостатки.
Шаблон проектирования именует, абстрагирует и идентифицирует ключевые аспекты структуры общего решения, которые позволяют применить его для создания повторно используемого дизайна. Он вычленяет участвующие классы и экземпляры, определяя их роль и отношения, а также функции. При описании каждого из шаблонов внимание акцентируется на конкретной задаче объектно ориентированного проектирования.
Шаблоны проектирования позволяют разными способами решать многие задачи, с которыми постоянно сталкиваются проектировщики объектно ориентированных приложений.
16.2.Принципы работы с шаблонами проектирования
В распоряжение проектировщика предоставлен каталог из более чем 20 шаблонов, и поэтому трудно решить, какой шаблон лучше всего подходит для конкретной задачи.
Можно привести следующие подходы к выбору подходящего шаблона: следует обдумать, как шаблоны решают проблему проектирования; проанализировать назначение шаблонов; изучить взаимосвязи шаблонов; проанализировать шаблоны со сходными целями; разобраться в причинах, вызывающих перепроектирование; посмотреть, что в дизайне должно изменяться.
68
Работа с шаблонами состоит из следующих этапов.
1.Прочитайте описание шаблона, чтобы получить о нем общее представление.
2.Убедитесь, что понимаете, что представляют собой упоминаемые
вшаблоне классы и объекты и как они взаимодействуют друг с другом.
3.Выберите для участников шаблона подходящие имена. Имена участников шаблона обычно слишком абстрактны, чтобы употреблять их непосредственно в коде. Тем не менее бывает полезно присвоить участнику такое же имя, как в программе. Например, если используется
шаблон «стратегия в алгоритме размещения текста», то классы могли бы называться SimpleLayoutStrategy или TeXLayoutStrategy.
4.Определите классы, объявите их интерфейсы, установите отношения наследования и определите переменные экземпляра, которыми будут представлены данные объекты и ссылки на другие объекты. Выявите имеющиеся в вашем приложении классы, на которые шаблон оказывает влияние, и соответствующим образом модифицируйте их.
5.Определите имена операций, встречающихся в шаблоне. Здесь, как и в предыдущем случае, имена обычно зависят от приложения. Руководствуйтесь теми функциями и взаимодействиями, которые ассоциированы с каждой операцией. Кроме того, будьте последовательны при выборе имен. Например, для обозначения фабричного метода можно было бы всюду использовать префикс «Create».
6.Реализуйте операции, которые выполняют обязанности и отвечают за отношения, определенные в шаблоне.
16.3. Основные типы шаблонов
Приведем краткий обзор шаблонов проектирования (таблица 16.1). Порождающие шаблоны абстрагируют процесс создания экземпляров классов и помогают сделать систему независимой от способа создания, композиции и представления объектов. Шаблоны, порождающие классы, используют наследование, чтобы различать создаваемые экземпляры, а шаблоны, порождающие объекты, поручают создание другим объектам. Данный тип шаблонов важен, когда система больше зависит от композиции объектов, чем от наследования классов. Порождающий шаблон обеспечивает большую гибкость при решении вопроса о том, что создается, кто это создает, как и когда. Можно собрать систему из «готовых» объектов с самой различной структурой и функциональностью статически (на этапе компиляции) или динамически (во время выполнения). В качестве примера можно привести известный шаблон «Фабрика классов», который позволяет порождать объекты одинаковой структуры, но с различными свойствами, используя полиморфные классы (на-
II Часть | пособие Учебное
69
ПРОГРАММНАЯ ИНЖЕНЕРИЯ
пример, порождение одного и того же лабиринта, с различными свойствами его элементов, в зависимости от класса, который их создает.
Таблица 16.1 Шаблоны проектирования и их краткое описание
Порожд. шаблоны |
Абстрактная |
Семейства порождаемых объектов |
||
|
|
|||
|
|
фабрика |
||
|
|
|
||
|
|
|
|
|
порождения |
|
Одиночка |
Единственный экземпляр класса |
|
|
|
|
||
|
Фабричный метод |
Инстанцируемый подкласс объекта |
||
|
|
Прототип |
Класс, из которого инстанцируется объект |
|
|
|
Строитель |
Способ создания составного объекта |
|
объектабез |
подкласса |
|
|
|
Адаптер |
Интерфейс к объекту |
|||
|
|
|||
Декоратор |
Обязанности объекта без порождения подкласса |
|||
|
|
|||
|
|
|
|
|
|
|
Заместитель |
Способ доступа к объекту, его местоположение |
|
Обязанности |
|
|
|
|
|
Компоновщик |
Структура и состав объекта |
||
|
|
|||
|
|
|
|
|
|
|
Мост |
Реализация объекта |
|
|
|
|
|
|
|
|
Приспособленец |
Накладные расходы при хранении объектов |
|
|
|
|
|
|
|
|
Фасад |
Интерфейс к подсистеме |
|
|
|
|
|
|
|
|
Интерпретатор |
Грамматика и интерпретация языка |
|
|
|
|
|
|
|
|
Итератор |
Способ обхода элементов агрегата |
|
|
|
|
|
|
|
|
Команда |
Время и способ выполнения запроса |
|
|
|
|
|
|
|
|
Наблюдатель |
Множество объектов, зависящих от другого объекта. |
|
поведения |
|
Способ, которым зависимые объекты поддерживают |
||
|
себя в актуальном состоянии |
|||
|
|
|||
Посетитель |
Операции, которые можно применить к объекту или |
|||
|
|
|||
|
|
|
объектам не меняя класса |
|
|
|
|
|
|
Шаблоны |
Посредник |
Объекты, взаимодействующие между собой, и способ |
||
|
их коопераций |
|||
|
|
|
||
|
|
|
|
|
|
|
Состояние |
Состояние объекта |
|
|
|
|
|
|
|
|
Стратегия |
Алгоритм |
|
|
|
|
|
|
|
|
Хранитель |
Закрытая информация, хранящаяся вне объекта, |
|
|
|
|
и время ее сохранения |
|
|
|
|
|
|
|
|
Цепочка обязанностей |
Объект, выполняющий запрос |
|
|
|
|
|
|
|
|
Шаблонный метод |
Шаги алгоритма |
|
|
|
|
|
|
Структурные шаблоны служат для рассмотрения вопроса о том, как из классов и объектов образуются более крупные структуры. Структур-
70
