策略模式就是定义了一个对象在不同场景下的不同操作。比如:发票这个对象,会根据不同的类型对应不同的统计方式。
但凡有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(); // 通过通用的规则统计发票 } }
解读:当具体策略很少(小于5)且每个策略的代码量不大(小于30行)同时业务逻辑简单的时候,其实两种方案的区别看上去似乎不大。但是当其中一种策略的代码量达到数百行的时候,你会发现一个if条件里面会有很多的if else for switch。。。这时修改一种策略的代码相应的维护成本、测试成本、开发成本都是很高的,这事其一;其二:如果领导说我们又加了三种发票,如果我们采用方案一,只能再写第三个if或者三个case,而且在实际工作当中这三个if十有八九不是一个人写,这个时候还有代码冲突的成本, 而方案二只需要新建一个类继承或实现抽象策略即可;其三:后期维护成本高,当领导给你这样一个需求,你打开这个类一看,上万行代码,相信经历过的小伙伴们一定不会忘记当时的感受。
