万字解析设计模式之原型模式与建造者模式

一、原型模式

1.1概述

原型模式是一种创建型设计模式,其目的是使用已有对象作为原型来创建新的对象。原型模式的核心是克隆,即通过复制已有对象来创建新对象,而不是通过创建新对象的过程中独立地分配和初始化所有需要的资源。这种方式可以节省创建对象的时间和资源,特别是在创建大量对象的情况下。

1.2结构

 原型模式的结构主要包括以下几个角色:

  1. 抽象原型(Prototype):定义一个用于克隆自身的接口,规定了具体原型对象必须实现的的 clone() 方法。

  2. 具体原型(Concrete Prototype):实现抽象原型接口的具体类,用于克隆自身;

  3. 客户端(Client):使用具体原型类中的 clone() 方法来复制新的对象。

1.3实现

原型模式的克隆分为浅克隆和深克隆。

  •  浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址
  • 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

区别:

  • 浅拷贝只复制对象的基本属性值,而不会复制对象的引用类型成员变量;
  • 深拷贝会递归复制对象及其所有的引用类型成员变量,从而完全复制一个对象。 

Java中的Object类中提供了 clone() 方法来实现浅克隆。 Cloneable 接口是上面的类图中的抽象原型类,而实现了Cloneable接口的子实现类就是具体的原型类

Realizetype(具体的原型类):

package com.yanyu.PrototypePattern;public class Realizetype implements Cloneable {public Realizetype() {System.out.println("具体的原型对象创建完成!");}@Overrideprotected Realizetype clone() throws CloneNotSupportedException {System.out.println("具体原型复制成功!");return (Realizetype) super.clone();}
}

PrototypeTest(测试访问类):

public class PrototypeTest {public static void main(String[] args) throws CloneNotSupportedException {Realizetype r1 = new Realizetype();Realizetype r2 = r1.clone();
​System.out.println("对象r1和r2是同一个对象?" + (r1 == r2));}
}

该代码实现了原型模式的具体原型类 Realizetype。该类实现了 Cloneable 接口,表示该类可以被克隆。当实例化 Realizetype 对象时,会打印出一条提示信息表明该原型对象已经创建完成。当调用 clone() 方法时,会打印出一条提示信息表明该具体原型对象已经复制成功并返回克隆后的实例。

需要注意的是,clone() 方法是将该对象进行复制,而不是创建新的对象。因此,在使用原型模式时,原型对象的构造方法不会被调用,因为对象是通过克隆得到的。

 1.4案例

用原型模式生成“三好学生”奖状

同一学校的“三好学生”奖状除了获奖人姓名不同,其他都相同,可以使用原型模式复制多个“三好学生”奖状出来,然后在修改奖状上的名字即可。

浅克隆 

package com.yanyu.PrototypePattern;//奖状类
public class Citation implements Cloneable {private String name;public void setName(String name) {this.name = name;}public String getName() {return (this.name);}public void show() {System.out.println(name + "同学:在2023学年第一学期中表现优秀,被评为三好学生。特发此状!");}@Overridepublic Citation clone() throws CloneNotSupportedException {return (Citation) super.clone();}
}
package com.yanyu.PrototypePattern;//测试访问类
public class CitationTest {public static void main(String[] args) throws CloneNotSupportedException {Citation c1 = new Citation();c1.setName("张三");//复制奖状Citation c2 = c1.clone();//将奖状的名字修改李四c2.setName("李四");c1.show();c2.show();}
}

深克隆

深克隆需要使用对象流

//奖状类
public class Citation implements Cloneable {private Student stu;
​public Student getStu() {return stu;}
​public void setStu(Student stu) {this.stu = stu;}
​void show() {System.out.println(stu.getName() + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");}
​@Overridepublic Citation clone() throws CloneNotSupportedException {return (Citation) super.clone();}
}
​
//学生类
public class Student {private String name;private String address;
​public Student(String name, String address) {this.name = name;this.address = address;}
​public Student() {}
​public String getName() {return name;}
​public void setName(String name) {this.name = name;}
​public String getAddress() {return address;}
​public void setAddress(String address) {this.address = address;}
}
​
public class CitationTest1 {public static void main(String[] args) throws Exception {Citation c1 = new Citation();Student stu = new Student("张三", "西安");c1.setStu(stu);
​//创建对象输出流对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\b.txt"));//将c1对象写出到文件中oos.writeObject(c1);oos.close();
​//创建对象出入流对象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\b.txt"));//读取对象Citation c2 = (Citation) ois.readObject();//获取c2奖状所属学生对象Student stu1 = c2.getStu();stu1.setName("李四");
​//判断stu对象和stu1对象是否是同一个对象System.out.println("stu和stu1是同一个对象?" + (stu == stu1));
​c1.show();c2.show();}
}

1.5应用场景

原型模式通常在以下情况下使用:

1. 当实例化类的成本较大时,例如从数据库或网络实例化对象时,使用原型模式可以避免重复的资源消耗。

2. 当需要创建大量相似对象时,使用原型模式可以提高创建对象的效率,避免重复的代码。

3. 当对象的创建过程比较复杂,并且具有很多依赖关系时,使用原型模式可以简化对象的创建过程,减少错误。

4. 当需要保护对象的状态时,使用原型模式可以防止对象被不小心改变,因为任何改变都是在副本上进行的而不是原始对象上进行的。

总的来说,原型模式适用于需要创建大量相似或者复杂对象的情况,并且可以提高创建效率、减少资源消耗。

 二、建造者模式

2.1概述

建造者模式是一种创建型设计模式,它允许你将一个复杂对象的构造过程分解成若干个简单的步骤,从而使得同样的构造过程可以创建不同的表示。建造者模式解决了在创建复杂对象时,对象内部的表现和构造是紧密耦合在一起的难题,使得同样的构建过程可以创建不同的表现形式,并且不需要修改构造过程的代码。

分离了部件的构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况。

2.2结构

建造者(Builder)模式包含如下角色:

  • 抽象建造者类(Builder):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。
  • 具体建造者类(ConcreteBuilder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例。
  • 产品类(Product):要创建的复杂对象。
  • 指挥者类(Director):负责管理具体建造者的调用顺序,以确保最终构建出的产品符合特定的要求和标准。

2.3实现

创建共享单车

生产自行车是一个复杂的过程,它包含了车架,车座等组件的生产。而车架又有碳纤维,铝合金等材质的,车座有橡胶,真皮等材质。对于自行车的生产就可以使用建造者模式。

这里Bike是产品,包含车架,车座等组件;Builder是抽象建造者,MobikeBuilder和OfoBuilder是具体的建造者;Director是指挥者。类图如下:

 Bike.java

package com.yanyu.BuilderPattern;//自行车类
public class Bike {private String frame;private String seat;public String getFrame() {return frame;}public void setFrame(String frame) {this.frame = frame;}public String getSeat() {return seat;}public void setSeat(String seat) {this.seat = seat;}
}

 Builder.java

package com.yanyu.BuilderPattern;// 抽象 builder 类
public abstract class Builder {protected Bike mBike = new Bike();public abstract void buildFrame();public abstract void buildSeat();public abstract Bike createBike();
}

MobikeBuilder.java

package com.yanyu.BuilderPattern;//摩拜单车Builder类
public class MobikeBuilder extends Builder {@Overridepublic void buildFrame() {mBike.setFrame("铝合金车架");}@Overridepublic void buildSeat() {mBike.setSeat("真皮车座");}@Overridepublic Bike createBike() {return mBike;}
}
package com.yanyu.BuilderPattern;//ofo单车Builder类
public class OfoBuilder extends Builder {@Overridepublic void buildFrame() {mBike.setFrame("碳纤维车架");}@Overridepublic void buildSeat() {mBike.setSeat("橡胶车座");}@Overridepublic Bike createBike() {return mBike;}
}

  Director .java

package com.yanyu.BuilderPattern;//指挥者类
public class Director {private Builder mBuilder;public Director(Builder builder) {mBuilder = builder;}public Bike construct() {mBuilder.buildFrame();mBuilder.buildSeat();return mBuilder.createBike();}
}

指挥者类聚合了抽象构件者,利用自己的装配流程得到最终产物,至于消费者除了创建指挥者还要自己指定品牌(厂商)

test

//测试类
public class Client {public static void main(String[] args) {showBike(new OfoBuilder());showBike(new MobikeBuilder());}private static void showBike(Builder builder) {Director director = new Director(builder);Bike bike = director.construct();System.out.println(bike.getFrame());System.out.println(bike.getSeat());}
}

上面示例是 Builder模式的常规用法,指挥者类 Director 在建造者模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把指挥者类和抽象建造者进行结合

2.4优缺点

优点:

  • 建造者模式的封装性很好。使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在指挥者类中对整体而言可以取得比较好的稳定性。
  • 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
  • 建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险。符合开闭原则。

缺点:

造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

2.5应用场景

建造者(Builder)模式创建的是复杂对象,其产品的各个部分经常面临着剧烈的变化,但将它们组合在一起的算法却相对稳定,所以它通常在以下场合使用。

  • 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
  • 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的。

2.6模式扩展

建造者模式除了上面的用途外,在开发中还有一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码可读性会非常差,而且很容易引入错误,此时就可以利用建造者模式进行重构。

三、 创建者模式对比

3.1工厂方法模式VS建造者模式

工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。

我们举个简单例子来说明两者的差异,如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷、能够飞翔、内裤外穿的超人;而如果使用建造者模式,则需要组装手、头、脚、躯干等部分,然后再把内裤外穿,于是一个超人就诞生了。

3.2 抽象工厂模式VS建造者模式

抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。

建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。

如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。

 四、实验

4.1原型模式浅克隆

任务描述

某高校自行开发了一套教务系统,但在使用过程中,越来越多的老师对教学周历的创建和编写模块产生了抱怨。追其原因,该高校的教务管理员发现,同一门课程会有多个班级,教师需要对每个班级都要录入教学周历,然后这些周历大多是完全一致的,只有细微的差别。但是现行系统每个班级默认创建的周历都是空白报表,老师只能通过重新输入或不断复制粘贴来填写重复的内容,极大降低了工作效率,浪费宝贵的时间。那么如何快速创建相同或者相似的教学周历呢 ?

本关任务:原型模式就能解决该问题,老师将创建好的周历保存为模板,通过对象浅克隆,然后在新的对象上稍作修改,再保存为一个新的周历。请按以下周历模板类(模拟版)编写。

,

相关知识

为了完成本关任务,你需要掌握:

  1. 模式理解;
  2. 实现方式。
模式理解

浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

实现方式
  1. 创建原型接口,并在其中声明克隆方法。如果你已有类层次结构,则只需在其所有类中添加该方法即可。

  2. 原型类必须另行定义一个以该类对象为参数的构造函数。构造函数必须复制参数对象中的所有成员变量值到新建实体中。如果你需要修改子类,则必须调用父类构造函数,让父类复制其私有成员变量值。

  3. 克隆方法通常只有一行代码:使用 new 运算符调用原型版本的构造函数。注意,每个类都必须显式重写克隆方法并使用自身类名调用 new 运算符。否则,克隆方法可能会生成父类的对象。

  4. 你还可以创建一个中心化原型注册表,用于存储常用原型。

  5. 你可以新建一个工厂类来实现注册表,或者在原型基类中添加一个获取原型的静态方法。该方法必须能够根据客户端代码设定的条件进行搜索。搜索条件可以是简单的字符串,或者是一组复杂的搜索参数。找到合适的原型后,注册表应对原型进行克隆,并将复制生成的对象返回给客户端。

最后还要将对子类构造函数的直接调用替换为对原型注册表工厂方法的调用。

编程要求

本次任务有两个文件“Client.java”和“TeachingCalendar.java”,请在右侧编辑器 Begin-End 内补充 TeachingCalendar.java 文件的代码,Client.java 文件不用修改。

测试说明

平台会对你编写的代码进行测试:

预期输出: TeachingCalendar[Teachingclass=19级1班,Weeks=1,Summary=内容摘要,Classroom=13教409] TeachingCalendar[Teachingclass=19级2班,Weeks=1,Summary=内容摘要,Classroom=13教409]

使用场景:

  1. 通过构造器创建对象的成本比较大,比如创建过程中时间、CPU、网络资源占用过多;
  2. 创建一个对象需要繁琐的数据准备或者权限设置等;
  3. 系统中需要大量使用该对象的副本,且各个调用者需要给它们各自的副本进行属性重新赋值。

应用案例: ArrayList,Spring 中原型 bean。

 

package step1;public class TeachingCalendar implements Cloneable {private int Weeks;private String Summary;private String Classroom;private String Teachingclass;public String getTeachingclass() {return Teachingclass;}public void setTeachingclass(String teachingclass) {Teachingclass = teachingclass;}public int getWeeks() {return Weeks;}public void setWeeks(int weeks) {Weeks = weeks;}public String getSummary() {return Summary;}public void setSummary(String summary) {Summary = summary;}public String getClassroom() {return Classroom;}public void setClassroom(String classroom) {Classroom = classroom;}public TeachingCalendar clone(){/********** Begin *********/try {return (TeachingCalendar) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}/********** End *********/}public  String toString(){return "TeachingCalendar[Teachingclass="+Teachingclass+",Weeks="+Weeks+",Summary="+Summary+",Classroom="+Classroom+"]";}
}
package step1;public class Client  {public static void main(String[] args) {TeachingCalendar calendar = new TeachingCalendar();calendar.setTeachingclass("19级1班");calendar.setWeeks(1);calendar.setSummary("内容摘要");calendar.setClassroom("13教409");TeachingCalendar calendar2 = calendar.clone();calendar2.setTeachingclass("19级2班");System.out.println(calendar.toString());System.out.println(calendar2.toString());}
}

 4.2原型模式深克隆

任务描述

某高校自行开发了一套实践教学系统,但在使用过程中,老师们希望实验项目能共享,例如张三老师制作的实验项目可以引入到李四老师的在线课程。那么如何实现呢 ?

本关任务:原型模式就能解决该问题,通过对象深克隆,然后在新的对象上稍作修改,再保存为一个新的实验项目。实验项目类的结构图如下。

,

相关知识

为了完成本关任务,你需要掌握:

  1. 模式理解;
  2. 实现方式。
模式理解

深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

实现方式
  1. 创建原型接口, 并在其中声明 克隆方法。 如果你已有类层次结构, 则只需在其所有类中添加该方法即可。
  2. 原型类必须另行定义一个以该类对象为参数的构造函数。 构造函数必须复制参数对象中的所有成员变量值到新建实体中。 如果你需要修改子类, 则必须调用父类构造函数, 让父类复制其私有成员变量值。
  3. 克隆方法通常只有一行代码: 使用 new 运算符调用原型版本的构造函数。 注意, 每个类都必须显式重写克隆方法并使用自身类名调用 new 运算符。 否则, 克隆方法可能会生成父类的对象。
  4. 你还可以创建一个中心化原型注册表, 用于存储常用原型。
  5. 你可以新建一个工厂类来实现注册表, 或者在原型基类中添加一个获取原型的静态方法。 该方法必须能够根据客户端代码设定的条件进行搜索。 搜索条件可以是简单的字符串, 或者是一组复杂的搜索参数。 找到合适的原型后, 注册表应对原型进行克隆, 并将复制生成的对象返回给客户端。

最后还要将对子类构造函数的直接调用替换为对原型注册表工厂方法的调用。

编程要求

本次任务有3个文件“Client.java”、“Codefile”和“Experiment.java”。在右侧编辑器 Begin-End 内补充 Experiment.java 文件中的代码,其它文件完整。

测试说明

平台会对你编写的代码进行测试:

预期输出: Experiment[teacher=张三,name=序列化深度克隆实验,describe=原型模式] Experiment[teacher=李四,name=序列化深度克隆实验,describe=原型模式] false

package step2;import java.io.*;public class Experiment implements Serializable {private String name;private String teacher;private String describe;public Codefile getFile() {return file;}private Codefile file;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getTeacher() {return teacher;}public void setTeacher(String teacher) {this.teacher = teacher;}public String getDescribe() {return describe;}public void setDescribe(String describe) {this.describe = describe;}public String toString(){return "Experiment[teacher="+teacher+",name="+name+",describe="+describe+"]";}public Experiment(Codefile file){this.file =file;}public Object DeepClone() throws IOException, ClassNotFoundException, OptionalDataException {/********** Begin *********///将对象写入流中ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(this);// 从流中取出对象ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return ois.readObject();//将对象从流中取出/********** End *********/}
}

  Codefile.java

package step2;import java.io.Serializable;public class Codefile implements Serializable {public String getFilepath() {return filepath;}public void setFilepath(String filepath) {this.filepath = filepath;}private String filepath;
}

 Client.java

package step2;public class Client {public static void main(String[] args) {Experiment experiment,copyExperiment=null;Codefile codefile =new Codefile();codefile.setFilepath("D://Code/Code.java");experiment=new Experiment(codefile);experiment.setName("序列化深度克隆实验");experiment.setDescribe("原型模式");experiment.setTeacher("张三");try{copyExperiment=(Experiment)experiment.DeepClone();copyExperiment.setTeacher("李四");System.out.println(experiment.toString());System.out.println(copyExperiment.toString());System.out.println((experiment.getFile() == copyExperiment.getFile()));}catch(Exception e){e.printStackTrace();}}
}

4.3建筑者模式

任务描述

有一个虚拟仿真的应用程序,需求适配大部分用户群体,且能给不同用户带来不一样的视觉体验。因此要求系统自动检测用户机器配置,以此来动态产生场景中树叶(leaf)、房子(house)、路面(way)不同的渲染效果。

本关任务:用建筑者模式编写构建低配和高配的渲染方案,详细如下。 高配【树叶反射浅绿色,房子上玻璃发亮,路面有些高光】 低配【树叶反射深绿色,房子上玻璃发暗,路面不反射】

实现方式

  1. 清晰地定义通用步骤, 确保它们可以制造所有形式的产品。 否则你将无法进一步实施该模式。

  2. 在基本生成器接口中声明这些步骤。

  3. 为每个形式的产品创建具体生成器类, 并实现其构造步骤。不要忘记实现获取构造结果对象的方法。 你不能在生成器接口中声明该方法, 因为不同生成器构造的产品可能没有公共接口, 因此你就不知道该方法返回的对象类型。 但是, 如果所有产品都位于单一类层次中, 你就可以安全地在基本接口中添加获取生成对象的方法。

  4. 考虑创建主管类。 它可以使用同一生成器对象来封装多种构造产品的方式。

  5. 客户端代码会同时创建生成器和主管对象。 构造开始前, 客户端必须将生成器对象传递给主管对象。 通常情况下, 客户端只需调用主管类构造函数一次即可。 主管类使用生成器对象完成后续所有制造任务。 还有另一种方式, 那就是客户端可以将生成器对象直接传递给主管类的制造方法。

  6. 只有在所有产品都遵循相同接口的情况下, 构造结果可以直接通过主管类获取。 否则, 客户端应当通过生成器获取构造结果。

编程要求

根据提示,在右侧编辑器 Begin-End 内补充“Director.java,HighRenderBuilder.java,LowRenderBuilder.java”文件中的代码,XMLUtil 类和 xml 文件已完成但被隐藏。

测试说明

平台会对你编写的代码进行测试:

测试输入:从已有的 XML 文件中读取;例如 LowRenderBuilder

预期输出: 树叶反射深绿色 房子上玻璃发暗 路面不反射

测试输入:从已有的 XML 文件中读取;例如 HighRenderBuilder 预期输出: 树叶反射浅绿色 房子上玻璃发亮 路面有些高光

 建造者模式主要适用于以下应用场景:

  1. 相同的方法,不同的执行顺序,产生不同的结果。
  2. 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。
  3. 产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。
  4. 初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。

  Client.java

package step1;public class Client {public static void main(String[] args) {// 从配置文件中获取具体的建造者对象IBuilder mb=(IBuilder)XMLUtil.getBean();// 创建指导者对象Director director=new Director();// 设置指导者所使用的建造者对象director.setIBuilder(mb);// 使用指导者构建产品对象Rendermap map=director.construct();// 输出构建得到的产品的信息System.out.println(map.getLeaf());System.out.println(map.getHouse());System.out.println(map.getWay());}
}

 Director.java

package step1;public class Director {private IBuilder ib;/*** 设置指导者所使用的建造者对象* @param mb 建造者对象*/public void setIBuilder(IBuilder mb){ib=mb;}/*** 构建产品对象的方法* @return 构建得到的产品对象*/public Rendermap construct(){/********** Begin *********/// 通过建造者对象依次构建产品的各个部分ib.buildLeaf();ib.buildHouse(); ib.buildWay(); // 返回构建得到的产品对象return ib.getMap();/********** End *********/}}

具体建造者

package step1;public class HighRenderBuilder extends IBuilder {/********** Begin *********/public void buildLeaf(){map.setLeaf("树叶反射浅绿色");}public void buildHouse(){map.setHouse("房子上玻璃发亮");}public void buildWay(){map.setWay("路面有些高光");}/********** End *********/
}
package step1;public class LowRenderBuilder extends IBuilder {
/********** Begin *********/public void buildLeaf(){map.setLeaf("树叶反射深绿色");}public void buildHouse(){map.setHouse("房子上玻璃发暗");}public void buildWay(){map.setWay("路面不反射");}
/********** End *********/}

抽象建筑者

package step1;public abstract class IBuilder {protected Rendermap map=new Rendermap();public abstract void buildLeaf();public abstract void buildHouse();public abstract void buildWay();public Rendermap getMap(){return map;}
}

package step1;
///场景中的对象在模拟程序中用String替代
public class Rendermap {private String leaf;private String house;private String way;public String getLeaf() {return leaf;}public void setLeaf(String leaf) {this.leaf = leaf;}public String getHouse() {return house;}public void setHouse(String house) {this.house = house;}public String getWay() {return way;}public void setWay(String way) {this.way = way;}}

解析类

package step1;import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;public class XMLUtil {public static Object getBean(){try{//创建文档对象DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = dFactory.newDocumentBuilder();Document doc;doc = builder.parse(new File("/data/workspace/myshixun/src/Builderconfig.xml"));//获取包含类名的文本节点NodeList nl = doc.getElementsByTagName("className");Node classNode=nl.item(0).getFirstChild();String cName=classNode.getNodeValue();//通过类名生成实例对象并将其返回Class c=Class.forName(cName);Object obj=c.newInstance();return obj;}catch(Exception e){e.printStackTrace();return null;}}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

excel求差公式怎么使用?

利用excel求差,可能有许多的小伙伴已经会了,不过还是存在一些不太熟悉的朋友们,所以这里有必要讲解一下。其实求差的实现主要就是一个公式,就是用一个单元格中的数字“减去”另一个单元格中的数字“等于”第三个单元格。此公式掌握…

手把手教你如何实现TNAS与云盘之间的无缝同步技巧

嘿,铁粉们! 云盘的下载速度总是让我们抓耳挠腮 数据安全隐私问题让人担心不已 但在购入NAS之前 众多数据存放在云盘里 同时也想把NAS的数据备份在云盘里 实现备份321法则? 不用烦恼 铁威马来帮忙 无需其他多余操作 只要下载CloudSyn…

华为OD机考算法题:生日礼物

题目部分 题目生日礼物难度易题目说明小牛的孩子生日快要到了,他打算给孩子买蛋糕和小礼物,蛋糕和小礼物各买一个,他的预算不超过x元。蛋糕 cake 和小礼物 gift 都有多种价位的可供选择。输入描述第一行表示cake的单价,以逗号分隔…

通过python操作neo4j

在neo4j中创建结点和关系 创建结点 创建电影结点 例如:创建一个Movie结点,这个结点上带有三个属性{title:‘The Matrix’, released:1999, tagline:‘Welcome to the Real World’} CREATE (TheMatrix:Movie {title:The Matrix, released:1999, tagl…

Python Django 之模板继承详解(extends)

文章目录 1 概述1.1 目的1.2 标签:block、extends1.3 目录结构 2 templates 目录2.1 base.html:父页面2.2 login.html:子页面 3 其它代码3.1 settings.py3.2 views.py3.3 urls.py 1 概述 1.1 目的 模板继承 和 类继承 的目的是一样的&#…

【P2P owt】owt-client-native-p2p-e2e-test vs2017构建7:依赖库及路径

依赖库 G:\CDN\LiveServiceMesh\cdnsignal\third_party\libeva\thirdparty\janbar-openssl\out32\ssl\Debug\libssl-

2、NLP文本预处理技术:词干提取和词形还原

一、说明 在上一篇文章中,我们解释了文本预处理的重要性,并解释了一些文本预处理技术。在本文中,我们将介绍词干提取和词形还原主题。 词干提取和词形还原是两种文本预处理技术,用于将单词还原为其基本形式或词根形式。这些技术的…

SpringBoot集成与应用Neo4j

文章目录 前言集成使用定义实体配置定义Repository查询方法方式一:Query方式二:Cypher语法构建器方式三:Example条件构建器方式四:DSL语法 自定义方法自定义接口继承自定义接口实现自定义接口neo4jTemplateNeo4jClient 自定义抽象…

企业级JAVA、数据库等编程规范之命名风格 —— 超详细准确无误

🧸欢迎来到dream_ready的博客,📜相信你对这两篇博客也感兴趣o (ˉ▽ˉ;) 📜 表白墙/留言墙 —— 初级SpringBoot项目,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学 📜 用户登录前后端…

【计算机网络笔记】传输层——可靠数据传输原理之Rdt协议

系列文章目录 什么是计算机网络? 什么是网络协议? 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能(1)——速率、带宽、延迟 计算机网络性能(2)…

基于深度学习的人脸表情识别 计算机竞赛

文章目录 0 前言1 技术介绍1.1 技术概括1.2 目前表情识别实现技术 2 实现效果3 深度学习表情识别实现过程3.1 网络架构3.2 数据3.3 实现流程3.4 部分实现代码 4 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 基于深度学习的人脸表情识别 该项目较…

视频汇聚平台EasyCVR分发的流如何进行token鉴权?具体步骤是什么?

视频监控EasyCVR平台能在复杂的网络环境中,将分散的各类视频资源进行统一汇聚、整合、集中管理,在视频监控播放上,TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放,可同时播放多路视频流,也能支持视…

智安网络|保护您的应用程序免受攻击:重要的安全强化措施

在今天的数字化时代,应用程序安全成为了企业和个人必须重视的重要领域。应用程序普遍存在的安全漏洞成为黑客们进行攻击的一个突破口。为了保护敏感数据和个人隐私,我们必须了解并实施一系列的关键措施来加固应用程序的安全性。 首先,一个关…

SSM培训报名管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 培训报名管理系统是一套完善的信息系统,结合SSM框架完成本系统,对理解JSP java编程开发语言有帮助系统采用SSM框架(MVC模式开发),系统具有完整的源代码和数据库,系统主 要采用B/S模式开…

Mac docker+vscode

mac 使用docker vs code 通过vscode 可以使用docker容器的环境。 可以在容器安装gdb, 直接调试代码。 创建容易时候可以指定目录和容易目录可以共享文件。

十年回望 -- JAVA

十年 十年时间,弹指一挥,好像一直都是在为工作奔波,匆匆忙忙的十年。 一、个人介绍 本人毕业于一所很普通的公办专科院校(全日制统招大专),专业是软件技术,当初能进入计算机这一行业&#xff0…

数字孪生与智慧城市:开启未来智慧生活

在数字时代的浪潮中,数字孪生技术和智慧城市的理念相互交织,共同塑造了一个更智能、更可持续、更宜居的未来。数字孪生是一项前沿技术,将虚拟世界与现实世界相融合,为城市管理者和市民带来了前所未有的机遇和便捷。 数字孪生模型是…

FreeRTOS深入教程(空闲任务和Tick中断深入分析)

文章目录 前言一、空闲任务源码分析二、Tick中断深入分析总结 前言 本篇文章主要带大家深入分析空闲任务和Tick中断的作用。 一、空闲任务源码分析 在启动调度器时会创建出空闲任务: /* 启动调度器 */ vTaskStartScheduler();在空闲任务中会调用到prvCheckTasks…

Unity地面交互效果——2、动态法线贴图实现轨迹效果

Unity引擎动态法线贴图制作球滚动轨迹 大家好,我是阿赵。   之前说了一个使用局部UV采样来实现轨迹的方法。这一篇在之前的基础上,使用法线贴图进行凹凸轨迹的绘制。 一、实现的目标 先来回顾一下,上一篇最终我们已经绘制了一个轨迹的贴图…

ASCB1系列智能微型断路器在科技馆中的应用-安科瑞黄安南

【摘要】:安科瑞电气厂家直供黄安南1876-15//06-237,ASCB1系列智能微型断路器是安科瑞电气股份有限公司全新推出的智慧用电产品,产品由智能微型断路器与智能网关两部分组成,可用于对用电线路的关键电气因素,如电压、电…