My logo
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代码中的

命令模式,可以用在更多别的场景中思想是不一样的,也许实现上,接口、实现类、工厂来做的,适合的场景是不一样的。