模式的秘密——策略模式
一、 什么是策略模式
策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
二、 实例说明
例一
设计一个书籍电子商务网站的购物车系统,最简单的情况就是把所有商品的单价乘上数量,但是实际情况比这要复杂。比如,系统可能对高级会员提供每本书20%的折扣;对中级会员提供每本书10%的折扣;对初级会员没有折扣。
根据描述,实付金额是根据以下几个策略进行计算的:
策略一:初级会员没有折扣
策略二:中级会员提供10%的折扣
策略三:高级会员提供20%的折扣
源码实现:
1、 会员折扣的抽象类MemberStrategy.java
public interfaceMemberStrategy {
/*
* 计算图书的价格
*
* @param booksPrice 图书的原价
*
* @return 计算出打折后的价格
*/
public doublecalcPrice(double booksPrice);
}
2、 初级会员折扣策略PrimaryMemberStrategy.java
public classPrimaryMemberStrategy implementsMemberStrategy{
public PrimaryMemberStrategy() {
// TODO Auto-generated constructor stub
}
@Override
public doublecalcPrice(double booksPrice) {
// TODO Auto-generated method stub
System.out.println("初级会员不享受折扣");
return booksPrice;
}
}
3、 中级会员的折扣策略InterMediateMemberStrategy.java
public classInterMediateMemberStrategy implementsMemberStrategy {
public InterMediateMemberStrategy() {
// TODO Auto-generated constructor stub
}
@Override
public doublecalcPrice(double booksPrice) {
// TODO Auto-generated method stub
System.out.println("中级会员享受10%的折扣");
return booksPrice * 0.9;
}
}
4、 高级会员的折扣策略AdvancedMemberStrategy.java
public classAdvancedMemberStrategy implementsMemberStrategy {
public AdvancedMemberStrategy() {
// TODO Auto-generated constructor stub
}
@Override
public doublecalcPrice(double booksPrice) {
// TODO Auto-generated method stub
System.out.println("高级会员享受20%的折扣");
return booksPrice * 0.8;
}
}
5、 价格计算类Price.java
public class Price {
// 一个具体的策略对象
private MemberStrategy strategy;
public Price(MemberStrategy strategy) {
this.strategy = strategy;
}
/*
* 计算图书的价格
*/
public double quote(double booksPrice) {
return this.strategy.calcPrice(booksPrice);
}
}
6、 客户端
public class Client {
public static void main(String[] args) {
// 选择并创建需要使用的策略对象
MemberStrategystrategy = null;
// strategy= new PrimaryMemberStrategy();
// strategy = new InterMediateMemberStrategy();
strategy = new AdvancedMemberStrategy();
Priceprice = new Price(strategy);
double quote = price.quote(300);
System.out.println("图书的实际价格为:" + quote);
}
}
运行截图:
由此我们可以看出,策略模式仅仅封装对问题的不同处理方式和不同的算法,提供新的算法插入到也有系统中,以及老算法从系统中“退休”的方法,策略模式并不决定何时使用何种算法。在什么情况下使用什么算法由客户端决定。
例二
这个例子来自慕课网关于策略模式的教学视频,讲的是关于鸭子能不能飞的问题。
1、 鸭子的抽象类Duck.java
public abstract class Duck {
/*
* 鸭子发出叫声通用行为,由超类实现
*/
public void quack() {
System.out.println("嘎嘎嘎");
}
/*
* 显示鸭子的外观鸭子的外观各不相同,声明为abstract,由子类实现
*/
public abstract void display();
private FlyingStrategy flyingStrategy;
public FlyingStrategy getFlyingStrategy() {
return flyingStrategy;
}
public voidsetFlyingStrategy(FlyingStrategy flyingStrategy) {
this.flyingStrategy = flyingStrategy;
}
public void fly(){
flyingStrategy.performFly();
}
}
2、 鸭子的飞行策略接口FlyingStrategy.java
/*
* 策略接口,实现鸭子的飞行行为
*/
public interfaceFlyingStrategy {
void performFly();
}
3、 不能飞行的策略FlyNoWay.java
public class FlyNoWay implements FlyingStrategy {
@Override
public void performFly() {
// TODO Auto-generated method stub
System.out.println("我不会飞行");
}
}
4、 使用翅膀飞行策略FlyWithWin.java
public class FlyWithWin implements FlyingStrategy {
@Override
public void performFly() {
// TODO Auto-generated method stub
System.out.println("振翅高飞");
}
}
5、 活物红头鸭RedHeadDuck.java
public class RedheadDuck extends Duck {
public RedheadDuck() {
super();
super.setFlyingStrategy(new FlyWithWin());
// TODO Auto-generated constructor stub
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("我的头是红色的");
}
}
6、 玩具塑料鸭RubberDuck.java
public class RubberDuck extends Duck {
public RubberDuck() {
// TODO Auto-generated constructor stub
super();
super.setFlyingStrategy(new FlyNoWay());
}
@Override
public void display() {
// TODO Auto-generated method stub
System.out.println("我是塑料做的");
}
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("嘎~嘎~嘎~");
}
}
7、 测试
public class DuckTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("测试鸭子的程序");
Duckduck = null;
// duck= new RedheadDuck();
duck = new RubberDuck();
duck.display();
duck.quack();
duck.fly();
System.out.println("测试完毕");
}
}
8、 运行结果
我们在实现鸭子飞行的时候可以很容易想到的方法如下:
方法一:继承
在父类中提供一个实现方法,子类通过继承获得父类中的飞行行为。这种方法充分地利用了面向对象的思想,简单总结一下这种方式的优缺点。
优点:简单易用,已有应用可以快速添加飞行的能力
缺点:不具有灵活性,对未来变更支持差。需要通过在子类中覆写飞行的方法以提供飞行的行为。这很容易造成错误(粗心的程序员容易忘记覆写)。
方法二:抽象方法
在父类中提供抽象方法,强迫子类实现自己的飞行行为。这种方法的优缺点如下。
优点:足够灵活,小伙伴再也不用担心忘记覆写代码了。
缺点:工作量大,每个子类都要实现一遍代码,即使是相同的行为也不例外。
三、 策略模式的优点
1、 使用了组合,使架构更加灵活
2、 富有弹性,可以较好的应对变化(开——闭原则)
3、 更好的代码复用性(相对于继承)
4、 消除大量的条件语句
四、 策略模式的缺点
1、 客户代码需要了解每个策略实现的细节
2、 增加了对象的数目
五、 适用场景
1、 许多相关的类仅仅是行为差异
2、 运行时选取不同的算法变体
3、 通过条件语句在多个分支中选取一个