Общее·количество·просмотров·страницы

Java Dev Notes - разработка на Java (а также на JavaScript/Python/Flex и др), факты, события из АйТи

Показаны сообщения с ярлыком design patterns. Показать все сообщения
Показаны сообщения с ярлыком design patterns. Показать все сообщения

среда, 20 октября 2010 г.

Паттерн Строитель (Builder)

Строитель (Builder) - порождающий шаблон проектирования, который выделяет процесс создания сложного объекта так, чтобы различные реализации строителя могли бы создавать различные реализации объекта. UML диаграмма паттерна Строитель представлена ниже (взято из Wikipedia):



Объекты на диаграмме:

  • Builder - абстрактный строитель, определяющий интерфейс

  • Concrete Builder - предоставляет конкретную реализацию строителя. Конкретный строитель создает и собирает части объекта в единое целое.

  • Director - класс Директора отвечает за задание корректной последовательности конструирования объекта. Класс получает Конкретного Строителя, как параметр, и выполняет необходимые операции с ним.

  • Product - конечный продукт. Создается Директором с помощью Коннкретного Строителя


Мотивы применения паттерна:

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

  • процесс конструирования должен обеспечивать различные представления конструируемого объекта


Плюсы применения паттерна Строитель:

  • изолирует код, реализующий конструирование и представление

  • предоставляет полный контроль над процессом создания объекта

  • позволяет независимо изменять ход процесса создания объекта



Пусть имеется код создания некоторого объекта. Со временем, объект меняется, добавляются новые вариации объекта, появляются новые методы. Без данного паттерна пришлось бы переписывать код создания объекта и его последующего использования столько раз, сколько произошло изменений и во всех местах, где этот объект создается. При использовании паттерна Строитель код и порядок создания объекта инкапсулирован в класс Конкретного Строителя. При изменении процесса создания изменить код придется только в одном месте - в этом Конкретном Строителе. При добавлении новой вариации объекта придется лишь добавить еще одного Конкретного Строителя.

Для чего нужен Директор? Опишем это на примере. Пусть подрядчик (Строитель) строит дом. Подрядчик знает, как нужно делать фундамент, возводить стены, делать стропила и класть крышу. Директор (распорядитель) говорит подрядчику, в какой последовательности это делать: сначала нужно заложить фундамент, затем стены, а после этого сделать крышу. В коде это будет выглядеть так:

public class HouseBuilder {
protected House house;
 
public House getHouse() {
return house;
}
 
public void createHouse() {
house = new House();
}
 
public abstract void buildFoundation();
public abstract void buildWalls();
public abstract void buildRoof();
}
 
public class Director {
public House constructHouse(HouseBuilder builder) {
builder.createHouse();
builder.buildFoundation();
builder.buildWalls();
builder.buildRoof();
return builder.getHouse();
}
}


Однако, если Директор плохой, то он может перепутать что-нибудь, и потребовать от Строителя сначала сделать крышу и стены, а потом возвести фундамент. В таком случае для Строителя будет безопаснее самому управлять ходом строительства, а для Директора оставить лишь одну функцию - пусть он командует, что надо построить дом. А Строитель уже сам сделает все, что надо в нужной последовательности. Код этого будет следующий:

public class HouseBuilder {
protected House house;
 
public House getHouse() {
return house;
}
 
public void createHouse() {
house = new House();
buildFoundation();
buildWalls();
buildRoof();
}
 
protected abstract void buildFoundation();
protected abstract void buildWalls();
protected abstract void buildRoof();
}
 
public class Director {
public House constructHouse(HouseBuilder builder) {
builder.createHouse();
return builder.getHouse();
}
}


В заключении можно добавить, что Строитель часто используется вместе с паттерном Компоновщик (Composite).

понедельник, 11 октября 2010 г.

Паттерн Одиночка (Singleton)

Шаблон (паттерн) проектирования "Одиночка" реализует математическую концепцию синглетона. В математике, синглетон - множество с одним элементом. Например, {10} - это синглетон, т.е. множество, состоящее из одного элемента - числа 10.

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

Рассмотрим варианты реализации одиночки на Java. При этом будем обращать внимание на многопоточность и связанные с ней возможные проблемы. Изложенное ниже следует статье в англоязычной Википедии Singleton pattern. Также использовались некоторые другие источники.

Самая простая реализация:
public class Singleton {
 
private static final Singleton INSTANCE = new Singleton();
 
// Private constructor prevents instantiation from other classes
private Singleton() {
}
 
public static Singleton getInstance() {
return INSTANCE;
}
}

Эта реализация потокобезопасна. Объект INSTANCE создается тогда, когда класс инициализируется. Например, это может произойти, когда будет вызван какой-нибудь статический метод класса. Конструктор объявляется private, доступ к экземпляру класса возможен только через getInstance(). Такая реализация, однако, может не подойти, если создание экземпляра класса требует много ресурсов. В этом случае нужно подумать об отложенной инициализации синглетона.

Вариант Уильяма Пу (William Pugh)
Это вариант отложенной инициализации (или еще говорят: ленивой инициализации)
public class Singleton {
 
// Private constructor prevents instantiation from other classes
private Singleton() {
}
 
/**
* SingletonHolder is loaded on the first execution of Singleton.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/

private static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
}
 
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
 
}

Эта реализация "ленива" настолько, насколько это возможно. Здесь используется идиома Initialization on demand holder idiom - т.е. отложенная инициализация с использованием объекта-холдера.

Несмотря на отсутствие synchronized в коде, эта реализация потокобезопасна. Такая реализация Одиночки работает на всех версиях Java. Здесь используются та часть спецификации Java (см. 8.3.1.1 static Fields и 12.4.2 Detailed Initialization Procedure), в которой описывается порядок инициализации класса. При инициализации класса Java-машина сама заботится о синхронизации, т.к. нет нужды подставлять использовать synchronized.

Т.к. на внутренний класс SingletonHolder нет ссылок нигде, кроме метода getInstance, то и инциализирован он будет не ранее, чем метод getInstance будет вызван. При такой инициализации Java-машина сама позаботится о синхронизации. Так что этот вариант - пример отличной потокобезопасной реализации паттерна Одиночка.

Реализация с double-checked locking (DCL)
Рассмотрим еще одну реализацию. Эта реализация использует двойную проверку блокировки. Она будет корректно работать в многопоточной среде только начиная с версии Java 5, т.к. в Java 5 была изменена модель памяти. Ранее существовавшая модель памяти не позволяла корректно выполняться такой реализации в многопоточной среде. Вот код:
public class Singleton {
 
// volatile is needed so that multiple thread can reconcile the instance
// semantics for volatile changed in Java 5.
private volatile static Singleton singleton;
 
private Singleton() {
 
}
 
// synchronized keyword has been removed from here
public static Singleton getSingleton() {
// needed because once there is singleton available no need to acquire
// monitor again & again as it is costly
if(singleton==null) {
synchronized(Singleton.class){
// this is needed if two threads are waiting at the monitor at the
// time when singleton was getting instantiated
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

Однако, лучше не использовать double-checked locking. Это старый прием, который был вызван особенностями ранних JVM. Теперь его иногда даже рассматривают как анти-паттерн.

Подводя итоги, можно рекомендовать релализацию синглетона с помощью с использованием объекта-холдера.

среда, 25 ноября 2009 г.

Reactor pattern

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

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

Ссылки:

Reactor pattern - Википедия

Patterns for Concurrent, Parallel, and Distributed Systems

Comparing Two High-Performance I/O Design Patterns

Merlin brings nonblocking I/O to the Java platform

Java Performance Tuning - тюнинг производительности Джавы.

Schmidt - Reactor_An_Object_Behavioral_Pattern_for_Demultiplexing_and_Dispatching_Handles_for_Synchronous_Events

Постоянные читатели