среда, 2 января 2013 г.

Паттерны Ruby. Шаблонный метод

Предполагается несколько статей, написанных на основе книги Design Patterns in Ruby. Первая из них будет о паттерне с названием Template Method (Шаблонный метод).
Шаблонный метод представляет собой один из простейших паттернов, описанных GoF (Gang of Four - Банда Четырех) в своей книге  Design Patterns: Elements of Reusable Object-Oriented Software.
Основная идея метода состоит в создании абстрактного класса, содержащего метод - "каркас" (или "шаблон"), который управляет поведением объекта, вызывая различные абстрактные методы. При этом ему не известно, что будут делать эти методы, т. е. локальное управление передается уже конкретным реализациям данного класса.
Диаграмма классов паттерна Шаблонный метод


Рассмотрим пример - построение отчета. Допустим, нам нужно создать класс, экземпляры которого будут выдавать различные отчеты. При этом мы знаем, что отчет обычно состоит из заголовка, тела отчета и "подвала". Исходя из этих соображений, построим простейший класс:


Отлично, теперь у нас есть класс, выводящий простейший HTML отчет! Но что делать, если вдруг понадобилось также создать отчет в текстовом виде? Попробуем пойти "в лоб".


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


Абстрактный класс запрещает вызов своих методов - каждый конкретный класс должен будет осуществить свою реализацию.

Первая реализация интерфейса - HTMLReporter - осуществляет вывод html отчета.


Текстовые отчеты будет выдавать класс PlainTextReporter. Многие методы родительского класса ему не нужны, поэтому они представляются просто заглушками типа def method; end


Таким образом, добавление нового формата не должно принести больших проблем и, скорее всего, будет состоять только из добавления кода без изменения существующего.
Недостатком данного принципа является использование наследования. Получается так, что каждый формат требует класса, который вынужден реализовывать все методы абстрактного родителя, даже если они ему не нужны (так в PlainTextReporter реализуются пустые   output_start,  output_body_start, output_body_end,  output_end). Кроме того, наследование вынужденно создает новые крепкие связи между классами, чего следует избегать при возможности. 

Комментариев нет:

Отправить комментарий