设计模式 - 策略模式

为什么?

.一个类的操作,或方法在运行期间是动态改变的,我们会根据场景的不同调用不同的方法,
即使用 if...else 调用不同的方法,则会带来代码的复杂度和难以维护,于是我们提出了策略模式;

 


是什么?

1.在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

2.策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。

 


怎么做?

 1. 策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。下面就以一个示意性的实现讲解策略模式实例的结构。

这个模式涉及到三个角色:

●  环境(Context)角色:持有一个Strategy的引用。

●  抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需接口。

●  具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

具两个例子:

一个是运行计算的,用户根据不同的操作,实现不同的运算。

我们将创建一个定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。

StrategyPatternDemo,我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。

package com.xxq.rest.strategy.demo;

/**
 * @description 抽象操作接口
 * @author xq
 *
 */
public interface Strategy {
	public int doOperation(int num1, int num2);
}
package com.xxq.rest.strategy.demo;

/**
 * 添加方法
 * @author xq
 *
 */
public class OperationAdd implements Strategy {

	@Override
	public int doOperation(int num1, int num2) {
		return num1 + num2;
	}

}
package com.xxq.rest.strategy.demo;


/**
 * @description 乘的实现类
 * @author xq
 *
 */
public class OperationMultiply implements Strategy {

	@Override
	public int doOperation(int num1, int num2) {
		return num1 * num2;
	}

}
package com.xxq.rest.strategy.demo;


/**
 * @description 减的实现
 * @author xq
 *
 */
public class OperationSubstract implements Strategy {

	@Override
	public int doOperation(int num1, int num2) {
		return num1 - num2;
	}

}

 

package com.xxq.rest.strategy.demo;
/**
 * @description 调用策略的环境角色
 * @author xq
 *
 */
public class Context {
	
    // 使用的算法
	private Strategy strategy;

	public Context(Strategy strategy) {
		this.strategy = strategy;
	}

	public int executeStrategy(int num1, int num2) {
		return strategy.doOperation(num1, num2);
	}

}
package com.xxq.rest.strategy.demo;

/**
 * @description 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
 *              在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context
 *              对象的执行算法。
 * 
 *              介绍 意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
 * 
 *              主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
 * 
 *              何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
 * 
 *              如何解决:将这些算法封装成一个一个的类,任意地替换。
 * 
 *              关键代码:实现同一个接口。
 * 
 *              应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。
 * 
 *              2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 .
 * 
 *              优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
 * 
 *              缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
 * 
 *              使用场景:
 *              1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
 *              2、一个系统需要动态地在几种算法中选择一种。
 *              3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
 * 
 *              注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
 * @author xq
 *
 */
public class StrategyPatternDemo {
	public static void main(String[] args) {

		Context context = new Context(new OperationAdd());
		System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

		context = new Context(new OperationMultiply());
		System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

		context = new Context(new OperationMultiply());
		System.out.println("10 * 5 = " + context.executeStrategy(10, 5));

	}
}

运算结果:

10 + 5 = 15
10 - 5 = 50
10 * 5 = 50

锦囊妙计的例子:

以下为转载(设计模式之禅)

描述:刘备要到江东娶老婆了,走之前诸葛亮给赵云(伴郎)三个锦囊妙计,说是按天机拆开解决棘手问题,嘿,还别说,真是解决了大问题,搞到最后是周瑜陪了夫人又折兵呀,那咱们先看看这个场景是什么样子的。先说这个场景中的要素:三个锦囊,三个妙计,一个赵云,妙计是小亮同志给的,妙计是放置在锦囊里,俗称就是锦囊妙计嘛,那赵云就是一个干活的人,从锦囊中取出妙计,执行,然后获胜,用 JAVA 程序怎么表现这个呢?我们先看类图:
 

三个妙计是同一类型的东东,那咱就写个接口:

 

package com.gumx.strategy;
 
/**
* @author 
* I'm glad to share my knowledge with you all.
* 首先定一个策略接口,这是诸葛亮老人家给赵云的三个锦囊妙计的接口
*
*/
public interface IStrategy {
     //每个锦囊妙计都是一个可执行的算法
     public void operate();
 
}

然后再写三个实现类,有三个妙计嘛:

package com.gumx.strategy;
 
/**
* @author 
* I'm glad to share my knowledge with you all.
* 找乔国老帮忙,使孙权不能杀刘备
*/
public class BackDoor implements IStrategy {
 
    public void operate() {
        System.out.println("找乔国老帮忙,让吴国太给孙权施加压力");
    }
}
 
package com.gumx.strategy;
 
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 求吴国太开个绿灯
*/
public class GivenGreenLight implements IStrategy {
 
    public void operate() {
        System.out.println("求吴国太开个绿灯,放行!");
    }
}
 
package com.gumx.strategy;
 
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 孙夫人断后,挡住追兵
*/
public class BlockEnemy implements IStrategy {
 
    public void operate() {      
        System.out.println("孙夫人断后,挡住追兵!");
    }
}

好了,大家看看,三个妙计是有了,那需要有个地方放这些妙计呀,放锦囊呀:

package com.gumx.strategy;
 
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 计谋有了,那还要有锦囊
*/
public class Context {
    //构造函数,你要使用那个妙计
    private IStrategy straegy;
    public Context(IStrategy strategy){
        this.straegy = strategy;
    }
 
    //使用计谋了,看我出招了
    public void operate(){
        this.straegy.operate();
    }
}


然后就是赵云雄赳赳的揣着三个锦囊,拉着已步入老年行列的、还想着娶纯情少女的、色迷迷的刘老爷子去入赘了,嗨,还别说,小亮的三个妙计还真是不错,瞅瞅:

 


package com.gumx.strategy;
 
/**
* @author gumx
* I'm glad to share my knowledge with you all.
*/
public class ZhaoYun {
/**
* 赵云出场了,他根据诸葛亮给他的交代,依次拆开妙计
*/
    public static void main(String[] args) {
        Context context;
        //刚刚到吴国的时候拆第一个
        System.out.println("-----------刚刚到吴国的时候拆第一个-------------");
        context = new Context(new BackDoor()); //拿到妙计
        context.operate();  //拆开执行
        System.out.println("\n\n\n\n\n\n\n\n");
 
        //刘备乐不思蜀了,拆第二个了
        System.out.println("-----------刘备乐不思蜀了,拆第二个了-------------");
        context = new Context(new GivenGreenLight());
        context.operate();  //执行了第二个锦囊了
        System.out.println("\n\n\n\n\n\n\n\n");
 
        //孙权的小兵追了,咋办?拆第三个
        System.out.println("-----------孙权的小兵追了,咋办?拆第三个-------------");
        context = new Context(new BlockEnemy());
        context.operate();  //孙夫人退兵
        System.out.println("\n\n\n\n\n\n\n\n");
        /*
        *问题来了:赵云实际不知道是那个策略呀,他只知道拆第一个锦囊,
        *而不知道是BackDoor这个妙计,咋办? 似乎这个策略模式已经把计谋名称写出来了
        * 错!BackDoor、GivenGreenLight、BlockEnemy只是一个代码,你写成first、second、third,没人会说你错!
        * 策略模式的好处就是:体现了高内聚低耦合的特性呀,缺点嘛,这个那个,我回去再查查
        */
    }
}

就这三招,搞的周郎是“陪了夫人又折兵”呀!这就是策略模式,高内聚低耦合的特点也表现出来了,还有一个就是扩展性,也就是 OCP 原则,策略类可以继续增加下去,只要修改 Context.java 就可以了;

 


小结:

1.策略模式,就是为了减少重复代码,然后对变动的代码进行抽象,然后使用多态调用方法

2.策略模式 就是减少动态代码的调用,一个方法,可能面对不同的用户是不同的算法,那么这个时间,就可以用大量的if来判断,来使用,所以,为了减少代码。对同一个方法,不同的人调用,会进行一个策略模式。

    比如一个操作,不同的人会对这个操作,进行,具体的,增,删,改,操作是一个抽象,是一个接口 ,

这个接口可以定义为Strategy 策略,增,删,改,是类,是具体实现这个方法的,实现。 是不同的算法,

所以这里,在需要使用的某种策略的类,比如(strategy) 所以要set 参数,用这个策略类去实现方法,

    一个简单的策略模式,只需要,策略接口 ,若干个实现,一个实现了策略接口的类,就可以了,他的本质上是,多态,即引用指子类。子类覆盖父类的方法。

3.策略模式,底层核心,用的就是多态。


spring中的策略模式

/** 抽象策略接口:定义一个实例化指定类的最优策略*/
public interface InstantiatorStrategy {
 
   /**
    *创建类的实例策略方法
    *
    * @param type 待实例化的类
    * @return实例化方法
    */
   <T> ObjectInstantiator<T>newInstantiatorOf(Class<T> type);
}
/**
  * 具体策略
  */
public class SingleInstantiatorStrategy implements InstantiatorStrategy {
 
   private Constructor<?> constructor;
 
   public SingleInstantiatorStrategy(Class<?> instantiator) {
      try {
         constructor =instantiator.getConstructor(Class.class);
      } catch (NoSuchMethodException e) {
         throw new ObjenesisException(e);
      }
   }
 
   @SuppressWarnings("unchecked")
   public <T> ObjectInstantiator<T> newInstantiatorOf(Class<T>type) {
      try {
         return (ObjectInstantiator<T>) constructor.newInstance(type);
      } catch (InstantiationException e) {
         throw new ObjenesisException(e);
      } catch (IllegalAccessException e) {
         throw new ObjenesisException(e);
      } catch (InvocationTargetException e) {
         throw new ObjenesisException(e);
      }
   }
}

/** 连环计:具体策略*/
public class Chain implements ThirtySixStrategy {
    public void useStrategy() {
        System.out.println("程序猿使出了* 连环计 *对程序媛进行地毯式轰炸,俘获了程序媛。。。");
    }
}
 
/**欲擒故纵:具体策略*/
public class Loose2Capture implements ThirtySixStrategy {
    public void useStrategy() {
        System.out.println("程序猿使出了* 欲擒故纵 *计俘获了程序媛。。。");
    }
}

 

/**
  *环境角色,拥有策略接口的引用,使用具体策略
  */
public class ObjenesisBase implements Objenesis {
   protected finalInstantiatorStrategy strategy;
   protected ConcurrentHashMap<String,ObjectInstantiator<?>> cache;
  //…其他代码
   public ObjenesisBase(InstantiatorStrategy strategy) {
      this(strategy, true);
   public <T> T newInstance(Class<T> clazz) {
      return getInstantiatorOf(clazz).newInstance();
   }
}

实现步骤

通过上面的两部分代码可以总结策略模式的实现步骤:

1.      通过分离变化得出的策略接口Strategy

2.      Strategy的实现类

3.      客户程序有一个Strategy

4.      在客户程序中选择/组装正确的Strategy实现

适用场景

1.      许多相关的类仅仅是行为差异

2.      运行时选取不同的算法变体

3.      通过条件语句在多个分支中选取一

策略模式的优缺点
优点:

(1)      提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公用的代码移到父类里面,从而可以避免重复的代码。

(2)      策略模式提供了可以替换继承关系的办法。继承可以处理多种算法行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不能再独立烟花。继承使得动态改变算法或行为变得不可能。

(3)      使用策略模式可以避免使用多重条件转移语句。

缺点:

(1)      客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。

(2)      策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。
 


参考文章:

https://www.cnblogs.com/ludashi/p/5302269.html

http://www.runoob.com/design-pattern/strategy-pattern.html

https://blog.csdn.net/u012401711/article/details/52463347

https://blog.csdn.net/Goskalrie/article/details/52472734

《设计模式之禅》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值