Java设计模式之策略模式
一、策略模式简介
策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。
二、策略模式使用场景
2.1. 策略模式组成
这个模式涉及到三个角色:
- 环境(
Context
)角色:持有一个Strategy
的引用。
- 抽象策略(
Strategy
)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口
- 具体策略(
ConcreteStrategy
)角色:包装了相关的算法或行为。
2.2. 使用场景
- 去掉硬编码,可以用策略模式进行优化;(例如:不同用户不同的结算方法)
- 动态切换数据源; (例如:可以动态切换
Mysql
或者 oracle
数据库)
- 其他的具体看业务或者架构去实际使用吧。说白了就是对多个
if...else
的优化。
2.3. 优缺点
优点: 替换继承关系,避免使用多重条件转移语句。
缺点: 客户端必须知道所有策略类,并自行决定使用哪一种策略类。如果算法较多,则会造成很多的策略类。
三、策略模式实现
抽象策略角色:
/**
* @author 墨龙吟
* @version 1.0.0
* @ClassName Strategy.java
* @Description 四则运算策略接口
* @createTime 2019年11月15日
*/
public interface Strategy {
/**
* 操作
* @param a
* @param b
* @return
*/
Integer operator(Integer a, Integer b);
}
具体四则运算实现:
/**
* @author 墨龙吟
* @version 1.0.0
* @ClassName AddStrategy.java
* @Description 加法操作策略
*/
public class AddStrategy implements Strategy {
@Override
public Integer operator(Integer a, Integer b) {
return a + b;
}
}
/**
* @author 墨龙吟
* @version 1.0.0
* @ClassName DivStrategy.java
* @Description 除法操作策略
*/
public class DivStrategy implements Strategy {
@Override
public Integer operator(Integer a, Integer b) {
return a / b;
}
}
/**
* @author 墨龙吟
* @version 1.0.0
* @ClassName MulStrategy.java
* @Description 乘法操作策略
*/
public class MulStrategy implements Strategy {
@Override
public Integer operator(Integer a, Integer b) {
return a * b;
}
}
/**
* @author 墨龙吟
* @version 1.0.0
* @ClassName SubStrategy.java
* @Description 减法操作策略
*/
public class SubStrategy implements Strategy {
@Override
public Integer operator(Integer a, Integer b) {
return a - b;
}
}
上下文类 :
/**
* @author 墨龙吟
* @version 1.0.0
* @ClassName Context.java
* @Description 上下文类
*/
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public Integer getResult(Integer a, Integer b) {
return strategy.operator(a, b);
}
}
测试类:
public class Main {
public static void main(String[] args) {
Integer a = 10, b = 90;
Integer res = new Context(new AddStrategy()).getResult(a ,b);
System.out.println("a (" + a + ") + b (" + b + ") = " + res);
}
}
四、策略模式与工厂模式的区别
4.1. 相同之处:
- 都有一个抽象类或者公共接口,并且在抽象类中需要顶一个抽象方法;
- 总体来说,模式结构上差不多。
4.2. 不同之处
- 工厂模式主要使用来创建对象(创建型模式,关注对象创建),通过不同的条件创建对象;
- 策略模式主要是实现的封装(同一个对象的不同行为,主要关注行为的选择),算法不同,需要增加子类进行实现;
- 策略模式需要有一个上下文类,通过组合方式(不是继承)获取子类,并且提供统一的客户端接口。
五、策略模式和工厂模式整合
只需要修改上下文类:
/**
* @author 墨龙吟
* @version 1.0.0
* @ClassName Context.java
* @Description TODO
*/
public class Context {
private Strategy strategy;
// 整合和工厂模式创建实现类
public Context(Class clazz) {
try {
this.strategy = (Strategy) Class.forName(clazz.getName()).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
}
public Integer getResult(Integer a, Integer b) {
return strategy.operator(a, b);
}
}
测试类:
public class Main {
public static void main(String[] args) {
Integer a = 10, b = 90;
Integer res = new Context(new AddStrategy()).getResult(a ,b);
System.out.println("a (" + a + ") + b (" + b + ") = " + res);
}
}
六、进阶:策略枚举
在设计模式之禅中还有一个策略枚举的方法可以实现。其实很简单,简单介绍下,我们还是对上面的那个四则运算的策略方法进行修改。
/**
* @author 墨龙吟
* @version 1.0.0
* @ClassName StrategyEnum.java
* @Description 策略枚举
*/
public enum StrategyEnum {
/** 加法 */
ADD("+") {
@Override
public Integer operator(Integer a, Integer b) {
return a + b;
}
},
/** 减法 */
SUB("-") {
@Override
public Integer operator(Integer a, Integer b) {
return a - b;
}
},
/** 除法 */
DIV("/") {
@Override
public Integer operator(Integer a, Integer b) {
return a / b;
}
},
/** 乘法 */
MUL("*") {
@Override
public Integer operator(Integer a, Integer b) {
return a * b;
}
}
;
private String key;
StrategyEnum(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public abstract Integer operator(Integer a, Integer b);
}
测试:
public class Main {
public static void main(String[] args) {
System.out.println(StrategyEnum.ADD.operator(2, 5));
System.out.println(StrategyEnum.SUB.operator(2, 5));
System.out.println(StrategyEnum.DIV.operator(2, 5));
System.out.println(StrategyEnum.MUL.operator(2, 5));
}
}
七、总结
设计模式关键,还是在实际代码中的使用体验,在完成日常开发的过程中,对自己写的代码想想是不是还有优化空间,力求写出完美的代码,一直在学习的路上,一起加油!
最后欢迎关注下公众号