策略模式(Strategy Pattern)
策略模式(Strategy Pattern)
策略模式的定义:
策略模式(Strategy Pattern)也叫做政策模式(Policy Pattern)其定义:定义一组算法,将他们封装起来,使它们可以相互替换。
策略模式的优点:
1. 算法直接可以相互替换。这是因为策略都实现策略接口。
2. 可以避免多重条件的情况出现。假设一个策略家族有N个成员,当一会需要策略A,另一会需要策略B,不使用策略模式的话,只能使用ifelse或switch语句实现,但是这样的程序不容易维护,可读性也比较差。使用策略模式后可以由其他模块确定策略。
3. 有良好的扩展性。增加一个策略,只需要实现策略接口,就这么简单。
策略模式的缺点:
1. 子类膨胀,一个策略一个类,策略类的数量比较惊人。
2. 调用者必须知道所有具体策略的存在(调用一个策略还需要自己new出来)。
策略模式的应用场景:
1. 多个类只在算法或行为稍有不同(在抽象概念上是相同的行为)。
2. 算法需要自由切换。
策略模式的通用类图:
Strategy(抽象策略角色):定义了每个算法必须有的算法和属性。
StrategyA(具体策略角色):实现了抽象策略定义的接口。
Context(封装角色):将策略封装起来,屏蔽了调用者直接访问具体策略。
策略模式的通用代码:
抽象策略角色:
public interface Strategy
{
public void operation();
}
具体策略角色:
public class StrategyA implements Strategy
{
@Override
public void operation()
{
System.out.println("strategyA");
}
}
封装角色:
public class Context
{
private Strategy strategy = null;
public Context(Strategy strategy)
{
this.strategy = strategy;
}
/*实现策略可以相互替换*/
public void replaceStrategy(Strategy strategy)
{
this.strategy = strategy;
}
/*封装角色提供接口,让调用使用策略,屏蔽高层模块直接使用策略*/
public void execute()
{
this.strategy.operation();
}
}
场景类:
public class Client
{
public static void main(String[] args)
{
/*这里可以看出策略模式的缺点,调用者必须要知道调用的策略(自己new出来StrategyA的对象)*/
Context context = new Context(new StrategyA());
context.execute();
/*可以实现自由切换策略*/
context.replaceStrategy(new StrategyB());
context.execute();
}
}
举个比较简单的例子理解策略模式
例子:"实现一个可以计算两个整数加减乘除计算的功能"。
实现一:不使用策略模式
class Calculator
{
/*代表加减乘除的字符串,用于判断使用什么计算策略*/
private final static String ADD_SYMBOL = "+";
private final static String SUB_SYMBOL = "-";
private final static String MUL_SYMBOL = "*";
private final static String DIV_SYMBOL = "/";
/*策略选择函数,operator字符串参数用于判断,a、b两个参数使用的计算策略*/
public int execute(int a,int b,String operator)
{
int result = 0;
if(ADD_SYMBOL.equals(operator))
{
result = this.add(a, b);
}
else if(SUB_SYMBOL.equals(operator))
{
result = this.sub(a, b);
}
else if(MUL_SYMBOL.equals(operator))
{
result = this.multi(a, b);
}
else if(DIV_SYMBOL.equals(operator))
{
result = this.div(a, b);
}
return result;
}
/*加法功能实现*/
private int add(int a,int b)
{
return a+b;
}
/*减法功能实现*/
private int sub(int a,int b)
{
return a-b;
}
/*乘法功能实现*/
private int multi(int a,int b)
{
return a*b;
}
/*除法功能实现*/
private int div(int a,int b)
{
//只是为了说明问题,除数为0就不考虑了
return a/b;
}
}
这个实现比较容易想到,通过"operator"这个参数决定使用什么计算策略。这样的实现看上去完美的处理了需求,还是有很多需要改进的地方。首先,扩展性很差——若要添加一个求余的需求,除了改源代码旗本没其他办法了,添加一个需求改一次,这样还了得。其次,可读性不行,别看这个程序看上去还比较清晰,要是考虑上异常、"operator"检查,功能变多后等等就不顺眼了。最后,这个类职责不清晰,又要选择策略又要实现策略。
实现二:使用策略模式
抽象策略角色(定义了每个策略都有的接口):
public interface IStrategy
{
public int doAction(int a, int b);
}
具体策略角色(实现抽象策略):
/*实现加法策略*/
public class AddStrategy implements IStrategy
{
@Override
public int doAction(int a,int b)
{
return a+b;
}
}
/*实现除法策略*/
public class DivStrategy implements IStrategy
{
@Override
public int doAction(int a, int b)
{
return a/b;
}
}
/*实现乘法策略*/
public class MultiStrategy implements IStrategy
{
@Override
public int doAction(int a, int b)
{
return a*b;
}
}
/*实现减法策略*/
public class SubStrategy implements IStrategy
{
@Override
public int doAction(int a, int b)
{
return a-b;
}
}
封装角色:
public class StrategyContext
{
private IStrategy strategy = null;
public StrategyContext(IStrategy strategy)
{
this.strategy = strategy;
}
public void replaceStrategy(IStrategy strategy)
{
this.strategy = strategy;
}
public int doAction(int a,int b)
{
return strategy.doAction(a, b);
}
}
场景类:
public class Client
{
public static void main(String[] args)
{
/*计算加法,策略模式自身缺点,必须知道具体策略(自己new出来AddStrategy对象)*/
StrategyContext context = new StrategyContext(new AddStrategy());
int result = context.doAction(2, 2);
System.out.println("result : "+result);
}
}
这是按照常规的策略模式实现加减乘除功能的。这样的实现基本上解决了第一种实现方式的缺点。增加一个功能,只需要实现接口 "IStrategy",扩展非常简单便捷。if判断语句消失了,可读性大大提高。每个类的职责现在更清晰了。尽管如此,这个实现还是有一个非常大缺点,这个缺点是该模式自身的缺点,就是调用者必须知道具体策略(这个缺点可以使用混合模式解决)。
实现三:枚举策略模式
enum EnumStrategy
{
/*加法策略的实现*/
ADD{
@Override
public int execute(int a, int b)
{
return a+b;
}
},
/*减法策略的实现*/
SUB{
@Override
public int execute(int a, int b)
{
return a-b;
}
},
/*乘法策略的实现*/
MUL{
@Override
public int execute(int a, int b)
{
return a*b;
}
},
/*除法策略的实现*/
DIV{
@Override
public int execute(int a, int b)
{
return a/b;
}
};
abstract public int execute(int a,int b);
}
来看看场景类:
public class EnumStrategyClient
{
public static void main(String[] args)
{
System.out.println(EnumStrategy.ADD.execute(2, 2));
}
}
这种变形的策略模式真是太那个了(省略各种赞叹)....太精妙了,不得不佩服那些牛人,这样的实现,让可读性提高到最高点了,一眼就能看明白。连调用都如此简单(你说更看不懂了-_-!!那不是可读性的问题,是你不理解枚举,每一个枚举值其实是这个枚举类型的一个实例,它默认的前缀是public final static的,他其实和类差不多,只不过编译器为我们做了许多事情)。它扩展性没有实现二好,这是受限于enum类型,所以这种变形可以运用在策略不易改变的地方。