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

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

Шаблон Строитель включает двух участников процесса (рис. 10). Строитель (Builder) предоставляет методы для сборки частей объекта, при необходимости преобразовывает исходные данные в нужный вид, создаёт и выдаёт объект. Распорядитель (Director) определяет стратегию сборки: собирает данные и определяет порядок вызовов методов строителя. Задача распорядителя – сокрытие стратегии сборки. Это позволит, при необходимости, модифицировать или даже полностью менять её, не затрагивая остальной код.

Рис. 10. Дизайн шаблона Строитель.

Разберём пример использования шаблона Строитель. Пусть необходимо генерировать страницу на сайте. Определим шаги конструирования страницы: создаём шапку (Header), добавляем элементы меню (MenuItems), выводим публикации (Post) и завершаем страницу кодом подвала (Footer). Эти четыре шага и метод получения готовой страницы будут определять интерфейс Строителя.

public interface IPageBuilder

{

void BuildHeader(string header);

void BuildMenu(string menuItems);

void BuildPost(string post);

void BuildFooter(string footer);

string GetResult();

}

Два конкретных класса отвечают за построение «нормальной» страницы и версии страницы для печати (эта страница содержит только публикации).

public class PageBuilder : IPageBuilder

{

private string _page = string.Empty;

 

public void BuildHeader(string header)

{

_page += header + Environment.NewLine;

}

 

public void BuildMenu(string menuItems)

{

_page += menuItems + Environment.NewLine;

}

 

public void BuildPost(string post)

{

_page += post + Environment.NewLine;

}

 

public void BuildFooter(string footer)

{

_page += footer + Environment.NewLine;

}

 

public string GetResult()

{

return _page;

}

}

 

public class PrintPageBuilder : IPageBuilder

{

private string _page = string.Empty;

 

public void BuildHeader(string header)

{

}

 

public void BuildMenu(string menuItems)

{

}

 

public void BuildPost(string post)

{

_page += post + Environment.NewLine;

}

 

public void BuildFooter(string footer)

{

}

 

public string GetResult()

{

return _page;

}

}

Приступим к реализации распорядителя. Ограничимся одной стратегией создания страницы, а значит, одним классом распорядителя.

public class PageDirector

{

private readonly IPageBuilder _builder;

 

public PageDirector(IPageBuilder builder)

{

_builder = builder;

}

 

public string BuildPage(int pageId)

{

_builder.BuildHeader(GetHeader(pageId));

_builder.BuildMenu(GetMenuItems(pageId));

foreach (var post in GetPosts(pageId))

{

_builder.BuildPost(post);

}

_builder.BuildFooter(GetFooter(pageId));

return _builder.GetResult();

}

 

private string GetHeader(int pageId)

{

// реализация упрощена

return "Header of page " + pageId;

}

 

private string GetMenuItems(int pageId)

{

// реализация упрощена

return "Menu";

}

 

private IEnumerable<string> GetPosts(int pageId)

{

// реализация упрощена

return new List<string> {"Post 1", "Post 2"};

}

 

private string GetFooter(int pageId)

{

// реализация упрощена

return "Footer of page " + pageId;

}

}

Теперь всё готово к использованию:

var pageBuilder = new PageBuilder();

var pageDirector = new PageDirector(pageBuilder);

 

var page = pageDirector.BuildPage(123);