设计模式学习(二)——策略模式

策略模式定义

策略模式(Strategy Pattern)中定义一系列算法,并将每一个算法封装起来,使它们可以相互替换,策略模式让算法独立于使用它的客户而变化。

有许多算法可以实现同一功能,比如存在多种搜索算法、排序算法等。如果将这些算法硬编码在程序中,则会导致系统变得庞大而难以维护,在增加新的算法或改变现有算法时也将变得十分困难。为了解决类似问题,可以定义一些独立的类来封装不同的算法,每一个封装算法的类成为策略

策略模式把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式使用起来很方便,如需要提供一个灵活的网站搜索工具,可以有多种搜索策略,并且还会在以后根据实际情况增加新的搜索方式,就可以使用策略模式,创建一个搜索管理器用于和外部环境代码交互,在外部代码中针对抽象层编程,从而使得新的搜索方式出现时外部代码无需做任何改变,同时使得搜索算法的重用变得更为灵活。


策略模式结构图

图中:

  • Context表示环境类,它通过ConcreteStrategy对象配置其执行环境,并维护一个对Strategy的引用实例,可以定义一个接口供Strategy存取其数据;

  • Strategy表示抽象策略类,它定义一个公共的接口给所有支持的算法,Context可以使用这个接口调用ConcreteStrategy定义的算法;

  • ConcreteStrategyA和ConcreteStrategyB表示具体策略类,它们实现Strategy接口定义的算法。


策略模式实例——电影票打折

实例说明

某电影院售票系统为不同类型的用户提供了不同的打折方式(Discount),学生凭学生证可享受8折优惠(StudentDiscount),儿童可享受减免10元的优惠(ChildrenDiscount),VIP用户除享受半价优惠外还可以进行积分(VIPDiscount)。使用策略模式设计该系统,结合场景绘制相应的类图并编码实现。

实例类图

实例类图中与策略模式结构图对应的类主要有:

  • MovieTicket为环境类;

  • Discount为抽象策略类;

  • StudentDiscount、ChildrenDiscount和VIPDiscount为具体策略类。

实例代码

MovieTicket.java:

//电影票类:环境类
class MovieTicket
{

private double price;
private Discount discount;
public void setPrice(double price)
{

this.price = price;
}
public void setDiscount(Discount discount)
{

this.discount = discount;
}
public double getPrice()
{

return discount.calculate(this.price);
}
}

Discount.java:

//折扣类:抽象策略类
interface Discount
{
public double calculate(double price);
}

StudentDiscount.java:

//学生折扣类:具体策略类
class StudentDiscount implements Discount
{

public double calculate(double price)
{

System.out.print("学生-->");
return price * 0.8;
}
}

ChildrenDiscount.java:

//儿童折扣类:具体策略类
class ChildrenDiscount implements Discount
{

public double calculate(double price)
{

System.out.print("儿童-->");
return price - 10;
}
}

VIPDiscount.java:

//VIP用户折扣类:具体策略类
class VIPDiscount implements Discount
{
public double calculate(double price)
{

System.out.print("VIP用户-->");
System.out.println("增加积分!");
return price * 0.5;
}
}

Client.java:

//客户端测试类
class Client
{
public static void main(String args[])
{

double price = 50.00;
MovieTicket mt = new MovieTicket();
mt.setPrice(price);
double currentPrice;

System.out.println("电影票原价为:" + price + "元");
System.out.println("---------------------------------");

Discount obj;
obj = new StudentDiscount(); //可通过配置文件实现
mt.setDiscount(obj);
currentPrice = mt.getPrice();
System.out.println("折后票价为:" + currentPrice + "元");
System.out.println("---------------------------------");
obj = new VIPDiscount();
mt.setDiscount(obj);
currentPrice = mt.getPrice();
System.out.println("折后票价为" + currentPrice + "元");
}
}

运行结果

附加描述

在本实例中,可以通过配置文件来存储具体策略类的类名,再使用反射机制生产对象,如果需要更换具体策略或使用新增加的具体策略,无需修改任何源代码(包括客户端代码),只需修改配置文件即可,完全符合开闭原则。


后话

学习设计模式,是一场修行。