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

Рассмотрим участников шаблона:

Builder — это, собственно, и есть Строитель, представляющий собой абстрактный интерфейс для создания объекта Product по частям;
ConcreteBuilder — конкретный интерфейс Строителя. Если вы решите, что ваш Строитель собирает ПК, то конкретные строители могут создавать компьютеры разных марок, например Apple, Acer или Hewlett-Packard;
Director класс, занимающийся конструированием объекта, используя интерфейс, представленный Строителем;
Product — итоговый продукт, получающийся в результате конструирования.

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

А теперь, чтобы лучше понять суть паттерна Builder, рассмотрим практический пример, в котором вы будете собирать объект HappyMeal — знаменитый детский набор, предлагаемый в сети закусочных McDonald’s. В первом случае вы соберете бюджетный вариант HappyMeal (маленькая порция пепси-колы, гамбургер, картошка и игрушка), а во втором — BigHappyMeal (гамбургер вы замените на бигмак и увеличите порцию напитка).

Начните с реализации продукта:

class HappyMeal 
  { 
    // содержит информацию о // составе HappyMeal 
    ArrayList parts = new ArrayList(); 
    // Добавляете информацию // о новой составной части 
    public void Add(string part) 
    { 
      parts.Add(part); 
    } 
    // Выводите информацию о // всем наборе 
    public void Show() 
    { 
      Console.WriteLine(" Happy Meal Parts ——-"); 
      foreach (string part in parts) 
      Console.WriteLine (part); 
    } 
  }

В этой части работы нет ничего сложного. Все данные о составе HappyMeal будут храниться в массиве ArrayList (удостоверьтесь, что в списке using присутствует System.Collections). С помощью метода Add добавьте данные в ArrayList. Метод Show просто «проходит» по вашему объекту, выводя строку за строкой.

Теперь реализуйте Строитель:

// "Строитель — абстрактный интерфейс для создания объекта HappyMeal по частям" 
  abstract class HappyMealBuilder 
  {    
    public abstract void BuildBurger(); 
    public abstract void BuildPepsi(); 
    public abstract void BuildFries();    
    public abstract void BuildToy(); 
    public abstract HappyMeal GetProduct(); 
  }

Всего получилось пять абстрактных методов. Первые четыре (начинающиеся со слов Build) как раз и предназначены для конструирования какой-то части вашего объекта. Из их названий понятно, что они создают. Пятый (называющийся GetProduct) просто возвращает сконструированный вами HappyMeal.

Теперь реализуйте свой первый конкретный Строитель, создающий большой HappyMeal:

// Строитель для большого // HappyMeal 
  class BigHappyMealBuilder : HappyMealBuilder 
  { 
    private HappyMeal happy_meal = new HappyMeal(); 
    public override void BuildBurger() 
    { 
      happy_meal.Add("BigMac"); 
    } 
    public override void BuildPepsi() 
    { 
      happy_meal.Add("Pepsi 0.7"); 
    } 
    public override void BuildFries() 
    { 
      happy_meal.Add("BigFries"); 
    } 
    public override void BuildToy() 
    { 
      happy_meal.Add("Two toys"); 
    } 
    public override HappyMeal GetProduct() 
    { 
      return happy_meal; 
    } 
  }

Вы видите, что у данного класса есть одна переменная happy_meal, имеющая тип вашего продукта. Ее-то вы и будете использовать во всех переопределенных методах. В каждом из них вы должны добавить информацию о продукте с помощью метода Add, описанного ранее. В методе GetProduct вы просто возвращаете вашу переменную.

С маленьким HappyMeal все делается аналогично:

// Строитель для маленького // HappyMeal 
  class SmallHappyMealBuilder : HappyMealBuilder 
 { 
    private HappyMeal happy_meal = new HappyMeal(); 
    public override void BuildBurger() 
   { 
      happy_meal.Add("Hamburger"); 
   } 
    public override void BuildPepsi() 
   { 
      happy_meal.Add("Pepsi 0.3"); 
   } 
    public override void BuildFries() 
   { 
      happy_meal.Add("SmallFries"); 
   } 
    public override void BuildToy() 
   { 
      happy_meal.Add("One toy"); 
   } 
    public override HappyMeal GetProduct() 
   { 
     return happy_meal; 
    } 
  }

Пришло время реализовать класс Director, который будет отвечать за конструирование объектов:

// "Этот класс будет // заниматься конструированием" 
  class Director 
  { 
    // Конструирование объекта // по частям 
    public void Construct(HappyMealBuilder builder) 
   { 
      builder.BuildBurger(); 
      builder.BuildPepsi(); 
      builder.BuildFries(); 
      builder.BuildToy(); 
    } 
  }

Как видно из фрагмента листинга, все действие сосредоточено вокруг метода Construct. В него вы передаете объект HappyMealBuilder, после чего начинаете пошаговую сборку, поочередно вызывая каждый из Build-методов.

Наконец поработайте с созданными вами классами:

public static void Main() 
    { 
      // Создаете класс // Director и строителей // для двух наборов // HappyMeal 
      Director director = new Director(); 
      HappyMealBuilder big_hmbuilder = new BigHappyMealBuilder(); 
      HappyMealBuilder small_hmbuilder = new SmallHappyMealBuilder(); 
      // Конструируете два // продукта 
      director.Construct(big_hmbuilder); 
      HappyMeal hm1 = big_hmbuilder.GetProduct(); 
      hm1.Show(); 
      director.Construct(small_hmbuilder); 
      HappyMeal hm2 = small_hmbuilder.GetProduct(); 
      hm2.Show(); 
      // Ожидаете действия // пользователя 
      Console.Read(); 
    }

Работа с созданными классами очень проста. Сначала вы создаете объект класса Director, а затем два объекта Builder, которые будут переданы в метод Construct класса Director, чтобы собрать объект HappyMeal. После конструирования очередного HappyMeal вы передаете его в переменную hm1 или hm2 типа HappyMeal и вызываете метод Show, показывающий содержимое набора.

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


Рисунок