策略模式属于对象行为模式[GOF95]。其用意是针对一组算法,将每一个算法封装到具体共同缄口的独立的类中,从来是的他们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化
问题
架设现在要设计一个贩卖各类书籍的电子商务网站的购物车(shopping cart)系统。一个简单的情就是把所有货品的单价乘上数量,但是实际情况肯定比这复杂,比如对不同的图书给与折扣的问题
解决方案
1/把所有的业务逻辑都放在客户端里面。客户端利用条件选择语句决定使用哪一个算法。这样一来,客户端代码会变得复杂难以维护。
2/客户端可以利用继承的办法在子类里面实现不同的行为,但是这样会使得环紧和行为紧密耦合在一起,使得两者不能单独演化。
3/使用策略模式。策略模式把行为和环境分割开来。环境负责维持和查询行为类,各类算法则在具体的策略类(ConcreteStrategy)中提供。由于算法和环境独立开来,算法的增减,修改都不会影响环境和客户端。
策略模式正是解决这个问题的系统化方法。当出现新的折扣或现有的折扣出现变化,只需要实现新的策略类,并在客户端登记即可。策略模式相当于“可插入式(Pluggable)的算法”。
当准备在一个系统里使用策略模式时,首先必须找到需要包装的算法,看看算法是否可以从环境中分割开来,最后在考虑这些算法是否会在以后发生变化。
引进策略模式
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是“准备一组算法,并将每一个算法封装起来,使得他们可以互换。”
//环境类的源代码
using System;
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public class Context
{
private Strategy strategy;
public void contextInterface()
{
strategy.strategyInterface();
}
}
}
//抽象策略类的源代码
using System;
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public abstract class Strategy
{
public abstract void strategyInterface();
}
}
//具体策略类的源代码
using System;
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public class ConcreteStrategy : Strategy
{
public override void strategyInterface()
{
throw new Exception("The method or operation is not implemented.");
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public class Context
{
private Strategy strategy;
public void contextInterface()
{
strategy.strategyInterface();
}
}
}
//抽象策略类的源代码
using System;
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public abstract class Strategy
{
public abstract void strategyInterface();
}
}
//具体策略类的源代码
using System;
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public class ConcreteStrategy : Strategy
{
public override void strategyInterface()
{
throw new Exception("The method or operation is not implemented.");
}
}
}
策略模式的实现有以下注意的地方
1/经常见到的是,所有的具体策略类都有一些公有的行为。这时候,就应当把这些公有的行为放到共同的抽象策略角色Strategy类里面。当然这时候抽象策略角色必须要用java抽象类实现,而不能使用java接口
2/策略模式在每一个时刻都只能使用一个策略对象,但是有时候一个应用程序同时与几个策略对象相联系。换言之,在应用程序启动时,所有的策略对象就已经被创立出来,而应用程序可以在几个策略对象之间调换。
图书折扣的计算
using System;
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public abstract class DiscountStrategy
{
private float price;
private int copies;
public DiscountStrategy(float price, int copies)
{
this.price = price;
this.copies = copies;
}
public abstract double calclateDiscount();
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public class FlatTateStrategy : DiscountStrategy
{
private float price;
private int copies;
private float acmount;
public FlatTateStrategy(float price, int copies)
{
this.price = price;
this.copies = copies;
}
public float Amount
{
get
{
return this.acmount;
}
set
{
this.acmount = value;
}
}
public override double calclateDiscount()
{
return copies * acmount;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public class PercenttageStrategy : DiscountStrategy
{
private float percent;
private float price;
private int copies;
public PercenttageStrategy(float price, int copies)
{
this.price = price;
this.copies = copies;
}
public float Percent
{
get
{
return this.percent;
}
set
{
this.percent = value;
}
}
public override double calclateDiscount()
{
return copies * price * percent;
}
}
}
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public abstract class DiscountStrategy
{
private float price;
private int copies;
public DiscountStrategy(float price, int copies)
{
this.price = price;
this.copies = copies;
}
public abstract double calclateDiscount();
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public class FlatTateStrategy : DiscountStrategy
{
private float price;
private int copies;
private float acmount;
public FlatTateStrategy(float price, int copies)
{
this.price = price;
this.copies = copies;
}
public float Amount
{
get
{
return this.acmount;
}
set
{
this.acmount = value;
}
}
public override double calclateDiscount()
{
return copies * acmount;
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace StrategyPattern
{
public class PercenttageStrategy : DiscountStrategy
{
private float percent;
private float price;
private int copies;
public PercenttageStrategy(float price, int copies)
{
this.price = price;
this.copies = copies;
}
public float Percent
{
get
{
return this.percent;
}
set
{
this.percent = value;
}
}
public override double calclateDiscount()
{
return copies * price * percent;
}
}
}
在什么情况下应当使用策略模式
1/如果在一个系统里面有许多类,他们之间的却别仅在于他们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一中行为。
2/一个系统需要动态的在几种算法中选择一种。那么这些算法可以包装到一个个的具体算法类里面,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,由于多太性原则,客户端可以选择用任何一个具体算法类,并持有一个数据类型的抽象算法类的对象。
3/一个系统的算法使用的数据不可以让客户端知道。策略模式可以避免让客户端涉及到不必要接触到的复杂的和算法有关的数据。
4/如果一个对象有很多行为,如果不用适当的模式,这些行为就只好使用多重的条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句,并体现面向对象设计的概念