首页 > 编程语言 >Java设计模式-策略模式

Java设计模式-策略模式

时间:2024-01-02 18:02:33浏览次数:61  
标签:Java 策略 模式 class charge ChargeStrategy ChargeType 设计模式 public

简介

策略模式是指有一定行动内容的相对稳定的策略名称,策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法

策略模式:

  • 定义了一组算法(业务规则)
  • 封装了每个算法
  • 这族的算法可互换代替

组成

  • 抽象策略角色: 策略类,通常由一个接口或者抽象类实现
  • 具体策略角色:包装了相关的算法和行为
  • 环境角色:持有一个策略类的引用,最终给客户端调用

应用场景

  1. 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为
  2. 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现
  3. 对客户隐藏具体策略(算法)的实现细节,彼此完全独立
  4. 一个类定义了多种行为,并且这些行为在类的操作中以多个条件语句的形式出现

优点:

  • 策略模式符合开闭原则
  • 避免使用多重条件转移语句,如if...else...语句、switch 语句
  • 使用策略模式可以提高算法的保密性和安全性

缺点:

  • 客户端必须知道所有的策略,并且自行决定使用哪一个策略类
  • 代码中会产生非常多策略类,增加维护难度

实际应用

抽象策略类

/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
public interface ChargeStrategy {

    /**
     * 计算收费
     *
     * @param cost
     * @return
     */
    double charge(long cost);
}

具体策略类-内部收费类

/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
@TaxTypeAnnotation(taxType = ChargeType.INTERNAL)
class InternalStrategy implements ChargeStrategy {
    @Override
    public double charge(long cost) {
        final double taxRate = 0.2;
        return cost * taxRate;
    }
}

具体策略类-外部收费类

/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
@TaxTypeAnnotation(taxType = ChargeType.EXTERNAL)
class ExternalTaxStrategy implements ChargeStrategy {
    @Override
    public double charge(long cost) {

        return cost;
    }
}

收费类型定义

public enum ChargeType {
    INTERNAL, EXTERNAL
}

策略工厂

通过if-else获取策略类

/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
@Component
public class ChargeStrategyFactory {
    public static ChargeStrategy getChargeStrategy(ChargeType taxType) throws Exception {
        
        if (taxType == ChargeType.INTERNAL) {
            return new InternalStrategy();
        } else if (taxType == ChargeType.EXTERNAL) {
            return new ExternalTaxStrategy();
        } else {
            throw new Exception("未配置相应策略");
        }
    }
}
@Test
public void test1(){
    try {
        ChargeStrategy chargeStrategy = ChargeStrategyFactory.getChargeStrategy(ChargeType.INTERNAL);
        double charge = chargeStrategy.charge(100);
        System.out.println("内部价格:"+charge);

        chargeStrategy = ChargeStrategyFactory.getChargeStrategy(ChargeType.EXTERNAL);
        charge = chargeStrategy.charge(100);
        System.out.println("外部价格:"+charge);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

输出结果:

内部价格:20.0
外部价格:100.0
  • 如图,通过传入不同的收费类型,由策略工厂进行判断,赋予相应的处理策略
  • 可以看到,如果通过if语句来获取不同的税策略,当增加新的税策略时就不得不修改已有代码,当方法很多时,就不那么好看,同时也增加了复杂度

通过Map获取策略类

/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
public class MapChargeStrategyFactory {
    
    /**
     *存储策略
     */
    static Map<ChargeType, ChargeStrategy> chargeStrategyMap = new HashMap<>();

    // 注册默认策略
    static {
        registerChargeStrategy(ChargeType.INTERNAL, new InternalStrategy());
        registerChargeStrategy(ChargeType.EXTERNAL, new ExternalTaxStrategy());
    }

    /**
     * 提供注册策略接口,外部只需要调用此接口接口新增策略
     */
    public static void registerChargeStrategy(ChargeType taxType, ChargeStrategy taxStrategy) {
        chargeStrategyMap.put(taxType, taxStrategy);
    }

    /**
     * 通过map获取策略,当增加新的策略时无需修改代码
     */

    public static ChargeStrategy getChargeStrategy(ChargeType taxType) throws Exception {
        // 当增加新的税类型时,需要修改代码,同时增加圈复杂度
        if (chargeStrategyMap.containsKey(taxType)) {
            return chargeStrategyMap.get(taxType);
        } else {
            throw new Exception("未配置相应策略");
        }
    }
}
@Test
public void test2(){
    try {
        ChargeStrategy chargeStrategy = MapChargeStrategyFactory.getChargeStrategy(ChargeType.INTERNAL);
        double charge = chargeStrategy.charge(100);
        System.out.println("内部价格:"+charge);

        chargeStrategy = MapChargeStrategyFactory.getChargeStrategy(ChargeType.EXTERNAL);
        charge = chargeStrategy.charge(100);
        System.out.println("外部价格:"+charge);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

输出:

内部价格:20.0
外部价格:100.0
  • 可以看到,通过map进行获取策略结果相同,进化后if语句没有了,减少了复杂度,增加新的策略后只需调用策略注册接口就好,不需要修改获取策略的代码

通过策略自动注册获取策略类

策略类添加自动注册策略的方法

/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
public interface ChargeStrategy {

    /**
     * 计算收费
     *
     * @param cost
     * @return
     */
    double charge(long cost);

    /**
     * 自动注册策略方法
     */
    void register();
}

具体策略实现类

/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
@TaxTypeAnnotation(taxType = ChargeType.EXTERNAL)
class ExternalTaxStrategy implements ChargeStrategy {
    @Override
    public double charge(long cost) {

        return cost;
    }

    @Override
    public void register() {
        AutoRegisterChargeStrategyFactory.registerChargeStrategy(ChargeType.EXTERNAL, this);
    }
}
/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
@TaxTypeAnnotation(taxType = ChargeType.INTERNAL)
class InternalStrategy implements ChargeStrategy {
    @Override
    public double charge(long cost) {
        final double taxRate = 0.2;
        return cost * taxRate;
    }

    @Override
    public void register() {
        AutoRegisterChargeStrategyFactory.registerChargeStrategy(ChargeType.INTERNAL, this);
    }
}
/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
public class AutoRegisterChargeStrategyFactory {

    /**
     * 存储策略
     */
    static Map<ChargeType, ChargeStrategy> chargeStrategyMap = new HashMap<>();

    static {
        autoRegisterTaxStrategy();
    }

    /**
     * 通过map获取策略,当增加新的策略时无需修改代码
     */
    public static ChargeStrategy getChargeStrategy(ChargeType taxType) throws Exception {
        // 当增加新的税类型时,需要修改代码,同时增加圈复杂度
        if (chargeStrategyMap.containsKey(taxType)) {
            return chargeStrategyMap.get(taxType);
        } else {
            throw new Exception("未配置相应策略");
        }
    }

    /**
     * 提供税注册策略接口
     */
    public static void registerChargeStrategy(ChargeType chargeType, ChargeStrategy chargeStrategy) {
        chargeStrategyMap.put(chargeType, chargeStrategy);
    }

    /**
     * 自动注册策略
     */

    private static void autoRegisterTaxStrategy() {
        try {
            // 通过反射找到所有的策略子类进行注册
            Reflections reflections = new Reflections(new ConfigurationBuilder()
                    .setUrls(ClasspathHelper.forPackage(ChargeStrategy.class.getPackage().getName()))
                    .setScanners(new SubTypesScanner()));
            Set<Class<? extends ChargeStrategy>> taxStrategyClassSet = reflections.getSubTypesOf(ChargeStrategy.class);

            if (taxStrategyClassSet != null) {
                for (Class<?> clazz : taxStrategyClassSet) {
                    ChargeStrategy chargeStrategy = (ChargeStrategy) clazz.newInstance();
                    // 调用策略的自注册方法
                    chargeStrategy.register();
                }
            }
        } catch (InstantiationException | IllegalAccessException e) {
            // 自行定义异常处理
            e.printStackTrace();
        }
    }
}
@Test
public void test3(){
    try {
        ChargeStrategy chargeStrategy = AutoRegisterChargeStrategyFactory.getChargeStrategy(ChargeType.INTERNAL);
        double charge = chargeStrategy.charge(100);
        System.out.println("内部价格:"+charge);

        chargeStrategy = AutoRegisterChargeStrategyFactory.getChargeStrategy(ChargeType.EXTERNAL);
        charge = chargeStrategy.charge(100);
        System.out.println("外部价格:"+charge);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

输出

内部价格:20.0
外部价格:100.0
  • 可以看到,通过策略自动注册进行获取策略结果相同,当添加新的税策略时,就完全不需要修改已有的策略工厂代码,基本完美做到开闭原则,唯一需要修改的是类型定义

通过注解减少耦合

新增加自定义注解类

/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/26
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface ChargeTypeAnnotation {
    ChargeType taxType();
}

策略去掉了注册方法,添加ChargeTypeAnnotation注解来识别是哪种税类型

/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
public interface ChargeStrategy {

    /**
     * 计算收费
     *
     * @param cost
     * @return
     */
    double charge(long cost);
}
/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
@ChargeTypeAnnotation(taxType = ChargeType.INTERNAL)
class InternalStrategy implements ChargeStrategy {
    @Override
    public double charge(long cost) {
        final double taxRate = 0.2;
        return cost * taxRate;
    }
}
/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/11
 */
@ChargeTypeAnnotation(taxType = ChargeType.EXTERNAL)
class ExternalTaxStrategy implements ChargeStrategy {
    @Override
    public double charge(long cost) {

        return cost;
    }
}

注解类策略工厂

/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/26
 */
public class AnnotationChargeStrategyFactory {

    /**
     * 存储策略
     */
    static Map<ChargeType, ChargeStrategy> chargeStrategyMap = new HashMap<>();

    static {
        registerChargeStrategy();
    }

    /**
     *     通过map获取策略,当增加新的策略时无需修改代码
      */
    public static ChargeStrategy getChargeStrategy(ChargeType chargeType) throws Exception {
        // 当增加新的类型时,需要修改代码
        if (chargeStrategyMap.containsKey(chargeType)) {
            return chargeStrategyMap.get(chargeType);
        } else {
            throw new Exception("未配置相应策略");
        }
    }

    /**
     * 自动注册策略
     */

    private static void registerChargeStrategy() {
        // 通过反射找到所有的策略子类进行注册
        Reflections reflections = new Reflections(new ConfigurationBuilder()
                .setUrls(ClasspathHelper.forPackage(ChargeStrategy.class.getPackage().getName()))
                .setScanners(new SubTypesScanner()));
        Set<Class<? extends ChargeStrategy>> taxStrategyClassSet = reflections.getSubTypesOf(ChargeStrategy.class);

        if (taxStrategyClassSet != null) {
            for (Class<?> clazz : taxStrategyClassSet) {
                // 找到类型注解,自动完成策略注册
                if (clazz.isAnnotationPresent(ChargeTypeAnnotation.class)) {
                    ChargeTypeAnnotation taxTypeAnnotation = clazz.getAnnotation(ChargeTypeAnnotation.class);
                    ChargeType chargeType = taxTypeAnnotation.taxType();
                    try {
                        chargeStrategyMap.put(chargeType, (ChargeStrategy) clazz.newInstance());
                    } catch (InstantiationException | IllegalAccessException e) {
                        e.getStackTrace();
                    }
                }
            }
        }
    }
}
@Test
public void test4(){
    try {
        ChargeStrategy chargeStrategy = AnnotationChargeStrategyFactory.getChargeStrategy(ChargeType.INTERNAL);
        double charge = chargeStrategy.charge(100);
        System.out.println("内部价格:"+charge);

        chargeStrategy = AnnotationChargeStrategyFactory.getChargeStrategy(ChargeType.EXTERNAL);
        charge = chargeStrategy.charge(100);
        System.out.println("外部价格:"+charge);

    } catch (Exception e) {
        e.printStackTrace();
    }
}



  • 可以看到,通过注解方式确定策略类的方式获取策略结果相同,是代码更加简洁。

注意: 测试阶段这么写不会出问题,但是小编在通过优化代码时,调用接口,发现这样会导致注入的对象为null值,从而导致程序报空指针异常,起初没发现,不知道为什么按照正常程序开发,会导致通过autowired方式注入为null,经过查阅资料和断点代码,发现了问题所在

  • 图中框住的部分,通过反射代码获取类的时候,我们获得类的方式是 class.newInstance, 这种写法没有与Spring容器关联起来获取bean,虽然也能拿到但是如果类里面有@Autowired这种方式注入的对象就会空了
解决方式

通过spring上下文获得类

@Service
public  class ApplicationContextHelper implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {

        applicationContext = context;
    }

    /**
     * 获取bean
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T popBean(Class<T> clazz) {
        //先判断是否为空
        if (applicationContext == null) {
            return null;
        }
        return applicationContext.getBean(clazz);
    }


    public static <T> T popBean(String name, Class<T> clazz) {
        if (applicationContext == null) {
            return null;
        }

        return applicationContext.getBean(name, clazz);

    }
}
/**
 * @author feiniu
 * @description 描述
 * @date 2021/10/26
 */
public class AnnotationChargeStrategyFactory {

    /**
     * 存储策略
     */
    static Map<ChargeType, ChargeStrategy> chargeStrategyMap = new HashMap<>();

    static {
        registerChargeStrategy();
    }

    /**
     *     通过map获取策略,当增加新的策略时无需修改代码
      */
    public static ChargeStrategy getChargeStrategy(ChargeType chargeType) throws Exception {
        // 当增加新的类型时,需要修改代码
        if (chargeStrategyMap.containsKey(chargeType)) {
            return chargeStrategyMap.get(chargeType);
        } else {
            throw new Exception("未配置相应策略");
        }
    }

    /**
     * 自动注册策略
     */

    private static void registerChargeStrategy() {
        // 通过反射找到所有的策略子类进行注册
        Reflections reflections = new Reflections(new ConfigurationBuilder()
                .setUrls(ClasspathHelper.forPackage(ChargeStrategy.class.getPackage().getName()))
                .setScanners(new SubTypesScanner()));
        Set<Class<? extends ChargeStrategy>> taxStrategyClassSet = reflections.getSubTypesOf(ChargeStrategy.class);

        if (taxStrategyClassSet != null) {
            for (Class<?> clazz : taxStrategyClassSet) {
                // 找到类型注解,自动完成策略注册
                if (clazz.isAnnotationPresent(ChargeTypeAnnotation.class)) {
                    ChargeTypeAnnotation taxTypeAnnotation = clazz.getAnnotation(ChargeTypeAnnotation.class);
                    ChargeType chargeType = taxTypeAnnotation.taxType();
                    chargeStrategyMap.put(chargeType, (ChargeStrategy) ApplicationContextHelper.popBean(clazz));
                }
            }
  • 通过这种方式就可以解决上述问题,完成自动注册策略的优化

标签:Java,策略,模式,class,charge,ChargeStrategy,ChargeType,设计模式,public
From: https://blog.51cto.com/u_15590807/9071780

相关文章

  • 如何在 Ubuntu 20.04 上以独立模式设置 MinIO 对象存储服务器
    前些天发现了一个人工智能学习网站,通俗易懂,风趣幽默,最重要的屌图甚多,忍不住分享一下给大家。点击跳转到网站。如何在Ubuntu20.04上以独立模式设置MinIO对象存储服务器介绍存储非结构化对象数据blob并使其可通过HTTPAPI(称为对象存储)进行访问的能力是现代技术领域不可或......
  • javafx中PDF文件的展示(转图片)
    try{//加载pdf文件PDDocumentdocument=PDDocument.load(file);//将pdf转换成图片PDFRendererrenderer=newPDFRenderer(document);//获取pdf的页数intnumberOfPages=document.getNumberOfPages();PDFRendererpdfRenderer=newPDF......
  • 无涯教程-Java 正则 - \Z匹配函数
    边界匹配器[\Z]匹配输入的末尾,但匹配最终终止符(如果有)。Z-示例以下示例显示了边界匹配器的用法。packagecom.learnfk;importjava.util.regex.Matcher;importjava.util.regex.Pattern;publicclassBoundaryMatcherDemo{privatestaticfinalStringREGEX......
  • 无涯教程-Java 正则 - \A匹配函数
    边界匹配器[\A]匹配输入的开头。A-示例以下示例显示了边界匹配器的用法。packagecom.learnfk;importjava.util.regex.Matcher;importjava.util.regex.Pattern;publicclassBoundaryMatcherDemo{privatestaticfinalStringREGEX="\\A";privatestat......
  • 无涯教程-Java 正则 - \B匹配函数
    边界匹配器[\B]匹配非单词边界。B-示例以下示例显示了边界匹配器的用法。packagecom.learnfk;importjava.util.regex.Matcher;importjava.util.regex.Pattern;publicclassBoundaryMatcherDemo{privatestaticfinalStringREGEX="\\B";privatestat......
  • 【设计模式】单例模式——clone()方法破坏单例模式的情景
    Java的对象不仅可以new关键字产生,也可以利用clone()方法产生,或者利用反射和反序列化产生。用DCL或静态内部类实现单例模式都是利用private修饰构造函数来解决new关键字产生多个对象的问题的,但单例模式仍然可以被clone()方法、反射和反序列化破坏。如下代码所示,让单例类实现Cloneabl......
  • 安卓期末大作业-记账app(含源码+导出app+运行截图)安卓期末大作业-记账app(含源码+导出ap
    安卓期末大作业-记账app(含源码+导出app+运行截图)开发软件:AndroidStudio开发语言:Java2023年上半年移动开发期末大作业记账app,老师给了95分,可以记录各种类型的账目支出记录,收入记录,存储到数据库中,可以隐藏账目记录,可以记录和删除记录,还可以将收支记录转图表分析,也可清空所有数据。具......
  • 无涯教程-Java 正则 - ^匹配函数
    边界匹配器[^]匹配行的开头。^-示例下面的示例显示边界匹配器的用法。packagecom.learnfk;importjava.util.regex.Matcher;importjava.util.regex.Pattern;publicclassBoundaryMatcherDemo{privatestaticfinalStringREGEX="^";privatestaticfi......
  • 一款降压型开关模式转换器解决方案
    一、基本概述TX4145是一款降压型开关模式转换器。TX4145在6-60V宽输入电源范围内实现不同峰值输出电流,并且具有出色的线电压和负载调整率。TX4145采用PWM电流模工作模式,环路易于稳定并提供快速的瞬态响应。TX4145外部提供FS脚,可通过外接一个电阻设置工作频率。TX4145集成......
  • 无涯教程-Java 正则 - \P{InGreek}匹配函数
    字符类\P{InGreek}匹配除希腊符号以外的任何字符。P{InGreek}-示例下面的示例显示Unicode字符类匹配的用法。packagecom.learnfk;importjava.util.regex.Matcher;importjava.util.regex.Pattern;publicclassUnicodeCharacterClassDemo{privatestaticfina......