又是一个阳光明媚的周末。岁月静好,浅笑安然!
一、前言
在讲策略模式之前,先来看一下JDK源码中java.util.Arrays类的一个对象排序的方法,如下截图:
主要看1544行,我们可以知道,这个mergeSort()排序方法的实现过程中,for循环中调用compare方法的对象c是我们自己外部传入的。我们自己定义的对象比较的类c实现了Comparator接口,并且实现了该接口中的compare(T, T)方法,然后进行具体的排序工作。所以,JDK提供的Arrays类的这个排序方法,实际上使用的排序算法是我们自己定义好的;这样的话,我们自己传入什么排序算法,就可以进行什么方式的排序,非常灵活。这就是策略模式思想。
二、什么是策略模式
1、策略模式的定义
策略模式它定义了一组算法,并且将每个算法都封装起来,各个算法可以灵活的相互切换。就像上面介绍的排序算法一样,我们自己定义的多个算法,然后根据应用程序的需要传入,就可以非常方便的按照我们自己的算法进行排序。
2、策略模式的有点
1)、策略模式很好的体现了拥抱变化,封装变化的概念。该模式在编程过程中使用接口,即面向接口编程。
2)、策略模式使得客户端在使用这些算法的时候,可以非常方便、灵活的互换且互相不影响。
3)、策略模式使得开发人员可以开发出各种可以互相替换的软件组件,并且各个组件之间是弱连接的关系。这就使
得软件具有更好更强的可扩展性和易维护性。更重要的是,它大大提高了软件的可重用性。
3、策略模式的各个组成角色
策略模式主要由以下3个角色组成:
1)、抽象策略角色:也就是策略类,它通常由一个接口或者抽象类来实现。
2)、具体策略角色:实现了抽象策略角色,包装了一组相关的算法和行为。
3)、环境角色:该角色持有一个策略类的引用,且该引用的类型是抽象策略类的类型。该角色最终给客户端调用。
4、策略模式的编写步骤
1)、对策略对象定义一个公共的接口,即定义抽象策略类。
2)、编写具体策略类,该策略类实现上面的抽象策略类。
3)、在使用策略对象的类(即环境角色)中持有一个对策略对象的引用。
4)、还要在环境角色中实现对策略对象的设值(set)和取值(get),或者使用构造方法完成对策略对象的赋值。
5、具体事例代码如下
1、抽象策略类:
package com.strategy.pattern.demo;
/**
*
* @ClassName: CarProduceStrategy
* @Description: 抽象策略角色,制造汽车
* @author admin
* @date 2016年12月3日 上午10:44:11
* @version V1.0
*/
public interface CarProduceStrategy {
public void produceCar();
}
2、下面是3个具体策略类,实现了抽象策略类,且实现了抽象策略类中的方法:
package com.strategy.pattern.demo;
/**
*
* @ClassName: BMWProduce
* @Description: 具体策略类,制造宝马品牌的汽车
* @author admin
* @date 2016年12月3日 上午10:46:20
* @version V1.0
*/
public class BMWProduce implements CarProduceStrategy {
@Override
public void produceCar() {
System.out.println("宝马汽车制造...");
}
}
package com.strategy.pattern.demo;
/**
*
* @ClassName: BENZProduce
* @Description: 具体策略类,制造奔驰品牌的汽车
* @author admin
* @date 2016年12月3日 上午10:46:20
* @version V1.0
*/
public class BENZProduce implements CarProduceStrategy {
@Override
public void produceCar() {
System.out.println("奔驰汽车制造...");
}
}
package com.strategy.pattern.demo;
/**
*
* @ClassName: AUDIProduce
* @Description: 具体策略类,制造奥迪品牌的汽车
* @author admin
* @date 2016年12月3日 上午10:46:20
* @version V1.0
*/
public class AUDIProduce implements CarProduceStrategy {
@Override
public void produceCar() {
System.out.println("奥迪汽车制造...");
}
}
3、环境角色:
package com.strategy.pattern.demo;
/**
*
* @ClassName: Environment
* @Description: 环境角色
* @author admin
* @date 2016年12月3日 上午10:51:30
* @version V1.0
*/
public class Environment {
private CarProduceStrategy produceStrategy;
public Environment(CarProduceStrategy produceStrategy) {
this.produceStrategy = produceStrategy;
}
public CarProduceStrategy getProduceStrategy() {
return produceStrategy;
}
public void setProduceStrategy(CarProduceStrategy produceStrategy) {
this.produceStrategy = produceStrategy;
}
/**
*
* @Title: produceCar
* @Description: 调用具体的某个策略进行汽车制造
* @param
* @return void
* @throws
*/
public void produceCar(){
produceStrategy.produceCar();
}
}
4、客户端:
package com.strategy.pattern.demo;
/**
*
* @ClassName: Client
* @Description: 具体策略调用者,客户端
* @author admin
* @date 2016年12月3日 上午10:54:26
* @version V1.0
*/
public class Client {
public static void main(String[] args) {
CarProduceStrategy produceStrategy = new BMWProduce();
//宝马汽车制造策略
Environment environment = new Environment(produceStrategy);
environment.produceCar();
//奔驰汽车制造策略
produceStrategy = new BENZProduce();
environment.setProduceStrategy(produceStrategy);
environment.produceCar();
//奥迪汽车制造策略
produceStrategy = new AUDIProduce();
environment.setProduceStrategy(produceStrategy);
environment.produceCar();
}
}
5、测试打印结果:
三、总结
从上面的例子可以看出,策略模式其实还是比较容易理解和实现的。但是,有没有发现什么问题呢?假如我们
还要实现一个制造大众品牌的汽车,我们会怎么写?毫无疑问,我们会在写一个策略类,去实现抽象策略类,并且实现里面的抽象方法来实现制造大众品牌的汽车的逻辑。这样在逻辑上是没任何问题,但是在上升到编程规范和艺术上来说,问题很明显:
1、会导致我们的系统拥有很多的策略类。
2、由于系统拥有大量策略类,这就要求客户端必须知道系统中所有的策略类,并自行决定使用哪一个策略类。
四、解决办法:采用工厂方法。
未完待续。