• Java基础:设计模式之原型模式


    原型模式是一种创建型设计模式,它允许我们通过复制现有的对象来创建新的对象,而不是每次都通过构造函数新建。这种模式适用于那些创建新对象成本较高或者构造过程复杂的情况。在原型模式中,一个对象通过实现Cloneable接口并重写其clone()方法来作为原型。当需要创建新对象时,客户端代码请求原型对象克隆自身,返回一个与原对象状态相同的新对象。

    结构与参与者
    1. Prototype(原型): 这是一个接口或抽象类,声明了克隆自己的方法(通常是clone())。所有具体原型类都必须实现这个接口或继承这个抽象类。

    2. ConcretePrototype(具体原型): 实现了Prototype接口或继承了抽象原型类的具体类。它们实现了clone()方法以创建自己的副本(克隆)。

    工作流程
    1. 客户端 需要新对象时,不是直接调用构造函数创建,而是请求一个已存在的原型对象克隆自身。

    2. 原型对象 通过实现Cloneable接口,并重写Object类中的clone()方法来提供克隆能力。clone()方法通常会创建一个新的对象,并将当前对象的状态(属性值)复制到新对象中。

    3. 新对象 作为原型对象的副本被返回给客户端,新对象与原型对象状态相同但不共享任何引用数据(除非在深复制中特殊处理)。

    适用场景
    1. 对象创建成本高:如果创建一个对象需要大量计算、消耗大量资源(如数据库查询、网络通信、复杂的初始化逻辑等),原型模式可以快速复制已有对象,避免重复高昂的创建成本。

    2. 对象初始化过程复杂:当对象的初始化涉及许多步骤、依赖关系或者权限控制时,使用原型模式可简化创建过程,只需克隆已配置好的原型即可。

    3. 需要大量相似对象:在系统中需要大量具有相同或相似属性的对象,但又不希望引入额外的开销(如频繁的构造函数调用),可以通过复制少量原型对象来生成大量实例。

    4. 保护性拷贝:当一个对象需要提供给多个调用者使用,且调用者可能需要修改对象的属性,通过原型模式可以为每个调用者提供对象的一个独立副本,防止相互干扰。

    示例说明
    import java.util.Date;
    
    // 原型接口
    interface Prototype {
        Prototype clone();
    }
    
    // 具体原型类:员工信息
    class Employee implements Prototype {
        private String name;
        private int id;
        private Date hireDate;
    
        // 构造函数和常规setter/getter省略...
    
        @Override
        public Prototype clone() {
            try {
                return (Prototype) super.clone(); // 调用Object的clone方法进行浅复制
            } catch (CloneNotSupportedException e) {
                throw new RuntimeException("Clone not supported", e);
            }
        }
    }
    
    // 客户端代码
    public class PrototypeDemo {
        public static void main(String[] args) {
            // 创建一个原型员工对象
            Employee original = new Employee("Alice", 1, new Date());
    
            // 使用原型模式克隆出新员工
            Employee cloned = original.clone();
    
            // 改变克隆员工的信息
            cloned.setName("Bob");
    
            System.out.println("Original employee: " + original.getName());
            System.out.println("Cloned employee: " + cloned.getName());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在这个例子中,Employee类实现了Prototype接口并提供了clone()方法。客户端代码通过调用original.clone()快速创建了一个与原对象状态相同的cloned对象。之后修改cloned对象的名称不会影响到原始对象。

    存在的问题
    1. 深拷贝与浅拷贝问题:Java中的clone()方法默认执行浅拷贝,即只复制对象本身及其基本类型字段,对于引用类型字段,只是复制了引用,导致原对象和克隆对象共享同一份引用所指向的数据。在某些场景下,这可能导致意外的数据修改。若需完全复制引用类型字段的内容(深拷贝),需要在clone()方法中手动复制这些字段。

    2. Cloneable接口不强制约束:Java的Cloneable接口没有定义任何方法,只是一个标记接口,表明该类支持被克隆。但如果不重写Objectclone()方法并抛出CloneNotSupportedException,则直接调用clone()会抛出异常。这种设计可能导致不符合预期的行为,需要开发者明确了解并正确实现克隆逻辑。

    3. 代码侵入性:为了实现原型模式,需要修改类以实现Cloneable接口并重写clone()方法,这可能对已有代码产生一定侵入性,特别是对于那些原本不需要支持克隆的类。

    4. 错误使用可能导致内存浪费:如果频繁地克隆大对象或含有大量数据的集合,且实际场景并不需要这么多对象副本,可能会造成不必要的内存消耗。

    5. 线程安全问题:在多线程环境中,如果没有正确同步原型对象的访问和克隆过程,可能会导致数据不一致或并发问题。

    综上所述,虽然原型模式在特定场景下能有效提高对象创建效率,但使用时需谨慎权衡其带来的复杂性、潜在风险以及对系统设计的影响。在实际应用中,可能还需要结合工厂方法、单例模式等其他创建型模式来优化对象创建过程。

  • 相关阅读:
    ES6数组遍历与ES5数组遍历
    计算机视觉与图形学-神经渲染专题-NeRF-SR
    架构师的职位需求
    【译】快速开始 Compose 跨平台项目
    istio 学习笔记
    windows安装部署node.js并搭建Vue项目
    万恶的 eval() ?
    Leetcode 297. 二叉树的序列化与反序列化
    K8S容忍toleration的声明语法、配置项含义及和污点的配合使用
    3.测试教程 - 基础篇
  • 原文地址:https://blog.csdn.net/zhouwenxiamg/article/details/138174743