1 策略模式概念
1.1 介绍
通常如果一个问题有多个解决方案时,最简单的就是利用if-else或者switch-case方式根据不同的情景选择不同的解决方案,但是这样耦合性太高 、代码臃肿、难以维护等。
如果将这些算法或者策略抽象出来,提供一个统一的接口,不同的算法或者策略有不同的实现类,这样在程序客户端就可以通过注入不同的实现对象来实现算法或者策略的动态替换,这种模式的可扩展性、可维护性也就更高。也就是本章的策略模式。
1.2 定义
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
1.3 使用场景
(1)针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。
(2)需要安全地封装多种同一类型的操作时。
(3)出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体子类时。
1.4 重点
策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
2 策略模式UML类图通用
(1)环境(Context)角色 :持有一个Strategy的引用。
(2)抽象策略(Strategy)角色 :这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
(3)具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
3 策略模式通用模板
(1)抽象的策略角色
public interface Strategy {
public void doSomething();
}
(2)具体策略角色
public class ConcreteStrategyA implements Strategy {
public void doSomething() {
System.out.println("具体策略A的运算法则");
}
}
public class ConcreteStrategyB implements Strategy {
public void doSomething() {
System.out.println("具体策略B的运算法则");
}
}
(3)封装角色
public class Context {
private Strategy strategy = null; // 构造函数设置具体策略
public Context(Strategy _strategy) {
this.strategy = _strategy;
}
// 封装后的策略方法
public void doAnythinig() {
this.strategy.doSomething();
}
}
(4)高层模块
public class Client {
public static void main(String[] args) {
// 声明一个具体的策略
Strategy strategy = new ConcreteStrategy1();
// 声明上下文对象
Context context = new Context(strategy);
// 执行封装后的方法
context.doAnythinig();
}
}
(5)小总结
基本策略模式就是这么简单,偷着乐吧,它就是采用了面向对象的继承和多态机制。
4 策略模式简单实现
需求:计算图书价格,初级会员没有折扣,中级会员打9折,高级会员打8折。如果一般写法,应该是if-else判断他是什么级别的会员,在计算相应的折扣。下面使用策略模式来进行实现。
得知算法;
算法一:对初级会员没有折扣。
算法二:对中级会员提供10%的促销折扣。
算法三:对高级会员提供20%的促销折扣。
(1)抽象折扣类
public interface MemberStrategy {
/**
* 计算图书的价格
* @param booksPrice 图书的原价
* @return 计算出打折后的价格
*/
public double calcPrice(double booksPrice);
}
(2)初级会员折扣类
public class PrimaryMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于初级会员的没有折扣");
return booksPrice;
}
}
(3)中级会员折扣类
public class IntermediateMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于中级会员的折扣为10%");
return booksPrice * 0.9;
}
}
(4)高级会员折扣类
public class AdvancedMemberStrategy implements MemberStrategy{
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于高级会员的折扣为20%");
return booksPrice * 0.8;
}
}
(5)价格类
public class Price {
// 持有一个具体的策略对象
private MemberStrategy strategy;
/**
* 构造函数,传入一个具体的策略对象
*/
public Price(MemberStrategy strategy) {
this.strategy = strategy;
}
/**
* 计算图书的价格
* @param booksPrice 图书的原价
* @return 计算出打折后的价格
*/
public double quote(double booksPrice) {
return this.strategy.calcPrice(booksPrice);
}
}
(6)客户端
public class Client {
public static void main(String[] args) {
MemberStrategy strategy = new IntermediateMemberStrategy();// 选择并创建需要使用的策略对象
Price price = new Price(strategy);// 创建环境
double quote = price.quote(300);// 计算价格
System.out.println("图书的最终价格为:" + quote);
System.out.println("-----------------------------");
MemberStrategy strategy2 = new AdvancedMemberStrategy();
Price price2 = new Price(strategy2);
double quote2 = price2.quote(300);
System.out.println("图书的最终价格为:" + quote2);
}
}
(7)结果
5 Android源码中的策略模式
(1)时间插值器(TimeInterpolator)
LinearInterpolator、AccelerateInterpolator、CycleInterpolator等实现Interpolator,通过getInterpolator(float input)获取当前的时间百分比,以此来计算动画的属性值。
6 策略模式和工厂模式的区别
7 总结
7.1 优点
(1)策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
(2)使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法让子类实现
7.2 缺点
(1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
(2)由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。
8 参考文章与链接
《Android源码设计模式解析与实战》
《设计模式之禅》