• JAVA编程思想,去掉别扭的if,自注册策略模式优雅满足开闭原则,80%的人都不知道


    1. 策略模式原型举例

    现在要实现一个算税策略,税计算类型有价内税和价外税,将来可能会增加新的税类型,初始设计类结构如下:

    职责
    TaxStrategy 税策略接口
    InterTaxStrategy 价内税策略,负责计算价内税
    OuterTaxStrategy 价外税策略,负责计算价外税
    TaxType 税类型定义,当前只有价内税和价外税
    TaxStrategyFactory 税策略工厂,根据税类型获取不同的税策略来算税

    这里还要注意:不管你是为了JAVA高薪还是爱好,记住:项目开发经验永远是核心,如果你没有最新JAVA架构实战视频教程及大厂面试宝典,可以去小编的Java架构学习.裙 :七吧伞吧零而衣零伞 (数字的谐音)转换下可以找到了,里面很多新JAVA架构项目教程,还可以跟老司机交流讨教! 


    2. 代码

    2.1. 税策略代码

    public interface TaxStrategy {
        double calc(long amount);
    }
    
    class InterTaxStrategy implements TaxStrategy {
        @Override public double calc(long amount) {
            final double taxRate = 0.2;  // 获取税率
            return amount * taxRate;
        }
    }
    
    class OuterTaxStrategy implements TaxStrategy {
        @Override public double calc(long amount) {
            final double taxRate = 0.2;  // 获取税率
            return amount / (1 + taxRate) * taxRate;
        }
    }
    
    // 税类型定义
    public enum TaxType {
        INTER, OUTER
    }
    复制代码

    2.2. IF语句实现的税策略工厂

    // 税策略工厂
    public class TaxStrategyFactory {
        public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
            // 当增加新的税类型时,需要修改代码,同时会增加圈复杂度
            if (taxType == TaxType.INTER) {
                return new InterTaxStrategy();
            } else if (taxType == TaxType.OUTER) {
                return new OuterTaxStrategy();
            } else {
                throw new Exception("The tax type is not supported.");
            }
        }
    }
    复制代码

    可以看到,如果通过if语句来获取不同的税策略,当增加新的税策略时就不得不修改已有代码,当算税方法很多时,就不那么好看,同时也增加了圈复杂度

    2.3. 首次优化 税策略工厂中使用Map替代if

    public class MapTaxStrategyFactory {
        // 存储税策略
        static Map<TaxType, TaxStrategy> taxStrategyMap = new HashMap<>();
    
        // 注册默认税策略
        static {
            registerTaxStrategy(TaxType.INTER, new InterTaxStrategy());
            registerTaxStrategy(TaxType.OUTER, new OuterTaxStrategy());
        }
    
        // 提供税注册策略接口,外部只需要调用此接口接口新增税策略,而无需修改策略工厂内部代码
        public static void registerTaxStrategy(TaxType taxType, TaxStrategy taxStrategy) {
            taxStrategyMap.put(taxType, taxStrategy);
        }
    
        // 通过map获取税策略,当增加新的税策略时无需修改代码,对修改封闭,对扩展开放,遵循开闭原则
        public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
            // 当增加新的税类型时,需要修改代码,同时增加圈复杂度
            if (taxStrategyMap.containsKey(taxType)) {
                return taxStrategyMap.get(taxType);
            } else {
                throw new Exception("The tax type is not supported.");
            }
        }
    }
    复制代码

    可以看到,进化后IF语句没有了,减少了圈复杂度,增加新的策略后只需调用策略注册接口就好,不需要修改获取税策略的代码

    2.4. 二次优化 策略自动注册

    在上面的实现中,要注册新的税策略,必须手动调用MapTaxStrategyFactory的注册接口,这样,每新增加一个税策略都需要修改已有代码,或者要找到一个合适的初始化调用点,去注册税策略,如何能完美的符合开闭原则,对修改关闭,对扩展开放呢

    再次优化后,类结构如下:

    职责
    TaxStrategy 税策略接口,提供算税接口,同时自注册到税策略工厂中
    InterTaxStrategy 价内税策略,负责计算价内税
    OuterTaxStrategy 价外税策略,负责计算价外税
    TaxType 税类型定义,当前只有价内税和价外税
    AutoRegisterTaxStrategyFactory 税策略工厂,根据税类型获取不同的税策略来算税,同时提供税策略注册接口

    下面我看变化后的代码:

    2.4.1. 税策略

    public interface TaxStrategy {
        double calc(long amount);
        // 新增自注册接口
        void register();
    }
    
    class InterTaxStrategy implements TaxStrategy {
        @Override public double calc(long amount) {
            final double taxRate = 0.2;  // 获取税率
            return amount * taxRate;
        }
    
        @Override public void register() {
            // 自己注册到策略工厂中
            AutoRegisterTaxStrategyFactory.registerTaxStrategy(TaxType.INTER, this);
        }
    }
    
    class OuterTaxStrategy implements TaxStrategy {
        @Override public double calc(long amount) {
            final double taxRate = 0.2;  // 获取税率
            return amount / (1 + taxRate) * taxRate;
        }
    
        @Override public void register() {
            // 自己注册到策略工厂中
            AutoRegisterTaxStrategyFactory.registerTaxStrategy(TaxType.OUTER, this);
        }
    }
    复制代码

    2.4.2. 税工厂

    import java.util.*;
    
    import org.reflections.Reflections;
    import org.reflections.scanners.SubTypesScanner;
    import org.reflections.util.ClasspathHelper;
    import org.reflections.util.ConfigurationBuilder;
    
    public class AutoRegisterTaxStrategyFactory {
        // 存储税策略
        static Map<TaxType, TaxStrategy> taxStrategyMap = new HashMap<>();
    
        static {
            // 注册税策略
            autoRegisterTaxStrategy();
        }
    
        // 通过map获取税策略,当增加新的税策略时无需修改代码,对修改封闭,对扩展开放,遵循开闭原则
        public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
            // 当增加新的税类型时,需要修改代码,同时增加圈复杂度
            if (taxStrategyMap.containsKey(taxType)) {
                return taxStrategyMap.get(taxType);
            } else {
                throw new Exception("The tax type is not supported.");
            }
        }
    
        // 提供税注册策略接口,外部只需要调用此接口接口新增税策略,而无需修改策略工厂内部代码
        public static void registerTaxStrategy(TaxType taxType, TaxStrategy taxStrategy) {
            taxStrategyMap.put(taxType, taxStrategy);
        }
    
        // 自动注册税策略
        private static void autoRegisterTaxStrategy() {
            try {
                // 通过反射找到所有的税策略子类进行注册
                Reflections reflections = new Reflections(new ConfigurationBuilder()
                        .setUrls(ClasspathHelper.forPackage(TaxStrategy.class.getPackage().getName()))
                        .setScanners(new SubTypesScanner()));
                Set<Class<? extends TaxStrategy>> taxStrategyClassSet = reflections.getSubTypesOf(TaxStrategy.class);
    
                if (taxStrategyClassSet != null) {
                    for (Class<?> clazz: taxStrategyClassSet) {
                        TaxStrategy taxStrategy = (TaxStrategy)clazz.newInstance();
                        // 调用税策略的自注册方法
                        taxStrategy.register();
                    }
                }
            } catch (InstantiationException | IllegalAccessException e) {
                // 自行定义异常处理
                e.printStackTrace();
            }
        }
    }
    复制代码

    注:代码中反射工具需要添加的依赖如下.

            <dependency>
                <groupId>org.reflections</groupId>
                <artifactId>reflections</artifactId>
                <version>0.9.12</version>
            </dependency>
    
            <dependency>
                <groupId>org.dom4j</groupId>
                <artifactId>dom4j</artifactId>
                <version>2.1.1</version>
                <optional>true</optional>
            </dependency>
    复制代码

    2.4.3. 使用

    public class DecisionDemo {
        public static void main(String[] args) throws Exception {
            TaxStrategy taxStrategy = AutoRegisterTaxStrategyFactory.getTaxStrategy(TaxType.INTER);
            System.out.println(taxStrategy.calc(100));
        }
    }
    复制代码

    至此,当添加新的税策略时,就完全不需要修改已有的税策略工厂代码,基本完美做到开闭原则,唯一需要修改的是税类型定义。

    2.5. 三次优化 通过注解减少耦合(网友辜圆圆建议)

    基本思路是在税策略上使用注解说明是哪种税类型,在税策略工厂中自动根据注解完成税策略注册,无需在每个税策略中调用税策略工厂的注册接口。其类结构图如下:

    下面看看变化的代码。

    2.5.1. TaxTypeAnnotation.java

    import java.lang.annotation.*;
    
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    public @interface TaxTypeAnnotation {
        TaxType taxType();
    }
    复制代码

    2.5.2. 策略类

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

    public interface TaxStrategy {
        double calc(long amount);
    }
    
    @TaxTypeAnnotation(taxType = TaxType.INTER)
    class InterTaxStrategy implements TaxStrategy {
        @Override public double calc(long amount) {
            final double taxRate = 0.2;  // 获取税率
            return amount * taxRate;
        }
    }
    
    @TaxTypeAnnotation(taxType = TaxType.OUTER)
    class OuterTaxStrategy implements TaxStrategy {
        @Override public double calc(long amount) {
            final double taxRate = 0.2;  // 获取税率
            return amount / (1 + taxRate) * taxRate;
        }
    }
    复制代码

    2.5.3. 工厂类

    public class AnnotationTaxStrategyFactory {
        // 存储税策略
        static Map<TaxType, TaxStrategy> taxStrategyMap = new HashMap<>();
    
        static {
            registerTaxStrategy();
        }
    
        // 通过map获取税策略,当增加新的税策略时无需修改代码,对修改封闭,对扩展开放,遵循开闭原则
        public static TaxStrategy getTaxStrategy(TaxType taxType) throws Exception {
            // 当增加新的税类型时,需要修改代码,同时增加圈复杂度
            if (taxStrategyMap.containsKey(taxType)) {
                return taxStrategyMap.get(taxType);
            } else {
                throw new Exception("The tax type is not supported.");
            }
        }
    
        // 自动注册税策略
        private static void registerTaxStrategy() {
            try {
                // 通过反射找到所有的税策略子类进行注册
                Reflections reflections = new Reflections(new ConfigurationBuilder()
                        .setUrls(ClasspathHelper.forPackage(TaxStrategy.class.getPackage().getName()))
                        .setScanners(new SubTypesScanner()));
                Set<Class<? extends TaxStrategy>> taxStrategyClassSet = reflections.getSubTypesOf(TaxStrategy.class);
    
                if (taxStrategyClassSet != null) {
                    for (Class<?> clazz: taxStrategyClassSet) {
                        // 找到税类型注解,自动完成税策略注册
                        if (clazz.isAnnotationPresent(TaxTypeAnnotation.class)) {
                            TaxTypeAnnotation taxTypeAnnotation = clazz.getAnnotation(TaxTypeAnnotation.class);
                            TaxType taxType = taxTypeAnnotation.taxType();
                            taxStrategyMap.put(taxType, (TaxStrategy)clazz.newInstance());
                        }
                    }
                }
            } catch (InstantiationException | IllegalAccessException e) {
                // 自行定义异常处理
                e.printStackTrace();
            }
        }
    }
    复制代码

    此方式减少了税策略和税工厂的依赖,只需要关注自己的算法实现,解耦做得最好

    2.6. 终极优化 提炼通用设计模式

    在软件系统中,类似如上的策略算法会很多,不同的算税策略,不同的加密策略,不同的XXX策略等等,这些可以统一再次抽象,提取出公共的策略接口,策略工厂类,这样就不需要每种策略都有一套代码实现,共用一套代码足矣。

    这个抽象出来的设计模式可以称为自注册策略模式,实际代码就不写了,留给大家自行思考完成(提示:使用泛型来抽象)。

    3. 总结
    注意:不管你是为了JAVA高薪还是爱好,记住:项目开发经验永远是核心,如果你没有最新JAVA架构实战视频教程及大厂面试宝典,可以去小编的Java架构学习.裙 :七吧伞吧零而衣零伞 (数字的谐音)转换下可以找到了,里面很多新JAVA架构项目教程,还可以跟老司机交流讨教! 

    1. Map替代if实现策略选择,可提高扩展性,减少圈复杂度
    2. 自注册策略模式优雅的满足了开闭原则,对修改封闭,对扩展开放。
    3. 越熟悉Java基础特性越能想到更好的方案,例如在文中使用的注解特性。所以平时应多学习JAVA基础特性,不要觉得已经够用就不去了解新特性,新特性出来一定有它的优点,比如解决性能,优雅编码,解耦等等。

    本文的文字及图片来源于网络加上自己的想法,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理

  • 相关阅读:
    正则表达式
    Requests库基本使用(转载)
    prometheus + grafana + pushgateway 搭建监控可视化系统
    Docker的系统资源限制(转载)
    DAY8 文件操作
    DAY7 集合,深浅copy
    DAY6 Python之代码块,小数据池的详解
    DAY5 Python基础类型之字典
    DAY4 Python数据类型之列表
    DAY3 python基础之数据类型总览
  • 原文地址:https://www.cnblogs.com/chengxuyuanaa/p/12825886.html
Copyright © 2020-2023  润新知