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

news2024/11/16 3:45:05

一、原型模式

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

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

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

相关文章

CMT2310A一款低功耗高性能Sub-1GHz射频收发器芯片

CMT2310A是一款超低功耗,高性能,适用于各种113至960 MHz无线应用的00K,(G)FSK 和4(G)FSK 射频收发器。它是CMOSTEK NextGenRFTM 射频产品线的一部分,这条产品线包含完整的发射器,接收器和收发器。CMT2310A的高集成度,简…

npm package.json属性详解

npm package.json属性详解 概述 package.json必须是一个严格的json文件,而不仅仅是js里边的一个对象。其中很多属性可以通过npm-config来生成 name package.json中最重要的属性是name和version两个属性,这两个属性是必须要有的,否则模块就…

【机器学习(二) 线性代数基础I(Linear Algebra Foundations)】

机器学习(二) 线性代数基础I(Linear Algebra Foundations) 这一节主要介绍一些线性代数的基础。 目录 机器学习(二) 线性代数基础I(Linear Algebra Foundations)1. 向量 Vectors2. 复杂度 Complexity3.线…

基于3D点云的语义分割模型调研(最新更新2023.10.30)

文章目录 3D点云分割数据集点云模型的评价指标3D点云语义分割方法发展PointSIFT模型的效果 https://blog.csdn.net/toCVer/article/details/126265782 基于深度学习的三维点云分割综述 3D点云分割数据集 传统的点云分割方法包括基于边缘检测的方法、基于区域增长的算法、基于特…

【Linux】:Linux开发工具之Linux编辑器vim的使用

🔫1.Linux编辑器-vim使用 📤 vi/vim的区别简单点来说,它们都是多模式编辑器,不同的是vim是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以…

deepsort算法 卡尔曼滤波 匈牙利算法

目标追踪最核心的两个算法就是卡尔曼滤波和匈牙利算法算法。 卡尔曼滤波:根据当前帧中的轨迹预测下一帧的轨迹。匈牙利算法:将预测的目标位置与检测到的目标位置进行匹配,实现对目标的准确跟踪。Sort算法 Sort算法分为以下几个步骤: 1.卡尔曼…

AS/400-物理文件-02

物理文件 - Physical file Physical file物理文件中的条目级别相关命令 Physical file 简介物理文件 这是一个文件。包含预定义的结构化格式的数据。它是PF类型。通过使用CRTPF命令创建PF。PF中包含的字段的最大数量为8000。最多包含120个关键字段。 PF 的结构如下 TYPE SPECIF…

【C++】多态 ⑥ ( 函数重定义涉及的问题 - 子类覆盖父类函数名 )

文章目录 一、函数重定义涉及的问题1、执行出错的代码2、代码分析3、错误原因分析 - 函数重定义问题 : 子类覆盖父类函数名4、正确调用函数的方法 一、函数重定义涉及的问题 1、执行出错的代码 错误代码示例 : #include "iostream" using namespace std;// 父类 cla…

Openssl数据安全传输平台014:OCCI环境搭建和使用:Centos8-Oracle19c代码跑通 + Window代码没跑通(不影响本项目)

文章目录 0 代码仓库1 启动Centos oracle数据库2 Winsows安装配置OCCI库2.1 下载文件2.2 VS 配置2.2.1 VC包含目录2.2.2 VC库目录2.2.3 连接器-附加依赖项2.2.4 代码测试-Oracle11g2.2.4.1 准备2.2.4.2 代码测试 3 Centos安装配置occi库3.0 强调3.1 下载instantclient库文件压缩…

国产思仪 1765A/B/C/D/E程控直流电源

1765A/B/C/D/E程控直流电源 产品综述 1765系列程控直流电源主要用于储能设备系统的测量分析和自动测试系统的加电测试。作为一种双象限直流电源,将电源输出和功率吸收的功能完全集成到单一系统中,可实现电源与负载功能的无缝转换,解决双向能源…

RT-Thread 内存管理(一)

内存管理 在计算系统中,通常存储空间可以分为两种:内部存储空间和外部存储空间。 内部存储空间通常访问速度比较快,能够按照变量地址随机访问,也就是我们通常所说的RAM(随机访问存储器),可以把…

Linux gzip命令:压缩文件或目录

gzip 是 Linux 系统中经常用来对文件进行压缩和解压缩的命令,通过此命令压缩得到的新文件,其扩展名通常标记为“.gz”。 再强调一下,gzip 命令只能用来压缩文件,不能压缩目录,即便指定了目录,也只能压缩目录…

使用WebDav服务远程操作本地服务器?试试群晖NAS【内网穿透】

🎬 鸽芷咕:个人主页 🔥 个人专栏:《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活! 文章目录 1. 在群晖套件中心安装WebDav Server套件1.1 安装完成后,启动webdav服务,并勾选HTTP复选…

Linux 文件系统简介

文章目录 一、磁盘简介1.1 简介1.2 机械硬盘与固态硬盘1.2.1 机械磁盘(HDD)1.2.2 固态磁盘(SSD)1.2.3 I/O操作 二、文件系统简介2.1. 简介2.2 文件系统特点2.3 Linux文件系统 三、文件数据存储方式3.1 连续存储3.2 链接表存储3.3 …

Ansible中常用模块

目录 一、Ansible实现管理的方式 二、Ad-Hoc执行方式中如何获得帮助 三、Ansible命令运行方式及常用参数 四、Ansible的基本颜色代表信 五、Ansible中的常用模块 1、command模块 2、shell模块、script模块 3、copy模块、fetch模块 4、file模块 5、archive模块、unarc…

FPGA_Signal TapII 逻辑分析仪 在线信号波形抓取

FPGA_Signal TapII 逻辑分析仪 在线信号波形抓取 由于一些工程的仿真文件不易产生,所以我们可以利用 quartus 软件自带的 SignalTap 工具对波形进行抓取 对各个信号进行分析处理,让电子器件与FPGA进行正常通讯工作,也验证所绘制的波形图是否一…

Spring面试题:(一)IoC,DI,AOP和BeanFactory,ApplicationContext

IoC,DI,AOP思想 IOC就是控制反转,是指创建对象的控制权的转移。以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系。对象与对…

NSSCTF做题第十页(1)

[GXYCTF 2019]禁止套娃 看源代码也没什么东西&#xff0c;扫一下看看 发现了git泄露 话不多说直接开整 下载下来了 flag.php 还是代码审计 <?php include "flag.php"; echo "flag在哪里呢&#xff1f;<br>"; if(isset($_GET[exp])){ if (!preg_…

怎么看电脑有几个cpu?还有cpu是几核的?

一、怎么查看电脑有几个cpu 电脑CPU是电脑的核心&#xff0c;CPU是中央处理器&#xff0c;是电脑进行线程调度的关键&#xff0c;可以通过查看电脑CPU性能个数可以判定一个电脑的性能。今天小编介绍下如何查看电脑CPU个数。 简单的方式直接查看CPU个数。启动电脑后&#xff0c;…

喜讯!合合信息顺利通过CMMI3级评估

近日&#xff0c;在擎标顾问团的咨询辅导下&#xff0c;上海合合信息科技股份有限公司&#xff08;简称“合合信息”&#xff09;顺利通过了CMMI3级评估。CMMI是国际上最流行、最实用的一种软件生产过程标准和软件企业成熟度等级认证的标准&#xff0c;通过该认证表明企业在开发…