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

一、原型模式

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("具体的原型对象创建完成!");
    }
    @Override
    protected 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学年第一学期中表现优秀,被评为三好学生。特发此状!");
    }
    @Override
    public 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学年第一学期中表现优秀,被评为三好学生。特发此状!");
    }
​
    @Override
    public 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 {
    @Override
    public void buildFrame() {
        mBike.setFrame("铝合金车架");
    }
    @Override
    public void buildSeat() {
        mBike.setSeat("真皮车座");
    }
    @Override
    public Bike createBike() {
        return mBike;
    }
}
package com.yanyu.BuilderPattern;

//ofo单车Builder类
public class OfoBuilder extends Builder {
    @Override
    public void buildFrame() {
        mBike.setFrame("碳纤维车架");
    }
    @Override
    public void buildSeat() {
        mBike.setSeat("橡胶车座");
    }
    @Override
    public 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;
        }
    }
}

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

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

相关文章

nrf52832 开发板入手笔记:J-Flash 蓝牙协议栈烧写

前言 nrf52832 想要开启 蓝牙功能,比如蓝牙主从机功能,需要额外烧写 蓝牙协议栈的固件:softdevice,换句话说,蓝牙协议栈等代码是不开放的,只提供一个 二进制文件。 也就是 nrf52832 Flash 与 RAM 要分区了…

[激光原理与应用-72]:PLC架构与工作原理

目录 一、PLC简介 1.1 概述 1.2 基本组成 1.3 常见的PLC品牌比较 二、PLC程序执行原理 2.1 PLC有操作系统吗? 2.2 PLC程序执行 2.3 PLC编程语言 2.4 PLC编程过程 三、PLC编程工具 3.1 编程工具 四、PLC与工控机协同 4.1 PLC需要配置工控机吗&#xff1…

滴滴学生认证拉新上线了 地推网推百搭项目

滴滴学生认证可以通过“聚量推客”申请,是一手官签服务商 量级足够大 优势:实时数据T1结算 百分百数据 滴滴学生认证可搭配小红书签到拉新、美团私域校园版拉新、美团圈圈拉新一起做,这样能赚取更多佣金

springboot和flask整合nacos,使用openfeign实现服务调用,使用gateway实现网关的搭建(附带jwt续约的实现)

环境准备: 插件版本jdk21springboot 3.0.11 springcloud 2022.0.4 springcloudalibaba 2022.0.0.0 nacos2.2.3(稳定版)python3.8 nacos部署(docker) 先创建目录,分别创建config,logs&#xf…

Chatgpt网页版根据关键词自动批量写原创文章软件【可多开自动登录切换gpt账号】

Chatgpt网页版根据关键词自动批量写原创文章软件介绍: 1、需要放入GPT账号和密码放入在账号库.txt里,可以放入多组账号密码,账号切换轮流使用。 2、可以自定义回答指令,也可多个回答指令随机切换。 3、可以给关键词加双标题&…

【EI会议征稿】 2024年遥感、测绘与图像处理国际学术会议(RSMIP2024)

2024年遥感、测绘与图像处理国际学术会议(RSMIP2024) 2024 International Conference on Remote Sensing, Mapping and Image Processing 2024年遥感、测绘与图像处理国际学术会议(RSMIP2024)将于2024年1月19日-21日在中国厦门举行。会议主要围绕遥感、测绘与图像处理等研究领…

宝塔Linux面板Java项目前后端部署 (PHP部署前端文件)

1. 上传前端文件 将整个文件夹拖进来 2. PHP项目 (添加站点) 添加证书SSL 新增配置文件 location /dev-api/{proxy_set_header Host $http_host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header REMOTE-HOST $remote_addr;proxy_set_header X-Forwarded-For $proxy_…

网络新闻发稿为何经久不衰?

有的老板可能看不到新闻营销的直接回报,一直不乐意在此方面投入,但是却看到竞争对手一直在搞新闻营销,也就安排个PR做做新闻公关。小马识途营销顾问观察,自互联网诞生以来,新闻营销一直是网络营销工作中的一个重点。 如…

Mysql系列 -索引数据结构

索引就是排好序的数据结构,可以帮助我们快速的查找到数据,那么底层的数据到底是如何存储的呢? 为什么InnoDB 用的是Btree 存储结构? 大家可以看看这个可视化的网站 数据结构和算法的可视化工具 可以看到数据结构里面有链表&…

学习笔记|配对样本均数T检验|SPSS常用的快捷键|规范表达|《小白爱上SPSS》课程:SPSS第六讲 | 配对样本均数T检验

目录 学习目的软件版本原始文档配对样本均数T检验一、实战案例二、案例解析三、统计策略四、SPSS操作1、正态性检验2、配对样本T检验 五、结果解读六、规范报告1、规范表格2、规范文字 划重点Tips:SPSS常用的快捷键 学习目的 SPSS第六讲 | 配对样本均数T检验 软件版本 IBM S…

【JavaSE专栏58】“Java构造函数:作用、类型、调用顺序和最佳实践“ ⚙️⏱️

解析Java构造函数:作用、类型、调用顺序和最佳实践" 🚀📚🔍🤔📝🔄⚙️⏱️📖🌐 摘要引言1. 什么是构造函数 🤔2. 构造函数的类型与用途 📝1.…

课题学习(九)----阅读《导向钻井工具姿态动态测量的自适应滤波方法》论文笔记

一、 引言 引言直接从原论文复制,大概看一下论文的关键点: 垂直导向钻井工具在近钻头振动和工具旋转的钻井工作状态下,工具姿态参数的动态测量精度不高。为此,通过理论分析和数值仿真,提出了转速补偿的算法以消除工具旋…

【如何写论文】硕博学位论文的结构框架、过程与大纲分析

硕士论文可以说是毕业前最重要的一部分,也可以说是展示和检验你3年研究生学习的成果的一个考试。硕士论文答辩和检验合格,才能够顺利拿到毕业生和学位证,可见其重要性。 目录 一、基础框架1.1、摘要(Abstract)1.2、绪论…

PFAF-Net

I 1 _1 1​和I 2 _2 2​是多模态图像,I F _F F​是融合图像。FT 1 _1 1​是基于空间注意力的融合,FT 2 _2 2​是基于通道注意力的融合 作者未提供代码

【贝叶斯回归】【第 1 部分】--pyro库应用

Bayesian Regression - Introduction (Part 1) — Pyro Tutorials 1.8.6 documentation 一、说明 我们很熟悉线性回归的问题,然而,一些问题看似不似线性问题,但是,用贝叶斯回归却可以解决。本文使用土地平整度和国家GDP的关系数据…

1.4 安全服务

思维导图: 1.4 安全服务 定义:在通信开放系统中,为系统或数据传输提供足够安全的协议层服务。 RFC4949 定义:由系统提供的对系统资源进行特殊保护的处理或通信服务。安全服务通过安全机制来实现安全策略。 分类:X.800 …

Flask-SQLAlchemy事件钩子介绍

一、前言 前几天在搜资料的时候无意中看到有介绍SQLAlchemy触发器,当时感觉挺奇怪的,触发器不是数据库层面的概念吗,怎么flask-SQLAlchemy这个ORM框架会有这玩意。 二、SQLAlchemy触发器一个简单例子 考虑到效率博客表中有两个字段&#xf…

ELFK(filebeat)部署

部署环境 主机名ip地址主要软件系统node1192.168.154.70ElasticSearh、KibanaCentos7.5node2192.168.154.60ElasticSearhCentos7.5Apache192.168.154.50Logstash、ApacheCentos7.5Filebeat192.168.154.40FilebeatCentos7.5 Node1节点上安装Filebeat #上传软件包 filebeat-6…

nodejs+vue学生考勤综合平台的设计与实现-计算机毕业设计

在当今高度发达的信息中,信息管理改革已成为一种更加广泛和全面的趋势。 “学生考勤综合平台”是基于Mysql数据库,在 程序设计的基础上实现的。为确保中国经济的持续发展,信息时代日益更新,蓬勃发展。 因此,国内外技术…

【JavaSE专栏56】Java面向对象编程:深入理解类、对象、属性和方法的核心概念

Java面向对象编程:深入理解类、对象、属性和方法的核心概念 📚🧬💻 摘要引言1. Java中的类和对象 📚🧬1.1 什么是Java类和对象? 🤔1.2 类和对象在面向对象编程中的作用 &#x1f3af…