当前位置: 首页 > news >正文

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

一、原型模式

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;}}
}

相关文章:

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

一、原型模式 1.1概述 原型模式是一种创建型设计模式,其目的是使用已有对象作为原型来创建新的对象。原型模式的核心是克隆,即通过复制已有对象来创建新对象,而不是通过创建新对象的过程中独立地分配和初始化所有需要的资源。这种方式可以节…...

深度学习数据集大合集—疾病、植物、汽车等

最近又收集了一大批深度学习数据集,今天分享给大家!废话不多说,直接上数据! 1、招聘欺诈数据集 招聘欺诈数据集:共收集了 200,000 条数据,来自三个网站。 该数据集共收集了 200.000 条数据,分别…...

物联网中的ESP8266该这么用!

🙌秋名山码民的主页 😂oi退役选手,Java、大数据、单片机、IoT均有所涉猎,热爱技术,技术无罪 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 获取源码,添加WX 目录 1. 前言…...

django中循环生成的多个btn,只有第一个btn会弹出模态框

django中循环生成的多个btn&#xff0c;只有第一个btn会弹出模态框 需求&#xff1a;为每个button按钮都绑定同一点击事件&#xff0c;点击每个btn都可弹出模态框 原因 问题代码 <button idbtnDel type"button" class"btn btn-primary btn-lg" > […...

JVM第二十三讲:Java动态调试技术原理

Java动态调试技术原理 本文是JVM第二十三讲&#xff0c;Java动态调试技术原理。转载自 美团技术团队胡健的Java 动态调试技术原理及实践&#xff0c;通过学习java agent方式进行动态调试&#xff0c;了解目前很多大厂开源的一些基于此的调试工具 (例如来自阿里开源的Arthas)。 …...

制造企业如何三步实现进销存管理?

制造企业如何三步实现进销存管理&#xff1f; 一、什么是进销存软件&#xff1f; 进销存软件是一种针对制造业企业设计的管理软件系统&#xff0c;旨在协调和优化企业的生产、采购、销售以及库存管理等方面的活动。该系统的主要目标是提高企业的生产效率、降低库存成本、优化…...

封装localstorage为对象 js

export const LocalStorageManager {recordKey: "Record",// 获取本地存储中的值get(key) {try {const value localStorage.getItem(key);if (value null || value undefined || value "") {return null;}return JSON.parse(localStorage.getItem(key…...

算法通关村第五关|白银|队栈和Hash的经典算法题【持续更新】

1.用栈实现队列 用两个栈实现队列。 class MyQueue {Deque<Integer> inStack;Deque<Integer> outStack;public MyQueue() {inStack new LinkedList<Integer>();outStack new LinkedList<Integer>();}public void push(int x) {inStack.push(x);}pu…...

java--构造器

1.构造器是什么样子 构造器分为无参构造(就相当于你有车子&#xff0c;但是里面是空的)和带参构造(就相当于你有车子&#xff0c;里面还有几个妹纸&#xff0c;你真该死啊) 2.构造器有什么特点 创建对象时&#xff0c;对象会去调用构造器。 3.构造器的常见应用场景 创建对象…...

纪念基于JavaScript 实现的后台桌面 UI 设计

目录 前言 C/S 到 B/S ASP Builder 的诞生 关于 Craneoffice.net 开发环境配置 后台界面的 UI 区域要素 桌面系统的想法和设计 搜索引擎 导航面板 快捷访问 二级导航 小组件及其它 设置桌面壁纸 小时钟 附件小程序 计算器界面设计 日历与任务 系统设置 天气小…...

C++11 auto限制

限制&#xff1a; auto 不能用于函数参数auto 不能用于非静态成员变量auto 无法定义数组auto 无法推导出模板参数 推荐一个零声学院项目课&#xff0c;个人觉得老师讲得不错&#xff0c;分享给大家&#xff1a; 零声白金学习卡&#xff08;含基础架构/高性能存储/golang云原生…...

公司老项目springmvc jsp 自定义多数据源 转到springboot 整理

真实完整步骤&#xff0c;踩坑整理 有同样的坑&#xff0c;欢迎补充整理 网上的案例老是少了很多配置&#xff0c;本案例涉及到 spring-mvc&#xff0c;自定义多数据源&#xff0c;统一前缀&#xff0c;事务&#xff0c;mybatis&#xff0c;jsp访问异常&#xff0c;静态文件。…...

Java之SpringCloud Alibaba【七】【Spring Cloud微服务网关Gateway组件】

一、网关简介 大家都都知道在微服务架构中&#xff0c;一个系统会被拆分为很多个微服务。那么作为客户端要如何去调用这么多的微服务呢?如果没有网关的存在&#xff0c;我们只能在客户端记录每个微服务的地址&#xff0c;然后分别去用。 这样的架构&#xff0c;会存在着诸多…...

探讨jdk源码中的二分查找算法返回值巧妙之处

文章目录 1.什么是二分查找算法1.1 简介1.2 实现思路 2.二分查找的示例3.jdk 中的 Arrays.binarySearch()4.jdk 中核心二分查找方法解析4.1 为什么 low 是插入点4.2 为什么要进行取反&#xff1a;-&#xff08;low 1&#xff09;4.3 为什么不直接返回 插入点 low 的相反数&…...

深度学习实战:基于TensorFlow与OpenCV的手语识别系统

文章目录 写在前面基于TensorFlow与OpenCV的手语识别系统安装环境一、导入工具库二、导入数据集三、数据预处理四、训练模型基于CNN基于LeNet5基于ResNet50 五、模型预测基于OpenCV 写在后面 写在前面 本期内容&#xff1a;基于TensorFlow与OpenCV的手语识别系统 实验环境&…...

学习整理nginx常用屏蔽规则,让网站更安全

学习整理nginx常用屏蔽规则&#xff0c;让网站更安全 注意一、防止文件被下载二、屏蔽非常见蜘蛛&#xff08;爬虫&#xff09;三、禁止某个目录执行脚本四、屏蔽某个IP或IP段 注意 在开始之前&#xff0c;希望您已经熟悉的Nginx常用命令&#xff08;如停止&#xff0c;重启等…...

四十一、【进阶】索引使用SQL提示

1、SQL提示使用情景 在使用MySQL时&#xff0c;当一个字段参在于多个索引中时&#xff0c;默认情况下&#xff0c;MySQL会自动选择一个索引&#xff0c;但我们可以指定索引吗&#xff1f;可以忽略某一种索引吗&#xff1f; 答案是可以的。 前提&#xff1a;profession字段已经…...

AI智能分析网关高空抛物算法如何实时检测高楼外立面剥落?

高楼外立面剥落是一种十分危险的行为&#xff0c;会造成严重的人身伤害和财产损失。TSINGSEE青犀智能分析网关利用高楼外立面剥落的信息&#xff0c;结合高空抛物算法来进行处理就可很好解决此问题。 1. 数据收集 首先&#xff0c;需要收集关于高楼外立面剥落的数据。这可以通…...

微信小程序 - 页面继承(非完美解决方案)

微信小程序 - 面页继承&#xff08;非完美解决方案&#xff09; 废话思路首页 indexindex.jsindex.jsonindex.wxml 父页面 page-basepage-base.jspage-base.wxml 子页面 page-apage-a.jspage-a.wxml 子页面 page-bpage-b.jspage-b.wxml 其它app.jsapp.jsonapp.wxss 参考资料 废…...

智能配件管理系统有什么用?企业如何实现管理数字化转型?

在当今高度信息化的时代&#xff0c;企业运营的各个环节都离不开准确及时的数据支持。特别是在制造业中&#xff0c;生产数据的记录和管理对于提高生产效率、降低成本、优化资源配置等方面具有至关重要的作用。然而&#xff0c;许多企业仍在采用纸质流转卡来记录生产数据&#…...

【HarmonyOS 5】针对 Harmony-Cordova 性能优化,涵盖原生插件开发、线程管理和资源加载等关键场景

1. ‌原生图片处理插件&#xff08;Java&#xff09; package com.example.plugin; import ohos.media.image.ImageSource; import ohos.media.image.PixelMap; import ohos.app.Context; public class ImageProcessor { private final Context context; public ImagePro…...

有公网ip但外网访问不到怎么办?内网IP端口映射公网连接常见问题和原因

有公网IP但外网访问不到的核心原因通常包括&#xff1a;端口未正确映射、防火墙限制、DNS解析问题、运营商端口屏蔽或路由配置错误‌。需依次排查这些关键环节&#xff0c;其中端口映射和防火墙设置是最常见的原因。‌‌ ‌内网IP端口映射公网连接常见问题和原因及解决方案 1…...

基于Android的医院陪诊预约系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…...

三种经典算法优化无线传感器网络(WSN)覆盖(SSA-WSN、PSO-WSN、GWO-WSN),MATLAB代码实现

三种经典算法优化无线传感器网络(WSN)覆盖&#xff08;SSA-WSN、PSO-WSN、GWO-WSN&#xff09;&#xff0c;MATLAB代码实现 目录 三种经典算法优化无线传感器网络(WSN)覆盖&#xff08;SSA-WSN、PSO-WSN、GWO-WSN&#xff09;&#xff0c;MATLAB代码实现效果一览基本介绍程序设…...

【LeetCode 题解】两数之和(C++/Python 双解法):从语法到算法的全面解析

【LeetCode题解】两数之和&#xff08;C/Python双解法&#xff09;&#xff1a;从语法到算法的全面解析 一、题目描述 题目链接&#xff1a;1. 两数之和 难度&#xff1a;简单 要求&#xff1a;给定一个整数数组 nums 和一个整数目标值 target&#xff0c;在数组中找出两个数…...

【图像处理基石】如何进行图像畸变校正?

图像畸变校正常用于计算机视觉、摄影测量学和机器人导航等领域&#xff0c;能够修正因镜头光学特性或传感器排列问题导致的图像失真。下面我将介绍几种常用的图像畸变校正算法&#xff0c;并提供Python实现和测试用例。 常用算法及Python实现 1. 径向畸变校正 径向畸变是最常…...

一:UML类图

一、类的设计 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 学习设计模式的第一步是看懂UML类图,类图能直观的表达类、对象之间的关系,这将有助于后续对代码的编写。 类图在软件设计及应用框架前期设计中是不可缺少的一部分,它的主要成分包括:类名、…...

GpuGeek如何成为AI基础设施市场的中坚力量

AI时代&#xff0c;算力基础设施已成为支撑技术创新和产业升级的关键要素。作为国内专注服务算法工程师群体的智算平台&#xff0c;GpuGeek通过持续创新的服务模式、精准的市场定位和系统化的生态建设&#xff0c;正快速成长为AI基础设施领域的中坚力量。本文将深入分析GpuGeek…...

Java面试实战:从Spring Boot到微服务与AI的全栈挑战

场景一&#xff1a;初步了解和基本技术问题 面试官&#xff1a;我们先从基础开始&#xff0c;谢先生&#xff0c;你能简单介绍一下你在Java SE上的经验吗&#xff1f; 谢飞机&#xff1a;当然&#xff01;Java就像是我的老朋友&#xff0c;尤其是8和11版本。我用它们做过很多…...

python h5py 读取mat文件的<HDF5 object reference> 问题

我用python加载matlab的mat文件 mat文件&#xff1a; 加载方式&#xff1a; mat_file h5py.File(base_dir str(N) _nodes_dataset_snr- str(snr) _M_ str(M) .mat, r) Signals mat_file["Signals"][()] Tp mat_file["Tp"][()] Tp_list mat_fil…...