java策略模式去掉冗余if-else支付案例实战
前言
上一篇文章介绍了 java中冗余if-else代码块的优化(策略模式法,反射法)
,里面详细介绍了策略模式+工厂模式来解决代码中大量的if-else结构。
这一篇就举个实战例来说明,怎么使用策略模式。
1.支付案例需求
假设你在做的某个项目,需求就是页面选择不同的付款渠道,计算不同的折扣,进行扣款。
看到这个图片,有小伙伴在遇到诸如此类的需求的时候,想到的会是,很简单,if else 判段下就可以了嘛,if else 确实可以解决我们的问题,但是弊端也很明显:
- 代码冗长
- 维护不便
谁叫我们学习过了策略模式+工厂模式来解决代码中大量的if-else结构呢?
2.策略设计模式干掉if-else(通用写法)
策略模式+工厂模式解决代码中大量的if-else结构的(通用写法),以后遇到多条件的业务需求,就直接可以复用了。
文件目录:
1.定义充值支付的枚举类
RechargeTypeEnum.java 源码如下:
package PayDemo;
/**
* @Description: 1.定义枚举类型
* @Author: Zoutao
* @Date: 2020/4/10
*/
public enum RechargeTypeEnum {
/**
* 枚举类型,根据业务需求增删
*/
ABC_BANK(1,"农业银行"),
ICBC_BANK(2,"工商银行"),
/*根据自己到底业务需求增加个数*/
CCB_BANK(3,"建设银行"),
CMB_BANK(4,"招商银行"),;
/**
* 状态值
*/
private int code;
/**
* 类型描述
*/
private String description;
RechargeTypeEnum(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
// 根据code获取对应的枚举类型
public static RechargeTypeEnum getPayTypeEnum(int code) {
for (RechargeTypeEnum calcTypeEnum : RechargeTypeEnum.values()) {
if (calcTypeEnum.getCode() == code) {
return calcTypeEnum;
}
}
System.out.println("对应支付计算策略不存在,[type={\"+code+\"}]");
return null;
}
}
2.定义支付的计算策略接口类
PayStrategy.java:
package PayDemo;
/**
* @Description: 2.定义支付的策略接口类
* @Author: Zoutao
* @Date: 2020/4/10
*/
public interface PayStrategy {
/**
* 策略行为方法:定义根据金额进行结算的方法
* @param charge
* @param type
* @return
*/
public Double calRecharge(Double charge ,RechargeTypeEnum type);
}
3.定义多个结算渠道具体的结算实现类
创建实现类的数量视自己的业务需求而定。
(针对每一类情况来分别实现上面接口,不是继承,是实现!!!
所以要在实现的方法中写好具体的计算规则代码。)
ABCPayStrategy.java
ICBCPayStrategy.java
CCBPayStrategy.java
…
比如我们写一个农业银行的,一个工商银行的。
ABCPayStrategy.java代码如下:
package PayDemo;
/**
* @Description: 具体有多少业务实现类,视自身业务而定
* @Author: Zoutao
* @Date: 2020/4/10
*/
public class ABCPayStrategy implements PayStrategy {
/**
* 农业银行的结算实现类
* 优惠98折
* @param charge 金额
* @param type 支付类型
* @return
*/
@Override
public Double calRecharge(Double charge, RechargeTypeEnum type) {
return charge*0.98;
}
}
ICBCPayStrategy.java代码如下:
package PayDemo;
/**
* @Description:
* @Author: Zoutao
* @Date: 2020/4/10
*/
public class ICBCPayStrategy implements PayStrategy{
/**
* 这边就是具体业务代码的实现方法
* 工商银行的结算实现类
* 收取5元手续费
* @return
*/
@Override
public Double calRecharge(Double charge, RechargeTypeEnum type) {
return charge+5;
}
}
…
实现类的数量视自己业务而定。
4.策略上下文类PayContext.java
作用就是根据类型路由到具体的实现类。
其中,创建接口的对象。
调用工厂类的getInstance和creator方法来路由到具体实现方法中。
package PayDemo;
/**
* @Description: 4.策略上下文,作用就是根据类型路由到具体的实现类
* @Author: Zoutao
* @Date: 2020/4/10
*/
public class PayContext {
// 创建接口的对象
private PayStrategy strategy;
public Double calRecharge(Double charge, Integer type) {
try{
strategy= StrategyFactory.getInstance().creator(type); // 调用工厂类的getInstance和creator方法
}catch (Exception e){
//log
System.out.println("程序出错了!");
}
return strategy.calRecharge(charge,RechargeTypeEnum.getPayTypeEnum(type));
}
public PayStrategy getStrategy() {
return strategy;
}
public void setStrategy(PayStrategy strategy) {
this.strategy = strategy;
}
}
5.建立工程模式类StrategyFactory.java,
工厂模式来批量创建我们需要的类型的对象。new ABCPayStrategy(),new ICBCPayStrategy()。。。
package PayDemo;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 工厂模式-根据类型,把具体的实现类对象创建出来
* @Author: Zoutao
* @Date: 2020/4/10
*/
public class StrategyFactory {
//生成工厂对象
private static StrategyFactory factory = new StrategyFactory();
private StrategyFactory(){
}
private static Map<Integer ,PayStrategy> strategyMap = new HashMap<>();
static{
//通过put或注解的方式把对象加载到map中
strategyMap.put(RechargeTypeEnum.ABC_BANK.getCode(), new ABCPayStrategy());
strategyMap.put(RechargeTypeEnum.ICBC_BANK.getCode(), new ICBCPayStrategy());
strategyMap.put(RechargeTypeEnum.CCB_BANK.getCode(), new CCBPayStrategy());
/* 自行增加业务需求
strategyMap.put(RechargeTypeEnum.CMB_BANK.getCode(), new CardStrategy());...
*/
}
//工厂模式来创建我们需要的对象
public PayStrategy creator(Integer type){
return strategyMap.get(type);
}
//返回工厂对象
public static StrategyFactory getInstance(){
return factory;
}
}
6.接口的测试类PayTest.java,
必须创建上下文对象,调用接口方法calRecharge计算策略,得到对应的需求结果。
package PayDemo;
/**
* @Description: 上下文对象来调用对应的支付方式测试
* @Author: Zoutao
* @Date: 2020/4/10
*/
public class PayTest {
public static void main(String[] args) {
// 创建上下文对象来调用对应的支付方式
PayContext context = new PayContext();
// 农业充值100 需要付多少
Double money = context.calRecharge(100D,
RechargeTypeEnum.ABC_BANK.getCode());
System.out.println(money);
// 工商充值100 需要付多少
Double money2 = context.calRecharge(100D,
RechargeTypeEnum.ICBC_BANK.getCode());
System.out.println(money2);
// 新增-建设银行充值方式
Double money3 = context.calRecharge(100D,
RechargeTypeEnum.CCB_BANK.getCode());
System.out.println(money3);
}
}
以上就完成了一个策略模式+工厂模式的通用代码结构,在文章解决掉了所有的if-else结构,优化了代码,有利于后期的维护。
7.如果要新增一个新功能业务?
–以新建一个建设银行支付渠道为例。
(只需要新增代码而不需要修改原代码。这样设计程序的好处就体现出来了。)
a.首先在枚举类RechargeTypeEnum中新增类型和描述。
CCB_BANK(3,“建设银行”),
b.在增一个对应接口实现类CCBPayStrategy。
定义具体的接口方法内容。
package PayDemo;
public class CCBPayStrategy implements PayStrategy {
/**
* 建设银行实现类
* 优惠96折
* @param charge
* @param type
* @return
*/
@Override
public Double calRecharge(Double charge, RechargeTypeEnum type) {
return charge * 0.96;
}
}
c.然后在工厂类StrategyFactory当中添加,生成对应的类对象和枚举类型。
strategyMap.put(RechargeTypeEnum.CCB_BANK.getCode(), new CCBPayStrategy());
d.最终在PayTest调用即可。
// 新增-建设银行充值方式
Double money3 = context.calRecharge(100D,
RechargeTypeEnum.CCB_BANK.getCode());
System.out.println(money3);
结果:
至此我们业务功能就完成了,可能大家会觉得,设计模式的引入反而造成了类太多,还不如直接if else写在一个文件里的直观。
但是,对于后期的程序开发中,如果要新增一个付款渠道,使用策略模式的方式将变得很简单,只要定义一个具体的实现类,加上对应的工厂对象调用就解决了,根本不需要去阅读之前别人写的其他支付渠道的代码,松耦合的目的就在于此。
参考地址:
注解版:https://zhuanlan.zhihu.com/p/33383648
https://www.iteye.com/blog/alaric-1920714
https://blog.csdn.net/u014395955/article/details/103804479
https://www.toutiao.com/i6813584351410782723/