设计是保留可变性的艺术,而非达到完美性的行为
1 | class Gear |
1 | class ObscuringReference |
如何确定单一职责
- 假设它存在意识,然后质询它
- 齿轮先生,请问你的比率是多少? ## 靠谱的询问
- 齿轮先生,请问你的轮胎尺寸是多少?## 荒唐可笑的询问
- 尝试用一句话来描述类,如果出现 and 或者 or,则说明不遵循单一职责原则
- 高内聚(cohesion)
OO的设计者使用内聚来描述某个类的所有内容都与其中心目标相关联的情况。一个类有责任设计其目标。
依赖行为,不要依赖数据
- 实例变量被引用多次,如果需要进行调整,则需要大量重改。
- 将数据处理成行为,对行为的一次更改,可以作用在被引用的所有数据上。
- 隐藏数据结构
方法 diameters 不仅知道如何计算直径,他还知道在哪里找到钢圈(cell[0])和轮胎(cell[1]).
解决方案
将数据结构的知识,封装在单一方法内部。
1
2
3
4
5
6
7
8
9
10
11Wheel = Struct.new(:rim,:tire)
def wheelify(data)
data.collect {
|cell| Wheel.new(cell[0],cell[1])
}
end
def diameters
wheels.collect {|wheel| wheel.rim * (wheel.tire * 2)}
end将额外的责任从方法里提取出来
1
2
3
4
5
6
7
8
9
10
11
12def diameters
wheels.collect {|wheel| diameter wheel}
end
def diameter(wheel)
wheel.rim + (wheel.tire * 2)
end
def gear_inches
ration * diameter
end将类里的职责隔离
暂时缺乏足够的信息证明Wheel需要一个独立的类。有的时候,我们是否需要创建一个新的对象并不是有明确界限的。隔离起来总是不错的选择。同时要注意,不要讲无关的职责混入自己的类。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Gear
attr_reader :chainring, :cog, :wheel
def initialize(chainring,cog,rim,tire)
@chainring = chainring
@cog = cog
@wheel = Wheel.new(rim,tire)
end
def ratio
chainring / cog.to_f
end
def gear_inches
ratio * wheel.diameter
end
Wheel = Struct.new(:rim,:tire) do # <----- 虽然Wheel存在于Gear里不算很好的选择,但是隔离Wheel的职责是不错的选择
def diameter
rim + (tire * 2)
end
end
end
puts Gear.new(52,11,26,1.5).gear_inches
需求的更改
如果此时,我们需要计算轮子的周长。这正是我们一直等待的信息,因为它提供了做下一步设计决定时所需要的信息。 注意敏捷开发里设计和代码迭代互相交互的原则。
现在我们有理由将Wheel独立成一个类了。因为我们将Wheel的职责隔离过,此时独立出它是很容易的。
1 | class Gear |