描述: 写代码时,接口中尽量封装为对外透明,外面只管调用,不用关心
自己的状态。
比较下三段比较简单的代码:用于下单和删除时客户付费状态
1. if (ChanOrder.CONVERT_FEE_ORDER.equals(order.getIsConvertFee())) {
this.updateCustomerPayingToExperience(order.getCustomerId());
}
}
this.updateCustomerPayingToExperience(order.getCustomerId());
}
}
if (ChanOrder.CONVERT_FEE_ORDER.equals(order.getIsConvertFee())) {
this.updateCustomerExperienceToPaying(order.getCustomerId());
}
updateCustomerPayingToExperience
this.updateCustomerExperienceToPaying(order.getCustomerId());
}
updateCustomerPayingToExperience
updateCustomerExperienceToPaying 分开
2.第一次修改,两个方法合并,状态互转,只要都调这个方法就够了,调用接口处还是
要判断。
public void updateCustomerFeetype(CustomerDO customer) {
String feeType = ChannelConstant.CHAN_CUSTOMER_FEE_TYPE_EXPERIENCE;
// 获取客户当前付费状态,如果是试用客户,转化为付费客户。
if (ChannelConstant.CHAN_CUSTOMER_FEE_TYPE_EXPERIENCE.equals(customer.getFeeType())) {
feeType = ChannelConstant.CHAN_CUSTOMER_FEE_TYPE_PAYING;
}
// 修改客户付费状态
chanCustomerBOService.updateCustomerFeetype(customer.getCustomerId(), feeType);
}
String feeType = ChannelConstant.CHAN_CUSTOMER_FEE_TYPE_EXPERIENCE;
// 获取客户当前付费状态,如果是试用客户,转化为付费客户。
if (ChannelConstant.CHAN_CUSTOMER_FEE_TYPE_EXPERIENCE.equals(customer.getFeeType())) {
feeType = ChannelConstant.CHAN_CUSTOMER_FEE_TYPE_PAYING;
}
// 修改客户付费状态
chanCustomerBOService.updateCustomerFeetype(customer.getCustomerId(), feeType);
}
3.第二次修改,根据订单状态判断,任何地方调用都不会出错,接口出无需判断
public void modifyCustomerFeeType(String customerId) {
boolean hasFeeOrder = false;// 存在付费订单(包含试用转付费)
boolean hasExperienceOrder = false;// 存在试用订单
String customerFeeType;// 客户订单状态
boolean hasFeeOrder = false;// 存在付费订单(包含试用转付费)
boolean hasExperienceOrder = false;// 存在试用订单
String customerFeeType;// 客户订单状态
// 1.取得客户所有订单
List<ChanOrder> orderList = channelOrderCommonDAO.getOrdersByCustomerId(customerId);
for (ChanOrder order : orderList) {
if(ChanOrder.ORDER_STATUS_ABOLISHED.equals(order.getStatus())){
//作废订单不作为判断依据
continue;
}
// 判断是否包含试用订单和付费订单
if (ChanOrder.FEE_TYPE_YES.equals(order.getFeeType())) {
hasFeeOrder = true;
} else {
hasExperienceOrder = true;
}
}
List<ChanOrder> orderList = channelOrderCommonDAO.getOrdersByCustomerId(customerId);
for (ChanOrder order : orderList) {
if(ChanOrder.ORDER_STATUS_ABOLISHED.equals(order.getStatus())){
//作废订单不作为判断依据
continue;
}
// 判断是否包含试用订单和付费订单
if (ChanOrder.FEE_TYPE_YES.equals(order.getFeeType())) {
hasFeeOrder = true;
} else {
hasExperienceOrder = true;
}
}
// 2.仅当存在试用订单且不存在付费订单时,客户为试用客户
if (hasExperienceOrder && !hasFeeOrder) {
customerFeeType = CustomerDO.CUSTOMER_FEE_TYPE_FREE;
} else {
customerFeeType = CustomerDO.CUSTOMER_FEE_TYPE_PAID;
}
// 3.更新客户付费状态
chanCustomerBOService.updateCustomerFeetype(customerId, customerFeeType);
if (hasExperienceOrder && !hasFeeOrder) {
customerFeeType = CustomerDO.CUSTOMER_FEE_TYPE_FREE;
} else {
customerFeeType = CustomerDO.CUSTOMER_FEE_TYPE_PAID;
}
// 3.更新客户付费状态
chanCustomerBOService.updateCustomerFeetype(customerId, customerFeeType);
}
总结下:
降低耦合度是封装的要求,最近看了《代码大全》这本书关于接口的要求,颇有感触:
其中有这样一句话
"不要对类的使用者做出任何假设
类的设计和实现应该符合在类的接口中所隐含的契约。它不应该对接口会被如何使用或不会被如何使用做出任何假设——除非在接口中有过明确说明"
显然就是说要假设调用你的接口的人就是什么也不懂,随随便便就调用了,这种情况下不会出错。这也许就是对耦合度最朴素的解释。正常的流程就是这样,你提供了接口,就要对接口负责,我调用者不需要了解细节,对调用者完全是透明的。
如下几段关于耦合的话也比较经典,不加修饰的摘抄一下:
让阅读代码比编写代码更方便 阅读代码的次数要比编写代码多得多,即使在开发的初期也是如此。因此,为了让编写代码更方便而降低代码的可读性是非常不经济的。尤其是在创建类的接口时,即使某个子程序与接口的抽象不很相配,有时人们也往往把这个子程序加到接口里,从而让正开发的这个类的某处调用代码能更方便地使用它。然而,这段子程序的添加正是代码走下坡路的开始,所以还是不要走出这一步为好。
留意过于紧密的耦合关系 “耦合(coupling)”是指两个类之间关联的紧密程度。通常,这种关联越松(loose)越好。根据这一概念可以得出以下一些指导建议:
■ 尽可能地限制类和成员的可访问性。
■ 避免友元类,因为它们之间是紧密耦合的。
■ 在基类中把数据声明为private而不是protected,以降低派生类和基类之间耦合的程度。
■ 避免在类的公开接口中暴露成员数据。
■ 要对从语义上破坏封装性保持警惕。
■ 察觉“Demeter(得墨忒耳)法则”(见本章第6.3节)
耦合性与抽象和封装性有着非常密切的联系。紧密的耦合性总是发生在抽象不严谨或封装性遭到破坏的时候。如果一个类提供了一套不完整的服务,其他的子程序就可能要去直接读写该类的内部数据。这样一来就把类给拆开了,把它从一个黑盒子变成了一个玻璃盒子,从而事实上消除了类的封装性。
同样关于,内聚性,这里也有比较好的诠释,搭车写一下:
内聚性要求接口类要实现抽象的一致性,方法要实现数据操作,存取等的一致性,我们写代码时,往往关注于快速实现和所谓的敏捷开发,而忘记了这个接口最初是用来干什么的,让接口变得臃肿不堪,难于维护,这实际上会让你后来的开发变得越来越不敏捷。因此良好的内聚性和接口的良好封装也应该成为我们的规范。