通过精心设计的对象都具有单一的职责,因此他们实际上是通过合作来完成复杂的任务。这种合作强大而危险。为了实现一个合作,一个对象必须知道其他对象的某些情况。这种知道便创建了一种依赖关系。当两三个对象耦合在一起,它们变回表现得像一个整体,不可能只重用其中的一个。如果对依赖关系不仔细加以管理,那么这些依赖关系将毁掉整个应用程序。
理解依赖关系
- 知道另外一个类的名字 >
Gear
对Wheel
的引用深入到gear_inches
方法里,并将其硬编码,那么这便是明确说明它只愿意为Wheel.gear
的实例计算齿轮英寸数,从而拒绝与其他任何类型的对象合作。 - 消息的名字 >
Gear
需要访问可以相应diameter
的对象。实际上,对Gear
来说,计算gear_inches
并不需要知道这个对象的类,只要这个对象可以相应diameter
就行了。我们可以把这个对象理解成鸭子类型。 - 消息所要求的参数 >
Gear
也并不需要知道Wheel
需要使用rim
和tire
进行初始化 - 参数的顺序 >
Gear
不需要知道Wheel
初始化时参数的顺序
1 | class Gear |
隔离依赖关系
隔离实例的创建
虽然此时 Gear
类仍然知道得太多,但是已经有所改进。如下的编码风格减少了 gear_inches
的依赖关系数量,同时公开暴露了 Gear
对 Wheel
的依赖。它们没有将依赖关系隐藏起来,而是将它们显露出来。这样降低了重用这段代码的门槛。
1 | class Gear |
隔离外部消息
如下的编码方式,wheel.diameter
被深度嵌套在 gear_inches
方法里。这个方法依赖 Gear
才能相应 Wheel,并且依赖Wheel
才能响应diameter。完全没有必要在gear_inches
里嵌入这种外部依赖,这使得 Gear
更加脆弱。
1 | def gear_inches |
现在我们将wheel.gear
隔离在一个单独的方法里,并且 gear_inches
可以依赖于某条发送给自己的消息。如果 Wheel
更改了diameter
的名字或者签名,那么对Gear
的副作用将会限定在一个简单的包裹方法里。
1 | def gear_inches |
移除参数顺序依赖关系
- 使用散列表来进行初始化同时显示地指定默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Gear
attr_reader :chainring, :cog, :wheel
def initialize(args)
# args = defaults.merge(args)
@chainring = args.fetch(:chainring,40)
@cog = args.fetch(:cog,18)
@wheel = args[:wheel]
end
# def defaults
# {:chainring => 40, :cog => 18}
# .......
end - 隔离多参数初始化操作 有时候我们会被迫依赖某个要求参数初始化顺序的固定方法,由于那里不属于我们的地盘,我们无法更改这个方法。此时可以创建一个单一的方法,将外部接口包裹起来。
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
module SomeFramework
class Gear
attr_reader :chainring, :cog, :wheel
def initialize(chainring,cog,wheel)
@chainring = chainring
@cog = cog
@wheel = wheel
end
def ratio
chainring / cog.to_f
end
def gear_inches
ratio * wheel.diameter
end
end
end
class Wheel
attr_reader :rim,:tire
def initialize(rim,tire)
@rim = rim
@tire = tire
end
def diameter
rim + (tire * 2)
end
def circumference
diameter * Math::PI
end
end
module GearWrapper
def self.gear(args)
SomeFramework::Gear.new(args[:chainring],args[:cog],args[:wheel])
end
end
p GearWrapper.gear(:chainring => 52, :cog => 11, :wheel => Wheel.new(16,1.5)).gear_inches - 反转依赖关系
- 有些类比其他类更容易管理
- 具体类比抽象类更容易发生变化
- 更改拥有许多关系的类会造成广泛的变化
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
36class Gear
attr_reader :chainring, :cog
def initialize(chainring,cog)
@chainring = chainring
@cog = cog
end
def ratio
chainring / cog.to_f
end
def gear_inches(diameter)
ratio * diameter
end
end
class Wheel
attr_reader :rim,:tire, :gear
def initialize(rim, tire, chainring, cog)
@rim = rim
@tire = tire
@gear = Gear.new(chainring, cog)
end
def diameter
rim + (tire * 2)
end
def gear_inches
gear.gear_inches diameter
end
end
p Wheel.new(16,1.5, 52,11).gear_inches