|
1 |
| -using System; |
| 1 | +// EN: Builder Design Pattern |
| 2 | +// |
| 3 | +// Intent: Separate the construction of a complex object from its representation |
| 4 | +// so that the same construction process can create different representations. |
| 5 | +// |
| 6 | +// RU: Паттерн Строитель |
| 7 | +// |
| 8 | +// Назначение: Отделяет построение сложного объекта от его |
| 9 | +// представления так, что один и тот же процесс построения может создавать |
| 10 | +// разные представления объекта. |
| 11 | + |
| 12 | +using System; |
2 | 13 | using System.Collections.Generic;
|
3 | 14 |
|
4 | 15 | namespace RefactoringGuru.DesignPatterns.Builder.Conceptual
|
5 | 16 | {
|
6 |
| - class Program |
| 17 | + // EN: The Builder interface specifies methods for creating the different |
| 18 | + // parts of the Product objects. |
| 19 | + // |
| 20 | + // RU: Интерфейс Строителя объявляет создающие методы для различных частей |
| 21 | + // объектов Продуктов. |
| 22 | + public interface Builder |
7 | 23 | {
|
8 |
| - static void Main(string[] args) |
9 |
| - { |
10 |
| - Builder builder = new ConcreteBuilder(); |
11 |
| - Director director = new Director(builder); |
12 |
| - |
13 |
| - Client client = new Client(); |
14 |
| - client.ClientCode(director, builder); |
15 |
| - } |
| 24 | + void BuildPartA(); |
| 25 | + |
| 26 | + void BuildPartB(); |
| 27 | + |
| 28 | + void BuildPartC(); |
16 | 29 | }
|
17 |
| - |
18 |
| - public class Client |
| 30 | + |
| 31 | + // EN: The Concrete Builder classes follow the Builder interface and provide |
| 32 | + // specific implementations of the building steps. Your program may have |
| 33 | + // several variations of Builders, implemented differently. |
| 34 | + // |
| 35 | + // RU: Классы Конкретного Строителя следуют интерфейсу Строителя и |
| 36 | + // предоставляют конкретные реализации шагов построения. Ваша программа |
| 37 | + // может иметь несколько вариантов Строителей, реализованных по-разному. |
| 38 | + public class ConcreteBuilder : Builder |
19 | 39 | {
|
20 |
| - public void ClientCode(Director director, Builder builder) |
| 40 | + private Product _product = new Product(); |
| 41 | + |
| 42 | + // EN: A fresh builder instance should contain a blank product object, |
| 43 | + // which is used in further assembly. |
| 44 | + // |
| 45 | + // RU: Новый экземпляр строителя должен содержать пустой объект |
| 46 | + // продукта, который используется в дальнейшей сборке. |
| 47 | + public ConcreteBuilder() |
21 | 48 | {
|
22 |
| - Console.WriteLine("Standart basic product:"); |
23 |
| - director.buildMinimalViableProduct(); |
24 |
| - Console.WriteLine(builder.GetProduct().ListParts()); |
25 |
| - |
26 |
| - Console.WriteLine("Standart full featured product:"); |
27 |
| - director.buildFullFeaturedProduct(); |
28 |
| - Console.WriteLine(builder.GetProduct().ListParts()); |
29 |
| - |
30 |
| - Console.WriteLine("Custom product:"); |
31 |
| - builder.BuildPartA(); |
32 |
| - builder.BuildPartC(); |
33 |
| - Console.WriteLine(builder.GetProduct().ListParts()); |
| 49 | + this.Reset(); |
34 | 50 | }
|
35 |
| - } |
36 |
| - |
37 |
| - public class Director |
38 |
| - { |
39 |
| - Builder builder; |
40 |
| - |
41 |
| - public Director(Builder builder) |
| 51 | + |
| 52 | + public void Reset() |
42 | 53 | {
|
43 |
| - this.builder = builder; |
| 54 | + this._product = new Product(); |
44 | 55 | }
|
45 |
| - |
46 |
| - public void buildMinimalViableProduct() |
| 56 | + |
| 57 | + // EN: All production steps work with the same product instance. |
| 58 | + // |
| 59 | + // RU: Все этапы производства работают с одним и тем же экземпляром |
| 60 | + // продукта. |
| 61 | + public void BuildPartA() |
47 | 62 | {
|
48 |
| - builder.BuildPartA(); |
| 63 | + this._product.Add("PartA1"); |
49 | 64 | }
|
50 | 65 |
|
51 |
| - public void buildFullFeaturedProduct() |
| 66 | + public void BuildPartB() |
52 | 67 | {
|
53 |
| - builder.BuildPartA(); |
54 |
| - builder.BuildPartB(); |
55 |
| - builder.BuildPartC(); |
| 68 | + this._product.Add("PartB1"); |
56 | 69 | }
|
57 |
| - } |
58 |
| - |
59 |
| - public abstract class Builder |
60 |
| - { |
61 |
| - public abstract void BuildPartA(); |
62 |
| - |
63 |
| - public abstract void BuildPartB(); |
64 | 70 |
|
65 |
| - public abstract void BuildPartC(); |
| 71 | + public void BuildPartC() |
| 72 | + { |
| 73 | + this._product.Add("PartC1"); |
| 74 | + } |
66 | 75 |
|
67 |
| - public abstract Product GetProduct(); |
68 |
| - } |
| 76 | + // EN: Concrete Builders are supposed to provide their own methods for |
| 77 | + // retrieving results. That's because various types of builders may |
| 78 | + // create entirely different products that don't follow the same |
| 79 | + // interface. Therefore, such methods cannot be declared in the base |
| 80 | + // Builder interface (at least in a statically typed programming |
| 81 | + // language). |
| 82 | + // |
| 83 | + // Usually, after returning the end result to the client, a builder |
| 84 | + // instance is expected to be ready to start producing another product. |
| 85 | + // That's why it's a usual practice to call the reset method at the end |
| 86 | + // of the `GetProduct` method body. However, this behavior is not |
| 87 | + // mandatory, and you can make your builders wait for an explicit reset |
| 88 | + // call from the client code before disposing of the previous result. |
| 89 | + // |
| 90 | + // RU: Конкретные Строители должны предоставить свои собственные методы |
| 91 | + // получения результатов. Это связано с тем, что различные типы |
| 92 | + // строителей могут создавать совершенно разные продукты с разными |
| 93 | + // интерфейсами. Поэтому такие методы не могут быть объявлены в базовом |
| 94 | + // интерфейсе Строителя (по крайней мере, в статически типизированном |
| 95 | + // языке программирования). |
| 96 | + // |
| 97 | + // Как правило, после возвращения конечного результата клиенту, |
| 98 | + // экземпляр строителя должен быть готов к началу производства |
| 99 | + // следующего продукта. Поэтому обычной практикой является вызов метода |
| 100 | + // сброса в конце тела метода GetProduct. Однако такое поведение не |
| 101 | + // является обязательным, вы можете заставить своих строителей ждать |
| 102 | + // явного запроса на сброс из кода клиента, прежде чем избавиться от |
| 103 | + // предыдущего результата. |
| 104 | + public Product GetProduct() |
| 105 | + { |
| 106 | + Product result = this._product; |
| 107 | + |
| 108 | + this.Reset(); |
69 | 109 |
|
| 110 | + return result; |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + // EN: It makes sense to use the Builder pattern only when your products are |
| 115 | + // quite complex and require extensive configuration. |
| 116 | + // |
| 117 | + // Unlike in other creational patterns, different concrete builders can |
| 118 | + // produce unrelated products. In other words, results of various builders |
| 119 | + // may not always follow the same interface. |
| 120 | + // |
| 121 | + // RU: Имеет смысл использовать паттерн Строитель только тогда, когда ваши |
| 122 | + // продукты достаточно сложны и требуют обширной конфигурации. |
| 123 | + // |
| 124 | + // В отличие от других порождающих паттернов, различные конкретные строители |
| 125 | + // могут производить несвязанные продукты. Другими словами, результаты |
| 126 | + // различных строителей могут не всегда следовать одному и тому же |
| 127 | + // интерфейсу. |
70 | 128 | public class Product
|
71 | 129 | {
|
72 |
| - List<object> parts = new List<object>(); |
| 130 | + private List<object> _parts = new List<object>(); |
73 | 131 |
|
74 | 132 | public void Add(string part)
|
75 | 133 | {
|
76 |
| - parts.Add(part); |
| 134 | + this._parts.Add(part); |
77 | 135 | }
|
78 | 136 |
|
79 | 137 | public string ListParts()
|
80 | 138 | {
|
81 | 139 | string str = string.Empty;
|
82 | 140 |
|
83 |
| - for (int i = 0; i < parts.Count; i++) |
| 141 | + for (int i = 0; i < this._parts.Count; i++) |
84 | 142 | {
|
85 |
| - str += parts[i] + ", "; |
| 143 | + str += this._parts[i] + ", "; |
86 | 144 | }
|
87 | 145 |
|
88 | 146 | str = str.Remove(str.Length - 2); // removing last ",c"
|
89 | 147 |
|
90 | 148 | return "Product parts: " + str + "\n";
|
91 | 149 | }
|
92 | 150 | }
|
93 |
| - |
94 |
| - public class ConcreteBuilder : Builder |
| 151 | + |
| 152 | + // EN: The Director is only responsible for executing the building steps in |
| 153 | + // a particular sequence. It is helpful when producing products according to |
| 154 | + // a specific order or configuration. Strictly speaking, the Director class |
| 155 | + // is optional, since the client can control builders directly. |
| 156 | + // |
| 157 | + // RU: Директор отвечает только за выполнение шагов построения в |
| 158 | + // определённой последовательности. Это полезно при производстве продуктов в |
| 159 | + // определённом порядке или особой конфигурации. Строго говоря, класс |
| 160 | + // Директор необязателен, так как клиент может напрямую управлять |
| 161 | + // строителями. |
| 162 | + public class Director |
95 | 163 | {
|
96 |
| - Product product = new Product(); |
97 |
| - |
98 |
| - public override void BuildPartA() |
| 164 | + private Builder _builder; |
| 165 | + |
| 166 | + public Builder Builder |
99 | 167 | {
|
100 |
| - product.Add("PartA1"); |
| 168 | + set { _builder = value; } |
101 | 169 | }
|
102 |
| - |
103 |
| - public override void BuildPartB() |
| 170 | + |
| 171 | + // EN: The Director can construct several product variations using the same |
| 172 | + // building steps. |
| 173 | + // |
| 174 | + // RU: Директор может строить несколько вариаций продукта, используя |
| 175 | + // одинаковые шаги построения. |
| 176 | + public void buildMinimalViableProduct() |
104 | 177 | {
|
105 |
| - product.Add("PartB1"); |
| 178 | + this._builder.BuildPartA(); |
106 | 179 | }
|
107 | 180 |
|
108 |
| - public override void BuildPartC() |
| 181 | + public void buildFullFeaturedProduct() |
109 | 182 | {
|
110 |
| - product.Add("PartC1"); |
| 183 | + this._builder.BuildPartA(); |
| 184 | + this._builder.BuildPartB(); |
| 185 | + this._builder.BuildPartC(); |
111 | 186 | }
|
112 |
| - |
113 |
| - public override Product GetProduct() |
| 187 | + } |
| 188 | + |
| 189 | + public class Client |
| 190 | + { |
| 191 | + // EN: The client code creates a builder object, passes it to the |
| 192 | + // director and then initiates the construction process. The end result |
| 193 | + // is retrieved from the builder object. |
| 194 | + // |
| 195 | + // RU: Клиентский код создаёт объект-строитель, передаёт его директору, |
| 196 | + // а затем инициирует процесс построения. Конечный результат |
| 197 | + // извлекается из объекта-строителя. |
| 198 | + public void ClientCode(Director director) |
114 | 199 | {
|
115 |
| - Product result = product; |
| 200 | + var builder = new ConcreteBuilder(); |
| 201 | + director.Builder = builder; |
| 202 | + |
| 203 | + Console.WriteLine("Standard basic product:"); |
| 204 | + director.buildMinimalViableProduct(); |
| 205 | + Console.WriteLine(builder.GetProduct().ListParts()); |
116 | 206 |
|
117 |
| - this.Reset(); |
| 207 | + Console.WriteLine("Standard full featured product:"); |
| 208 | + director.buildFullFeaturedProduct(); |
| 209 | + Console.WriteLine(builder.GetProduct().ListParts()); |
118 | 210 |
|
119 |
| - return result; |
| 211 | + // EN: Remember, the Builder pattern can be used without a Director |
| 212 | + // class. |
| 213 | + // |
| 214 | + // RU: Помните, что паттерн Строитель можно использовать без класса |
| 215 | + // Директор. |
| 216 | + Console.WriteLine("Custom product:"); |
| 217 | + builder.BuildPartA(); |
| 218 | + builder.BuildPartC(); |
| 219 | + Console.Write(builder.GetProduct().ListParts()); |
120 | 220 | }
|
121 |
| - |
122 |
| - public void Reset() |
| 221 | + } |
| 222 | + |
| 223 | + class Program |
| 224 | + { |
| 225 | + static void Main(string[] args) |
123 | 226 | {
|
124 |
| - product = new Product(); |
| 227 | + var client = new Client(); |
| 228 | + var director = new Director(); |
| 229 | + client.ClientCode(director); |
125 | 230 | }
|
126 | 231 | }
|
127 | 232 | }
|
0 commit comments