设计是保留可变性的艺术,而非达到完美性的行为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Gear def initialize(chainring, cog, rim, tire) @chainring = chainring @cog = cog @rim = rim @tire = tire end
def ratio @chainring / @cog.to_f end
def gear_inches @ratio * (@rim + (@tire * 2)) end
end
puts Gear.new(52,11,26,1.5).gear_inches
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| class ObscuringReference attr_reader :data
def initialize(data) @data = data end
def diameters data.collect {|cell| cell[0] + (cell[1] * 2) } end end
|
如何确定单一职责
- 假设它存在意识,然后质询它
- 齿轮先生,请问你的比率是多少? ## 靠谱的询问
- 齿轮先生,请问你的轮胎尺寸是多少?## 荒唐可笑的询问
- 尝试用一句话来描述类,如果出现 and 或者 or,则说明不遵循单一职责原则
- 高内聚(cohesion) OO的设计者使用内聚来描述某个类的所有内容都与其中心目标相关联的情况。一个类有责任设计其目标。
依赖行为,不要依赖数据
- 实例变量被引用多次,如果需要进行调整,则需要大量重改。
- 将数据处理成行为,对行为的一次更改,可以作用在被引用的所有数据上。
- 隐藏数据结构 方法 diameters 不仅知道如何计算直径,他还知道在哪里找到钢圈(cell[0])和轮胎(cell[1]).
解决方案
- 将数据结构的知识,封装在单一方法内部。
1 2 3 4 5 6 7 8 9 10 11
| Wheel = 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 12
| def 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 25
| class 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 def diameter rim + (tire * 2) end end end
puts Gear.new(52,11,26,1.5).gear_inches
|
需求的更改
如果此时,我们需要计算轮子的周长。这正是我们一直等待的信息,因为它提供了做下一步设计决定时所需要的信息。 注意敏捷开发里设计和代码迭代互相交互的原则。
现在我们有理由将Wheel独立成一个类了。因为我们将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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| class Gear attr_reader :chainring, :cog, :wheel def initialize(chainring,cog,wheel = nil) @chainring = chainring @cog = cog @wheel = wheel end
def ratio chainring / cog.to_f end
def gear_inches ratio * wheel.diameter 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
@wheel = Wheel.new(12,1.5) puts @wheel.circumference
puts Gear.new(52,11,@wheel).gear_inches puts Gear.new(52,11).ratio
|