那些超经典的设计模式—原型模式

好记忆不如烂笔头,能记下点东西,就记下点,有时间拿出来看看,也会发觉不一样的感受.
原型模式模式是java中很常见的设计模式,如下将好好讲解下java设计抽象工厂方法模式相关的知识。本文还是将从:它的前世今生;使用场景;注意事项;代码样例等四个方面,详细阐述下原型模式的细节内容。

一、原型模式的前世今生

  1. 起源与发展

    • 原型模式是一种创建型设计模式,最早在设计模式的经典著作《设计模式:可复用面向对象软件的基础》(GoF)中被提出。在软件开发过程中,人们发现有时需要创建与现有对象相同或相似的对象,直接复制已有对象比重新创建更高效,原型模式便应运而生。

    • 随着面向对象编程的发展,原型模式在不同编程语言中得到了广泛应用和演变。在 Java 中,通过克隆机制(Cloneable 接口和 clone() 方法)为实现原型模式提供了基础支持。

  2. 基本思想

    • 原型模式允许一个对象通过自身复制来创建新的对象实例,而不是通过传统的构造函数创建。它基于已经存在的对象,快速构建一个与之具有相同或相似属性和行为的新对象,减少因创建新对象带来的资源消耗和时间成本。

  3. 与其他模式的关系

    • 与工厂模式:工厂模式侧重于通过工厂类创建对象,而原型模式则通过对象自身复制来创建新对象。原型模式在某种程度上可以看作是工厂模式的特殊实现方式。

    • 与单例模式:单例模式保证一个类只有一个实例,而原型模式可以创建多个具有相同属性的实例,但它们的属性可以独立修改。

二、原型模式的使用场景

  1. 性能优化场景

    • 当创建新对象比复制现有对象成本更高时,如需要进行复杂的初始化操作或涉及大量资源分配(如文件读取、数据库连接等),使用原型模式可以通过复制已有对象来快速获取新对象,提高性能。例如,在一个包含大量图像对象的应用中,若创建图像对象需要从磁盘读取文件并进行解码等操作,而某些图像对象具有相似属性,此时可使用原型模式,先创建一个基础图像对象,其他图像对象通过复制该基础对象并适当修改部分属性来实现。

  2. 复杂对象创建场景

    • 当一个对象的创建过程较为复杂,涉及多个步骤或依赖于其他对象的创建时,使用原型模式可以简化对象的创建过程。比如,在一个游戏开发场景中,创建一个游戏角色对象可能需要设置角色的属性、装备、技能等多个方面,并且这些属性之间可能存在相互依赖关系。此时,可以先创建一个典型角色对象作为原型,其他角色对象通过复制原型对象并在需要的地方进行修改来实现。

  3. 动态加载或运行时决定对象类型场景

    • 在某些情况下,系统在运行时才确定需要创建哪种类型的对象,且这些对象具有相似的结构和行为。原型模式可以根据已有的原型对象,动态地创建出符合要求的新对象。例如,一个文档编辑软件中,支持多种文档格式(如文本文档、表格文档、图片文档等),每种文档对应一个对象类。在打开文档时,系统根据文档的实际类型(运行时确定)找到对应的原型对象,通过克隆原型对象来创建新的文档对象进行编辑。

三、原型模式使用的注意事项

  1. 实现 Cloneable 接口

    • 在 Java 中,若想实现原型模式,类需要实现 Cloneable 接口,该接口是一个标记接口,没有方法需要实现。实现此接口的目的是让对象具备被克隆的能力,否则在调用 clone() 方法时会抛出 CloneNotSupportedException 异常。

  2. 重写 clone() 方法

    • 虽然 Object 类中提供了 clone() 方法,但默认实现是浅拷贝。如果对象包含对其他对象的引用,仅复制引用地址,而不会复制引用对象本身。因此,在实际应用中,若需要深拷贝,需要根据具体需求重写 clone() 方法,对对象中的引用类型成员变量进行深度复制。例如,对于一个包含集合对象或自定义对象成员变量的类,需要在克隆过程中重新创建这些引用对象,以确保新对象与原对象在内存上完全独立。

  3. 对象的独立性

    • 即使通过原型模式创建了新对象,新对象与原对象在内存上是相互独立的。但在浅拷贝情况下,它们可能共享一些引用对象。因此,在修改新对象或原对象的属性时,需要注意对共享对象的影响,避免出现数据不一致的问题。例如,若两个对象共享一个列表对象,修改其中一个对象中列表的内容会影响另一个对象。

  4. 线程安全问题

    • 如果在多线程环境下使用原型模式,且原型对象可能被多个线程同时访问和克隆,需要考虑线程安全问题。可以通过将克隆方法设置为同步方法(synchronized)或采用其他线程安全机制来确保在并发情况下对象克隆的正确性。

四、原型模式的代码样例

// 原型接口,声明克隆方法
public interface Prototype {
    Prototype clone();
}

// 具体原型类,实现原型接口
public class ConcretePrototype implements Prototype, Cloneable {
    private String name;
    // 引用类型成员变量,用于演示深拷贝
    private List<String> list;

    public ConcretePrototype(String name) {
        this.name = name;
        this.list = new ArrayList<>();
    }

    @Override
    public ConcretePrototype clone() {
        try {
            // 调用 Object 的 clone() 方法进行浅拷贝
            ConcretePrototype cloneObj = (ConcretePrototype) super.clone();
            // 对引用类型成员变量进行深拷贝,重新创建一个新的列表对象并复制内容
            cloneObj.list = new ArrayList<>(this.list);
            return cloneObj;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void addListItem(String item) {
        list.add(item);
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        ConcretePrototype prototype1 = new ConcretePrototype("Prototype1");
        prototype1.addListItem("Item1");
        prototype1.addListItem("Item2");

        // 克隆原型对象
        ConcretePrototype prototype2 = prototype1.clone();
        System.out.println("prototype1 name: " + prototype1.getName());
        System.out.println("prototype1 list: " + prototype1.getList());
        System.out.println("prototype2 name: " + prototype2.getName());
        System.out.println("prototype2 list: " + prototype2.getList());

        // 修改克隆后的对象
        prototype2.setName("Prototype2");
        prototype2.addListItem("Item3");
        System.out.println("\nAfter modification:");
        System.out.println("prototype1 name: " + prototype1.getName());
        System.out.println("prototype1 list: " + prototype1.getList());
        System.out.println("prototype2 name: " + prototype2.getName());
        System.out.println("prototype2 list: " + prototype2.getList());
    }
}

运行结果示例:

prototype1 name: Prototype1

prototype1 list: [Item1, Item2]

prototype2 name: Prototype1

prototype2 list: [Item1, Item2]

After modification:

prototype1 name: Prototype1

prototype1 list: [Item1, Item2]

prototype2 name: Prototype2

prototype2 list: [Item1, Item2, Item3]

通过这个示例可以看出,原型模式成功创建了对象的副本,并且在克隆过程中对引用类型成员变量进行了深拷贝处理,使得修改克隆后的对象不会影响原对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值