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

一、原型模式

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.mzph.cn/news/127957.shtml

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

相关文章

shell script 案例二

需求,运行程序,用户输入firstname,回车,再次提示输入lastname,然后回车,屏幕打印fullname信息 注意:前期写程序要注意规范,方便以后自己写多了回头看可以看的懂,程序代码…

如何在麒麟上安装 ONLYOFFICE 桌面编辑器

我们很高兴地告诉大家,ONLYOFFICE 桌面编辑器现已上架麒麟软件商店。请阅读下文了解详情。 关于麒麟 麒麟是一款国产操作系统,主要是为了满足中国市场的需求和偏好而设计的。 它能够与各种硬件平台和软件应用程序的广泛兼容,因而受到认可。…

路由器如何设置IP地址

IP地址是计算机网络中的关键元素,用于标识和定位设备和主机。在家庭或办公室网络中,路由器起到了连接内部设备和外部互联网的关键作用。为了使网络正常运行,需要正确设置路由器的IP地址。本文将介绍如何设置路由器的IP地址,以确保…

性能测试 —— Jmeter日志查看与分析

一、Jmeter日志概览 Jmeter日志文件保存在bin目录中,名称为jmeter.log。我们可以在面板中直接察看日志,点击右上角黄色标志物可以打开日志面板,再次点击收起 另外,Jmeter可以很方便地设置日志输出级别: 通过这种方式修…

腾讯云轻量级服务器哪个镜像比较好?

腾讯云轻量应用服务器镜像是什么?镜像就是操作系统,轻量服务器镜像系统怎么选择?如果是用来搭建网站腾讯云百科txybk.com建议选择选择宝塔Linux面板腾讯云专享版,镜像系统根据实际使用来选择,腾讯云百科来详细说下腾讯…

使用shardingJDBC中的雪花算法生成id

常用ID解决方案 数据库自增id 依靠数据库系统的功能实现,但是未来扩容麻烦主从切换时的不⼀致可能会导致重复发号性能瓶颈存在单台sql上 UUID 性能非常高,没有网络消耗无序的字符串,不具备趋势自增特性UUID太长,不易于存储&am…

Django开发实例总结(入门级、4.2.6、详细)

目录 概述 Django的核心组件包括 Django的项目结构 创建工程(4.2.6) 实例一:Hello world 实例二:访问一个自定义主页 实例三:通过登录跳转到主页 实例四:主页添加静态文件,包含js、css、…

Android开发笔记(三)—Activity篇

活动组件Activity 启动和结束生命周期启动模式信息传递Intent显式Intent隐式Intent 向下一个Activity发送数据向上一个Activity返回数据 附加信息利用资源文件配置字符串利用元数据传递配置信息给应用页面注册快捷方式 启动和结束 (1)从当前页面跳到新页…

0032Java程序设计-基于JavaEE的智能化酒店点餐收款系统的设计与实现论文

文章目录 摘 要目录系统设计开发环境 摘 要 酒店点餐收款系统是为了实现酒店餐饮自动化管理而设计的,它完全取代了原来酒店餐饮管理一直使用的人工处理的工作方式,并且避免了由于管理人员的工作疏忽以及管理质量问题所造成的各种错误,为及时…

cudnn too short

原因是libcudnn.so为软链接,相当于快捷键,但是没有映射到真正的libcudnn.so.8.9.5上 cd /usr/local/cuda-11.6/lib64 ln -s libcudnn.so.8.9.5 libcudnn.so.8 ln -s libcudnn.so.8.9.5 libcudnn.so

亚马逊云科技为奇点云打造全面、安全、可扩展的数据分析解决方案

刘莹奇点云联合创始人、COO:伴随云计算的发展,数据技术也在快速迭代,成为客户迈入DT时代、实现高质量发展的关键引擎。我们很高兴能和云计算领域的领跑者亚马逊云科技一同,不断为客户提供安全可靠的产品与专业的服务。 超过1500家…

Object转List<>,转List<Map<>>

这样就不会局限在转换到List<Map<String,Object>>这一种类型上了.可以转换成List<Map<String,V>>上等,进行泛型转换虽然多了一个参数,但是可以重载啊注: 感觉field.get(key) 这里处理的不是很好,如果有更好的办法可以留言 public static <K, V> …

0基础学习PyFlink——个数滚动窗口(Tumbling Count Windows)

大纲 Tumbling Count WindowsmapreduceWindow Size为2Window Size为3Window Size为4Window Size为5Window Size为6 完整代码参考资料 之前的案例中&#xff0c;我们的Source都是确定内容的数据。而Flink是可以处理流式&#xff08;Streaming&#xff09;数据的&#xff0c;就是…

【C++】set和multiset

文章目录 关联式容器键值对一、set介绍二、set的使用multiset 关联式容器 STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元…

【JAVA学习笔记】58 - 泛型

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter15/src/com/yinhai/generic_ https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter15/src/com/yinhai/customgeneric_ 一、泛型的入门和好处 1)请编写程序&#xff0c;…

人工智能基础_机器学习016_BGD批量梯度下降求解多元一次方程_使用SGD随机梯度下降计算一元一次方程---人工智能工作笔记0056

然后上面我们用BGD计算了一元一次方程,那么现在我们使用BGD来进行计算多元一次方程 对多元一次方程进行批量梯度下降. import numpy as np X = np.random.rand(100,8) 首先因为是8元一次方程,我们要生成100行8列的X的数据对应x1到x8 w = np.random.randint(1,10,size = (8…

B端企业形象设计的正确姿势,你学会了吗?

如今&#xff0c;企业形象设计在B端市场中变得越来越重要。它是企业与客户之间建立联系的桥梁&#xff0c;也是吸引目标客户的重要方式。为了帮助您打造一个独特而专业的企业形象设计&#xff0c;我将为您提供十个步骤。 步骤1&#xff1a;了解企业定位和目标 在设计B端企业形…

完美解决:Nginx安装后,/etc/nginx/conf.d下面没有default.conf文件

目录 1 问题&#xff1a; 2 解决方法 方法一&#xff1a; 方法二&#xff1a; 3 查看 1 问题&#xff1a; /etc/nginx/conf.d下面没有default.conf文件。 2 解决方法 方法一&#xff1a; 自己创建default.conf文件。 vi /etc/nginx/conf.d/default.conf 添加如下内容&…

恒驰服务 | 华为云数据使能专家服务offering之大数据建设

恒驰大数据服务主要针对客户在进行智能数据迁移的过程中&#xff0c;存在业务停机、数据丢失、迁移周期紧张、运维成本高等问题&#xff0c;通过为客户提供迁移调研、方案设计、迁移实施、迁移验收等服务内容&#xff0c;支撑客户实现快速稳定上云&#xff0c;有效降低时间成本…

js字符串支持多个分隔符分割

js字符串支持多个分隔符分割 场景代码 场景 用户输入内容后&#xff0c;支持多个分隔符&#xff08;比如&#xff1a;中英文逗号&#xff0c;分号以及换号&#xff09;对字符串进行分割&#xff0c;之后提交给后台同学解析。 代码 function splitString(inputString, separat…