Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Программная инженерия.Часть II. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
678 Кб
Скачать

// Целая часть числа

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

re = i_re; im = i_im;
re = i_re; im = i_im;
re = obj.re; im = obj.im;

ПРОГРАММНАЯ ИНЖЕНЕРИЯ

// Конструктор по умолчанию

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]