概述:
策略模式就是定义了一个对象在不同场景下的不同操作。比如:发票这个对象,会根据不同的类型对应不同的统计方式。
使用场景:
但凡有3个以上的if 出现的地方,都可以考虑使用策略模式来代替,当然要依据具体的业务场景,比如应用程序对数据库的操作,就四种(select insert update delete)像这样的场景我们没有必要为了用策略而用。 我们用设计模式的初衷是增强程序的健壮性、稳定性、扩展性。下面我们通过一个简单的案例具体说明。
组成:
抽象策略:通常是一个接口或者一个类
具体策略:集成抽象策略或者实现策略接口
环境角色:持有一个抽象策略的引用
实战:
业务场景:税务系统中,需要对不同类型的发票根据不同的规则做统计,然后按照不同的税率计算税款。
方案一:通过if else或者 switch 相信大家都会
/**
* @date 2020年9月4日17:50:37
* @author liujl
*/
public class InvoiceCount {
private static final List<String> INVOICE_TYPE = Arrays.asList("ZZS", "TXS", "HGS");
private static Object countByIf(List<?> invoices, String invoiceType) {
if (!INVOICE_TYPE.contains(invoiceType)) {
return null;
}
if (Objects.equals(INVOICE_TYPE.get(0), invoiceType)) {
System.out.println("按照增值税规则统计发票");
}
if (Objects.equals(INVOICE_TYPE.get(1), invoiceType)) {
System.out.println("按照通行税规则统计发票");
}
if (Objects.equals(INVOICE_TYPE.get(2), invoiceType)) {
System.out.println("按照海关税规则统计发票");
}
return null;
}
private static Object countBySwitch(List<?> invoices, String invoiceType) {
switch (invoiceType) {
case "ZZS":
System.out.println("按照增值税规则统计发票");
break;
case "TXS":
System.out.println("按照通行税规则统计发票");
break;
case "HGS":
System.out.println("按照海关税规则统计发票");
break;
default:
break;
}
return null;
}
public static void main(String[] args) {
List<?> invoices = new ArrayList<>();
for (String object : INVOICE_TYPE) {
countByIf(invoices, object);
}
for (String object : INVOICE_TYPE) {
countBySwitch(invoices, object);
}
}
}
方案二:使用策略模式,以下代码:
// 抽象策略
public interface InvoiceStrategy {
default Object count(List<?> invoices) {
// 通用的统计规则
System.out.println("通过通用的规则统计发票");
return null;
}
}
下面是三个具体策略:
// 增值税策略
public class ZzsInvoice implements InvoiceStrategy {
@Override
public Object count(List<?> invoices) {
System.out.println("按照增值税发票的规则进行统计");
return null;
}
}
// 海关税策略
public class HgsInvoice implements InvoiceStrategy {
@Override
public Object count(List<?> invoices) {
System.out.println("按照海关税发票的规则进行统计");
return null;
}
}
// 通行税,使用默认策略
public class TxsInvoice implements InvoiceStrategy {}
/**
* 环境角色
*/
public class InvoiceContext {
private InvoiceStrategy invoiceStrategy;
private List<?> invoices;
public InvoiceContext(InvoiceStrategy invoiceStrategy, List<?> invoices) {
this.invoiceStrategy = invoiceStrategy;
this.invoices = invoices;
}
public Object count() {
return this.invoiceStrategy.count(invoices);
}
/**
* 触发点
* @param args
*/
public static void main(String[] args) {
InvoiceContext zzs = new InvoiceContext(new ZzsInvoice(), new ArrayList<Object>());
zzs.count(); // 按照增值税发票的规则进行统计
InvoiceContext hgs = new InvoiceContext(new HgsInvoice(), new ArrayList<Object>());
hgs.count(); // 按照海关税发票的规则进行统计
InvoiceContext txs = new InvoiceContext(new TxsInvoice(), new ArrayList<Object>());
txs.count(); // 通过通用的规则统计发票
}
}
优缺点比对:
方案一 | 方案二 | |
文件数量 | 1 | n(具体策略的数量)+2 |
可读性 | 中 | 好 |
可维护性 | 差 | 好 |
扩展性 | 差 | 好 |
解读:当具体策略很少(小于5)且每个策略的代码量不大(小于30行)同时业务逻辑简单的时候,其实两种方案的区别看上去似乎不大。但是当其中一种策略的代码量达到数百行的时候,你会发现一个if条件里面会有很多的if else for switch。。。这时修改一种策略的代码相应的维护成本、测试成本、开发成本都是很高的,这事其一;其二:如果领导说我们又加了三种发票,如果我们采用方案一,只能再写第三个if或者三个case,而且在实际工作当中这三个if十有八九不是一个人写,这个时候还有代码冲突的成本, 而方案二只需要新建一个类继承或实现抽象策略即可;其三:后期维护成本高,当领导给你这样一个需求,你打开这个类一看,上万行代码,相信经历过的小伙伴们一定不会忘记当时的感受。