“ 在软件工程中,行为型设计模式为设计模式的一种类型,用来识别对象之间的常用交流模式并加以实现。如此,可以在交流时增强灵活性。”
01
—
入门小例子
定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。
我们来看一下下面的小例子,首先我们定义了一个公共接口,然后它有两个实现类,分别输出 每天学Java 和 每天学Java之策略模式学习。
public interface Strategy {
public void strategyInter();
}
class StrategyImpO implements Strategy {
@Override
public void strategyInter() {
System.out.println("每天学Java");
}
}
class StrategyImp1 implements Strategy {
@Override
public void strategyInter() {
System.out.println("每天学Java之策略模式学习");
}
}
如果在平时我们会一般会这样去调用这两个实现类的方法
Strategy strategy0 = new StrategyImpO();
Strategy strategy1 = new StrategyImp1();
strategy0.strategyInter();
strategy1.strategyInter();
假设一下这个接口的实现类有很多,而且每个实现类都在不同地方被多次实现,那是不是意味这我们重复写了很多相同的代码。遇到这种情况你可能写一个中间类(具体策略角色:包装了相关的算法或行为),其构造方法参数是这个接口的实例,在客户端调用的时候去传入不同的实现类来达到减少重复代码。
class Context {
private Strategy strategy;
/**
* 构造函数,传入具体的策略对象
*
* @param strategy
*/
public Context(Strategy strategy) {
this.strategy = strategy;
}
/**
* 调用策略方法
*/
public void method() {
strategy.strategyInter();
}
}
然后客户端这样去调用:
A:new Context(new StrategyImpO()).method();
B:new Context(new StrategyImp1()).method();
这种方式确实比上一种方法高级一些,但是这种方式很好吗?我们来看一下,如果Strategy接口有N个实现类,C客户端在实现Context这个接口时候,难道你要让他了解每一个实现类吗?这种方法是不合理的。拿自己来说,我们公司的产品再调用第三方的接口的时候,一般给的文档肯定是这种形式:
method(int/String/...... 参数) // 参数1代表什么,参数2代表什么,参数3代表什么
不会是
method(接口 参数)//接口实例类1是什么,接口实例类2是什么。
再有就是:假如接口的实例类A突然不能用了,我们要换一个新的实现类来替代,然后你给客户打电话说,那个方法的参数不能用了,你要改成XXX实现类。这肯定不合适
说白一点,就是耦合性太高了。(耦合性:也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息。)
那么我们一般在那个中间类上面做一下改动:
class Context {
private Strategy strategy;
/**
* 调用策略方法
*/
public void method(int param) {
if (param > 0) {
strategy = new StrategyImpO();
} else if (param < 0) {
strategy = new StrategyImp1();
}
strategy.strategyInter();
}
}
这样改动之后,在后续即使StrategyImpO有了更高级的类去代替,我们也仅仅需要修改一下这个Context类,并不会影响客户端的调用(在实际工作中,客户根本不会关心你的接口内部有了什么改动,不影响我使用就可以了,如果你接口内部有了改动,作为客户的我也要有相应的改动,我肯定会不开心)。
看到这里,我想大家就明白类什么是策略模式:
策略模式定义和封装了一系列的算法,它们是可以相互替换的,也就是说它们具有共性,而它们的共性就体现在策略接口的行为上,另外为了达到最后一句话的目的,也就是说让算法独立于使用它的客户而独立变化,我们需要让客户端依赖于策略接口
02
—
深化策略模式
在上面我们初步的玩了一下策略模式,但是我们回归一下最开始的问题,如果Strategy接口有N个实现类,是用来策略模式后,虽然客户端不在关心接口实现类的改动,但是这个工作量放在服务端也是很烦的,我想没有一个开发人员想去写N个if else语句,那么这个时候该怎么办呢?(推荐一篇优秀的博文:大家可以去看看https://blog.csdn.net/u012124438/article/details/70039943/)
那就是使用注解, 所以我们需要给注解加入属性的值,用来解决判断实例化那一个实现类的问题。
首先我们写一个注解接口:如果对于注解还不是很清楚,可以看一下解读Java 注解 (Annotation)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TraInterface {
int param() default 0;
}
这个注解中param就是我们要去传的值
@TraInterface(param = 1)
class StrategyImpO implements Strategy {
@Override
public void strategyInter() {
System.out.println("每天学Java");
}
}
@TraInterface(param = -1)
class StrategyImp1 implements Strategy {
@Override
public void strategyInter() {
System.out.println("每天学Java之策略模式学习");
}
}
也就是说,通过这个注解,我们来判断调用那个实现类。现在我们来通过一个工厂方法来实现具体的功能:
class FactoryStrategy {
private ClassLoader classLoader = getClass().getClassLoader();
//策略列表,也就是实现类的列表
private List<Class<? extends Strategy>> list;
public Strategy createStrategy(int val) {
for (Class<? extends Strategy> claszz : list) {
TraInterface traInterface = handleAnnotation(claszz);
if (traInterface.param() == val) {
//是的话我们返回一个当前策略的实例
try {
return claszz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
throw new RuntimeException("策略获得失败");
}
//处理注解,我们传入一个策略类,返回它的注解
private TraInterface handleAnnotation(Class<? extends Strategy> clazz) {
Annotation[] annotations = clazz.getDeclaredAnnotations();
if (annotations == null || annotations.length == 0) {
return null;
}
for (int i = 0; i < annotations.length; i++) {
if (annotations[i] instanceof TraInterface) {
return (TraInterface) annotations[i];
}
}
return null;
}
//单例
private FactoryStrategy() {
init();
}
//在工厂初始化时要初始化策略列表
private void init() {
list = new ArrayList<Class<? extends Strategy>>();
System.out.println(FactoryStrategy.class.getResource(""));
File[] resources = getResources();//获取到包下所有的class文件
Class<Strategy> calPriceClazz = null;
try {
calPriceClazz = (Class<Strategy>) classLoader.loadClass(Strategy.class.getName());//使用相同的加载器加载策略接口
} catch (ClassNotFoundException e1) {
throw new RuntimeException("未找到策略接口");
}
for (int i = 0; i < resources.length; i++) {
try {
//载入包下的类
Class<?> clazz = classLoader.loadClass(resources[i].getName().replace(".class", ""));
//判断是否是CalPrice的实现类并且不是CalPrice它本身,满足的话加入到策略列表
if (Strategy.class.isAssignableFrom(clazz) && clazz != calPriceClazz) {
list.add((Class<? extends Strategy>) clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//获取扫描的包下面所有的class文件
private File[] getResources() {
try {
System.out.println(classLoader.getResource(""));
File file = new File(classLoader.getResource("").toURI());
return file.listFiles(new FileFilter() {
public boolean accept(File pathname) {
if (pathname.getName().endsWith(".class")) {//我们只扫描class文件
return true;
}
return false;
}
});
} catch (URISyntaxException e) {
throw new RuntimeException("未找到策略资源");
}
}
public static FactoryStrategy getInstance() {
return CalPriceFactoryInstance.instance;
}
private static class CalPriceFactoryInstance {
private static FactoryStrategy instance = new FactoryStrategy();
}
}
而Context类只需要有如下代码即可实现去除if else语句
class Context {
private Strategy strategy;
/**
* 调用策略方法
*/
public void method(int param) {
strategy = FactoryStrategy.getInstance().createStrategy(param);
strategy.strategyInter();
}
}
而客户端不用改变
public static void main(String[] args) {
new Context().method(-1);
}
如果大家对于第二部分不是很明白,可以手动去实现试试,还是很有意思的。
今天小程序更细题库:进入小程序
1.什么是URL?如何创建URL?
2.什么情况下需要序列化?序列化的注意事项,如何实现java 序列化(串行化)?
3.序列化有哪些注意事项?
4.java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?
5.HTTP和HTTPS的区别
这里是今天策略模式的源码:
import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.*;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
/**
* @Auther: chenlong
* @Date: 2018/10/2 10:37
* @Description:策略
*/
public interface Strategy {
public void strategyInter();
}
@TraInterface(param = 1)
class StrategyImpO implements Strategy {
@Override
public void strategyInter() {
System.out.println("每天学Java");
}
}
@TraInterface(param = -1)
class StrategyImp1 implements Strategy {
@Override
public void strategyInter() {
System.out.println("每天学Java之策略模式学习");
}
}
class Context {
private Strategy strategy;
/**
* 调用策略方法
*/
public void method(int param) {
strategy = FactoryStrategy.getInstance().createStrategy(param);
strategy.strategyInter();
}
}
class FactoryStrategy {
private ClassLoader classLoader = getClass().getClassLoader();
private List<Class<? extends Strategy>> list;//策略列表,也就是实现类的列表
public Strategy createStrategy(int value) {
for (Class<? extends Strategy> claszz : list) {
TraInterface traInterface = handleAnnotation(claszz);
if (traInterface.param() == value) {
//是的话我们返回一个当前策略的实例
try {
return claszz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
throw new RuntimeException("策略获得失败");
}
//处理注解,我们传入一个策略类,返回它的注解
private TraInterface handleAnnotation(Class<? extends Strategy> clazz) {
Annotation[] annotations = clazz.getDeclaredAnnotations();
if (annotations == null || annotations.length == 0) {
return null;
}
for (int i = 0; i < annotations.length; i++) {
if (annotations[i] instanceof TraInterface) {
return (TraInterface) annotations[i];
}
}
return null;
}
//单例
private FactoryStrategy() {
init();
}
//在工厂初始化时要初始化策略列表
private void init() {
list = new ArrayList<Class<? extends Strategy>>();
System.out.println(FactoryStrategy.class.getResource(""));
File[] resources = getResources();//获取到包下所有的class文件
Class<Strategy> calPriceClazz = null;
try {
calPriceClazz = (Class<Strategy>) classLoader.loadClass(Strategy.class.getName());//使用相同的加载器加载策略接口
} catch (ClassNotFoundException e1) {
throw new RuntimeException("未找到策略接口");
}
for (int i = 0; i < resources.length; i++) {
try {
//载入包下的类
Class<?> clazz = classLoader.loadClass(resources[i].getName().replace(".class", ""));
//判断是否是CalPrice的实现类并且不是CalPrice它本身,满足的话加入到策略列表
if (Strategy.class.isAssignableFrom(clazz) && clazz != calPriceClazz) {
list.add((Class<? extends Strategy>) clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//获取扫描的包下面所有的class文件
private File[] getResources() {
try {
System.out.println(classLoader.getResource(""));
File file = new File(classLoader.getResource("").toURI());
return file.listFiles(new FileFilter() {
public boolean accept(File pathname) {
if (pathname.getName().endsWith(".class")) {//我们只扫描class文件
return true;
}
return false;
}
});
} catch (URISyntaxException e) {
throw new RuntimeException("未找到策略资源");
}
}
public static FactoryStrategy getInstance() {
return CalPriceFactoryInstance.instance;
}
private static class CalPriceFactoryInstance {
private static FactoryStrategy instance = new FactoryStrategy();
}
}
class Client {
public static void main(String[] args) {
new Context().method(-1);
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TraInterface {
int param() default 0;
}