拿《大话设计模式》里面的一个例子说事儿,某公司要开发一套工资管理系统,负责公司人员的工资统计、计算、发放。不同职位类型的工资计算方式显然不一样,比如,销售(基本工资+项目提成),行政(基本工资+绩效+全勤),经理(基本工资+项目提成+电话补+饭补+交通补+绩效+全勤+啪啪啪补),it屌丝儿(基本工资-bug扣款)等等;普通的做法是做一个工资发放类salary,根据员工编号和员工类型计算工资并返回,然后进行工资的发放(银行卡、现金?),类中有一个很长很长的if else条件判断语句,针对每一类员工设计一个函数实现工资计算。嗯,目前的需求搞定了,可以测试发布了。
一个月后公司业务拓展,增加了一个职位:技术顾问;由于之前没有这种职位的工资算法,所以要动salary类,在类里新建一个算法函数,if else 再变长一些。升级发布;一个月后,老板招了一个女秘书,负责其日常工作作息的安排,所以继续改salary类;一个月后,老板又招了一个女秘书,负责……呵呵;一个月后…………;你就改吧,salary类越来越复杂;一个月后,来了一个临时工,专门负责填坑,本想奉上级指示让他给老板的女秘书重新设定工资算法,但是他看了salary类后义愤填膺:这个公司的研发都是吃粑粑的吗?怎么挣得这么少,不行,我要给他们加工资,于是女秘书的工资算法没动,把我们的基本工资*2,然后走人了。嘿嘿,好爽!
这种salary类虽然对工资算法进行了封装,但是不方便维护,第一,工资发放类其实不用管工资是怎么计算的,只管如何发放就可以了(银行卡、现金?),更不应该了解工资计算的具体细节(相关数据,结构体等);第二,在某些场景中,每种算法不应该都放在一起,比如维护研发人员工资算法的人员不应该看到老板女秘书的工资是如何构成的,更不应该有修改的可能;这种封装方式也不方便扩展,随着算法的增加,salary类会越来越复杂,越来越不好维护。
基于这种情况,工资计算功能要进一步封装,我们声明一个接口IStrategy,用来规范“计算工资”这个功能的调用形式标准(virtual int calculateSalary(int positionType) = 0;),然后将此功能的所有算法函数封装成一个一个的类,并全部实现了此接口,然后再定义一个上下文类(Context)使用IStrategy接口指针维护一个实际算法类的引用,从而可以配置或调用此实际算法类实现的接口。而工资发放类只需要告诉Context具体职工编号和类型就能使用Context实现的函数获取工资,从而减少工资计算算法与使用者(工资发放类)间的耦合,减少每个工资计算算法间的耦合,如果新增加一个职位需要添加新的算法,我们只需要根据接口标准实现一个新的算法类放到Context类里,最多加个if else 判断就好了。这种模式就叫做策略模式!
策略模式将一批具有相同调用形式的算法或策略行为的复杂实现进行封装,实现算法的实现和算法的使用分离,使得使用者可以根据不同的场景选择使用不同的算法或执行不同的策略行为。
一、特征
1、相同的调用形式:这些需要封装的算法或策略应具有相同的调用形式,也就是它们应该遵循相同的接口约束,这样可以方便动态增加新的类;
2、算法的实现和算法的使用分离:添加上下文类以及接口类,使得算法间的解耦,使得算法的实现和算法的使用解耦;
二、实现
1. IStrategy:给出一个抽象接口,以规范所有算法或者策略行为的调用方式;
2. ConcreteStrategy:实现IStrategy接口,使其遵循IStrategy接口规范,实现具体某种算法或某种策略行为。
3. Context:维护某个具体算法或策略的引用,以抽象接口规范使用,返回算法返回的结果。
4. Client:具体使用Context的使用者。