44.md 9.3 KB
Newer Older
W
init  
wizardforcel 已提交
1 2
## 35.建造者模式

W
wizardforcel 已提交
3
假设你正在为网站提供高度定制化功能的计算机制造商编写一款软件。 一台计算机可以有很多零件,它们可以变化,因此有很多组合。 因此,我们不是将所有这些代码放在一个单一的大型计算机类中,而是创建了一个名为 builder 的类,在这种情况下,我们创建了一个名为`ComputerBuilder`的类,该类承担创建定制计算机的任务,并可能简化创建或创建过程。 构建过程也是如此。
W
init  
wizardforcel 已提交
4 5 6

假设我们需要不同类型的 CPU 来构建计算机,为此,我们编写了三种类型的 CPU 类,如下所示 [cpus.rb](code/design_patterns/builder/cpus.rb)

W
wizardforcel 已提交
7
```rb
W
init  
wizardforcel 已提交
8 9 10 11 12 13 14 15 16 17 18 19 20
 ```
1
2
3
4
5
6
7
8
9
10
11

W
wizardforcel 已提交
21
```rb
W
init  
wizardforcel 已提交
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

 |

```
class CPU
  # cpu stuff...
end

class BasicCPU < CPU
  # basic cpu stuff...
end

class TurboCPU < CPU
  # trubo fast cpu stuff...
end
W
wizardforcel 已提交
37
```rb
W
init  
wizardforcel 已提交
38 39 40 41

 | 
```

W
wizardforcel 已提交
42
计算机可能需要驱动器,我们也为此编写了一个类,如果你看到下面的代码 [drive.rb](code/design_patterns/builder/drive.rb) ,则为该驱动器提供了一个类以及一个可用于自定义的初始化程序 在一定程度上。
W
init  
wizardforcel 已提交
43

W
wizardforcel 已提交
44
```rb
W
init  
wizardforcel 已提交
45 46 47 48 49 50 51 52 53 54 55
 ```
1
2
3
4
5
6
7
8
9

W
wizardforcel 已提交
56
```rb
W
init  
wizardforcel 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69

 |

```
class Drive
  attr_reader :type, :size, :writable

  def initialize(type, size, writable)
    @type = type
    @size = size
    @writable = writable
  end
end
W
wizardforcel 已提交
70
```rb
W
init  
wizardforcel 已提交
71 72 73 74 75 76

 | 
```

以类似的方式,下面我们在[母板.rb](code/design_patterns/builder/motherboard.rb) 中为母板编码。

W
wizardforcel 已提交
77
```rb
W
init  
wizardforcel 已提交
78 79 80 81 82 83 84 85 86 87
 ```
1
2
3
4
5
6
7
8

W
wizardforcel 已提交
88
```rb
W
init  
wizardforcel 已提交
89 90 91 92 93 94 95 96 97 98 99 100

 |

```
class Motherboard
  attr_accessor :cpu, :memory_size

  def initialize(cpu = BasicCPU.new, memory_size = 1000)
    @cpu = cpu
    @memory_size = memory_size
  end
end
W
wizardforcel 已提交
101
```rb
W
init  
wizardforcel 已提交
102 103 104 105 106 107

 | 
```

现在让我们看一下`Computer`类(在 [computer.rb](code/design_patterns/builder/computer.rb) 中),这与`Drive``Motherboard`类非常相似。 我们的目标是了解构建器模式,因此继续前进。

W
wizardforcel 已提交
108
```rb
W
init  
wizardforcel 已提交
109 110 111 112 113 114 115 116 117 118 119
 ```
1
2
3
4
5
6
7
8
9

W
wizardforcel 已提交
120
```rb
W
init  
wizardforcel 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133

 |

```
class Computer
  attr_accessor :display, :motherboard, :drives

  def initialize(display = :crt, motherboard = Motherboard.new, drives = [])
    @display = display
    @motherboard = motherboard
    @drives = drives
  end
end
W
wizardforcel 已提交
134
```rb
W
init  
wizardforcel 已提交
135 136 137 138 139 140

 | 
```

现在,让我们分析一下本节的英雄`ComputerBuilder`类。 浏览下面的代码,我们将很快讨论。

W
wizardforcel 已提交
141
```rb
W
init  
wizardforcel 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
 ```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

W
wizardforcel 已提交
193
```rb
W
init  
wizardforcel 已提交
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246

 |

```
class ComputerBuilder
  attr_reader :computer

  def initialize
    @computer = Computer.new
  end

  def turbo(_has_turbo_cpu = true)
    @computer.motherboard.cpu = TurboCPU.new
  end

  def display(display)
    @computer.display = display
  end

  def memory_size(size_in_mb)
    @computer.motherboard.memory_size = size_in_mb
  end

  def add_cd(writable = false)
    @computer.drives << Drive.new(:cd, 760, writable)
  end

  def add_dvd(writable = false)
    @computer.drives << Drive.new(:dvd, 4700, writable)
  end

  def add_hard_disk(size_in_mb)
    @computer.drives << Drive.new(:hard_disk, size_in_mb, true)
  end

  def method_missing(name, *args)
    words = name.to_s.split('_')
    return super(name, *args) unless words.shift == 'add'
    words.each do |word|
      next if word == 'and'
      add_cd if word == 'cd'
      add_dvd if word == 'dvd'
      add_hard_disk(100_000) if word == 'harddisk'
      turbo if word == 'turbo'
    end
  end

  def computer
    raise 'Not enough memory.' if @computer.motherboard.memory_size < 250
    raise 'Too many drives.' if @computer.drives.size > 4
    @computer
  end
end
W
wizardforcel 已提交
247
```rb
W
init  
wizardforcel 已提交
248 249 250 251

 | 
```

W
wizardforcel 已提交
252
在上面的程序中,第 4 行中确实有`initialize`方法,但是如果你查看第 44 行,则在方法`computer`中将返回`Computer`实例。 如果你看到的是同一功能,那么如果没有足够的内存,或者如果计算机中添加了太多磁盘,则我们有代码引发异常。 因此,这是构建器模式的另一个优点,在该模型中,如果不满足特定条件,就可以防止构建复杂的对象。
W
init  
wizardforcel 已提交
253

W
wizardforcel 已提交
254
在构建器中,你具有`display`,`memory_size`之类的方法,以及名为`turbo`的函数来设置显示,内存和 CPU 类型的参数。 我们还具有`add_cd`,`add_dvd`和`add_hard_disk`之类的功能,可将这些内容添加到我们正在构建的计算机中。
W
init  
wizardforcel 已提交
255 256 257

现在让我们看一下将所有内容组合在一起的程序。 看看下面的程序 [main.rb](code/design_patterns/builder/main.rb) ,键入或复制并执行它。

W
wizardforcel 已提交
258
```rb
W
init  
wizardforcel 已提交
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
 ```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

W
wizardforcel 已提交
308
```rb
W
init  
wizardforcel 已提交
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359

 |

```
require_relative 'cpus'
require_relative 'drive'
require_relative 'motherboard'
require_relative 'computer'
require_relative 'computer_builder'

builder = ComputerBuilder.new
builder.turbo
builder.display(:lcd)
builder.add_cd
builder.add_dvd(true)
builder.add_hard_disk(100_000)

# manufacture 10 computers using the builder
computers = []
10.times { computers << builder.computer.clone }
computers.each { |computer| puts computer }

# computer must have at least 250 MB of memory
builder = ComputerBuilder.new
builder.memory_size(249)
begin
  builder.computer
rescue Exception => e
  puts e.message
end

# computer must have at most 4 drives
builder = ComputerBuilder.new
builder.add_cd
builder.add_dvd
builder.add_hard_disk(1000)
builder.add_cd
builder.add_dvd
begin
  builder.computer
rescue Exception => e
  puts e.message
end

# use magic method to rapidly build a computer
puts 'Computer built with magic method builder'
builder = ComputerBuilder.new
builder.add_cd_and_dvd_and_harddisk_and_turbo
computer = builder.computer
puts "CPU: #{computer.motherboard.cpu.class}"
computer.drives.each { |drive| puts "Drive: #{drive.type}" }
W
wizardforcel 已提交
360
```rb
W
init  
wizardforcel 已提交
361 362 363 364 365 366

 | 
```

输出量

W
wizardforcel 已提交
367
```rb
W
init  
wizardforcel 已提交
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
#<Computer:0x0000564155728610>
#<Computer:0x0000564155728598>
#<Computer:0x0000564155728570>
#<Computer:0x0000564155728548>
#<Computer:0x0000564155728520>
#<Computer:0x00005641557284f8>
#<Computer:0x00005641557284d0>
#<Computer:0x00005641557284a8>
#<Computer:0x0000564155728430>
#<Computer:0x0000564155728408>
Not enough memory.
Too many drives.
Computer built with magic method builder
CPU: TurboCPU
Drive: cd
Drive: dvd
Drive: hard_disk
```

现在让我们看看它是如何工作的。 从 1-5 行:

W
wizardforcel 已提交
389
```rb
W
init  
wizardforcel 已提交
390 391 392 393 394 395 396 397 398
require_relative 'cpus'
require_relative 'drive'
require_relative 'motherboard'
require_relative 'computer'
require_relative 'computer_builder'
```

我们需要该程序正常运行所需的文件。 在下面显示的以下几行中,我们创建一个名为`builder``ComputerBuilder`实例,该实例具有 Turbo CPU,LCD 显示屏,一个 CD 播放器,一个可写 DVD 和 100,000 MB 的硬盘:

W
wizardforcel 已提交
399
```rb
W
init  
wizardforcel 已提交
400 401 402 403 404 405 406 407 408 409
builder = ComputerBuilder.new
builder.turbo
builder.display(:lcd)
builder.add_cd
builder.add_dvd(true)
builder.add_hard_disk(100_000)
```

现在,使用此`builder`,我们可以克隆 &lt;sup class="footnote"&gt;[ [67](#_footnotedef_67 "View footnote.") ]&lt;/sup&gt; 以创建任意数量的计算机。 在下面的代码中,我们创建 10 个计算机克隆并打印它们

W
wizardforcel 已提交
410
```rb
W
init  
wizardforcel 已提交
411 412 413 414 415 416
# manufacture 10 computers using the builder
computers = []
10.times { computers << builder.computer.clone }
computers.each { |computer| puts computer }
```

W
wizardforcel 已提交
417
|  | 假设你正在编写游戏,并且需要创建 10 个对象,这种模式将非常方便。 |
W
init  
wizardforcel 已提交
418 419 420

现在,让我们测试如果内存不足则将引发异常的代码。 看下面的代码

W
wizardforcel 已提交
421
```rb
W
init  
wizardforcel 已提交
422 423 424 425 426 427 428 429 430 431 432 433
# computer must have at least 250 MB of memory
builder = ComputerBuilder.new
builder.memory_size(249)
begin
  builder.computer
rescue Exception => e
  puts e.message
end
```

在上面的代码中,我们在`builder = ComputerBuilder.new`中创建一个计算机生成器,接下来我们在`builder.memory_size(249)`行中故意分配低内存,在`begin` `end`块中,我们尝试从`builder`创建一个`Computer`实例。 使用`builder.computer`并确定会引发 ans 异常,该异常会被`rescue`下的`puts e.message`语句捕获并打印出来。 在输出中,我们将得到以下消息:

W
wizardforcel 已提交
434
```rb
W
init  
wizardforcel 已提交
435 436 437 438 439
Not enough memory.
```

我们以类似的方式尝试构建一台具有五个驱动器的计算机,即 2 个 CD,2 个 DVD 和 1 个硬盘。 在`ComputerBuilder`类中,我们将驱动器的最大数量限制为 4,因此下面的代码段将引发错误:

W
wizardforcel 已提交
440
```rb
W
init  
wizardforcel 已提交
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
# computer must have at most 4 drives
builder = ComputerBuilder.new
builder.add_cd
builder.add_dvd
builder.add_hard_disk(1000)
builder.add_cd
builder.add_dvd
begin
  builder.computer
rescue Exception => e
  puts e.message
end
```

捕获并打印了此错误,因此我们得到以下输出:

W
wizardforcel 已提交
457
```rb
W
init  
wizardforcel 已提交
458 459 460 461 462
Too many drives.
```

### 35.1。 行使

W
wizardforcel 已提交
463
你现在了解了构建器模式。 如果你想进一步探索,请看下面的代码
W
init  
wizardforcel 已提交
464

W
wizardforcel 已提交
465
```rb
W
init  
wizardforcel 已提交
466 467 468 469 470 471 472 473 474 475 476
# use magic method to rapidly build a computer
puts 'Computer built with magic method builder'
builder = ComputerBuilder.new
builder.add_cd_and_dvd_and_harddisk_and_turbo
computer = builder.computer
puts "CPU: #{computer.motherboard.cpu.class}"
computer.drives.each { |drive| puts "Drive: #{drive.type}" }
```

该代码产生以下输出

W
wizardforcel 已提交
477
```rb
W
init  
wizardforcel 已提交
478 479 480 481 482 483 484 485
Computer built with magic method builder
CPU: TurboCPU
Drive: cd
Drive: dvd
Drive: hard_disk
```

现在,在`ComputerBuilder`类中查看`method_missing`方法,并尝试解释`builder.add_cd_and_dvd_and_harddisk_and_turbo`语句的工作方式。