Interface Segmentation Principle

接口

接口有很多不同的概念,在这里,这个术语指的是类里的接口。类实现了许多方法,有些方法旨在被其他对象使用,这些方法就组成了它的公共接口。那些组成类的公共接口的方法组成了这个类呈现给外部世界的全貌。他们:

  1. 暴露了其主要的职责
  2. 期望被其它对象调用
  3. 不会因一时兴起而改变
  4. 对其他依赖它的对象来说很安全
  5. 在测试里被详细记录

领域对象

Domain Object 显而易见,因为他们代表了这个应用程序,代表了现实世界里的很大的、易于发现的事物。应该要注意的是,Domain Object 往往是给粗心大意设立的陷进。因为如果过度关注他们,就会倾向于给他们强加上行为。我们应该重点关注的是他们之间的消息传递,这些消息会引导你去发现其他的对象,而这些对象可远没有这么明显。

时序图

时序图的价值在于,他们明确制定了对象之间消息的传递。因为对象间只应使用公共接口进行通信,时序图便是一种用于暴露、实验并最终定义这些接口的工具。

请询问“要什么”,别告知“如何做”

如下图, Trip 告知 Mechanic 如何去准备每一辆 bicycle.

下图里,Trip 要求 Mechanic 去准备每一个 bicycle

消息的这种变化是代码可维护性的一个巨大改进。因为它大大缩短了公共接口,大大减少出现“违背其承诺,然后强迫许多类进行更改”的情况。

最小化上下文(context)

对象所了解到的关于其他对象的那些事情便构成了它的上下文。对象所期待的上下文会直接影响到它的重用难度。具有简单上下文的对象易于使用,也易于测试,他们对周边的环境期望很少。最好的情况是对象与他的上下文完全独立。如果某个对象在与其他对象进行合作时,不知道他们是谁,也不知道他们所做的事情,那么这个对象便可以按各种千奇百怪和完全无法预测的方式重用。

信任其他对象

我知道我需要什么,并且我相信你会做好你的本职工作。 这种盲目的信任是面向对象设计的基石。在允许对象进行合作的同时,它无需将自己束缚在上下文里,并且在任何期望壮大和变化的应用程序里它都是必不可少的。

创建显式接口

  1. 被明确标识;
  2. 多与 做什么 有关,少于 怎么做 有关;
  3. 尽可能让这些名字都稳定不变;
  4. 将散列表作为参数

迪米特法则

迪米特法则会限制可以向某个方法发送消息的对象集合。它会禁止这样的做法:将某条消息通过第二个不同类型的对象转发给第三个对象。

1
2
3
4
5
6
7
8
# 不合理
class Trip
def depart
customer.bicycle.wheel.tire
customer.bicycle.wheel.rotate
end
# ......
end
1
2
3
4
5
6
7
# 合理
class Trip
def depart
customer.go
end
# ......
end

鸭子类型

Ducking Type 指的是不会绑定到任何特定类的公共接口。这种跨类的接口能为应用程序带来巨大的灵活性,所采用的方式是利用更加宽容的消息依赖取代昂贵的类依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
## 不好的写法
class Trip
attr_reader :bicycles, :customers, :vehicle

def prepare(preparers)
preparers.each {|preparer|
case preparer
when Mechanic
preparer.prepare_bicycles(bicycles)
when TripCoordinator
preparer.buy_food(customers)
when Driver
preparer.gas_up(vehicle)
preparer.fill_water_tank(vehicle)
end
}
end
end

删除依赖关系的关键是要意识到:TripPrepare方法只服务于单个目的,因此它的参数出现在这里是希望可以协作完成同一个目标。每一个参数都因同样的理由出现在这里,具体的原因与这些参数的底层类无关。
设计鸭子类型的挑战是:要注意到你需要一个鸭子类型,并且要将其接口抽象出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## 好的写法
class Trip
attr_reader :bicycles, :customers, :vehicle

def prepare(preparers)
preparers.each {|preparer| preparer.prepare_trip(self)}
end
end


class Mechanic
def prepare_trip(trip)
trip.bicycles.each {|bicycle| prepare_bicycles(bicycle)}
end
# ...
end
Donate article here