原型模式和建造者模式学习心得
原型模式
原型模式作为创建者模式,它提供了一种简化对象创建的方式,尤其是对于那些复杂对象的创建。
背景
有时候需要多次创建某一类型的对象,为了简化创建过程,可以只创建一个对象,然后再通过克隆的方式复制出多个相同的对象,这就是原型模式的设计思想。
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