You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 第二章-构造器模式.md
+19-19Lines changed: 19 additions & 19 deletions
Original file line number
Diff line number
Diff line change
@@ -3,21 +3,21 @@
3
3
4
4
Imagine that we want to create an object that is composed of multiple parts and the composition needs to be done step by step. The object is not complete unless all its parts are fully created. That's where the Builder design pattern can help us. The Builder pattern separates the construction of a complex object from its representation. By keeping the construction separate from the representation, the same construction can be used to create several different representations [GOF95, page 110], [j.mp/builderpat].
A practical example can help us understand what the purpose of the Builder pattern is. Suppose that we want to create an HTML page generator, the basic structure (construction part) of an HTML page is always the same: it begins with <html> and finishes with </html>; inside the HTML section are the <head> and </head> elements, inside the head section are the <title> and </title> elements, and so forth. But the representation of the page can differ. Each page has its own title, its own headings, and different <body> contents. Moreover, the page is usually built in steps: one function adds the title, another adds the main heading, another the footer, and so on. Only after the whole structure of a page is complete can it be shown to the client using a final render function. We can take it even further and extend the HTML generator so that it can generate totally different HTML pages. One page might contain tables, another page might contain image galleries, yet another page contains the contact form, and so on.
The HTML page generation problem can be solved using the Builder pattern. In this pattern, there are two main participants: the builder and the director. The builder
13
13
is responsible for creating the various parts of the complex object. In the HTML example, these parts are the title, heading, body, and the footer of the page. The director controls the building process using a builder instance. The HTML example means for calling the builder's functions for setting the title, the heading, and so on. Using a different builder instance allows us to create a different HTML page without touching any code of the director.
The Builder design pattern is used in fast-food restaurants. The same procedure is always used to prepare a burger and the packaging (box and paper bag), even if there are many different kinds of burgers (classic, cheeseburger, and more) and different packages (small-sized box, medium-sized box, and so forth). The difference between a classic burger and a cheeseburger is in the representation, and not in the construction procedure. The director is the cashier who gives instructions about what needs to be prepared to the crew, and the builder is the person from the crew that takes care of the specific order. The following figure provided by *www. sourcemaking.com* shows a **Unified Modeling Language (UML)** sequence diagram of the communication that takes place between the customer (client), the cashier (director), and the crew (builder) when a kid's menu is ordered [j.mp/builderpat].
构造器模式被用在了快餐店上。相同的步骤一直用在了准备汉堡和打包上(盒子、纸袋),即使存在不同类型的汉堡(原味,起司,等等)以及不同的包装(小号的盒子,中号的盒子,等等)原味汉堡和起司汉堡之间不同在于外观,而不是制造过程上。管理器是给全体工作人员发出需要准备什么食材的收银员,构造器是员工之中负责特殊订单的人。下面是由*www. sourcemaking.com*提供的**Unified Modeling Language (UML)**图片,它展示了当产生一份儿童餐订单时,一张放置于消费者(用户),收银员(管理器),和员工(构造器)之间的沟通顺序图解。
21
21
22
22

23
23
@@ -28,17 +28,16 @@ The HTML example that was mentioned at the beginning of the chapter is actually
28
28
29
29
The **django-query-builder** library is another third-party Django library that relies on the Builder pattern. The django-query-builder library can be used for building SQL queries dynamically. Using this, we can control all aspects of a query and create a different range of queries, from simple to very complex [j.mp/djangowidgy].
We use the Builder pattern when we know that an object must be created in multiple steps, and different representations of the same construction are required. These requirements exist in many applications such as page generators (like the HTML page generator mentioned in this chapter), document converters [GOF95, page 110], and User Interface (UI) form creators [j.mp/pipbuild].
Some resources mention that the Builder pattern can also be used as a solution to the telescopic constructor problem [j.mp/wikibuilder]. The telescopic constructor problem occurs when we are forced to create a new constructor for supporting different ways of creating an object. The problem is that we end up with many constructors and long parameter lists, which are hard to manage. An example of the telescopic constructor is listed at the stackoverflow website [j.mp/sobuilder]. Fortunately, this problem does not exist in Python, because it can be solved in
39
-
at least two ways:
38
+
Some resources mention that the Builder pattern can also be used as a solution to the telescopic constructor problem [j.mp/wikibuilder]. The telescopic constructor problem occurs when we are forced to create a new constructor for supporting different ways of creating an object. The problem is that we end up with many constructors and long parameter lists, which are hard to manage. An example of the telescopic constructor is listed at the stackoverflow website [j.mp/sobuilder]. Fortunately, this problem does not exist in Python, because it can be solved in at least two ways:
@@ -57,7 +56,7 @@ to return the final object when it needs it [GOF95, page 113], [j.mp/builderpat]
57
56
58
57
The new computer analogy might help to distinguish between a Builder pattern and a Factory pattern. Assume that you want to buy a new computer. If you decide to buy a specific preconfigured computer model, for example, the latest Apple 1.4 GHz Mac mini, you use the Factory pattern. All the hardware specifications are already predefined by the manufacturer, who knows what to do without consulting you. The manufacturer typically receives just a single instruction. Code-wise, this would look like the following (*apple-factory.py*):
59
58
60
-
新计算机的分析有助于区别构造器模式和工厂模式。假设你需要买一台新电脑。如果你决定买预先配置好的电脑,例如最新的Apple 1.4Ghz Mac mini,你使用的是工厂模式。所有硬件规格都已经由厂商预定义了,
59
+
新计算机的分析有助于区别构造器模式和工厂模式。假设你需要买一台新电脑。如果你决定买预先配置好的电脑,例如最新的Apple 1.4Ghz Mac mini,你使用的是工厂模式。所有硬件规格都已经由厂商预定义了,厂家不用问你也知道该干什么。厂商通畅只会收到一条指令。代码使人明了,其内容如下(*apple-factory.py*):
61
60
62
61
```python
63
62
MINI14='1.4GHz Mac mini'
@@ -134,8 +133,8 @@ class HardwareEngineer:
134
133
self.builder.configure_gpu(gpu))]
135
134
136
135
@property
137
-
defcomputer(self):
138
-
returnself.builder.computer
136
+
defcomputer(self):
137
+
returnself.builder.computer
139
138
140
139
141
140
defmain():
@@ -151,21 +150,21 @@ if __name__ == '__main__':
151
150
152
151
The basic changes are the introduction of a builder *ComputerBuilder*, a director *HardwareEngineer*, and the step-by-step construction of a computer, which now supports different configurations (notice that *memory*, *hdd*, and *gpu* are parameters and not preconfigured). What do we need to do if we want to support the construction of tablets? Implement this as an exercise.
You might also want to change the computer *serial_number* into something that is different for each computer, because as it is now it means that all computers will have the same serial number (which is impractical).
Let's see how we can use the Builder design pattern to make a pizza ordering application. The pizza example is particularly interesting because a pizza is prepared in steps that should follow a specific order. To add the sauce, you first need to prepare the dough. To add the topping, you first need to add the sauce. And you can't start baking the pizza unless both the sauce and the topping are placed on the dough. Moreover, each pizza usually requires a different baking time, depending
162
161
on the thickness of its dough and the topping used.
We start with importing the required modules and declaring a few Enum parameters [j.mp/pytenum] plus a constant that are used many times in the application. The STEP_DELAY constant is used to add a time delay between the different steps of preparing a pizza (prepare the dough, add the sauce, and so on) as follows:
Our end product is a *pizza*, which is described by the *Pizza* class. When using the Builder pattern, the end product does not have many responsibilities, since it is
180
179
not supposed to be instantiated directly. A builder creates an instance of the end product and makes sure that it is properly prepared. That is why the *Pizza* class is so minimal. It basically initializes all data to sane default values. An exception is the *prepare_dough()* method. The *prepare_dough()* method is defined in the *Pizza* class instead of a builder for two reasons:
- To clarify the fact that the end product is typically minimal does not mean that you should never assign it any responsibilities
185
184
- To promote code reuse through composition [GOF95, page 32]
186
185
187
-
-为了理清终端产品的情况,
188
-
-
186
+
-要说明的是,终端产品通畅是最小化的,但是这并不意味着你从此就不能让它承担任何的职责
187
+
-通过使用合成来提升代码的复用性
189
188
190
189
```python
191
190
classPizza:
@@ -216,6 +215,7 @@ class MargaritaBuilder:
216
215
self.pizza = Pizza('margarita')
217
216
self.progress = PizzaProgress.queued
218
217
self.baking_time =5# in seconds for the sake of the example
218
+
# 因为考虑到示例所以这里使用了秒
219
219
defprepare_dough(self):
220
220
self.progress = PizzaProgress.preparation
221
221
self.pizza.prepare_dough(PizzaDough.thin)
@@ -269,7 +269,7 @@ class CreamyBaconBuilder:
269
269
270
270
The director in this example is the waiter. The core of the *Waiter* class is the *construct_pizza()* method, which accepts a builder as a parameter and executes all the pizza preparation steps in the right order. Choosing the appropriate builder, which can even be done in runtime, gives us the ability to create different pizza styles without modifying any code of the director (*Waiter*). The Waiter class also contains the *pizza()* method, which returns the end product (prepared pizza) as a variable to the caller as follows:
0 commit comments