【设计模式-工厂方法】想象力和创造力:你考虑过自动化实现工厂吗?

无限思维-想象力和创造力:自动化实现工厂方法

  • 前言
  • 一、《大话设计模式》对应的Java版本工厂方法
    • 类图先行:
    • 代码实现:
    • 思考升华:
  • 二、想象力:创新型思维+解决思路
    • 战略上:以无限思维的角度去想问题:
    • 部署上:将需求、问题等进行拆分:
    • 战术上:每一部分具体怎么实现:
    • 升华:
  • 三、创造力:通过创造力加以实现!
    • 新手上路——需求1:已有算法类,写一段程序让计算机自动化生成算法工厂类以及对应的客户端类
      • 方案:扫描——扫描算法类
        • 1、 实现思路:
        • 2、类图:
        • 3、项目结构图:
        • 4、 代码:
    • 小试牛刀——需求2:让计算机帮我们自动生成算法类、算法工厂类以及对应的客户端类,并且进行编译和运算(最终目的是要运算出结果)
      • 方案一:注册
          • 1、实现思路:
          • 2、类图:
          • 3、项目结构图:
          • 4、代码:
          • 5、运行效果:
      • 方案二:扫描——扫描运算类包
          • 1、实现思路:
          • 2、类图:
          • 3、项目结构图:
          • 4、代码:
    • 无限思维: 如何在程序运行过程中实时添加运算类且编译运行?
      • 目前考虑的方案:写个线程定时的去扫描
          • 代码:
    • 继续无限思维的思考:一直扫描则会一直占用资源,那么怎么样可以即实时,又能不一直占用资源呢?
      • 方案思路:(先浅浅的透露一下吧~~ )考虑通过事件与委托,简单的说就是考虑注册和扫描这两个方案相结合,代码还在优化中,敬请期待 ~~
  • 总结升华
    • 小测试:你无限思维的去思考问题了吗?

前言

设计模式之工厂方法,你能不能通过无限思维去考虑,提出一些想象力创造力的问题:
如果后期要动态的添加无数个运算类怎么办?
我们能不能让计算机帮我们实现自动化的添加运算类?
我们程序员手动添加的类和代码,能不能让计算机帮我们自动生成代码呢?
最重要的是你会提出来这样的问题吗?

我们做软件开发的需要有想象力和创造力,这是需要在学习代码过程中不断的去培养和提高的一个过程:
创新型思考+解决思路+实现!!!

一、《大话设计模式》对应的Java版本工厂方法

类图先行:

在这里插入图片描述
需要注意类图的规范:类、接口以及六大关系的线:

类图中的接口:
①接口名要加上<>
②接口中的方法必须是斜体的
抽象类:
①抽象类的名称必须是斜体的;
②抽象类中的抽象方法必须是斜体的,非抽象方法是不用斜体的。

对于类图画图规范有需要复习或者了解的,请移步博主的博客:
链接: 类图的作用与画图规范

代码实现:

客户端代码:

/**
 * @Author: Ariel(huan)
 * @Description:客户端
 * @CreateDate: 2023/3/24 22:37
 */
public class Client {
    public static void main(String[] args) {
        IOperationFactory operationFactory=new OperationAddFactory();
        Operation operation=operationFactory.createOperation();
        operation.setNumberOne(1);
        operation.setNumberTwo(2);
        double result = operation.getResult();
        System.out.println("result:"+result);
    }
}

运算类代码:

/**
 * @Author: Ariel(huan)
 * @Description:抽象类-算法类
 * @CreateDate: 2023/3/24 22:24
 */
public abstract class Operation {
    private double numberOne;
    private double numberTwo;

    public double getNumberOne() {
        return numberOne;
    }

    public double getNumberTwo() {
        return numberTwo;
    }

    public void setNumberOne(double numberOne) {
        this.numberOne = numberOne;
    }

    public void setNumberTwo(double numberTwo) {
        this.numberTwo = numberTwo;
    }
    public abstract double getResult();
}

/**
 * @Author: Ariel(huan)
 * @Description:加法类
 * @CreateDate: 2023/3/24 22:27
 */
public class OperationAdd extends Operation {
    @Override
    public double getResult() {
        return getNumberOne()+getNumberTwo();
    }
}

/**
 * @Author: Ariel(huan)
 * @Description:减法类
 * @CreateDate: 2023/3/24 22:28
 */
public class OperationSub extends Operation {
    @Override
    public double getResult() {
        return getNumberOne()-getNumberTwo();
    }
}

/**
 * @Author: Ariel(huan)
 * @Description:乘法类
 * @CreateDate: 2023/3/24 22:29
 */
public class OperationMul extends Operation {
    @Override
    public double getResult() {
        return getNumberOne()*getNumberTwo();
    }
}

/**
 * @Author: Ariel(huan)
 * @Description:除法类
 * @CreateDate: 2023/3/24 22:29
 */
public class OperationDiv extends Operation {
    @Override
    public double getResult() {
        if (getNumberTwo()!=0){
            return getNumberOne()/getNumberTwo();
        }else {
            System.out.println("除数不可以为0");
        }
        return 0;
    }
}

运算工厂类代码:

/**
 * @Author: Ariel(huan)
 * @Description:算法工厂接口
 * @CreateDate: 2023/3/24 22:25
 */
public interface IOperationFactory {
   Operation createOperation();
}

/**
 * @Author: Ariel(huan)
 * @Description: 创建加法类的工厂类
 * @CreateDate: 2023/3/24 22:33
 */
public class OperationAddFactory implements IOperationFactory{
    @Override
    public Operation createOperation() {
        return new OperationAdd();
    }
}

/**
 * @Author: Ariel(huan)
 * @Description: 创建减法类的工厂类
 * @CreateDate: 2023/3/24 22:34
 */
public class OperationSubFactory implements IOperationFactory {
    @Override
    public Operation createOperation() {
        return new OperationSub();
    }
}

/**
 * @Author: Ariel(huan)
 * @Description:创建乘法类的工厂类
 * @CreateDate: 2023/3/24 22:35
 */
public class OperationMulFactory implements IOperationFactory{
    @Override
    public Operation createOperation() {
        return new OperationMul();
    }
}

/**
 * @Author: Ariel(huan)
 * @Description:创建除法类的工厂类
 * @CreateDate: 2023/3/24 22:36
 */
public class OperationDivFactory implements IOperationFactory{
    @Override
    public Operation createOperation() {
        return new OperationDiv();
    }
}

思考升华:

书本上的工厂方法,学习并思考,提出问题:
工厂方法为什么叫工厂方法?简单工厂为什么叫简单工厂?
在代码中怎么体现的?一定要对应到代码上,不能模棱两可。
工厂方法对比简单工厂有哪些优化和好处?
工厂方法的出现有哪些革命性的意义?
工厂方法还存在什么问题,还可以怎么优化?
工厂方法还可以怎么实现?…

二、想象力:创新型思维+解决思路

战略上:以无限思维的角度去想问题:

1、后期动态的添加无数个运算类怎么办?
2、能不能让计算机帮我们自动生成代码呢?
3、后期动态添加的运算类能不能在程序运行过程中添加并执行运算?——不重启运行

部署上:将需求、问题等进行拆分:

1、我们程序员去创建新的运算类、运算工厂以及Client类是一个重复有规律的过程,能不能交给程序去完成,让计算机自动生成运算类、运算工厂类或者Client可不可以?
2、如果程序帮我们生成了对应的运算类、工厂以及Client我们要怎么在程序运行过程中,调用对应的运算方法进行运算呢?

战术上:每一部分具体怎么实现:

1、我们怎么让计算机自动生成运算类、运算工厂类或者Client?——提供模板,让计算机根据模板去动态的创建
2、如果程序帮我们生成了对应的运算类、工厂以及Client我们要怎么在程序运行过程中,调用对应的运算方法进行运算呢?——动态编译+反射执行
3、后期动态添加的类在运行过程中添加并执行运行?——对于新增的类进行动态注册或者扫描+动态编译+反射执行

升华:

想象力和创造力: 凡是我们能干的事情,计算机都能干;凡是软件能干的事情,硬件都能干…
最重要的是你会从无限思维的角度思考问题吗?

三、创造力:通过创造力加以实现!

一步一步思考和实现的过程如下:

新手上路——需求1:已有算法类,写一段程序让计算机自动化生成算法工厂类以及对应的客户端类

方案:扫描——扫描算法类

1、 实现思路:

①通过扫描算法类包,获取到所有的算法类文件,并获取到各类名;
②读取工厂类模板和客户端模板,获取到.java文件对应的模板内容;
③替换模板中的模板“占位符”,替换为我们对应的算法类名和算法符号;
④通过I/O输入输出流,创建新的.java文件,即算法工厂类和算法客户端类

2、类图:

3、项目结构图:

在这里插入图片描述

4、 代码:

工厂接口:

/**
 * @Author: Ariel(huan)
 * @Description:工厂接口
 * @CreateDate: 2023/3/14 11:23
 */
public interface IFactory {
    Operation createOperation();
}

运算类-抽象类:

/**
 * @Author: Ariel(huan)
 * @Description:算法抽象类
 * @CreateDate: 2023/3/14 11:37
 */
public abstract class Operation {
    private double numberA=0;
    private double numberB=0;

    public double getNumberA() {
        return numberA;
    }

    public double getNumberB() {
        return numberB;
    }

    public void setNumberA(double numberA) {
        this.numberA = numberA;
    }

    public void setNumberB(double numberB) {
        this.numberB = numberB;
    }

    public abstract double getResult();
}

运算类包operation下的运算类:

/**
 * @Author: Ariel(huan)
 * @Description:加法类
 * @CreateDate: 2023/3/14 11:51
 */
public class OperationAdd extends Operation {

    @Override
    public double getResult() {
        double result = 0;
        result = getNumberA() + getNumberB();
        return result;
    }
}

/**
 * @Author: Ariel(huan)
 * @Description:减法类
 * @CreateDate: 2023/3/15 19:56
 */
public class OperationSub extends Operation {
    @Override
    public double getResult() {
        double result=0;
        result=getNumberA()-getNumberB();
        return result;
    }
}

/**
 * @Author: Ariel(huan)
 * @Description:乘法类
 * @CreateDate: 2023/3/15 20:01
 */
public class OperationMul extends Operation {
    @Override
    public double getResult() {
        double result = 0;
        result = getNumberA() * getNumberB();
        return result;
    }
}

/**
 * @Author: Ariel(huan)
 * @Description:除法类
 * @CreateDate: 2023/3/15 19:57
 */
public class OperationDiv extends Operation {
    @Override
    public double getResult() {
        double result=0;
        try {
            if (getNumberB() == 0) {
                throw new Exception("除数不能为0");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        result=getNumberA()/getNumberB();
        return result;
    }
}

工厂模板:

/**
 * @Author: Ariel(huan)
 * @Description: 工厂模板类
 * @CreateDate: 2023/3/15 22:10
 */
public class FactoryTemplate implements IFactory {
    @Override
    public Operation createOperation() {
        return new OperationAdd();//这个模板里以 new OperationAdd为例
    }
}

客户端模板:

/**
 * @Author: Ariel(huan)
 * @Description:运算器的客户端模板类
 * @CreateDate: 2023/3/16 9:18
 */
public class OperationClientTemplate {
    public static void main(String[] args) {
        IFactory operFactory=new FactoryTemplate();
        Operation operation = operFactory.createOperation();
        operation.setNumberA(1);
        operation.setNumberB(2);
        double result = operation.getResult();
        System.out.println("result是:"+result);

    }
}

程序启动的客户端:

public class Client {
    public static void main(String[] args) throws IOException {
        //拿到算法类的类名集合
        ClassNameUtil classUtil = new ClassNameUtil();
        List<String> classNameList = classUtil.getClassNameList();

        //读取FactoryTemplate模板类.java文件,获取到内容,然后遍历classNameList进行类名的替换并指定路径生成Factory类
        String operationContent = readTemplateContent();
        for (int i = 0; i < classNameList.size(); i++) {
            String className = classNameList.get(i);
            String operationContentNew= operationContent.replace("FactoryTemplate",className+"Factory").replaceAll("OperationAdd", className);
            String filePath="E:\\Study\\TGB学习资料\\06设计模式研究\\李欢\\design-pattern\\newStudyHuan\\src\\com\\tfjy" +
                    "\\architecture\\factoryAutoCreate\\operationFactory\\"+className+"Factory.java";
            FileUtil.append(filePath,operationContentNew);
        }

        //读取 OperationClientTemplate模板类文件,获取到内容,然后遍历classNameList进行类名的替换并指定路径生成 OperationClient 类
        String OperClientTemContent = OperationClientTemplate();
        for (int i = 0; i < classNameList.size(); i++) {
            String className = classNameList.get(i);
            String OperClientTemContentNew= OperClientTemContent.replace("OperationClientTemplate",className+"Client").replaceAll("FactoryTemplate", className+"Factory");
            String filePath="E:\\Study\\TGB学习资料\\06设计模式研究\\李欢\\design-pattern\\newStudyHuan\\src\\com\\tfjy" +
                    "\\architecture\\factoryAutoCreate\\operationClient\\"+className+"Client.java";
            FileUtil.append(filePath,OperClientTemContentNew);
        }

    }

    private static String readTemplateContent() throws IOException {
        // 识别 FactoryTemplate.java文件
        String filePath = "E:\\Study\\TGB学习资料\\06设计模式研究\\李欢\\design-pattern\\newStudyHuan\\src\\com\\tfjy" +
                "\\architecture\\factoryAutoCreate\\operationFactory\\FactoryTemplate.java";
        FileInputStream fin = new FileInputStream(filePath);
        InputStreamReader reader = new InputStreamReader(fin);
        BufferedReader buffReader = new BufferedReader(reader);
        String strTmp = "";
        // FactoryTemplate.java文件中的文本内容
        StringBuffer operationContent = new StringBuffer();
        // 读一行文字,未读取任何字符的情况下到达流末尾,则不进行输出操作
        while((strTmp = buffReader.readLine())!=null){
            // 识别到了所有父类和子类的文本内容
            operationContent.append(strTmp);
            operationContent.append("\n");
        }
        buffReader.close();
        return operationContent.toString();
    }

    private static String OperationClientTemplate() throws IOException {
        // 识别 FactoryTemplate.java文件
        String filePath = "E:\\Study\\TGB学习资料\\06设计模式研究\\李欢\\design-pattern\\newStudyHuan\\src\\com\\tfjy" +
                "\\architecture\\factoryAutoCreate\\operationClient\\OperationClientTemplate.java";
        FileInputStream fin = new FileInputStream(filePath);
        InputStreamReader reader = new InputStreamReader(fin);
        BufferedReader buffReader = new BufferedReader(reader);
        String strTmp = "";
        // FactoryTemplate.java文件中的文本内容
        StringBuffer operationContent = new StringBuffer();
        // 读一行文字,未读取任何字符的情况下到达流末尾,则不进行输出操作
        while((strTmp = buffReader.readLine())!=null){
            // 识别到了所有父类和子类的文本内容
            operationContent.append(strTmp);
            operationContent.append("\n");
        }
        buffReader.close();
        return operationContent.toString();
    }

}

工具类:FileUtil 和 ClassNameUtil

/**
 * @Author: Ariel(huan)
 * @Description: 输入输出,生成新文件的工具类
 * @CreateDate: 2023/3/16 8:46
 */
public class FileUtil {

    /**
     * append:将一个字符写入一个已有的文件中,通过追加的方法追加到内容的末尾;
     * @author Bool
     * @param filePath
     * @param data
     */
    public static synchronized void append(String filePath , String data){

        String s;
        StringBuffer sb=new StringBuffer();

        try {

            //创建文件夹
            String dirPath=filePath.substring(0,filePath.lastIndexOf("/")+1);
            File dir=new File(dirPath);

            if(!dir.exists()){
                dir.mkdirs();
            }

            File f = new File(filePath);
            if (!f.exists()) {
                f.createNewFile();

                BufferedReader input = new BufferedReader(new FileReader(f));
                while ((s = input.readLine()) != null) {
                    sb.append(s);
                    sb.append("\n");
                }
                input.close();
                sb.append(data);
                BufferedWriter output = new BufferedWriter(new FileWriter(f));
                output.write(sb.toString());
                output.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


/**
 * @Author: Ariel(huan)
 * @Description: 扫描operation包下的所有类
 * @CreateDate: 2023/3/15 21:36
 */
public class ClassNameUtil {
    private static Set<Class<?>> classList;

    static {
        classList = getClasses("com.tfjy.architecture.factoryAutoCreate.operation");
    }

    /**
     * 从包package中获取所有的Class
     *
     * @param pack
     * @return
     */
    public static Set<Class<?>> getClasses(String pack) {

        // 第一个class类的集合
        Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
        // 是否循环迭代
        boolean recursive = true;
        // 获取包的名字 并进行替换
        String packageName = pack;
        String packageDirName = packageName.replace('.', '/');
        // 定义一个枚举的集合 并进行循环来处理这个目录下的things
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            // 循环迭代下去
            while (dirs.hasMoreElements()) {
                // 获取下一个元素
                URL url = dirs.nextElement();
                // 得到协议的名称
                String protocol = url.getProtocol();
                // 如果是以文件的形式保存在服务器上
                if ("file".equals(protocol)) {
                    // System.err.println("file类型的扫描");
                    // 获取包的物理路径
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    // 以文件的方式扫描整个包下的文件 并添加到集合中
                    findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
                } else if ("jar".equals(protocol)) {
                    // 如果是jar包文件
                    // 定义一个JarFile
                    // System.err.println("jar类型的扫描");
                    JarFile jar;
                    try {
                        // 获取jar
                        jar = ((JarURLConnection) url.openConnection()).getJarFile();
                        // 从此jar包 得到一个枚举类
                        Enumeration<JarEntry> entries = jar.entries();
                        // 同样的进行循环迭代
                        while (entries.hasMoreElements()) {
                            // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
                            JarEntry entry = entries.nextElement();
                            String name = entry.getName();
                            // 如果是以/开头的
                            if (name.charAt(0) == '/') {
                                // 获取后面的字符串
                                name = name.substring(1);
                            }
                            // 如果前半部分和定义的包名相同
                            if (name.startsWith(packageDirName)) {
                                int idx = name.lastIndexOf('/');
                                // 如果以"/"结尾 是一个包
                                if (idx != -1) {
                                    // 获取包名 把"/"替换成"."
                                    packageName = name.substring(0, idx).replace('/', '.');
                                }
                                // 如果可以迭代下去 并且是一个包
                                if ((idx != -1) || recursive) {
                                    // 如果是一个.class文件 而且不是目录
                                    if (name.endsWith(".class") && !entry.isDirectory()) {
                                        // 去掉后面的".class" 获取真正的类名
                                        String className = name.substring(packageName.length() + 1, name.length() - 6);
                                        try {
                                            // 添加到classes
                                            classes.add(Class.forName(packageName + '.' + className));
                                        } catch (ClassNotFoundException e) {
                                            // log
                                            // .error("添加用户自定义视图类错误
                                            // 找不到此类的.class文件");
                                            e.printStackTrace();
                                        }
                                    }
                                }
                            }
                        }
                    } catch (IOException e) {
                        // log.error("在扫描用户定义视图时从jar包获取文件出错");
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return classes;
    }

        /**
         * 以文件的形式来获取包下的所有Class
         *
         * @param packageName
         * @param packagePath
         * @param recursive
         * @param classes
         */
        public static void findAndAddClassesInPackageByFile(String packageName, String packagePath,final boolean recursive, Set<Class<?>> classes){
            // 获取此包的目录 建立一个File
            File dir = new File(packagePath);
            // 如果不存在或者 也不是目录就直接返回
            if (!dir.exists() || !dir.isDirectory()) {
                // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
                return;
            }
            // 如果存在 就获取包下的所有文件 包括目录
            File[] dirfiles = dir.listFiles(new FileFilter() {
                // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
                public boolean accept(File file) {
                    return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
                }
            });
            // 循环所有文件
            for (File file : dirfiles) {
                // 如果是目录 则继续扫描
                if (file.isDirectory()) {
                    findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,classes);
                } else {
                    // 如果是java类文件 去掉后面的.class 只留下类名
                    String className = file.getName().substring(0, file.getName().length() - 6);
                    try {
                        // 添加到集合中去
                        // classes.add(Class.forName(packageName + '.' +
                        // className));
                        // 经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
                        classes.add(
                                Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
                    } catch (ClassNotFoundException e) {
                        // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
                        e.printStackTrace();
                    }
                }
            }
        }


    public List<String> getClassNameList(){
        List<String> classNameList = new ArrayList<>();
        Object[] ts =   classList.toArray();
        for(Object t:ts){
            Class<?> tt = (Class<?>) t;
            System.out.println(tt.getName());
            //截取最后一个.后边的类名: 字符串.substring(字符串.lastIndexOf(".")+1);
            String className = tt.getName().substring(tt.getName().lastIndexOf(".") + 1);
            classNameList.add(className);
        }
       return classNameList;
    }
}

Readme 说明:
这一版实现的功能:
1、通过ClassNameUtil类扫描operation包下的所有类,通过反射拿到所有的全路径类名,并通过截取获得所有运算类的类名放到集合中
2、通过写两个模板,并识别模板文件内容,
3、通过遍历集合中的类名,替换需要替换的类名以及内容
4、通过FileUtil中的方法,替换并创建新的类文件
=========================================
该版待优化:
功能上:
1、只是实现了可以自动生成对应算法类的工厂和client,并没有编译新生成的工厂类和客户端类,也正常运行运算过程
2、还需要自动执行运算过程
3、需要让用户输入运算符来实现 动态添加运算类、对应的预算工厂和Client
4、根据用户输入的运算符来动态的运算结果
代码上:
1、代码重复未抽象、封装、复用
2、常量未封装抽出来,应该放到配置文件中
============================================
注:
1、实现扫描获取operation包下所有类的工具类ClassNameUtil以及里边方法都可以自行修改或替换,且涉及到的包名要修改为自己的
2、程序启动的客户端类Client中涉及到绝对路径,需要修改为自己的文件路径

小试牛刀——需求2:让计算机帮我们自动生成算法类、算法工厂类以及对应的客户端类,并且进行编译和运算(最终目的是要运算出结果)

方案一:注册

1、实现思路:

①让用户从控制台输入要进行的运算类的类名和运算符号
②读取运算类模板(这里OperationAdd类作为运算类模板)、工厂类模板(FactoryTemplate)、运算客户端类模板(OperationClientTemplate)
③将模板中的运算类名和运算符 替换为 用户输入的
④生成运算类.java(MulClient)、运算工厂类.java(MulFactory)、运算客户端类.java(MulClient)
⑤调用JavaCompiler(动态编译机制),将生成的三个类进行动态编译.class文件
⑥通过反射执行运算客户端MulCilent中的运算方法(getResult方法)

关于动态编译机制的参考资料:
链接: 动态编译(JavaCompiler)

2、类图:
3、项目结构图:

在这里插入图片描述

4、代码:

运算抽象类(父类):

/**
 * @Author: Ariel(huan)
 * @Description:算法抽象类
 * @CreateDate: 2023/3/14 11:37
 */
public abstract class Operation {
    private double numberA=0;
    private double numberB=0;
    public double getNumberA() {
        return numberA;
    }
    public double getNumberB() {
        return numberB;
    }
    public void setNumberA(double numberA) {
        this.numberA = numberA;
    }
    public void setNumberB(double numberB) {
        this.numberB = numberB;
    }

    public abstract double getResult();
}

工厂接口:

/**
 * @Author: Ariel(huan)
 * @Description:工厂接口
 * @CreateDate: 2023/3/14 11:23
 */
public interface IFactory {
    Operation createOperation();
}

运算类模板类:(这里是OperationAdd作为模板)

/**
 * @Author: Ariel(huan)
 * @Description:加法类
 * @CreateDate: 2023/3/14 11:51
 */
public class OperationAdd extends Operation {

    @Override
    public double getResult() {
        double result = 0;
        result = getNumberA() + getNumberB();
        return result;
    }
}

运算工厂类模板:

/**
 * @Author: Ariel(huan)
 * @Description: 工厂模板类
 * @CreateDate: 2023/3/15 22:10
 */
public class FactoryTemplate implements IFactory {

    @Override
    public Operation createOperation() {
        return new OperationAdd();
    }
}

运算客户端类模板:

/**
 * @Author: Ariel(huan)
 * @Description:运算器的客户端模板类
 * @CreateDate: 2023/3/16 9:18
 */
public class OperationClientTemplate {
    public static void main(String[] args) {
        IFactory operFactory=new FactoryTemplate();
        Operation operation = operFactory.createOperation();
        operation.setNumberA(1);
        operation.setNumberB(2);
        double result = operation.getResult();
        System.out.println("result是:"+result);
    }
}

程序启动客户端:

/**
 * @Author: Ariel(huan)
 * @Description: 注册(触发实现)
 * @CreateDate: 2023/3/15 21:23
 */
public class Client {
    public static void main(String[] args) throws Exception {
        while (true){   //这里表示一直可以循环让用户在控制台输入,便于运行中用户随时多次添加运算类
            //让用户输入要进行的运算
            Scanner scanner=new Scanner(System.in);
            System.out.println("请输入想要运算的算法类名(Add、Sub、Mul、Div):");
            String className=scanner.nextLine();
            System.out.println("请输入想要运算的算法符号(+、-、*、/):");
            String character=scanner.nextLine();
            //根据用户输入的运算,生成对应的类并编译
            createCompilerFile(className, character);
            //反射执行
            reflectInvoke(className);//注释掉为手动
        }

    }

    //根据用户输入的运算,生成对应的类
    private static void createCompilerFile(String className, String character) throws Exception {
        //调用一个编译源码的方法,将创建出的.java文件编译成.class文件
        Compiler compiler=new Compiler();

        //扫描算法类模板 OperationAdd.java文件,生成对应的算法类
        String operationContent = FileUtil.readTemplateContent(Constant.OPERATION_PATH+"\\OperationAdd.java");
        String operationContentNew= operationContent.replace("OperationAdd","Operation"+className).replace("+", character);
        String operationFilePath=Constant.OPERATION_PATH+"\\Operation"+className+".java";
        FileUtil.append(operationFilePath,operationContentNew);
        System.out.println("算法类生成:"+"Operation"+className);
        //调用一个编译源码的方法,将创建出的.java文件编译成.class文件
        compiler.compiler(operationFilePath);//注释掉为手动


        //扫描工厂类模板 FactoryTemplate.java文件,生成对应的算法工厂类
        String operFactoryContent = FileUtil.readTemplateContent(Constant.FACTORY_PATH+"\\FactoryTemplate.java");
        String operFactoryContentNew= operFactoryContent.replace("FactoryTemplate",className+"Factory").replaceAll("OperationAdd", "Operation"+className);
        String operFactoryFilePath=Constant.FACTORY_PATH+"\\"+className+"Factory.java";
        FileUtil.append(operFactoryFilePath,operFactoryContentNew);
        System.out.println("算法工厂类生成:"+className+"Factory");
        //调用一个编译源码的方法,将创建出的.java文件编译成.class文件
        compiler.compiler(operFactoryFilePath);//注释掉为手动

        //扫描工厂类模板 OperationClientTemplate.java文件,生成对应的算法客户端类
        String operClientContent = FileUtil.readTemplateContent(Constant.CLIENT_PATH+"\\OperationClientTemplate.java");
        String operClientContentNew= operClientContent.replace("OperationClientTemplate",className+"Client").replaceAll("FactoryTemplate", className+"Factory");
        String operClientFilePath=Constant.CLIENT_PATH+"\\"+className+"Client.java";
        FileUtil.append(operClientFilePath,operClientContentNew);
        System.out.println("算法客户端生成:"+className+"Client");
        //调用一个编译源码的方法,将创建出的.java文件编译成.class文件
        compiler.compiler(operClientFilePath);//注释掉为手动

    }

    /**
     * 通过反射,执行自动生成的客户端
     */
    private static void reflectInvoke(String className) throws Exception {
        //客户端类
        String compilerClassName="com.tfjy.architecture.factoryAutoCreateLogon.operationClient."+className+"Client";
        Class<?> classLoaderClass = Class.forName(compilerClassName);
        Method method = classLoaderClass.getMethod("main",String[].class);
        method.invoke(null,(Object)new String[]{""});

    }
}

涉及到的工具类:FileUtil


/**
 * @Author: Ariel(huan)
 * @Description:读模板文件和创建文件的工具类
 * @CreateDate: 2023/3/16 8:46
 */
public class FileUtil {

    /**
     * 扫描模板类文件
     */
    public static String readTemplateContent(String filePath) throws IOException {
        // 识别 .java文件
        FileInputStream fin = new FileInputStream(filePath);
        InputStreamReader reader = new InputStreamReader(fin);
        BufferedReader buffReader = new BufferedReader(reader);
        String strTmp = "";
        //文件中的文本内容
        StringBuffer operationContent = new StringBuffer();
        // 读一行文字,未读取任何字符的情况下到达流末尾,则不进行输出操作
        while((strTmp = buffReader.readLine())!=null){
            // 识别到了所有父类和子类的文本内容
            operationContent.append(strTmp);
            operationContent.append("\n");
        }
        buffReader.close();
        return operationContent.toString();
    }


    /**
     * append:将一个字符写入一个已有的文件中,通过追加的方法追加到内容的末尾;
     * @author Bool
     * @param filePath
     * @param data
     */
    public static synchronized void append(String filePath , String data){

        String s;
        StringBuffer sb=new StringBuffer();
        try {

            //创建文件夹
            String dirPath=filePath.substring(0,filePath.lastIndexOf("/")+1);
            File dir=new File(dirPath);

            if(!dir.exists()){
                dir.mkdirs();
            }

            File f = new File(filePath);
            //当文件不存在时,创建文件并写入内容
            if (!f.exists()) {
                f.createNewFile();

                BufferedReader input = new BufferedReader(new FileReader(f));
                while ((s = input.readLine()) != null) {
                    sb.append(s);
                    sb.append("\n");
                }
                input.close();
                sb.append(data);
                BufferedWriter output = new BufferedWriter(new FileWriter(f));
                output.write(sb.toString());
                output.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

编译器编译:

/**
 * @Author: Ariel(huan)
 * @Description:动态编译
 * @CreateDate: 2023/3/19 12:38
 */
public class Compiler extends ClassLoader{
    public void compiler(String filePath){
        System.out.println("开始编译并运行");
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int status = compiler.run(null, null, null, "-d",
                "E:\\Study\\TGB学习资料\\06设计模式研究\\李欢\\design-pattern\\out\\production\\newStudyHuan", filePath);
        if (status != 0) {
            System.out.println("没有编译成功!");
        }
    }
  }

路径常量类:这部分涉及到的绝对路径需要修改为自己项目实例的路径呦~~~~

/**
 * @Author: Ariel(huan)
 * @Description:路径常量类
 * @CreateDate: 2023/3/20 17:09
 */
public class Constant {
    //算法模板类的路径
    public static final String OPERATION_PATH="E:\\Study\\TGB学习资料\\06设计模式研究\\李欢\\design-pattern\\newStudyHuan\\src\\com" +
            "\\tfjy\\architecture\\factoryAutoCreateLogon\\operation";
    //factory模板类的路径
    public static final String FACTORY_PATH="E:\\Study\\TGB学习资料\\06设计模式研究\\李欢\\design-pattern\\newStudyHuan" +
            "\\src\\com\\tfjy\\architecture\\factoryAutoCreateLogon\\operationFactory";
    //client模板类的路径
    public static final String CLIENT_PATH="E:\\Study\\TGB学习资料\\06设计模式研究\\李欢\\design-pattern\\newStudyHuan" +
            "\\src\\com\\tfjy\\architecture\\factoryAutoCreateLogon\\operationClient";
    
}
5、运行效果:

在这里插入图片描述

方案二:扫描——扫描运算类包

1、实现思路:

①通过扫描算法类包,获取到所有的算法类文件,并获取到各类名;
②读取运算类模板(这里OperationAdd类作为运算类模板)、工厂类模板(FactoryTemplate)、运算客户端类模板(OperationClientTemplate)
③将模板中的运算类名和运算符 替换为 运算类包下的扫描到的运算类名以及其运算符号
④生成运算类.java(MulClient)、运算工厂类.java(MulFactory)、运算客户端类.java(MulClient)
⑤调用JavaCompiler(动态编译机制),将生成的三个类进行动态编译.class文件
⑥通过反射执行运算客户端MulCilent中的运算方法(getResult方法)

2、类图:
3、项目结构图:

在这里插入图片描述

4、代码:

注:
工厂接口IFactory、运算抽象类(父类)Operation、
运算类模板OperationAdd、运算工厂类FactoryTemplate、运算客户端模板类OperationClientTemplate以及FileUtil类和Compiler类的代码都是一样的,不同的是程序启动客户端内的逻辑——改为了扫描(获取)包下的所有运算类:

//程序启动客户端
public class ScanApplication {
    public static void main(String[] args) throws Exception {
        Scan scan = new Scan();
        scan.scan();
    }
}

核心业务类:

/**
 * 扫描业务类:获取所有的运算类并生成对应的工厂类和客户端类,并且编译执行
 */
public class Scan {
    public void scan() throws Exception {
        String packagePath = "E:\\Study\\TGB学习资料\\06设计模式研究\\李欢\\design-pattern\\newStudyHuan\\src\\com\\tfjy" +
                "\\architecture\\factoryAutoCreateSaomiao\\operation";

        File file = new File(packagePath);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            Map<Object, Method> map = new HashMap<>();
            for (File f : files) {
                String fileName = f.getAbsolutePath();
                if (fileName.endsWith(".java")) {
                    String fullyClassName = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".java"));
                    String className = fullyClassName.replace("com\\tfjy\\architecture\\factoryAutoCreateSaomiao" +
                            "\\operation\\", "");


                    //调用一个编译源码的方法,将创建出的.java文件编译成.class文件
                    String operationFilePath=Constant.OPERATION_PATH +"\\" + className+".java";
                    Compiler compilerOperation = new Compiler();
                    compilerOperation.compiler(operationFilePath);


                    //扫描工厂类模板 FactoryTemplate.java文件,生成对应的算法工厂类
                    String operFactoryContent = FileUtil.readTemplateContent(Constant.FACTORY_PATH +
                            "\\FactoryTemplate.java");
                    String operFactoryContentNew = operFactoryContent.replace("FactoryTemplate", className + "Factory"
                    ).replaceAll("OperationAdd", className);
                    String operFactoryFilePath = Constant.FACTORY_PATH + "\\" + className + "Factory.java";
                    FileUtil.append(operFactoryFilePath, operFactoryContentNew);
                    System.out.println("算法工厂类生成:" + className + "Factory");
                    //调用一个编译源码的方法,将创建出的.java文件编译成.class文件
                    Compiler compilerFactory = new Compiler();
                    compilerFactory.compiler(operFactoryFilePath);

                    //扫描工厂类模板 OperationClientTemplate.java文件,生成对应的算法客户端类
                    String operClientContent = FileUtil.readTemplateContent(Constant.CLIENT_PATH +
                            "\\OperationClientTemplate.java");
                    String operClientContentNew = operClientContent.replace("OperationClientTemplate", className +
                            "Client").replaceAll("FactoryTemplate", className + "Factory");
                    String operClientFilePath = Constant.CLIENT_PATH + "\\" + className + "Client.java";
                    FileUtil.append(operClientFilePath, operClientContentNew);
                    System.out.println("算法客户端生成:" + className + "Client");

                    //调用一个编译源码的方法,将创建出的.java文件编译成.class文件
                    Compiler compilerClient = new Compiler();
                    compilerClient.compiler(operClientFilePath);
                    //反射执行
                    String compileClassName = "com.tfjy.architecture.factoryAutoCreateSaomiao.operationClient." + className + "Client";
                    Class<?> classLoaderClass = Class.forName(compileClassName);
//                    Object instance = classLoaderClass.getConstructor().newInstance();
                    Method method = classLoaderClass.getMethod("main", String[].class);
                    method.invoke(null, (Object) new String[]{""});
//                    map.put(instance,method);
                }
            }
        }
        System.out.println("测试");
    }
}

无限思维: 如何在程序运行过程中实时添加运算类且编译运行?

如果要在运行过程中,手动添加一个运算类到operation包下,那么这个新的运算类如何在不重启项目的情况下,及时的编译并生成对象的工厂和运算客户端类,并且执行运算???——热加载实现动态的扩充类以及动态的编译、加载

目前考虑的方案:写个线程定时的去扫描

代码:
/**
 * @Author: Ariel(huan)
 * @Description:自动扫描的启动类
 * @CreateDate: 2023/3/23 8:24
 */
public class AutoScanApplication {
    public static void main(String[] args) throws Exception {
        AutoScan autoScan = new AutoScan();
        autoScan.run();
    }
}
/**
 * @Author: Ariel(huan)
 * @Description:自动扫描_线程池
 * @CreateDate: 2023/3/23 8:24
 */
public class AutoScan {
    public void run() {
        Scan scan = new Scan();
        // 需要定时执行的任务
        Runnable runnable = () -> {
            try {
                scan.scan();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
        ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
        //立即执行,并且每5秒执行一次
        ses.scheduleAtFixedRate(runnable, 0, 15000, TimeUnit.MILLISECONDS);
    }
}

继续无限思维的思考:一直扫描则会一直占用资源,那么怎么样可以即实时,又能不一直占用资源呢?

方案思路:(先浅浅的透露一下吧~~ )考虑通过事件与委托,简单的说就是考虑注册和扫描这两个方案相结合,代码还在优化中,敬请期待 ~~

总结升华

为什么要写一段代码,能够实现自动化工厂?
A.重复的逻辑自动化实现,减少重复造轮子,提高效率
B.无限思维看待问题,变——不断增加,不停的增加,那么我们就需要经常添加新的类。
无限思维不仅在于多,更在于变,在于创造。
比如:一条直线两头无限延伸,会变弯曲以及相交。
C. 动态扩充+热加载,降低后期的开发成本(扩充和维护)——注意前期的开发成本是高的,因为要求开发者的开发素质能力很高,以及要为了后期拓展打基础所以开发成本高。
什么叫维护?维护应该是用户来做的,通过用户的需求,动态的扩充和维护系统功能。
可变包括两个可变:维护可变、扩充可变。
把重复性有规律性的内容交给程序去完成,降低开发和维护的成本, 减少了人为操作出错的情况。

小测试:你无限思维的去思考问题了吗?

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/3337.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot整合数据可视化大屏使用

1 前言 DataV数据可视化是使用可视化应用的方式来分析并展示庞杂数据的产品。DataV旨让更多的人看到数据可视化的魅力,帮助非专业的工程师通过图形化的界面轻松搭建专业水准的可视化应用,满足您会议展览、业务监控、风险预警、地理信息分析等多种业务的展示需求, 访问地址:h…

文件上传的多种利用方式

文件上传的多种利用方式 文件上传漏洞除了可以通过绕过检测进行webshell的上传之外&#xff0c;还有多种其它的漏洞可以进行测试。 XSS漏洞 文件名造成的XSS 当上传任何文件时&#xff0c;文件名肯定是会反显示在网页上&#xff0c;可以使用 XSS Payload做文件名尝试将其上传到…

upload—labs(9-12)

pass9直接查看的源码&#xff0c;得知是黑名单过滤&#xff0c;而且过滤也都很全通过查看wp&#xff0c;得知我们可以使用. .(点空格点)进行绕过利用bp抓包进行更改trim删除文件名末尾的点&#xff0c;得到shell.php.空格&#xff0c;然后进行首尾去空得到shell.php.,黑名单过滤…

Java并发高频面试题

分享50道Java并发高频面试题。 线程池 线程池&#xff1a;一个管理线程的池子。 为什么平时都是使用线程池创建线程&#xff0c;直接new一个线程不好吗&#xff1f; 嗯&#xff0c;手动创建线程有两个缺点 不受控风险频繁创建开销大 为什么不受控&#xff1f; 系统资源有…

【机器学习基础 3】 sklearn库

目录 一、sklearn库简介 二、sklearn库安装 三、关于机器学习 四、sklearn库在机器学习中的应用 1、数据预处理 2、特征提取 3、模型选择与评估 五、常用的sklearn函数 1、数据集划分 2、特征选择 3、特征缩放 4、模型训练 5、模型预测 一、sklearn库简介 Scikit-l…

基于卷积神经网络进行股价预测(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 CNN是一种人工神经网络&#xff0c;CNN的结构可以分为3层&#xff1a; 卷积层(Convolutional Layer) - 主要作用是提取特征。 …

C语言基础——流程控制语句

文章目录一、流程控制语句 -- 控制程序的运行过程 9条&#xff08;一&#xff09;、条件选择流程控制语句&#xff1a;if语句if……else……语句if……else if……语句switch语句&#xff08;二&#xff09;、循环流程控制语句&#xff1a;for语句while语句do while……语句co…

Linux学习之端口、网络协议及查看端口占用情况

端口&#xff1a;设备与外界通讯交流的出口 网络协议&#xff1a;   网络协议是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则。 HTTP协议&#xff1a;   HTTP协议&#xff08;超文本传输协议&#xff09;是一种网络通信协议&#xff0c;它允许将超…

Mac和Linux安装Mongodb教程

Mac教程 在mongodb的官网中有mac环境的安装配置说明 在mac上安装mongodb有两种方式&#xff1a; &#xff08;1&#xff09;使用Homebrew来安装&#xff0c;如果电脑中有Homebrew&#xff0c;安装起来就比较简单&#xff0c;如果没有可以安装一个&#xff0c;以后安装其他的…

【C++学习】多态

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《C学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 多态&#x1f355;多态&#x1f35f;构成多态的条件&#x1f35f;C11 final override&#x1f35f;重…

thinkphp+vue水果购物商城网站

需要解决的主要问题&#xff1a; 1、网页编程环境和工具。 2、后台数据库的管理。 3、网站的基本功能建设。 4、对比实际应用中的购物网站的功能和运作流程&#xff0c;完善程序功能。 水果购物商城系统的主要使用者分为管理员&#xff1b;个人中心、用户管理、水果分类管理…

支持RT-Thread最新版本的瑞萨RA2E1开发板终于要大展身手了

支持RT-Thread最新版本的瑞萨RA2E1开发板终于要大展身手了 熟悉RT-Thread和瑞萨MCU的朋友都知道&#xff0c;当前RT-Thread仓库的主线代码是不支持RA2E1这个BSP的。刚好&#xff0c;最近我在联合瑞萨推广一个叫《致敬未来的攻城狮计划》&#xff0c;使用的就是RA2E1开发板&…

ES6技术总结与测试用例

一、介绍 ES6全称是ECMAScript ECMAScript 和 JavaScript 的关系 一个常见的问题是&#xff0c;ECMAScript 和 JavaScript 到底是什么关系&#xff1f; 要讲清楚这个问题&#xff0c;需要回顾历史。1996 年 11 月&#xff0c;JavaScript 的创造者 Netscape 公司&#xff0c…

连接数据库的方法和方式

前景说明&#xff1a; 在我们刚开始使用数据库的时候&#xff0c;发现只能在mysql编辑器里面使用sql语句来完成对数据库的操作&#xff0c;那我们怎么来通过Java来操控数据库呢&#xff1f;这个时候就有了JDBC的出现。 1.什么是JDBC JDBC 指java数据库连接&#xff08…

c#文案语音配图片一键生成视频

高手略过吧&#xff0c;功能太简单&#xff0c;我自己都不好意思。。 这个是我自己的需要&#xff0c;做产品类的短视频&#xff0c;东搞西搞剪辑啊啥的&#xff0c;特别麻烦&#xff0c;所以先搞这个最简版&#xff0c;以后再一步步丰富功能。 需求&#xff1a;几张图片生成…

网络安全行业0-5年规划,零基础如何入门网络渗透?

前言 网络安全这个行业优势有&#xff1a;需求量大&#xff0c;人才紧急&#xff0c;门槛低&#xff0c;工资高。对于许多未曾涉足IT行业「小白」来说&#xff0c;深入地学习网络安全是一件十分困难的事。 关于我 本人是一位90后奇安信驻场网络安全工程师。上海交通大学软件…

Web前端学习:章四 -- JavaScript初级(四)-- BOM

138&#xff1a;Object数据格式简介 1、object对象 JS中独有 的一种数据格式 名字可以随便取&#xff0c;值一般就那几种数据格式 139&#xff1a;BOM - JS跳转页面 BOM Browser Object Model&#xff1a;浏览器对象模型 使用JavaScript控制浏览器交互 控制浏览器里面的内…

【数据结构与算法】队列和栈的相互实现以及循环队列

目录&#x1f314;一.用队列实现栈&#x1f319;1.题目描述&#x1f319;2.思路分析&#x1f319;3.代码实现⛈二.用栈实现队列☔1.题目描述☔2.思路分析☔3.代码实现&#x1f308;三.实现循环队列&#x1f314;一.用队列实现栈 &#x1f319;1.题目描述 我们先看一下题目链接…

大数据技术之Hive SQL题库-初级

第一章环境准备1.1 建表语句hive>-- 创建学生表 DROP TABLE IF EXISTS student; create table if not exists student_info(stu_id string COMMENT 学生id,stu_name string COMMENT 学生姓名,birthday string COMMENT 出生日期,sex string COMMENT 性别 ) row format delim…

STM32学习(九)

IWDG的本质 Independent watchdog&#xff0c;即独立看门狗。 本质是&#xff1a;能产生系统复位信号的计时器。 递减的计数器。时钟由独立的RC振荡器提供&#xff08;不是来自于主时钟HSE或HSI&#xff09;&#xff0c;可在待机或者停止模式下运行。看门狗被激活后&#xff…
最新文章