原型模式和建造者模式学习心得

原型模式

原型模式作为创建者模式,它提供了一种简化对象创建的方式,尤其是对于那些复杂对象的创建。

背景

有时候需要多次创建某一类型的对象,为了简化创建过程,可以只创建一个对象,然后再通过克隆的方式复制出多个相同的对象,这就是原型模式的设计思想。

UML类图

Prototype:抽象原型类。可以利用Java自带Cloneable接口和Object类提供的clone()方法。无需自行创建

Realizetype:具体原型类

PrototypeTest:客户端

代码实例

在班级类(Clazz)里面有学生(Student)属性

//具体原型类Realizetype
public class Clazz implements Cloneable{

    private Student student;
    
    //重写clone()方法
    @Override
    public Clazz clone() throws CloneNotSupportedException {
        return (Clazz) super.clone();
    }

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }
}
public class Student{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
//客户端
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        //先创建一个具体原型类
        Clazz clazz1 = new Clazz();
        //通过clone方法从clazz1克隆
        Clazz clazz2 = clazz1.clone();
        System.out.println(clazz1==clazz2); //false

    }
}

讨论浅克隆和深克隆

浅克隆:只克隆了对象的表象,并没有将对象内部的成员变量复制,所以克隆出来的对象内部的成员变量和原型内部的成员变量共享。

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        //先创建一个具体原型类
        Clazz clazz1 = new Clazz();
        Student student1 = new Student();
        student1.setName("Jack");
        clazz1.setStudent(student1);
        //通过clone方法从clazz1克隆
        Clazz clazz2 = clazz1.clone();
        //设置克隆对象的studnet中name属性
        clazz2.getStudent().setName("Mike");
        System.out.println(clazz1.getStudent()==clazz2.getStudent()); //true
        //可以看出,两个name的属性是一样的,这是因为两个对象共享了一个student变量
        System.out.println(clazz1.getStudent().getName()); //Mike
        System.out.println(clazz2.getStudent().getName()); //Mike
    }
}

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

通过序列化和反序列化方式实现

    public Object deepClone() throws Exception{
        //将对象写到流里
        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();
    }
public class Test {
    public static void main(String[] args) throws Exception {
        //先创建一个具体原型类
        Clazz clazz1 = new Clazz();
        Student student1 = new Student();
        student1.setName("Jack");
        clazz1.setStudent(student1);
        //通过clone方法从clazz1克隆
        Clazz clazz2 = (Clazz) clazz1.deepClone();
        clazz2.getStudent().setName("Mike");
        System.out.println(clazz1.getStudent()==clazz2.getStudent()); //false
        System.out.println(clazz1.getStudent().getName()); //Jack
        System.out.println(clazz2.getStudent().getName()); //Mike
    }
}

优缺点

优点

简化对象创建过程:对于复杂的对象,使用原型模式可以简化对象的创建过程,避免重复初始化或执行耗时的操作。

提高性能:通过复制现有对象创建新对象,特别是在对象初始化成本较高的情况下,可以显著提高性能。

支持动态增加或修改产品类:如果产品类需要动态添加或修改,使用原型模式可以避免对客户端代码产生影响。

缺点

对象必须支持克隆:不是所有的对象都能很容易地实现克隆,尤其是包含复杂状态或引用其他对象时,克隆实现可能变得复杂。

深拷贝与浅拷贝问题:在复制包含其他对象引用的状态时,需要考虑是否需要深度复制这些引用对象,否则可能导致数据不一致或意外共享状态。

建造者模式

建造者模式将一个复杂对象的构建和表现分离,使得同样的构建可以创造出不同的表现。

UML类图

  • Builder:抽象建造者

  • ConcreteBuilder:具体建造者

  • Director:指挥者

  • Product:产品角色

代码实例

假如有一个复杂的手机对象,里面有多个复杂属性;

为了演示,目前只设置3个属性

不使用建造者模式来构建

public class Phone {
    private String name;
    private String modal;
    private double price;

    public Phone(String name, String modal, double price) {
        this.name = name;
        this.modal = modal;
        this.price = price;
    }
}
public class Test {
    public static void main(String[] args) {
        //目前只有3个属性,如果属性数量更多的话下面的语句可读性、维护性就很差
        Phone xiaomi = new Phone("XIAO MI", "Readmi", 2000);
    }
}

使用建造者模式来构建

public class Phone {
    private String name;
    private String modal;
    private double price;
    
    public Phone(Builder builder){
        this.name = builder.name;
        this.modal = builder.modal;;
        this.price = builder.price;
    }
    
    public static final class Builder{
        private String name;
        private String modal;
        private double price;
        
        public Builder name(String name){
            this.name = name;
            return this;
        }

        public Builder modal(String modal){
            this.modal = modal;
            return this;
        }

        public Builder price(double price){
            this.price = price;
            return this;
        }
        
        public Phone build(){
            return new Phone(this);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        //通过建造者模式,让人清楚的知道构建那个属性
        Phone phone = new Phone.Builder()
                .name("XIAO MI")
                .modal("Readmi")
                .price(2000)
                .build();
    }
}

这正是Lombok工具库@Builder注解

@Builder
public class Phone {
    private String name;
    private String modal;
    private double price;
}

优点

  • 在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。

  • 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。

  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。

缺点

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

心得

  • 在创建复杂类型对象时,可以使用原型模式来简化对象创建过程

  • 了解了深克隆和浅克隆的概念

  • 虽然原型模式简化了复杂对象的创建,但是有些对象的克隆过程非常复杂

  • 通过指挥官类,将产品的构建和表示分离

  • 了解了建造者模式适用于创建复杂对象,特别是当对象的创建过程涉及多个步骤,或者对象有多种不同的表示时。

参考

https://blog.csdn.net/weixin_45692705/article/details/121647915
https://blog.csdn.net/qq_45929019/article/details/142708877
https://www.bilibili.com/video/BV1Np4y1z7BU


原型模式和建造者模式学习心得
http://localhost:8090//archives/yuan-xing-mo-shi-he-jian-zao-zhe-mo-shi-xue-xi-xin-de
作者
LinWJ
发布于
2024年10月21日
许可协议