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

Технологии программирования. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
15.08.2026
Размер:
1 Мб
Скачать

Порождающие паттерны ‒ это паттерны, которые абстрагируют процесс инстанцирования или, иными словами, процесс порождения классов и объектов.

К таким шаблонам относятся:

-Абстрактная фабрика (AbstractFactory);

-Строитель (Builder);

-Фабричный метод (Factory Method);

-Прототип (Prototype);

-Одиночка (Singleton).

Структурные паттерны ‒ рассматривают, как классы и объекты образуют более крупные структуры - более сложные по характеру классы и объекты.

К таким шаблонам относятся:

-Адаптер (Adapter);

-Мост (Bridge);

-Компоновщик (Composite);

-Декоратор (Decorator);

-Фасад (Facade);

-Приспособленец (Flyweight);

-Заместитель (Proxy).

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

К таким шаблонам относятся:

-Цепочка обязанностей (Chainofresponsibility);

-Команда (Command);

-Интерпретатор (Interpreter);

-Итератор (Iterator);

-Посредник (Mediator);

-Хранитель (Memento);

-Наблюдатель (Observer);

31

-Состояние (State);

-Стратегия (Strategy);

-Шаблонный метод (Template method);

-Посетитель (Visitor).

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

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

3.3. Как использовать паттерны

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

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

32

3.4. Пример использования паттернов

Мы рассмотрим принципы использования патеров на примере паттерна «Фабричный метод» (FactoryMethod) применительно к языку C#. Более подробно о реализации и применении других паттернов можно прочитать в книге «Приемы объектноориентированного проектирования. Паттерны проектирования» авторов Ральф Джонсон, Джон Влиссидес, Ричард Хелм, Эрих Гамма

[3].

Фабричный метод ‒ это паттерн, который определяет интерфейс для создания объектов некоторого класса, но непосредственное решение о том, объект какого класса создавать происходит в подклассах. То есть паттерн предполагает, что базовый класс делегирует создание объектов классам-наследникам.

Когда надо применять паттерн:

-Когда заранее неизвестно, объекты, каких типов необходимо создавать.

-Когда система должна быть независимой от процесса создания новых объектов и расширяемой: в нее можно легко вводить новые классы, объекты которых система должна создавать.

-Когда создание новых объектов необходимо делегировать из базового класса классам наследникам.

Создадим программу управления грузовыми перевозками. Сначала вы рассчитываете перевозить товары только на автомобилях. Поэтому весь ваш код работает с объектами

класса «TransportTruck». В какой-то момент появляется необходимость добавить в программу поддержку Авиа логистики. Большая часть существующего кода привязана к классам «TransportTruck», и для того чтобы добавить в программу классы «TransportAir», понадобится перелопатить всю программу. Более того, если вы потом решите добавить в программу еще один вид транспорта, то всю эту работу придется повторить. Паттерн

33

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

Например, классы «TransportTruck» и «TransportAir»

реализуют интерфейс Transport с методом Deliver(). Каждый из этих классов реализует метод по-своему: грузовики везут грузы по земле,

а

самолеты

по

воздуху.

Фабричный

метод

в

классе «RoadLogistics»

вернет

объект-грузовик,

а

класс «AirLogistics» ‒ объект-судно. Для клиента фабричного метода нет разницы между этими объектами, так как он будет трактовать их как некий абстрактный «Transport». Для него будет важно, чтобы объект имел метод доставить, а как конкретно он работает ‒ не важно [4].

Код программы будет выглядеть следующим образом:

class Program

{

static void Main(string[] args)

{

Logistics dev = new RoadLogistics("Грузовые перевозки"); Transport transport2 = dev.Create();

dev = new AirLogistics("Авиа перевозки"); Transport transport = dev.Create(); Console.ReadLine();

}

}

abstract class Logistics

34

{

public string Name { get; set; }

public Logistics(string n)

{

Name=n;

}

// фабричный метод

abstract public Transport Create();

}

class RoadLogistics : Logistics

{

public RoadLogistics(string n)

: base(n)

{}

public override Transport Create()

{

return new TransportTruck();

}

}

class AirLogistics : Logistics

{

public AirLogistics(string n)

:base(n)

{}

public override Transport Create()

{

return new TransportAir();

}

}

interface Transport

35

{

}

class TransportTruck : Transport

{

public TransportTruck()

{

Console.WriteLine("Машина назначена");

}

}

class TransportAir : Transport

{

public TransportAir()

{

Console.WriteLine("Самолет готов");

}

}

На рисунке 3.1 представлена диаграмма UML, иллюстрирующая шаблон вышеприведенного примера:

Рисунок 3.1. Диаграмма UML реализации паттерна «Фабричный метод»

36

Вопросы к главе:

1.Чем отличаются паттерны «Фабричный метод» и «Абстрактная фабрика»?

2.Какие паттерны решают задачи эффективного и безопасного взаимодействия между объектами программы?

3.Какие паттерны отвечают за построение удобных в поддержке иерархий классов?

4.Какие паттерны отвечают за удобное и безопасное создание новых объектов или даже целых семейств объектов?

5.Когда имеет смысл применять паттерн «Компоновщик»?

6.Какой паттерн лучше использовать, если вам нужно добавлять обязанности объектам на лету, незаметно для кода, который их использует?

7.Какой паттерн лучше использовать, если вы работаете с объектом, поведение которого кардинально меняется в зависимости от внутреннего состояния, причем типов состояний много, и их код часто меняется?

8.Кто и когда придумал паттерны?

9.Какой паттерн проектирования гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа?

10.Так ли паттерны хороши на самом деле? Всегда ли можно их использовать?

11.Можно ли использовать язык паттернов вне разработки программного обеспечения?

12.Почему иногда паттерны бывают вредными?

13.Какой паттерн лучше использовать, если вам нужно представить простой или урезанный интерфейс к сложной подсистеме?

37

ГЛАВА 4. МОДУЛЬНОЕ ТЕСТИРОВАНИЕ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ

4.1. О модульном тестировании

Модульное тестирование (англ. unittesting) – процесс в программировании, позволяющий проверить на корректность отдельные модули исходного кода программы [5].

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

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

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

Модульное тестирование не применяют, когда:

- Решается комбинаторная задача. Например, каждое возможное значение булевской переменной потребует двух тестов: один ‒ на вариант TRUE, другой ‒ на вариант FALSE. В результате на каждую строку исходного кода потребуется 3-5 строк тестового кода.

- Результат

известен

лишь приблизительно. Например,

в математическом

моделировании во многих случаях качество

моделирования определяется

«на глаз», и последний результат

записывается как

«опорный».

Если найдено расхождение, новый

38

результат проверяют вручную и выясняют, какой качественнее: старый или новый.

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

Для получения пользы от модульного тестирования требуется строго следовать технологии тестирования на всем протяжении процесса разработки программного обеспечения (ПО). Нужно хранить не только записи обо всех проведенных тестах, но и обо всех изменениях исходного кода во всех модулях. С этой целью следует использовать систему контроля версий ПО. Если более поздняя версия ПО не проходит тест, который был успешно пройден ранее, будет несложным сверить варианты исходного кода и устранить ошибку. Также необходимо отслеживать и анализировать неудачные тесты. Игнорирование этого требования приведет к увеличению неудачных тестовых результатов.

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

39

4.2 Создание модульного теста средствами Visual Studio

При написании модульных тестов следует придерживаться единого стиля написания тела теста. Отлично зарекомендовал себя подход AAA (arrange, act, assert). Напишем тест для метода, который проверяет правильность вычисления суммы чисел. Для этого в Visual Studio создадим новый проект Visual C# и выбираем «Библиотека классов» (рисунок 4.1).

Рисунок 4.1. Окно «Создать проект»

Назовем его MyСalculation. «Class1» переименуем в «MyCalc».

namespace MyCalc

{

public class MyCalc

{

public int sum(int x, int y)

{

return x + y;

40

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