假设现在要实现一个简化的报价管理,实现如下的功能:
1. 对于普通客户或者新客户报全价
2. 对于老客户报的价格,统一折扣5%
3. 对于大客户报的价格,统一折扣10%
对不同的人员报不同的价格
到底该如何实现,才能够让价格类中的计算报价的算法,能很容易地实现可维护,可扩展,又能动态地切换变化呢?
策略模式的定义:
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换,使得算法可独立于使用它的客户而变化
Strategy:策略接口,用来约束一系列具体的策略算法,Context使用这个接口来调用具体的策略实现定义的算法
ConcreteStrategy:具体的策略实现,也就是具体的算法实现
Context:上下文,负责和具体的策略类交互,通常会持有一个真正的策略实现
代码:
/**
* 策略,定义计算报价算法的接口
*/
public interface Strategy {
/**
* 计算应报的价格
* @param goodsPrice 商品销售原价
* @return 计算出来的,应该给客户报的价格
*/
public double calcPrice(double goodsPrice);
}
/**
* 具体算法实现,为新客户或者是普通客户计算应报的价格
*/
public class NormalCustomerStrategy implements Strategy{
public double calcPrice(double goodsPrice) {
System.out.println("对于新客户或者是普通客户,没有折扣");
return goodsPrice;
}
}
/**
* 具体算法实现,为老客户计算应报的价格
*/
public class OldCustomerStrategy implements Strategy{
public double calcPrice(double goodsPrice) {
System.out.println("对于老客户,统一折扣5%");
return goodsPrice*(1-0.05);
}
}
/**
* 具体算法实现,为大客户计算应报的价格
*/
public class LargeCustomerStrategy implements Strategy{
public double calcPrice(double goodsPrice) {
System.out.println("对于大客户,统一折扣10%");
return goodsPrice*(1-0.1);
}
}
/**
* 价格管理,主要完成计算向客户所报价格的功能
*/
public class Price {
/**
* 持有一个具体的策略对象
*/
private Strategy strategy = null;
/**
* 构造方法,传入一个具体的策略对象
* @param aStrategy 具体的策略对象
*/
public Price(Strategy aStrategy){
this.strategy = aStrategy;
}
/**
* 报价,计算对客户的报价
* @param goodsPrice 商品销售原价
* @return 计算出来的,应该给客户报的价格
*/
public double quote(double goodsPrice){
return this.strategy.calcPrice(goodsPrice);
}
}
public class Client {
public static void main(String[] args) {
//1:选择并创建需要使用的策略对象
Strategy strategy = new LargeCustomerStrategy();
//2:创建上下文
Price ctx = new Price(strategy);
//3:计算报价
double quote = ctx.quote(1000);
System.out.println("向客户报价:"+quote);
}
}
1)策略模式的功能:把具体的算法实现从具体业务处理中独立出来,把它们实现成为单独的算法类,从而形成一系列的算法,并让这些算法可以相互替换
2)Context和Strategy的关系
策略实现对象也可以从上下文获取所需要的数据,可以将上下文当作参数传递给策略实现对象。
/**
* 支付工资的策略的接口,公司有多种支付工资的算法
* 比如:现金、银行卡、现金加股票、现金加期权、美元支付等等
*/
public interface PaymentStrategy {
/**
* 公司给某人真正支付工资
* @param ctx 支付工资的上下文,里面包含算法需要的数据
*/
public void pay(PaymentContext ctx);
}
/**
* 人民币现金支付
*/
public class RMBCash implements PaymentStrategy{
public void pay(PaymentContext ctx) {
System.out.println("现在给"+ctx.getUserName()+"人民币现金支付"+ctx.getMoney()+"元");
}
}
/**
* 美元现金支付
*/
public class DollarCash implements PaymentStrategy{
public void pay(PaymentContext ctx) {
System.out.println("现在给"+ctx.getUserName()+"美元现金支付"+ctx.getMoney()+"元");
}
}
/**
* 支付到银行卡
*/
public class Card implements PaymentStrategy{
public void pay(PaymentContext ctx) {
//这个新的算法自己知道要使用扩展的支付上下文,所以强制造型一下
PaymentContext2 ctx2 = (PaymentContext2)ctx;
System.out.println("现在给"+ctx2.getUserName()+"的"+ctx2.getAccount()+"帐号支付了"+ctx2.getMoney()+"元");
//连接银行,进行转帐,就不去管了
}
}
/**
* 支付到银行卡
*/
public class Card2 implements PaymentStrategy{
/**
* 帐号信息
*/
private String account = "";
/**
* 构造方法,传入帐号信息
* @param account 帐号信息
*/
public Card2(String account){
this.account = account;
}
public void pay(PaymentContext ctx) {
System.out.println("现在给"+ctx.getUserName()+"的"+this.account+"帐号支付了"+ctx.getMoney()+"元");
//连接银行,进行转帐,就不去管了
}
}
/**
* 支付工资的上下文,每个人的工资不同,支付方式也不同
*/
public class PaymentContext {
/**
* 应被支付工资的人员,简单点,用姓名来代替
*/
private String userName = null;
/**
* 应被支付的工资的金额
*/
private double money = 0.0;
/**
* 支付工资的方式策略的接口
*/
private PaymentStrategy strategy = null;
/**
* 构造方法,传入被支付工资的人员,应支付的金额和具体的支付策略
* @param userName 被支付工资的人员
* @param money 应支付的金额
* @param strategy 具体的支付策略
*/
public PaymentContext(String userName,double money,PaymentStrategy strategy){
this.userName = userName;
this.money = money;
this.strategy = strategy;
}
/**
* 立即支付工资
*/
public void payNow(){
//使用客户希望的支付策略来支付工资
this.strategy.pay(this);
}
public String getUserName() {
return userName;
}
public double getMoney() {
return money;
}
}
/**
* 扩展的支付上下文对象
*/
public class PaymentContext2 extends PaymentContext {
/**
* 银行帐号
*/
private String account = null;
/**
* 构造方法,传入被支付工资的人员,应支付的金额和具体的支付策略
* @param userName 被支付工资的人员
* @param money 应支付的金额
* @param account 支付到的银行帐号
* @param strategy 具体的支付策略
*/
public PaymentContext2(String userName,double money,String account,PaymentStrategy strategy){
super(userName,money,strategy);
this.account = account;
}
public String getAccount() {
return account;
}
}
public class Client {
public static void main(String[] args) {
//创建相应的支付策略
PaymentStrategy strategyRMB = new RMBCash();
PaymentStrategy strategyDollar = new DollarCash();
//准备小李的支付工资上下文
PaymentContext ctx1 = new PaymentContext("小李",5000,strategyRMB);
//向小李支付工资
ctx1.payNow();
//切换一个人,给petter支付工资
PaymentContext ctx2 = new PaymentContext("Petter",8000,strategyDollar);
ctx2.payNow();
//测试新添加的支付方式
PaymentStrategy strategyCard = new Card();
PaymentContext ctx3 = new PaymentContext2("小王",9000,"010998877656",strategyCard);
ctx3.payNow();
//测试新添加的支付方式
PaymentStrategy strategyCard2 = new Card2("010998877656");
PaymentContext ctx4 = new PaymentContext("小张",9000,strategyCard2);
ctx4.payNow();
}
}
3) 容错恢复机制
比如说采用策略模式,把日志记录到数据库和把日志记录到文件当做两种记录日志的策略,然后再运行期间根据需要进行动态的切换
4)策略模式结合模板方法模式
发现一系列的算法的实现上存在公共功能,只是在某些局部步骤上有所不同。
/**
* 日志记录策略的接口
*/
public interface LogStrategy {
/**
* 记录日志
* @param msg 需记录的日志信息
*/
public void log(String msg);
}
/**
* 实现日志策略的抽象模板,实现给消息添加时间
*/
public abstract class LogStrategyTemplate implements LogStrategy{
public final void log(String msg) {
//第一步:给消息添加记录日志的时间
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
msg = df.format(new java.util.Date())+" 内容是:"+ msg;
//第二步:真正执行日志记录
doLog(msg);
}
/**
* 真正执行日志记录,让子类去具体实现
* @param msg 需记录的日志信息
*/
protected abstract void doLog(String msg);
}
/**
* 把日志记录到数据库
*/
public class DbLog extends LogStrategyTemplate{
public void doLog(String msg) {
//制造错误
if(msg!=null && msg.trim().length()>5){
int a = 5/0;
}
System.out.println("现在把 '"+msg+"' 记录到数据库中");
}
}
/**
* 把日志记录到数据库
*/
public class FileLog extends LogStrategyTemplate{
public void doLog(String msg) {
System.out.println("现在把 '"+msg+"' 记录到文件中");
}
}
/**
* 日志记录的上下文
*/
public class LogContext {
/**
* 记录日志的方法,提供给客户端使用
* @param msg 需记录的日志信息
*/
public void log(String msg){
//在上下文里面,自行实现对具体策略的选择
//优先选用策略:记录到数据库
LogStrategy strategy = new DbLog();
try{
strategy.log(msg);
}catch(Exception err){
//出错了,那就记录到文件中
strategy = new FileLog();
strategy.log(msg);
}
}
}
public class Client {
public static void main(String[] args) {
LogContext log = new LogContext();
log.log("记录日志");
log.log("再次记录日志");
}
}
5)策略模式的本质:分离算法,选择实现