策略模式

2018-05-14  本文已影响0人  jjjjxd

本质:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换,解除使用算法者之间与算法的耦合(当遇到多个if-else或switch使用)

实际使用: 在之前做的一个项目中有将xml转换成HashMap,xml类型有多种,不同种类型xml格式不同,因此有不同的转换方法,如果不用设计模式则必须使用大量if-else来判断xml类型,下面使用策略模式进行改造:

image image image image image

问题:在选择策略是也会遇到大量if-else

image

在选择策略时,使用 简单工厂模式,可以看到当报文类型更多时,if-else体积也会变得庞大,并且新增策略时就必须修改策略工厂,这与开放封闭-原则也不符,目前我们项目采用的是将策略注入到Spring容器当中,按照beanName查找策略,这种方法比较好想,但是如果策略不能和bean建立一一对应关系,则可以使用自定义注解实现

package com.msgconverter;

import java.io.File;
import java.io.FileFilter;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author jxdong
 * @date 2018/5/13
 */
public class MsgFactory {
    private static final String CONVERTER_PACKAGE = "com.msgconverter";//策略类所在的包
    private static List<Class<? extends MsgConverter>> CONVERTER_LIST;//策略列表
    private ClassLoader classLoader = getClass().getClassLoader();//我们加载策略时的类加载器,我们任何类运行时信息必须来自该类加载器

    //不允许通过构造函数实例工厂
    private MsgFactory() {
        init();
    }

    private void init() {
        CONVERTER_LIST = new ArrayList<>();
        File[] classFile = getClassFiles();
        Class msgConverterClazz = null;
        try {
            msgConverterClazz = classLoader.loadClass(MsgConverter.class.getName());
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("未找到策略接口");
        }
        for (File file : classFile) {
            try {
                //载入包下的类
                Class<?> clazz = classLoader.loadClass(CONVERTER_PACKAGE + "." + file.getName().replace(".class", ""));
                //判断是否是MsgConverter的实现类并且不是MsgConverter它本身,满足的话加入到策略列表
                if (MsgConverter.class.isAssignableFrom(clazz) && clazz != msgConverterClazz) {
                    CONVERTER_LIST.add((Class<? extends MsgConverter>) clazz);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

        }
    }

    public MsgConverter getMsgConverter(String msgType) throws Exception {
        //假设类型有Pay Refund两种
        for (Class<? extends MsgConverter> clazz : CONVERTER_LIST) {
            MsgType msgTypeAnnotation = clazz.getDeclaredAnnotation(MsgType.class);
            if (null != msgTypeAnnotation) {
                if (msgType.equals(msgTypeAnnotation.msgType())) {
                    return clazz.newInstance();
                }
            } else {
                throw new Exception("策略获取失败");
            }
        }
        return null;
    }

    private File[] getClassFiles() {
        try {
            File file = new File(classLoader.getResource(CONVERTER_PACKAGE.replace(".", "/")).toURI());
            return file.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    if (pathname.getName().endsWith(".class")) {//只扫描class文件
                        return true;
                    }
                    return false;
                }
            });
        } catch (URISyntaxException e) {
            throw new RuntimeException("未找到策略");
        }


    }

    //单例工厂
    public static class MsgFactoryHolder {
        private static MsgFactory INSTANCE = new MsgFactory();

        public static MsgFactory getInstance() {
            return INSTANCE;
        }

    }


}

在看策略模式的时候看到一个有趣的问题,上面的代码并不能实现多个策略重叠(本例中没有该需求),如果有则进行改造

上一篇下一篇

猜你喜欢

热点阅读