- Published on
strategy 策略模式(重要)
策略模式的原理与实现(Strategy Design Pattern)
定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它 们的客户端(这里的客户端代指使用算法的代码)。
1. 策略的定义
策略类的定义比较简单,包含一个策略接口和一组实现这个接口的策略类。因为所有的策略 类都实现相同的接口, 所以,客户端代码基于接口而非实现编程,可以灵活地替换不同的策 略。示例代码如下所示:
public interface Strategy {
void algorithmInterface();
}
public class ConcreteStrategyA implements Strategy {
@Override
public void algorithmInterface() {
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void algorithmInterface() {
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void algorithmInterface() {
}
}
2. 策略的创建
因为策略模式会包含一组策略,在使用它们的时候,一般会通过类型(type)来判断创建哪个策略来使用。 为了封装创建逻辑,我们需要对客户端代码屏蔽创建细节。我们可以把根据 type 创建策略的逻辑抽离出来, 放到工厂类中。示例代码如下所示:
/**
* 如果策略类是无状态的,不包含成员变量,只是纯粹的算法实现,这样的策略对象是可以被共享使用的,不需要在每次调用 getStrategy() 的时候,
* 都创建一个新的策略对象。针对这种情况,我们可以使用上面这种工厂类的实现方式,事先创建好每个策略对象,缓存到工厂类中,用的时候直接返回。
*/
public class StatelessStrategyFactory {
private static final Map<String, Strategy> strategies = new HashMap<>();
static {
strategies.put("A", new ConcreteStrategyA());
strategies.put("B", new ConcreteStrategyB());
}
public static Strategy getStrategy(String type) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type should not be empty.");
}
return strategies.get(type);
}
}
/**
* 如果策略类是有状态的,根据业务场景的需要,我们希望每次从工厂方法中,获得的都是新创建的策略对象,而不是缓存好可共享的策略对象,那我们就需要按照如下方式来实现策略工厂类。
*/
public class StrategyFactory {
public static Strategy getStrategy(String type) {
if (type == null || type.isEmpty()) {
throw new IllegalArgumentException("type should not be empty.");
}
if (type.equals("A")) {
return new ConcreteStrategyA();
} else if (type.equals("B")) {
return new ConcreteStrategyB();
}
return null;
}
}
3. 策略的使用
这里的“运行时动态”指的是,我们事先并不知道会使用哪个策略,而是在程序运行期间, 根据配置、用户输入、计算结果等这些不确定因素,动态决定使用哪种策略。 如何利用策略模式避免分支判断? 实际上, 能够移除分支判断逻辑的模式不仅仅有策略模式,后面我们要讲的状态模式也可以
。对于使用哪种模式,具体还要看应用场景来定。 策略模式适用于根据不同类型待动态,决定使用哪种策略这样一种应用场景。
场景(1)我们可以根据不同的参数来选择一种我们想要执行的优惠计价方式
package wang.jinggo.basics.strategy;
/**
* @author: wangyj
* @create: 2021-10-24
* @version: 1.0.0
**/
public class WithoutStrategyPatternDemo {
public static void main(String[] args) {
// 有一个参数,是discountStyle
// 如果这个参数1,那么选择一种优惠计价的方式
// 如果这个参数2,那么选择一种优惠计价的方式
// 如果这个参数3,那么选择一种优惠计价的方式
int discountStyle = 1;
if(discountStyle == 1) {
System.out.println("执行优惠计价方式1的复杂业务逻辑");
} else if(discountStyle == 2) {
System.out.println("执行优惠计价方式2的复杂业务逻辑");
} else if(discountStyle == 3) {
System.out.println("执行优惠计价方式3的复杂业务逻辑");
} else {
System.out.println("执行默认的优惠计价方式的复杂业务逻辑");
}
// 实际上在我们的业务代码,if else,看起来绝对不是这么短的,也不是这么简单
// 在实际的业务代码中,常见的结构是上面这样的,但是每个if和if else之间的代码行数,可能多达几十行,甚至几百行
// if和else if的判断条件,很模糊,经常就是用一堆变量的比较来判断,是走哪个分支
// 类似上面这样的代码,会让 我们在代码写好以后,1年之后,回过头来看这个代码,哇塞,看到了一坨屎一样
// 看都看不懂了:代码太多了,if else if之后的条件判断,我们都看不懂,不知道在判断,if else if里面的代码,量太大了
// 定位个bug,疯了,或者是对这段代码的业务逻辑做一些改动,疯了
// 基本上,你得花个一两天的时间,把这段代码重新读一遍,然后看懂,才能下手写代码
// 而我跟大家说句实话,很多同学,担心,培训班出来的人特别多,从0基础开始培训的那些人,他们出来就拿10k的月薪
// 市场上的行情是这样
// 不代表说,市场给他10k的薪资,他就写一手漂亮代码
// 他拿着10k,15k,20k薪资的同学,也还算是从一些比较好的互联网公司过来的
// 写出来了屎一样,没有任何面向对象的设计,大量的复杂业务逻辑就集中在少数一辆个类里面
// 每个类里面,大量的充斥着,一个方法几百行,上千行代码
// 大量的变量命名,int a = 1,if(b = 10)
// if else if else if else if else,大量这种垃圾
// 对自己没有任何追求,自己写一手烂代码,烂把,天气好,吃的好,日子好,跳槽重新是一条好汉
}
}
package wang.jinggo.basics.strategy;
/**
* @author: wangyj
* @create: 2021-10-24
* @version: 1.0.0
**/
public class StrategryPatternDemo {
public static void main(String[] args) {
int discountStyle = 1;
DiscountCalculateStrategy strategy = DiscountCalculateStrategryFactory
.getDiscountCalculateStrategy(discountStyle);
Context context = new Context();
context.setStrategy(strategy);
context.calculate();
// 要点1:必须将if else的代码,封装到不同的策略类中
// 要点2:将选择哪种策略的逻辑给放到一个工厂类中去,选择策略的代码务必很简洁
// 要点3:context可有可无,具体是看你的策略执行这块如果就一行代码调用,不需要context
// 如果context中的策略执行逻辑较为复杂一点,context来封装策略类的执行逻辑
}
public interface DiscountCalculateStrategy {
void calculate();
}
public static class DiscountCalculateStrategyA implements DiscountCalculateStrategy {
public void calculate() {
System.out.println("执行优惠计价方式1的复杂业务逻辑");
}
}
public static class DiscountCalculateStrategyB implements DiscountCalculateStrategy {
public void calculate() {
System.out.println("执行优惠计价方式2的复杂业务逻辑");
}
}
public static class DiscountCalculateStrategyC implements DiscountCalculateStrategy {
public void calculate() {
System.out.println("执行优惠计价方式3的复杂业务逻辑");
}
}
public static class DiscountCalculateStrategyDefault implements DiscountCalculateStrategy {
public void calculate() {
System.out.println("执行默认的优惠计价方式的复杂业务逻辑");
}
}
public static class DiscountCalculateStrategryFactory {
public static DiscountCalculateStrategy getDiscountCalculateStrategy(int discountStyle) {
if(discountStyle == 1) {
return new DiscountCalculateStrategyA();
} else if(discountStyle == 2) {
return new DiscountCalculateStrategyB();
} else if(discountStyle == 3) {
return new DiscountCalculateStrategyC();
} else {
return new DiscountCalculateStrategyDefault();
}
}
}
public static class Context {
private DiscountCalculateStrategy strategy;
public DiscountCalculateStrategy getStrategy() {
return strategy;
}
public void setStrategy(DiscountCalculateStrategy strategy) {
this.strategy = strategy;
}
public void calculate() {
strategy.calculate();
}
}
}
总结:
策略模式,将会成为最最高频使用的一种设计模式,他的常见应用场景,就是替换掉那一大坨复杂难懂的if else if else。
对于那种过于复杂的选择判断逻辑,完全可以将选择哪种策略的过程放到工厂里去。工厂,可以是简单工厂,也可以是工厂方法,也可以是抽象工厂。
我们这里打算用抽象工厂模式,跟策略模式结合起来,大家可以想象一下复杂的场景,一个复杂的业务逻辑里面,每个if else判断之后,
要跟一堆复杂的业务逻辑,我们可以将不同的业务逻辑抽取成不同的策略,然后一个具体的工厂实现,可以创建出一个策略组合来。
策略模式跟命令模式的区别?看起来一样的,但是用处不一样。命令是可以发送出去,然后可以经过一些队列的流转,比如先把命令发送到MQ,
接着再处理。策略是说选择了一组策略,立即就要执行的,不会经过其他别的什么处理。而且策略逻辑基本就是用在复杂的if else代码中的
命令模式,可以用在更多别的场景中思想是不一样的,也许实现上,接口、实现类、工厂来做的,适合的场景是不一样的。