开闭原则(OCP):一个软件实体(类、模块、函数)都应该对扩展开放,对修改封闭。在软件的生命周期内,因为变化、升级和维护等原因都会对原有代码进行修改,可能给原有代码引入错误。此时应尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有代码来实现。看下面一个最简单的代码例子。
package com.haozz.demo.fee;
import org.springframework.stereotype.Service;
/**
* <p>Coding</p>
*
* @author haozz
* @version $Id: FeeCalculatorService.java, v 0.1 2019/1/8 21:36 haozz Exp $
*/
@Service
public class FeeCalculatorService {
/**
* @Author haozz
* @Date 2019/1/8 21:45
* @Param [type, count]
* @return double
* @Description 根据不同的用户身份,计算打折后的金额
*/
public double calculate(String type, double count){
if(type.equals("vip")){
return count * 0.8;
}else if(type.equals("normal")){
return count * 0.98;
}else {
return count * 0.99;
}
}
}
这是一个非常简单的根据不同的用户身份,计算消费金额的Service。但是这样的代码是不满足开闭原则的,因为如果某一种用户的打折方案需要修改,或者需要新增一种用户身份以及打折方案,就需要修改这里的计算方法。而一般的计算金额的方法逻辑都不会是上述代码那么简单,所以此时修改核心的计算代码是非常不可取的方法。为了使代码符合开闭原则的标准,我们先做如下的改进:
首先,新增一个折扣的interface:
package com.haozz.demo.fee;
/**
* <p>Coding</p>
*
* @author haozz
* @version $Id: DemoClass.java, v 0.1 2019/1/8 21:51 haozz Exp $
*/
public interface DiscountInterface {
String type();
double discount(double count);
}
然后新增不同的用户身份的折扣class,并实现上面的接口:
package com.haozz.demo.fee;
import org.springframework.stereotype.Service;
/**
* <p>Coding</p>
*
* @author haozz
* @version $Id: VipDiscount.java, v 0.1 2019/1/8 21:52 haozz Exp $
*/
@Service
public class VipDiscount implements DiscountInterface {
@Override
public String type() {
return "vip";
}
@Override
public double discount(double count) {
return count * 0.8;
}
}
package com.haozz.demo.fee;
import org.springframework.stereotype.Service;
/**
* <p>Coding</p>
*
* @author haozz
* @version $Id: NormalDiscount.java, v 0.1 2019/1/8 21:54 haozz Exp $
*/
@Service
public class NormalDiscount implements DiscountInterface{
@Override
public String type() {
return "normal";
}
@Override
public double discount(double count) {
return count * 0.98;
}
}
此时,在FeeCalculatorService类的计算方法中,只需做如下的修改即可:
package com.haozz.demo.fee;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
/**
* <p>Coding</p>
*
* @author haozz
* @version $Id: FeeCalculatorService.java, v 0.1 2019/1/8 21:36 haozz Exp $
*/
@Service
public class FeeCalculatorService {
//定义一个map,存储用户打折类
HashMap<String,DiscountInterface> map = new HashMap<>();
//类初始化的时候,通过Spring将所有的用户打折类注入到构造函数中
public FeeCalculatorService(List<DiscountInterface> list) {
for (DiscountInterface discountInterface : list) {
map.put(discountInterface.type(),discountInterface);
}
}
/**
* @return double
* @Author haozz
* @Date 2019/1/8 21:45
* @Param [type, count]
* @Description 根据不同的用户身份,计算打折后的金额
*/
public double calculate(String type, double count) {
return map.get(type).discount(count);
}
}
这样代码就改写完成了。这里我们使用了策略模式,首先我们为不同的用户身份定义了不同的打折方案类,并在discount方法进行计算打折后的金额。在FeeCalculatorService中,通过Spring将所有的DiscountInterface的实现类注入到构造方法的一个list中,因为我们定义的用户打折类都交给了Spring管理,所以Spring在初始化的时候是可以拿到这些类的。然后将他们放在一个定义好的HashMap中。当进行具体的计算方法是,只需要在map中获取指定用户身份的用户打折类,并执行其中的discount方法。这样一来,当需求变化需要修改某一用户打折方案,或者新增用户类型时,我们只需要修改或者新增对应的用户打折类,而不需要对计算方法本身进行修改。