1.定义:
定义一系列的算法,把他们一个个封装起来,并且使它们可以相互替换,使得算法可独立于使用它的客户而变化。
策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
2.结构:
策略模式是对算法的包装,是把使用算法的责任和算法本身分隔开来,委派给不同的对象管理,策略模式通常把一系列的算法包装到一系列的策略类里,作为一个抽象策略类的子类。 通俗讲就是准备一组算法,并将每一个算法封装起来,使他们可以互换。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
抽象策略(Strategy)角色: 抽象角色,通常由一个接口或抽象类实现,此角色给出所有的具体策略类所需的接口。
环境(Context)角色:持有一个Strategy的引用
环境角色类:
public class Context {
//持有一个具体策略的对象
private Strategy strategy;
/** * 构造函数,传入一个具体策略对象 *
@param strategy 具体策略对象 */
public Context(Strategy strategy){
this.strategy = strategy;
}
/** * 策略方法 */
public void contextInterface(){
strategy.strategyInterface();
}
}
抽象策略类:
public Interface Strategy{
/**
* 策略方法
*/
public void strategyInterface()
}
具体策略类:
public class ConcreteStrategyA implements Strategy {
@Override
public void strategyInterface() {
//相关的业务 System.out.println("A算法")
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void strategyInterface() {
//相关的业务
System.out.println("B算法")
}
}
public class ConcreteStrategyC implements Strategy {
@Override
public void strategyInterface() {
//相关的业务
System.out.println("C算法")
}
}
3.使用场景
假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。比如,本网站可能对所有的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对初级会员没有折扣。
根据描述,折扣是根据以下的几个算法中的一个进行的:
算法一:对初级会员没有折扣。
算法二:对中级会员提供10%的促销折扣。
算法三:对高级会员提供20%的促销折扣。
使用策略模式来实现的结构图如下:
抽象折扣类(抽象策略类)
public interface MemberStrategy {
/**
* 计算图书的价格
* @param booksPrice 图书的原价
* @return 计算出打折后的价格
*/
public double calcPrice(double booksPrice);
}
初级会员折扣类(具体策略/封装特有算法)
public class PrimaryMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于初级会员的没有折扣");
return booksPrice;
}
}
中级会员折扣类(具体策略/封装特有算法)
public class IntermediateMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于中级会员的折扣为10%");
return booksPrice * 0.9;
}
}
高级会员折扣类(具体策略/封装特有算法)
public class AdvancedMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于高级会员的折扣为20%");
return booksPrice * 0.8;
}
}
价格类(环境类)
public class Price { //持有一个具体的策略对象 private MemberStrategy strategy; /** * 构造函数,传入一个具体的策略对象 * @param strategy 具体的策略对象 */ public Price(MemberStrategy strategy){ this.strategy = strategy; } /** * 计算图书的价格 * @param booksPrice 图书的原价 * @return 计算出打折后的价格 */ public double quote(double booksPrice){ return this.strategy.calcPrice(booksPrice); } }
客户端
public class Client {
public static void main(String[] args) {
//选择并创建需要使用的策略对象
MemberStrategy strategy = new AdvancedMemberStrategy();
//创建环境
Price price = new Price(strategy);
//计算价格 double quote = price.quote(300);
System.out.println("图书的最终价格为:" + quote);
}
}
从上面的例子可以看出,策略模式主要包含以下几个要点:
有一个存放公共接口的抽象策略类
多个实现抽象策略类的具体策略类封装了算法
策略模式并不决定在何时使用何种算法,这些是客户端决定的
提供新的算法插入到已有系统中,以及老算法从系统中退休的方法,符合开闭原则 对扩展开放,对修改关闭。
例2:在java加密中,我们会经常看到 MessageDigest md5 = MessageDigest.getInstance("MD5")、MessageDigest sha = MessageDigest.getInstance("SHA")。
给一个不同的参数,不一样的加密算法,(ps:java加密底层我也不是道是不是使用了策略模式、但是跟策略模式的定义很像,有空研究研究)
例3:出行旅游:我们可以有几个策略可以考虑:可以骑自行车,汽车,做火车,飞机。每个策略都可以得到相同的结果,但是它们使用了不同的资源。选择策略的依据是费用,时间,使用工具还有每种方式的方便程度 。
如何让算法和对象分开来,使得算法可以独立于使用它的客户而变化?
结构图如下:
出行旅游策略(Strategy):
/**
* 出行旅游
*
*
*/
public interface TravelStrategy{
public void travelAlgorithm();
}
具体策略类(ConcreteStrategy):乘坐飞机
/**
* 具体策略类(ConcreteStrategy)2:乘坐飞机
*/
class AirPlanelStrategy implements TravelStrategy {
public void travelAlgorithm(){
System.out.println("坐飞机")
}
}
具体策略类(ConcreteStrategy):坐火车
/**
* 具体策略类(ConcreteStrategy)2:乘坐火车
*/
class TrainStrategy implements TravelStrategy {
public void travelAlgorithm(){
System.out.println("坐火车");
}
}
具体策略类(ConcreteStrategy) : 骑自行车
/**
* 具体策略类(ConcreteStrategy)3:骑自行车
*/
class BicycleStrategy implements TravelStrategy {
public void travelAlgorithm(){
System.out.println("骑自行车");
}
}
环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。算法解决类,以提供客户选择使用何种解决方案:
class PersonContext{
private TravelStrategy travelStrategy ;
public PersonContext(TravelStrategy travelStrategy ){
this.travelStrategy = travelStrategy ;
}
/**
* 旅行
*/
public void travel(){
return this.travelStrategy.travelAlgorithm();
}
}
客户端:
public class Client {
public static void main(String[] args) {
//选择并创建需要使用的策略对象
TravelStrategy strategy = new BicycleStrategy();
//创建环境
PersonContext personContext = new PersonContext(strategy);
//选择自行车方式
personContext.travelAlgorithm();
}
}
4.使用场合
当存在以下情况时使用Strategy模式
1)• 许多相关的类仅仅是行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
2)• 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。
3)• 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
4)• 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
5.策略模式优缺点
优点
(1)策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
(2)使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。
缺点
(1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
(2)由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。
6.总结
策略模式重心
策略模式重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更加灵活,更好的维护性和扩展性。
算法的平等性
各个策略算法的平等性是策略模式的一个很大的特点。对于一系列具体的策略算法,大家的地位是完全一样的,正因为平等性,才能实现算法之间的相互替换。所有的策略算法在实现上可以相互独立,相互之间没有依赖。
所以可以这样描述策略算法,策略算法是相同行为的不同实现。
运行时策略模式的唯一性
运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态的在不同的策略实现中切换,但是同时只能使用一个。
公有的行为
经常见到的是,所有的具体策略类都有一些公有的行为。这时候,就应当把这些公有的行为放到共同的抽象策略角色Strategy类里面。当然这时候抽象策略角色必须要用Java抽象类实现,而不能使用接口。
这其实也是典型的将代码向继承等级结构的上方集中的标准做法。
摘自:
https://www.cnblogs.com/java-my-life/archive/2012/05/10/2491891.html
http://blog.csdn.net/hguisu/article/details/7558249
加深理解:
http://blog.csdn.net/hguisu/article/details/7558249