luodiab 的个人博客 luodiab 的个人博客

每个圣人都有无可告人的过去 每个罪人都有洁白无瑕的未来

目录
设计模式——原型模式
/  

设计模式——原型模式

摘要

1986年由央视推出的央视版《西游记》被我们认为是最经典的一个版本,我认为这是86版西游记承载了太多70、80、90甚至是00后的回忆,在那个精神文化生活还不是很富裕的年代,电视剧《西游记》的问世,在很多人心目中都有着很深的回忆。这也导致了后来翻拍的版本没一个比得上86版,但无论是哪个版本的《西游记》,孙悟空都是其中最出名的英雄主角之一,关于他(或它)拔毛变小猴的故事几乎人人皆知,孙悟空可以用猴毛根据自己的形象,复制(又称“克隆”或“拷贝”)出很多跟自己长得一模一样的“身外身”来。

而这拔毛变小猴的故事,也跟我们今天要了解的一个设计模式有点类似,该模式可以通过一个原型对象克隆出多个一模一样的对象,而这个模式就是——原型模式。

一、前言

原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制现有对象来创建新的对象,而无需显式地指定它们的类。

二、原型模式详解

1、定义

原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。

原型模式的工作原理很简单:将一个原型对象传给那个要发动创建的对象,该对象通过调用原型对象拷贝自己的方法来实现创建过程。

由于在软件系统中我们经常会遇到需要创建多个相同或者相似对象的情况,因此原型模式在真实开发中的使用频率还是非常高的。原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。

需要注意的是通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立的。通过不同的方式修改可以得到一系列相似但不完全相同的对象。

2、UML图

01原型模式.jpg

在原型模式结构图中包含如下几个角色:

Prototype(抽象原型类)

它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。

ConcretePrototype(具体原型类)

它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。

Client(客户类)

让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。

3、实现方式

原型模式有两种实现方式:浅克隆和深克隆

3.1、浅克隆

浅克隆只复制对象本身和其中的基本数据类型,而不会复制对象中的引用类型。也就是说,新对象中的引用类型变量和原对象中的引用类型变量指向同一个对象。

cloneable 接口

学过Java语言的人都知道,所有的Java类都继承自java.lang.Object。事实上,Object类提供一个clone()方法,可以将一个Java对象复制一份。因此在Java中可以直接使用Object提供的clone()方法来实现对象的克隆,Java语言中的原型模式实现很简单。

需要注意的是能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持被复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常。

实体类

public class ConcretePrototype implements Cloneable{

    private String name;
    private int age;
    private Address address;

    public Address getAddress() {
        return address;
    }

    @Override
    public ConcretePrototype clone() throws CloneNotSupportedException {
        Object object = null;
        try {
            object = super.clone();
        }catch (CloneNotSupportedException exception){
            System.err.println("Not support cloneable");
        }
        return (ConcretePrototype)object;
    }
}
public class Address {

    private String city;
    private String street;
}

客户类

public class Test {
    public static void main(String[] args) throws Exception{
        ConcretePrototype obj1  = new ConcretePrototype();
        ConcretePrototype obj2  = obj1.clone();

        System.out.println(obj1.getAddress() == obj2.getAddress());
    }
}

结果

03原型模式.jpg

3.2、深克隆

深克隆会复制对象本身和其中的引用类型变量,也就是说,新对象中的引用类型变量和原对象中的引用类型变量指向不同的对象。

序列化

序列化是一种将Java对象转换为字节流的技术,然后可以将字节流写入文件或通过网络传输。由于序列化将对象转换为字节流,因此可以使用序列化实现深克隆。首先将对象序列化到字节流中,然后再从字节流中反序列化出一个新的对象。

实体类

public class ConcretePrototype implements Cloneable, Serializable {

    private String name;
    private int age;
    private Address address;

    public ConcretePrototype() {
    }

    public ConcretePrototype(Address address) {
        this.address = address;
    }

    public Address getAddress() {
        return address;
    }

    @Override
    public ConcretePrototype clone() throws CloneNotSupportedException {
        ConcretePrototype obj = new ConcretePrototype(new Address());
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close();

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            ConcretePrototype clone = (ConcretePrototype) ois.readObject();
            ois.close();

            return clone;
        } catch (Exception e) {
            throw new RuntimeException("Error cloning object", e);
        }
    }
}
public class Address {

    private String city;
    private String street;
}

客户类

public class Test {
    public static void main(String[] args) throws Exception{
        ConcretePrototype obj1  = new ConcretePrototype(new Address());
        ConcretePrototype obj2  = obj1.clone();

        System.out.println(obj1.getAddress() == obj2.getAddress());
    }
}

结果

04原型模式.jpg

06原型模式.jpg

cloneable 接口

Java中的Cloneable接口用于标记可克隆的对象。当对象实现Cloneable接口时,可以调用其clone()方法进行浅拷贝,但是这不会复制对象的内部状态。要实现深拷贝,必须重写clone()方法并手动复制内部状态。

实体类

public class ConcretePrototype implements Cloneable, Serializable {

    private String name;
    private int age;
    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public ConcretePrototype clone(){
        ConcretePrototype clone = null;
        try {
            clone = (ConcretePrototype)super.clone();
            clone.setAddress(new Address());
        }catch (CloneNotSupportedException exception){
            System.err.println("Not support cloneable");
        }
        return clone;
    }
}
public class Address {

    private String city;
    private String street;
}

客户类

public class Test {
    public static void main(String[] args) throws Exception{
        ConcretePrototype obj1  = new ConcretePrototype();
        ConcretePrototype obj2  = obj1.clone();

        System.out.println(obj1.getAddress() == obj2.getAddress());
    }
}

结果

05原型模式.jpg

4、优缺点

优点

  1. 减少对象创建的成本:使用原型模式可以通过复制现有对象来创建新对象,而无需创建新对象的成本。因为它避免了重新初始化和设置对象的属性。
  2. 简化对象的创建过程:原型模式可以避免复杂对象的创建过程,尤其是当创建对象需要进行一系列复杂的步骤时。这意味着你可以在实际需要时轻松地创建和配置对象,而不必在程序的许多地方执行大量的初始化和配置代码。
  3. 提高程序性能:在大多数情况下,复制对象比创建新对象更快。原型模式可以在运行时使用它已经存在的对象,这有助于提高程序的性能。

缺点

  1. 复制对象可能会带来副作用:复制一个对象可能会导致副作用,例如如果原型对象的属性是可变的,那么它们的值可能会在对象被克隆时改变。
  2. 复制对象可能会导致深度克隆问题:如果一个对象具有多层嵌套结构,复制它可能会变得很困难,因为你需要确保复制所有嵌套的对象及其属性。这可能会导致深度克隆问题。
  3. 必须保证克隆的对象是可用的:在某些情况下,原型对象可能无法克隆,这可能是因为它没有实现 Cloneable 接口,或者它的构造函数是私有的。在这种情况下,原型模式无法使用。

三、总结

以上就是我个人关于 设计模式——原型模式 的一些笔记,如果有什么问题,可以将问题发我邮箱 luodiab@126.com ,欢迎各位的意见。

四、参考文章

对象的克隆——原型模式(一)

秒懂 Java 的原型模式

设计模式之原型模式

Java设计模式之创建型:原型模式


标题:设计模式——原型模式
作者:luodiab
地址:https://www.luodiab.top/articles/2023/08/10/1691661359815.html