23种设计模式 --原型模式

x33g5p2x  于2022-03-18 转载在 其他  
字(3.7k)|赞(0)|评价(0)|浏览(231)

原型模式

原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。原型模式包含以下角色:

  • 抽象原型类:规定了具体原型对象必须实现的clone()方法
  • 具体原型类:实现了抽象原型类的clone()方法,它是可以被复制的对象
  • 访问类:使用具体原型类中的clone()方法来复制新对象

原型模式的克隆分为浅克隆和深克隆

  • 浅克隆:创建一个新对象,对象种属性和原来对象的属性完全相同,对于非基本类型属性仍指向原有属性所指向的内存地址
  • 深克隆:创建一个新对象,属性中引用类型也会被克隆,不再指向原来属性所指向的内存地址

Java中的Object类中提供了clone()方法来实现浅克隆。Cloneable接口是Java提供的抽象原型类而实现了Cloneable接口的子实现类就是具体原型类

浅拷贝

【案例】

同一个学校的三好学生奖状除了获奖姓名不同,其他都相同,可以使用原型模式复制出多个三好学生奖状,然后修改奖状上的学生姓名即可

具体原型类

public class Citation implements Cloneable {
    private String name; //三好学生的姓名

    public String getName(){return name;}
    public void setName(String name) {this.name = name;}
    public void show(){System.out.println(name+"同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状");}
    public Citation clone() throws CloneNotSupportedException {
        return (Citation)super.clone();
    }
}

访问类

public class Test{
    public static void main(String[] args) throws CloneNotSupportedException {
        Citation citation = new Citation();//创建原型对象
        Citation clone = citation.clone();//复制原型对象
        citation.setName("张三");
        clone.setName("李四");
        citation.show();//张三同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
        clone.show();//李四同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
    }
}

浅克隆存在的问题

对于浅拷贝来说,具体原型对象的属性只能是基本数据类型和String类型,如果换成其他数据类型会发生什么呢

public class Citation implements Cloneable{
    private Student student;

    public void setStudent(Student student) { this.student = student;}
    public Student getStudent() {return student;}
    public void show(){System.out.println(student.getName()+"同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状");}
    protected Citation clone() throws CloneNotSupportedException {
        return (Citation)super.clone();
    }
    
}

class CitationTest{
    public static void main(String[] args) throws Exception {
        Citation citation = new Citation();//创建原型对象
        Student student = new Student();
        student.setName("张三");
        citation.setStudent(student);
        Citation clone = citation.clone();//拷贝原型对象
        clone.getStudent().setName("李四");
        citation.show();//李四同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
        clone.show();//李四同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
    }
}

class Student{
    private String name;
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
}

为什么两个输出都是李四同学?
因为Object类中提供了clone()方法实现的是浅克隆,对于非基本类型属性仍指向原有属性所指向的内存地址,原型类中的student对象和克隆类中的对象是同一个对象,当修改克隆类的student对象时也会修改原型类中的student对象

深拷贝就能解决以上问题

深拷贝

深拷贝的两种实现方式

  • 重写clone()方法
  • 序列化对象

重写clone()方法

以上面浅拷贝存在问题代码为例,我们只需要重写clone()方法和给Student类实现Cloneable接口并重写clone()方法即可完成深拷贝

//重写clone()方法
public Citation clone() throws CloneNotSupportedException {
	Citation deepClone = (Citation )super.clone();
	deepClone.student = (Student) student.clone();
	return deepClone;
}
//重写clone()方法后Student要实现Cloneable接口
class Student implements Cloneable{
	//其他代码...(和上面一样)
	@Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

序列化对象

以上面浅拷贝存在问题代码为例,在主方法中序列化具体原型对象然后反序列化即可

public static void main(String[] args) throws Exception {
        Citation citation = new Citation();
        Student student = new Student();
        student.setName("张三");
        citation.setStudent(student);
        /*序列化对象*/
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\uuu\\IdeaProjects\\DesignProject\\ch01-Singleton\\file\\b.txt"));//开启对象输出流
        oos.writeObject(citation);//序列化对象
        oos.close();//关闭对象输出流
        /*反序列化对象*/
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\uuu\\IdeaProjects\\DesignProject\\ch01-Singleton\\file\\b.txt"));//开启对象输入流
        Citation clone = (Citation ) ois.readObject();//反序列化对象
        ois.close();//关闭对象输入流
        clone.getStudent().setName("李四");
        citation.show();//张三同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
        clone.show();//李四同学,在2022学年第一学期中表现优异,被评为三好学生,特颁此状
}

//因为要序列化对象所以Student类和Citation类
class Citation implements Cloneable,Serializable{//代码...}
class Student implements Serializable{//代码...}

原型模式的优缺点

  • 优点:
    1、性能提高
    2、逃避构造函数的约束
  • 缺点:
    1、必须实现 Cloneable 接口
    2、需要为每一个类都配置一个 clone 方法

相关文章