面向对象设计范式(Object Oriented Design Paradigm)让我们更容易地抽象现实世界的对象,更容易地分析业务。但是,计算机对象不仅仅是现实世界中对象的简单映射。比如针对司机开车(Drivers drive cars)这个场景,在现实世界中开车(drive)是司机的行为,但是在计算机对象世界中, 为了高内聚(High Cohesion),如果Car拥有drive这样行为的所有信息,drive这个动作就应该写到Car对象中,而不是Driver对象中。高内聚,按照我的理解是,’一个类首先要依赖自己做计算‘。
下面以一个我工作中的例子阐述,一个类要‘首先依赖自己做计算’这个观点。
Figure 1
在面对‘判断一个User是否有某某角色’的需求时,我遇到很多人的写法都是设计成这样
Figure 2
这时候UserManager里有一个方法,像这样子UserManager.hasRole(User user, Role role)。这样的编程设计引进了一个新的类UserManager,增加的新的依赖,导致User对象和Role对象的变化可能引起UserManager的变化。
从上面的图片Figure 1中,我们看到User拥有‘判断一个User是否有某某角色’的所有信息,按照‘一个类首先要依赖自己做计算’的观点,hasRole这个方法要放到User对象中,以下是代码片段
public boolean hasAnyRole(String... roleNames) {
if (roleNames == null) {
throw new IllegalArgumentException("Role names cannot be null");
}
if (this.roles == null || roles.size() == 0) {
return false;
}
for (String roleName : roleNames){
for (Role userRole : roles){
if (roleName!=null && roleName.equals(userRole.getName())){
return true;
}
}
}
return false;
}
另外一个例子,‘把一个订单保存成订单模板’,我重构之前的设计是这样的:在接口OrderManager中有这样的一个签名 OrderTemplate saveAsTemplate(Order order);
但是,我们知道‘把一个订单保存成订单模板’的业务逻辑是,‘在下订单之前,把用户填写的部分订单信息保存成一个订单模板’。因为订单知道订单模板的所有信息,所以OrderTemplate saveAsTemplate(Order order) 应该写到Order对象里, 代码片段是
public OrderTemplate saveAsTemplate(Order oder){
OrderTemplate orderTemplate = new OrderTemplate();
oderTemplate.setProductId(productId);
…
return orderTemplate;
}