Практическое занятие 6. Использование шаблонов проектирования

 

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

В общем случае шаблон состоит из четырех основных элементов:

1) имя. Точное имя предоставляет возможность сразу понять проблему и определить решение. Уровень абстракции при проектировании повышается;

2) задача. Область применения в рамках решения конкретной проблемы;

3) решение. Абстрактное описание элементов дизайна задачи проектирования и способа ее решения с помощью обобщенного набора классов;

4) результаты.

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

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

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

К порождающим шаблонам относятся:

Abstract Factory (Абстрактная Фабрика) — предоставляет интерфейс для создания связанных между собой объектов семейств классов без указания их конкретных реализаций (families of product objects);

Factory Method (Фабричный метод) — определяет интерфейс для создания объектов из иерархического семейства классов на основе передаваемых данных (subclass of object that is instantiated);

Builder (Строитель) — создает объект конкретного класса различными способами (how a composite object gets created);

Singleton (Одиночка) — гарантирует существование только одного или конечного числа экземпляров класса (the sole instance of a class);

Prototype (Прототип) — применяется при создании сложных объектов.

На основе прототипа объекты сохраняются и воссоздаются, например, путем копирования (class of object that is instantiated).

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

К структурным шаблонам относятся:

Adapter (Адаптер) — применяется при необходимости использовать классы вместе с несвязанными интерфейсами. Поведение адаптируемого класса при этом изменяется на необходимое (interface to an object);

Bridge (Мост) — разделяет представление класса и его реализацию, позволяя независимо изменять то и другое (implementation of an object);

Composite (Компоновщик) — группирует объекты в иерархические древовидные структуры и позволяет работать с единичным объектом так же, как с группой объектов (structure and composition of an object);

Decorator (Декоратор) — представляет способ изменения поведения объекта без создания подклассов. Позволяет использовать поведение одного объекта в другом (responsibilities of an object without subclassing);

Facade (Фасад) — создает класс с общим интерфейсом высокого уровня к некоторому числу интерфейсов в подсистеме (interface to a subsystem);

Flyweight (Легковес) — разделяет свойства класса для оптимальной поддержки большого числа мелких объектов (storage costs of objects);

Proxy (Заместитель) — подменяет сложный объект более простым и осуществляет контроль доступа к нему (how an object is accessed... its location).

Шаблоны поведения GoF характеризуют способы взаимодействия классов или объектов между собой.

К шаблонам поведения относятся:

Chain of Responsibility (Цепочка Обязанностей) — организует независимую от объекта-отправителя цепочку не знающих возможностей друг друга объектов-получателей, которые передают запрос друг другу (object that can fulfill a request);

Command (Команда) — используется для определения по некоторому признаку объекта конкретного класса, которому будет передан запрос для обработки (when and how a request is fulfilled);

Iterator (Итератор) — позволяет последовательно обойти все элементы коллекции или другого составного объекта, не зная деталей внутреннего представления данных (how an aggregate’s elements are accessed, traversed);

Mediator (Посредник) — позволяет снизить число связей между классами при большом их количестве, выделяя один класс, знающий все о методах других классов (how and which objects interact with each other);

Memento (Хранитель) — сохраняет текущее состояние объекта для дальнейшего восстановления (what private information is stored outside an object, and when);

Observer (Наблюдатель) — позволяет при зависимости между объектами типа «один ко многим» отслеживать изменения объекта (number of objects that depend on another object; how the dependent objects stay up to date);

State (Состояние) — позволяет объекту изменять свое поведение за счет изменения внутреннего объекта состояния (states of an object);

Strategy (Стратегия) — задает набор алгоритмов с возможностью выбора одного из классов для выполнения конкретной задачи во время создания объекта (an algorithm);

Template Method (Шаблонный Метод) — создает родительский класс, использующий несколько методов, реализация которых возложена на производные классы (steps of an algorithm);

Visitor (Посетитель) — представляет операцию в одном или нескольких связанных классах некоторой структуры, которую вызывает специфичный для каждого такого класса метод в другом классе (operations that can be applied to object(s) without changing their class(es));

Interpreter (Интерпретатор) — для определенного способа представления информации определяет правила (grammar and interpretation of a language).

 

Задание. Разработать простое приложение Java с использованием шаблонов Factory, Builder и Chain of Responsibility.

Шаблон Factory

С помощью данного шаблона реализуем логику, которая выводит сообщения debug или error на консоль или записывает в файл. Интерфейс ILogger описывает два основных метода debug с параметром msg и error с параметром msg, где msg - сообщение, которое нужно отобразить на консоли или записать в файл. Созданы два класса, имплементирующие интерфейс ILogger, такие как ConsoleLoggerImpl – для вывода сообщения на консоль и FileLoggerImpl – для записи сообщения в файл. Так же создан класс Factory, который с помощью метода getLogger возвращает FileLoggerImpl или ConsoleLoggerImpl на основании свойства FileLogging = true (или false), находящемся в файле logger.properties. Если значение true, то getLogger вернет FileLoggerImpl, иначе ConsoleLoggerImpl. В классе FactoryMethodPatternDemo приведен пример использования шаблона Factory на практике. Ниже см. текст программы.

 

ILogger.java

package by.bsac.patterns.factory;

 

public interface ILogger {

// Write out a debug message

public void debug(String msg);

 

// Write out an error message

public void error(String msg);

}

 

ConsoleLoggerImpl.java

package by.bsac.patterns.factory;

 

public class ConsoleLoggerImpl implements ILogger {

 

ConsoleLoggerImpl() {} // Not accessible from other packages !

 

public void debug(String msg) {

System.out.println("DEBUG: " + msg);

}

 

public void error( String msg) {

System.err.println("ERROR: " + msg);

}

}

 

FileLoggerImpl.java

package by.bsac.patterns.factory;

 

import java.io.BufferedWriter;

import java.io.File;

import java.io.FileWriter;

import java.io.IOException;

 

public class FileLoggerImpl implements ILogger {

 

public void debug(String msg) {

try {

File file = new File("DebugLog.log");

 

// create file if doesn't exist

if (!file.exists()) {

file.createNewFile();

}

 

// true = append file

FileWriter fileWritter = new FileWriter(file.getName(), true);

BufferedWriter bufferWritter = new BufferedWriter(fileWritter);

bufferWritter.write("DEBUG:" + msg);

bufferWritter.close();

} catch (IOException e) {

e.printStackTrace();

}

}

 

public void error(String msg) {

try {

File file = new File("ErrorLog.log");

 

// create file if doesn't exist

if (!file.exists()) {

file.createNewFile();

}

 

// true = append file

FileWriter fileWritter = new FileWriter(file.getName(), true);

BufferedWriter bufferWritter = new BufferedWriter(fileWritter);

bufferWritter.write("ERROR:" + msg);

bufferWritter.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

 

Factory.java

package by.bsac.patterns.factory;

 

import java.io.IOException;

import java.util.Properties;

 

public class Factory {

 

public ILogger getLogger() {

if (isFileLoggingEnabled()) {

return new FileLoggerImpl();

} else {

return new ConsoleLoggerImpl();

}

}

 

// helper method, check if FileLogging is ON

// if so, log message to a file else print it to console.

private boolean isFileLoggingEnabled() {

Properties p = new Properties();

try {

p.load(getClass().getResourceAsStream("logger.properties"));

String fileLoggingValue = p.getProperty("FileLogging");

if ("true".equalsIgnoreCase(fileLoggingValue))

return true;

else

return false;

} catch (IOException e) {

return false;

}

}

}