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

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

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

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

  • 前言
  • 一、《大话设计模式》对应的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 {@Overridepublic double getResult() {return getNumberOne()+getNumberTwo();}
}/* @Author: Ariel(huan)* @Description:减法类* @CreateDate: 2023/3/24 22:28*/
public class OperationSub extends Operation {@Overridepublic double getResult() {return getNumberOne()-getNumberTwo();}
}/* @Author: Ariel(huan)* @Description:乘法类* @CreateDate: 2023/3/24 22:29*/
public class OperationMul extends Operation {@Overridepublic double getResult() {return getNumberOne()*getNumberTwo();}
}/* @Author: Ariel(huan)* @Description:除法类* @CreateDate: 2023/3/24 22:29*/
public class OperationDiv extends Operation {@Overridepublic 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{@Overridepublic Operation createOperation() {return new OperationAdd();}
}/* @Author: Ariel(huan)* @Description: 创建减法类的工厂类* @CreateDate: 2023/3/24 22:34*/
public class OperationSubFactory implements IOperationFactory {@Overridepublic Operation createOperation() {return new OperationSub();}
}/* @Author: Ariel(huan)* @Description:创建乘法类的工厂类* @CreateDate: 2023/3/24 22:35*/
public class OperationMulFactory implements IOperationFactory{@Overridepublic Operation createOperation() {return new OperationMul();}
}/* @Author: Ariel(huan)* @Description:创建除法类的工厂类* @CreateDate: 2023/3/24 22:36*/
public class OperationDivFactory implements IOperationFactory{@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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('.', '/');// 定义一个枚举的集合 并进行循环来处理这个目录下的thingsEnumeration<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 {// 获取jarjar = ((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 {// 添加到classesclasses.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){// 获取此包的目录 建立一个FileFile 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 {@Overridepublic 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 {@Overridepublic 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. 动态扩充+热加载,降低后期的开发成本(扩充和维护)——注意前期的开发成本是高的,因为要求开发者的开发素质能力很高,以及要为了后期拓展打基础所以开发成本高。
什么叫维护?维护应该是用户来做的,通过用户的需求,动态的扩充和维护系统功能。
可变包括两个可变:维护可变、扩充可变。
把重复性有规律性的内容交给程序去完成,降低开发和维护的成本, 减少了人为操作出错的情况。

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

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