Прототип (Prototype)

Объекты создаются путем клонирования прототипного экземпляра. Примера такого поведения в главе "Шаблон переделки".

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

Цель строителя состоит в разделении конструирования и "представления", чтобы позволить множественные различия в представлении. Процесс конструирования остается одинаковым, но результирующий объект может иметь различия в представлении. GoF указывает, что основные различия с Абстрактной Фабрикой заключаются в том, что Строитель создает объект шаг-за-шагом, так что фактически процесс создания размыт во времени, что важно. Кроме того, это выглядит так, что "директор" получает поток кусочков, передаваемых в Строитель, и каждый кусочек используется для выполнения одного шага процесса построения.

Один из примеров, данных в GoF, относится к конвертеру формата текста. Входящим форматом является RTF и пока он анализируется, директивы передаются в текстовый конвертер, который может быть реализован различными способами, в зависимости от того, должен ли результат быть в формате ASCII, TeX или "GUI Text Wiget". Хотя результирующий "объект" (полностью конвертированный текстовый файл) создается со временем, если вы решите конвертировать каждую директиву RTF в объект, то это будет выглядеть для меня, как Мост, поскольку специфичные типы конвертеров расширяют интерфейс базового класса. Так что, общим решением проблемы будет создание множество читателей с "передней стороны" и множество конвертеров с "задней стороны", что является основной характеристикой Моста.

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

Другой пример из GoF создает объект лабиринта и добавляет комнаты в лабиринт, и двери в эти комнаты. Таким образом, мы имеем многоходовый процесс, но увы, различными "представлениями" являются "Стандартный" и "Сложный" лабиринты - на самом деле это не различные виды лабиринтов, в просто лабиринты различной сложности. Я думаю, что попробую создать строитель лабиринтов, который будет обрабатывать лабиринты произвольной сложности. Финальной версией строителя лабиринтов будет нечто, что не создает лабиринты, а вместо этого считает комнаты существующего лабиринта.

Ни RTF конвертер, ни Построитель лабиринтов в общем случае не являются представителями Строителя. Читатели согласятся, что Sax XML анализатор и стандартный анализатор компилятора на самом деле могут быть объединены, как Строитель.

Здесь приведен пример, который немного более приближен, или дает больше представления об идее, что такое Строитель и что он делает. Средства информации могут иметь разные представления, в описанном здесь случае, это журнал и web сайт. Пример доказывает, что используемые шаги одинаковы, и могут быть абстрагированы в классе директора.

//: builder:BuildMedia.java
// Пример шаблона Строителя.
package builder;

import java.util.*;

import junit.framework.*;

// Различные "представления" средств информации:
class Media extends ArrayList {
}

class Book extends Media {
}

class Magazine extends Media {
}

class WebSite extends Media {
}

// ... содержат различные типы средств информации:
class MediaItem {
  
private String s;
  
  
public MediaItem(String s) {
     
this.s = s;
  
}
  
  
public String toString() {
     
return s;
  
}
}

class Chapter extends MediaItem {
  
public Chapter(String s) {
     
super(s);
  
}
}

class Article extends MediaItem {
  
public Article(String s) {
     
super(s);
  
}
}

class WebItem extends MediaItem {
  
public WebItem(String s) {
     
super(s);
  
}
}

// ... но используют одинаковые основные шаги создания:
class MediaBuilder {
  
public void buildBase() {
   }
  
  
public void addMediaItem(MediaItem item) {
   }
  
  
public Media getFinishedMedia() {
     
return null;
  
}
}

class BookBuilder extends MediaBuilder {
  
private Book b;
  
  
public void buildBase() {
     
System.out.println("Building book framework");
      b =
new Book();
  
}
  
  
public void addMediaItem(MediaItem chapter) {
     
System.out.println("Adding chapter " + chapter);
      b.add
(chapter);
  
}
  
  
public Media getFinishedMedia() {
     
return b;
  
}
}

class MagazineBuilder extends MediaBuilder {
  
private Magazine m;
  
  
public void buildBase() {
     
System.out.println("Building magazine framework");
      m =
new Magazine();
  
}
  
  
public void addMediaItem(MediaItem article) {
     
System.out.println("Adding article " + article);
      m.add
(article);
  
}
  
  
public Media getFinishedMedia() {
     
return m;
  
}
}

class WebSiteBuilder extends MediaBuilder {
  
private WebSite w;
  
  
public void buildBase() {
     
System.out.println("Building web site framework");
      w =
new WebSite();
  
}
  
  
public void addMediaItem(MediaItem webItem) {
     
System.out.println("Adding web item " + webItem);
      w.add
(webItem);
  
}
  
  
public Media getFinishedMedia() {
     
return w;
  
}
}

class MediaDirector { // a.k.a. "Context"
  
private MediaBuilder mb;
  
  
public MediaDirector(MediaBuilder mb) {
     
this.mb = mb; // Strategy-ish
  
}
  
  
public Media produceMedia(List input) {
     
mb.buildBase();
     
for (Iterator it = input.iterator(); it.hasNext();)
        
mb.addMediaItem((MediaItem) it.next());
     
return mb.getFinishedMedia();
  
}
}
;

public class BuildMedia extends TestCase {
  
private List input = Arrays.asList(new MediaItem[] {
        
new MediaItem("item1"), new MediaItem("item2"),
        
new MediaItem("item3"), new MediaItem("item4"), });
  
  
public void testBook() {
     
MediaDirector buildBook = new MediaDirector(new BookBuilder());
      Media book = buildBook.produceMedia
(input);
      String result =
"book: " + book;
      System.out.println
(result);
      assertEquals
(result, "book: [item1, item2, item3, item4]");
  
}
  
  
public void testMagazine() {
     
MediaDirector buildMagazine = new MediaDirector(new MagazineBuilder());
      Media magazine = buildMagazine.produceMedia
(input);
      String result =
"magazine: " + magazine;
      System.out.println
(result);
      assertEquals
(result, "magazine: [item1, item2, item3, item4]");
  
}
  
  
public void testWebSite() {
     
MediaDirector buildWebSite = new MediaDirector(new WebSiteBuilder());
      Media webSite = buildWebSite.produceMedia
(input);
      String result =
"web site: " + webSite;
      System.out.println
(result);
      assertEquals
(result, "web site: [item1, item2, item3, item4]");
  
}
  
  
public static void main(String[] args) {
     
junit.textui.TestRunner.run(BuildMedia.class);
  
}
}
// /:~

 

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

Упражнение

  1. Разбейте текстовый файл на поток слов (рассмотрите для этого использование регулярных выражений). Создайте Строитель, который помещает слова в java.util.TreeSet, и другой, который производит java.util.HashMap, содержащий слова и их расположение (что-то типа счетчика слов).