《23种设计模式(Java版)》| 原型模式(内附源码案例)。

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

1.png

一、概述


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


通俗理解:


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

原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。

工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()。


二、原理结构图

1.png


Prototype : 原型类,声明一个克隆自己的接口 。

ConcretePrototype: 具体的原型类, 实现一个克隆自己的操作 。

Client: 让一个原型对象克隆自己,从而创建一个新的对象(属性一样。


案例需求:


现在有一只羊tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和tom羊 属性完全相同的5只羊.


案例代码:


public class Sheep implements Cloneable {
  private String name;
  private int age;
  private String color;
  private String address = "蒙古羊";
  //克隆羊对象
  public Sheep friend; 
  public Sheep(String name, int age, String color) {
    super();
    this.name = name;
    this.age = age;
    this.color = color;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  public String getColor() {
    return color;
  }
  public void setColor(String color) {
    this.color = color;
  }
  @Override
  public String toString() {
    return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";
  }
  //克隆该实例,使用默认的clone方法来完成
  @Override
  protected Object clone()  {
    Sheep sheep = null;
    try {
      sheep = (Sheep)super.clone();
    } catch (Exception e) {
      // TODO: handle exception
      System.out.println(e.getMessage());
    }
    // TODO Auto-generated method stub
    return sheep;
  }
}
public class Client {
  public static void main(String[] args) {
    System.out.println("原型模式完成对象的创建");
    // TODO Auto-generated method stub
    Sheep sheep = new Sheep("tom", 1, "白色");
    sheep.friend = new Sheep("jack", 2, "黑色");
    Sheep sheep2 = (Sheep)sheep.clone(); //克隆
    Sheep sheep3 = (Sheep)sheep.clone(); //克隆
    Sheep sheep4 = (Sheep)sheep.clone(); //克隆
    Sheep sheep5 = (Sheep)sheep.clone(); //克隆
    System.out.println("sheep2 =" + sheep2 + "sheep2.friend=" + sheep2.friend.hashCode());
    System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.friend.hashCode());
    System.out.println("sheep4 =" + sheep4 + "sheep4.friend=" + sheep4.friend.hashCode());
    System.out.println("sheep5 =" + sheep5 + "sheep5.friend=" + sheep5.friend.hashCode());
  }
}

结果展示:


原型模式完成对象的创建
sheep2 =Sheep [name=tom, age=1, color=白色, address=蒙古羊]sheep2.friend=284720968
sheep3 =Sheep [name=tom, age=1, color=白色, address=蒙古羊]sheep3.friend=284720968
sheep4 =Sheep [name=tom, age=1, color=白色, address=蒙古羊]sheep4.friend=284720968
sheep5 =Sheep [name=tom, age=1, color=白色, address=蒙古羊]sheep5.friend=284720968


三、面试重点:请你聊聊深拷贝与浅拷贝?


浅拷贝

对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。


对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

前面克隆羊案例就是浅拷贝。

浅拷贝是使用默认的 clone()方法来实现sheep = (Sheep) super.clone();。

深拷贝

复制对象的所有基本数据类型的成员变量值。


为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝。

实现深拷贝的两种方式

方式一:重写clone方法


案例代码:


public class DeepProtoType implements Serializable, Cloneable{
  public String name; //String 属性
  public DeepCloneableTarget deepCloneableTarget;// 引用类型
  public DeepProtoType() {
    super();
  }
  //深拷贝 - 方式 1 使用clone 方法
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Object deep = null;
    //这里完成对基本数据类型(属性)和String的克隆
    deep = super.clone(); 
    //对引用类型的属性,进行单独处理
    DeepProtoType deepProtoType = (DeepProtoType)deep;
    deepProtoType.deepCloneableTarget  = (DeepCloneableTarget)deepCloneableTarget.clone();
    // TODO Auto-generated method stub
    return deepProtoType;
  }
}

方式一:通过对象序列化实现深拷贝(推荐)


案例代码:


public class DeepProtoType implements Serializable, Cloneable{
  public String name; //String 属性
  public DeepCloneableTarget deepCloneableTarget;// 引用类型
  public DeepProtoType() {
    super();
  }
  //深拷贝 - 方式2 通过对象的序列化实现 (推荐) 
  public Object deepClone() { 
    //创建流对象
    ByteArrayOutputStream bos = null;
    ObjectOutputStream oos = null;
    ByteArrayInputStream bis = null;
    ObjectInputStream ois = null;
    try {
      //序列化
      bos = new ByteArrayOutputStream();
      oos = new ObjectOutputStream(bos);
      oos.writeObject(this); //当前这个对象以对象流的方式输出
      //反序列化
      bis = new ByteArrayInputStream(bos.toByteArray());
      ois = new ObjectInputStream(bis);
      DeepProtoType copyObj = (DeepProtoType)ois.readObject();
      return copyObj;
    } catch (Exception e) {
      // TODO: handle exception
      e.printStackTrace();
      return null;
    } finally {
      //关闭流
      try {
        bos.close();
        oos.close();
        bis.close();
        ois.close();
      } catch (Exception e2) {
        // TODO: handle exception
        System.out.println(e2.getMessage());
      }
    }
  }
}


四、在JDK框架中源码分析


JDK 中的ArrayList类中,就使用了原型模式


public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
   /**
     * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
     * elements themselves are not copied.)
     *
     * @return a clone of this <tt>ArrayList</tt> instance
     */
    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
}


五、总结Tips


创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。

不用重新初始化对象,而是动态地获得对象运行时的状态。

如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码.。

在实现深克隆的时候可能需要比较复杂的代码。

缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则。


相关文章
|
1天前
|
存储 Java
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
Java基础复习(DayThree):字符串基础与StringBuffer、StringBuilder源码研究
|
1天前
|
数据采集 监控 安全
java数字工厂MES系统全套源码Java+idea+springboot专业为企业提供智能制造MES解决方案
"MES" 指的是制造执行系统(Manufacturing Execution System)。MES在制造业中扮演着至关重要的角色,它是位于企业资源计划(ERP)系统和车间控制系统之间的系统,用于实时收集、管理、分析和报告与制造过程相关的数据。
9 0
|
2天前
|
移动开发 监控 供应链
JAVA智慧工厂制造生产管理MES系统,全套源码,多端展示(app、小程序、H5、台后管理端)
一开始接触MES系统,很多人会和博主一样,对MES细节的应用不了解,这样很正常,因为MES系统相对于其他系统来讲应用比较多!
14 1
JAVA智慧工厂制造生产管理MES系统,全套源码,多端展示(app、小程序、H5、台后管理端)
|
2天前
|
设计模式 算法 Java
Java一分钟之-设计模式:策略模式与模板方法
【5月更文挑战第17天】本文介绍了策略模式和模板方法模式,两种行为设计模式用于处理算法变化和代码复用。策略模式封装不同算法,允许客户独立于具体策略进行选择,但需注意选择复杂度和过度设计。模板方法模式定义算法骨架,延迟部分步骤给子类实现,但过度抽象或滥用继承可能导致问题。代码示例展示了两种模式的应用。根据场景选择合适模式,以保持代码清晰和可维护。
7 1
|
2天前
|
设计模式 Java
Java一分钟之-设计模式:装饰器模式与代理模式
【5月更文挑战第17天】本文探讨了装饰器模式和代理模式,两者都是在不改变原有对象基础上添加新功能。装饰器模式用于动态扩展对象功能,但过度使用可能导致类数量过多;代理模式用于控制对象访问,可能引入额外性能开销。文中通过 Java 代码示例展示了两种模式的实现。理解并恰当运用这些模式能提升代码的可扩展性和可维护性。
7 1
|
2天前
|
设计模式 Java
Java一分钟之-设计模式:观察者模式与事件驱动
【5月更文挑战第17天】本文探讨了Java中实现组件间通信的观察者模式和事件驱动编程。观察者模式提供订阅机制,当对象状态改变时通知所有依赖对象。然而,它可能引发性能问题、循环依赖和内存泄漏。代码示例展示了如何实现和避免这些问题。事件驱动编程则响应用户输入和系统事件,但回调地狱和同步/异步混淆可能造成困扰。JavaFX事件驱动示例解释了如何处理事件。理解这两种模式有助于编写健壮的程序。
6 1
|
2天前
|
设计模式 Java
Java一分钟之-设计模式:工厂模式与抽象工厂模式
【5月更文挑战第17天】本文探讨了软件工程中的两种创建型设计模式——工厂模式和抽象工厂模式。工厂模式提供了一个创建对象的接口,延迟实例化到子类决定。过度使用或违反单一职责原则可能导致问题。代码示例展示了如何创建形状的工厂。抽象工厂模式则用于创建一系列相关对象,而不指定具体类,但添加新产品可能需修改现有工厂。代码示例展示了创建颜色和形状的工厂。根据需求选择模式,注意灵活性和耦合度。理解并恰当运用这些模式能提升代码质量。
11 2
|
2天前
|
NoSQL 算法 Java
【redis源码学习】持久化机制,java程序员面试算法宝典pdf
【redis源码学习】持久化机制,java程序员面试算法宝典pdf
|
2天前
|
存储 运维 Java
java云his系统源码一站式诊所SaaS系统Java版云HIS系统 八大特点
HIS系统采用面向技术架构的分析与设计方法,应用多层次应用体系架构设计,运用基于构件技术的系统搭建模式与基于组件模式的系统内核结构。通过建立统一接口标准,实现数据交换和集成共享,通过统一身份认证和授权控制,实现业务集成、界面集成。
28 1
|
2天前
|
设计模式 SQL 安全
Java一分钟之-设计模式:单例模式的实现
【5月更文挑战第16天】本文介绍了单例模式的四种实现方式:饿汉式(静态初始化)、懒汉式(双检锁)、静态内部类和枚举单例,以及相关问题和解决方法。关注线程安全、反射攻击、序列化、生命周期和测试性,选择合适的实现方式以确保代码质量。了解单例模式的优缺点,谨慎使用,提升设计效率。
18 3