一、介绍
在软件开发中也常常遇到这样的情况:实现某一个功能可以有多种算法或者策略,我们根据实际情况选择不同的算法或者策略来完成该功能。例如,排序算法,可以使用插入排序、归并排序、冒泡排序等。
针对这种情况,一种常规的方法是将多种算法写在一个类中。例如,需要提供多种排序算法,可以将这些算法写到一个类中,每一个方法对应一个具体的排序算法:当然,也可以将这些排序算法封装在一个统一的方法中,通过if…else…或者case等条件判断语句来选择具体的算法。这两种实现方法我们都可以称为硬编码。然而,当很多个算法集中在一个类中时,这个类就会变得臃肿,这个类的维护成本会变高,在维护时也更容易引发错误。如果我们需要增加一种新的排序算法,需要修改封装算法类的源代码。这就明显违反了OCP原则和单一职责原则。
如果将这些算法或者策略抽象出来,提供一个统一的接口,不同的算法或者策略有不同的实现类,这样在程序客户端就可以通过注入不同的实现对象来实现算法或者策略的动态替换,这种模式的可扩展性、可维护性也就更高,也就是我们本节要说的策略模式。
简单来说就是,通常如果一个问题有多个解决方案时,最简单的就是利用if-else或者switch-case方式根据不同的情景选择不同的解决方案,但是这样耦合性太高 、代码臃肿、难以维护等。这时就可以使用策略模式来解决。
二、定义
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
三、使用场景
针对同一类型问题的多种处理方式,仅仅是具体行为有差别时
需要安全的封装多种同一类型的操作时
出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体子类时
四、策略模式的UML类图
UML类图:
Context:用来操作策略的上下文环境。
Stragety:策略的抽象。
ConcreteStrategyA、ConcreteStrategyB:具体的策略实现。
五、简单实现
需求:计算图书价格,初级会员没有折扣,中级会员打9折,高级会员打8折。如果一般写法,应该是if-else判断他是什么级别的会员,在计算相应的折扣。下面使用策略模式来进行实现。
抽象折扣类:
public interface MemberStrategy {/*** 计算图书的价格* @param booksPrice 图书的原价* @return 计算出打折后的价格*/public double calcPrice(double booksPrice);
}
初级会员折扣类:
public class PrimaryMemberStrategy implements MemberStrategy{/*** 初级会员折扣*/@Overridepublic double calcPrice(double booksPrice) {System.out.println("对于初级会员的没有折扣");return booksPrice;}
}
中级会员折扣类:
public class IntermediateMemberStrategy implements MemberStrategy{/*** 中级会员折扣*/@Overridepublic double calcPrice(double booksPrice) {System.out.println("对于中级会员的折扣为10%");return booksPrice * 0.9;}
}
高级会员折扣类:
public class AdvancedMemberStrategy implements MemberStrategy{/*** 高级会员折扣*/@Overridepublic double calcPrice(double booksPrice) {System.out.println("对于高级会员的折扣为20%");return booksPrice * 0.8;}
}
价格类:
public class Price {//持有一个具体的策略对象private MemberStrategy strategy;/*** 构造函数,传入一个具体的策略对象* @param strategy 具体的策略对象*/public Price(MemberStrategy strategy){this.strategy = strategy;}/*** 计算图书的价格* @param booksPrice 图书的原价* @return 计算出打折后的价格*/public double quote(double booksPrice){return this.strategy.calcPrice(booksPrice);}
}
客户端:
public class Client {public static void main(String[] args) {//选择并创建需要使用的策略对象MemberStrategy strategy1 = new AdvancedMemberStrategy();//创建环境Price price = new Price(strategy1);//计算价格double quote = price.quote(300);System.out.println("图书的最终价格为:" + quote);}}
结果:
对于高级会员的折扣为20%
图书的最终价格为:240.0
六、策略模式和工厂模式的区别
七、Android源码中的策略模式实现
随着技术的发展,工程师们已经越来越重视用户体验、用户交互。因此,动画成了很多应用中必不可少的部分,一个简单的引导页面也要做成动画的效果,一个按钮的隐藏也需要加入淡入淡出的动画效果。动画的实现原理就是在短时间内快速的进行画面切换,这个切换频率需要达到人眼感觉不出卡顿,例如标准的电影是24帧/秒。在比较流畅时,Android上的动画能够达到60帧/秒,人眼基本看不出间隔,所以,在我们看到这个动画就非常流畅。
单纯是动画还不足以满足我们的需求,在动画执行的过程中,我们还需要一些动态效果,这有点类似于电影的慢镜头,有时候我们需要它慢一点,有时候需要快一点,这样动画也变得灵动起来。这些动态效果就是通过插值器(TimeInterpolator)实现的,我们只需要对Animation对象设置不同的插值器就可以实现不同的动态效果。
LinearInterpolator、AccelerateInterpolator、CycleInterpolator等实现Interpolator,通过getInterpolator(float input)获取当前的时间百分比,以此来计算动画的属性值。
八、总结
策略模式主要用来分离算法,在相同的行为抽象下有不同的具体实现策略。这个模式很好地演示了开闭原则,也就是定义抽象,注入不同的实现,从而达到很好的可扩展性。
优点:
结构清晰明了、使用简单直观。
耦合度相对而言较低,扩展方便。
操作封装也更为彻底,数据更为安全。
缺点:
- 随着策略的增加,子类也会变得繁多。