一个菜鸡JAVA后端的博客~

设计模式-策略模式

策略模式是一种行为模式,之所以放在工厂模式的后面来讲,是因为这个模式比较简单,而且和工厂模式混用起来很方便也很常用。

定义

  • Define a family of algorithms,encapsulate each one,and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使他们可以互换)

优点

  • 算法可以自由切换:只要实现了抽象策略,那么就成为了一个策略,通过封装角色对其封装,保证对外提供“可自由变换”的策略
  • 避免多重条件判断:简单来说就是可以减少你代码里面的if else。
  • 扩展良好:简单来说就是要增加一个if else的条件,只需要增加一个策略

缺点

  • 策略类的数量多:每一个策略对应一个类,复用的可能小。
  • 所有的策略类都要对外暴露:上层模块必须知道有那些策略,然后才能决定使用哪一个策略。不符合迪米特法则。(可以通过工厂模式,代理模式,享元模式来解决这个问题

使用场景

  • 需求:使用场景有多个
    • 多个类只有在算法或行为上稍有不同的场景
    • 算法需要自由切换的场景:如银行业务。
    • 需要屏蔽算法规则的场景:只需要知道输入就会给你相应的输出。

代码

通用类图

策略模式通用类图

基本的代码

类图

通用模板类图

代码

策略模式基本的代码很简单,而且某种程度上你会发现你甚至用过这段代码。
策略接口类(接口

package com.pattern.strategy.template;
/**
 * 策略的接口,只要实现了这个接口,那么就是一个策略
 */
public interface Strategy {
    
    void doSomething();
}

具体策略类(继承自策略接口类

package com.pattern.strategy.template.impl;

import com.pattern.strategy.template.Strategy;

public class StrategyImpl1 implements Strategy {
    @Override
    public void doSomething() {
        System.out.println("执行了策略1");
    }
}
package com.pattern.strategy.template.impl;

import com.pattern.strategy.template.Strategy;

public class StrategyImpl2 implements Strategy {
    @Override
    public void doSomething() {
        System.out.println("执行了策略2");
    }
}

封装类(

package com.pattern.strategy.template;
/**
 * 具体的封装类,用于封装算法
 */
public class Context {
    private Strategy strategy;
    public Context(Strategy strategy){
        this.strategy = strategy;
    }
    public void doSomething(){
        this.strategy.doSomething();
    }
}

测试代码

package com.pattern;

import com.pattern.strategy.template.Context;
import com.pattern.strategy.template.Strategy;
import com.pattern.strategy.template.impl.StrategyImpl1;
import org.junit.Test;

public class StrategyTest {
    @Test
    public void StrategyTemplateTest(){
        // 选择一个具体的策略
        Strategy strategy = new StrategyImpl1();
        Context context = new Context(strategy);
        context.doSomething();
    }
}

结果

执行结果

策略枚举

策略枚举是策略模式的一种扩展,为方便解释,这里采用加减法计算的场景。

代码

策略枚举代码

package com.pattern.strategy.strategyenum;

/**
 * 策略枚举:策略模式的一种变种,将枚举和策略结合在一起。可以有效的减少类的数量
 * 相当于一个封装类
 * 当然,我们也能创建一个类,然后将不同的策略类内置到封装类里面,但是会降低可读性,不推荐
 */
public enum Calculator {
    ADD("+") {
        @Override
        public int exec(int a, int b) {
            // TODO Auto-generated method stub
            return a + b;
        }
    },
    SUB("-") {
        @Override
        public int exec(int a, int b) {
            // TODO Auto-generated method stub
            return a - b;
        }
    };
    public abstract int exec(int a, int b);
    //运算符
    private String value = "";
    private Calculator(String value) {
        this.value = value;
    }
    public String getValue() {
        return value;
    }
}

测试代码

package com.pattern;

import com.pattern.strategy.strategyenum.Calculator;
import com.pattern.strategy.template.Context;
import com.pattern.strategy.template.Strategy;
import com.pattern.strategy.template.impl.StrategyImpl1;
import org.junit.Test;

public class StrategyTest {
    @Test
    public void StrategyEnumTest(){
        // 执行加法运算
        System.out.println("执行加法运算:");
        System.out.println("1+3 = " + Calculator.ADD.exec(1,3));
        // 执行减法运算
        System.out.println("执行减法运算:");
        System.out.println("5-3 = " + Calculator.SUB.exec(5,3));
    }
}

运行结果

运行结果

注意

  • 如果一个策略家族的具体策略数量超过了4个,就要考虑使用混合模式,来解决对外暴露的问题,否则会降低代码的可维护性。
  • 策略枚举是一个优秀的模式,但是受枚举类型的限制,每个枚举都是public、final、static的,所以扩展性会有一定的约束,在实际开发中,策略枚举一般担当不经常发生变化的角色。

NOTE: 文章若无特别说明均为原创文章,如果要转载请保留出处!