策略模式是一种行为型设计模式。策略模式的思想是:将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
策略模式涉及到3个角色:
- 环境(Context):持有一个抽象策略的引用,是策略的调用者。
- 抽象策略(Strategy):所有具体策略的抽象。
- 具体策略(Concrete Strategy):封装了算法和行为的具体策略。
结构图:
具体代码实现:
策略:
public interface Strategy {
void method();
}
public class ConcreteStrategyA implements Strategy {
@Override
public void method() {
System.out.println("method A");
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void method() {
System.out.println("method B");
}
}
环境:
public class Context {
private Strategy strategy; // 持有一个抽象策略的引用
public void method() {
strategy.method();
}
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
}
// 测试
class StrategyTest {
public static void main(String[] args) {
Context context = new Context();
context.setStrategy(new ConcreteStrategyA());
context.method();
context.setStrategy(new ConcreteStrategyB());
context.method();
}
}
运行结果:
method A
method B
可以看出,策略模式仅仅封装算法,并不决定在何时使用何种算法,在什么情况下使用什么算法是由客户端决定的。策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。
策略模式一个很大的特点就是各个具体策略的平等性。对于一系列具体的策略,大家的地位是完全一样的,正因为这个平等性,才能实现算法之间可以相互替换。所有的具体策略在实现上也是相互独立的,相互之间是没有依赖的。所以可以说,具体策略是相同行为的不同实现。
策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族(如下图)。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
(图片来源于网络)
策略模式只适用于客户知道算法或行为的情况。客户必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户必须理解这些算法的区别,以便适时选择恰当的算法类。
另外,由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么策略类的数量会随之增多。
下面再提供一个例子。某门店账单会根据客户是否会员进行打折。可以体会一下这里的抽象类和接口的区别,实际开发中这种情况出现的机率还是比较高的。
// 抽象策略
public interface Bill {
double cost();
}
// 具体策略的公共部分
public abstract class AbstractBill {
protected double getTotal() {
return 1000; // 模拟一个总价
}
}
// 非会员账单的具体算法
public class CommonBill extends AbstractBill implements Bill {
@Override
public double cost() {
return getTotal(); // 原价
}
}
// 会员账单的具体算法
public class VipBill extends AbstractBill implements Bill {
@Override
public double cost() {
return getTotal()*0.88; // 88折
}
}
public class Counter {
private Bill bill; // 持有一个抽象策略的引用
public Counter(Bill bill) {
this.bill = bill;
}
public void showBill() {
System.out.printf("the final bill cost %f\n", bill.cost());
}
}
// 测试
class CounterTest {
public static void main(String[] args) {
// 模拟会员结账
Counter counter = new Counter(new VipBill());
counter.showBill();
}
}
运行结果:
the final bill cost 880.000000
结构图: