43.md 4.7 KB
Newer Older
W
init  
wizardforcel 已提交
1 2 3 4
## 34.复合模式

看看下面的代码 [iling_egg.rb](code/design_patterns/boiling_egg.rb) 。 输入并执行

W
wizardforcel 已提交
5
```rb
W
init  
wizardforcel 已提交
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
 ```
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
50
51
52
53
54
55
56
57
58
59
60

W
wizardforcel 已提交
68
```rb
W
init  
wizardforcel 已提交
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133

 |

```
require_relative "node.rb"

class BuyEggs < Node
  attr_accessor :time_in_minutes

  def initialize
    @time_in_minutes = 15
    super "Buy eggs"
  end
end

class BoilEggs < Node
  attr_accessor :time_in_minutes

  def initialize
    @time_in_minutes = 10
    super "Boil eggs"
  end
end

class PeelEggs < Node
  attr_accessor :time_in_minutes

  def initialize
    @time_in_minutes = 3
    super "Peel eggs"
  end
end

class ServeEggs < Node
  attr_accessor :time_in_minutes

  def initialize
    @time_in_minutes = 2
    super "Serve eggs"
  end
end

class BoiledEggs < Node
  def initialize
    super "Boiled eggs"
    add_child BuyEggs.new
    add_child BoilEggs.new
    add_child PeelEggs.new
    add_child ServeEggs.new
  end

  def total_time_in_minutes
    total = 0

    for child in self.children
      total += child.time_in_minutes
    end

    total
  end
end

boiled_eggs = BoiledEggs.new
puts "Time (in minutes) to make boiled eggs is #{boiled_eggs.total_time_in_minutes}."

W
wizardforcel 已提交
134
```rb
W
init  
wizardforcel 已提交
135 136 137 138 139 140

 | 
```

输出量

W
wizardforcel 已提交
141
```rb
W
init  
wizardforcel 已提交
142 143 144 145 146
Time (in minutes) to make boiled eggs is 30.
```

在复合模式中,我们将一个类分为许多类。 假设需要以面向对象的方式表示煮鸡蛋,我们可以将`BoiledEggs`编写为如下所示的类。

W
wizardforcel 已提交
147
```rb
W
init  
wizardforcel 已提交
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
class BoiledEggs < Node
  def initialize
    super "Boiled eggs"
    add_child BuyEggs.new
    add_child BoilEggs.new
    add_child PeelEggs.new
    add_child ServeEggs.new
  end

  def total_time_in_minutes
    total = 0

    for child in self.children
      total += child.time_in_minutes
    end

    total
  end
end
```

W
wizardforcel 已提交
169
`BoiledEggs`继承了一个名为`Node`的类,该类有助于构建树结构。 稍后再讨论`Node`类。 现在让我们专注于复合模式。 如你在`initialize`方法中所见,在该类中`BoiledEggs`被划分为许多类,即`BuyEggs`至`ServeEggs`,并且它们被作为子节点添加到`BoiledEggs`中。
W
init  
wizardforcel 已提交
170

W
wizardforcel 已提交
171
如果你可以看到类`BuyEggs`如下所示:
W
init  
wizardforcel 已提交
172

W
wizardforcel 已提交
173
```rb
W
init  
wizardforcel 已提交
174 175 176 177 178 179 180 181 182 183 184 185
class BuyEggs < Node
  attr_accessor :time_in_minutes

  def initialize
    @time_in_minutes = 15
    super "Buy eggs"
  end
end
```

如果看到类`ServeEggs`,则如下所示:

W
wizardforcel 已提交
186
```rb
W
init  
wizardforcel 已提交
187 188 189 190 191 192 193 194 195 196 197 198 199 200
class ServeEggs < Node
  attr_accessor :time_in_minutes

  def initialize
    @time_in_minutes = 2
    super "Serve eggs"
  end
end
```

实际上,它看起来与`BuyEggs`非常相似,因此可以按照相似的方式处理这些类。 因此`BoiledEggs`就像是由不同对象组成的,可以用相似的方式对其进行处理。

现在`BoiledEggs`中的每个类都具有`time_in_minutes`属性。 因此,如果我们需要以分钟为单位计算制作煮鸡蛋的总时间,我们要做的就是编写一个函数`total_time_in_minutes`,如下所示

W
wizardforcel 已提交
201
```rb
W
init  
wizardforcel 已提交
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
class BoiledEggs < Node
 ...

  def total_time_in_minutes
    total = 0

    for child in self.children
      total += child.time_in_minutes
    end

    total
  end
end
```

因此,复合模式可以用于存在复杂对象的地方,可以将其功能分解为较小的对象,可以以非常相似的方式处理这些较小的对象,并将这些较小的对象放置在树状结构中。

W
wizardforcel 已提交
219
谈论树结构,我们建立了一个名为`Node`的类,可以在文件 [node.rb](code/design_patterns/node.rb) 中找到该类。 你可以在下面看到它。
W
init  
wizardforcel 已提交
220

W
wizardforcel 已提交
221
```rb
W
init  
wizardforcel 已提交
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
 ```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

W
wizardforcel 已提交
242
```rb
W
init  
wizardforcel 已提交
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264

 |

```
class Node
  attr_accessor :name, :parent, :children

  def initialize name = "node"
    @name = name
    @parent = nil
    @children = []
  end

  def to_s
    "&lt;Node: #{@name}, id: #{self.object_id}&gt;"
  end

  def add_child node
    children &lt;&lt; node
    node.parent = self
  end
end
W
wizardforcel 已提交
265
```rb
W
init  
wizardforcel 已提交
266 267 268 269

 | 
```

W
wizardforcel 已提交
270
它具有一个称为`add_child`的功能,通过它我们可以添加子元素。 你可以使用`parent`功能获取节点的父节点。 要查看此节点类的工作方式,请看下面的代码 [Composite.rb](code/design_patterns/composite.rb) :
W
init  
wizardforcel 已提交
271

W
wizardforcel 已提交
272
```rb
W
init  
wizardforcel 已提交
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
 ```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

W
wizardforcel 已提交
293
```rb
W
init  
wizardforcel 已提交
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315

 |

```
# composite.rb

require_relative "node.rb"

n1 = Node.new "n1"
n2 = Node.new "n2"
n3 = Node.new "n3"

n1.add_child n2
n1.add_child n3

puts "children of #{n1} are:"
for node in n1.children
  puts node
end

puts
puts "Parent of #{n3} is #{n3.parent}"
W
wizardforcel 已提交
316
```rb
W
init  
wizardforcel 已提交
317 318 319 320 321 322

 | 
```

Output

W
wizardforcel 已提交
323
```rb
W
init  
wizardforcel 已提交
324 325 326 327 328 329
children of <Node: n1, id: 47253445808700> are:
<Node: n2, id: 47253445808560>
<Node: n3, id: 47253445808360>

Parent of <Node: n3, id: 47253445808360> is <Node: n1, id: 47253445808700>
```