java设计模式之原型模式

【Java用法】Java设计模式之(三)原型模式(Prototype Pattern) 一、原型模式简介 1、原型模式简介 原型模式使用原型实例指定创建对象的种类,并且通过拷贝原型对象创建新的对象。 Prototype模式提供了一个通过已存在对象进行新对象创建的接口(clone), clone()实现和具体的语言相关,在C++中通过拷贝构造函数实现。 原型模式实际上就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。在初始化的信息不发生变化的情况下,克隆是最好的办法,既隐藏了对象创建的细节,又大大提高了性能。因为如果不用clone,每次new都需要执行一次构造函数, 阅读详情

java设计模式之原型模式

 学习难度:★★★☆☆,使用频率:★★★☆☆】

张纪中版《西游记》以出乎意料的造型和雷人的台词遭到广大观众朋友的热议,我们在此对该话题不作过多讨论。但无论是哪个版本的《西游记》,孙悟空都是其中的一号雄性主角,关于他(或它)拔毛变小猴的故事几乎人人皆知,孙悟空可以用猴毛根据自己的形象,复制(又称“克隆”或“拷贝”)出很多跟自己长得一模一样的“身外身”来。在设计模式中也存在一个类似的模式,可以通过一个原型对象克隆出多个一模一样的对象,该模式称之为原型模式。

7.1 大同小异的工作周报 

Sunny软件公司一直使用自行开发的一套OA (Office Automatic,办公自动化)系统进行日常工作办理,但在使用过程中,越来越多的人对工作周报的创建和编写模块产生了抱怨。追其原因,Sunny软件公司的OA管理员发现,由于某些岗位每周工作存在重复性,工作周报内容都大同小异,如图7-1工作周报示意图。这些周报只有一些小地方存在差异,但是现行系统每周默认创建的周报都是空白报表,用户只能通过重新输入或不断复制粘贴来填写重复的周报内容,极大降低了工作效率,浪费宝贵的时间。如何快速创建相同或者相似的工作周报,成为Sunny公司OA开发人员面临的一个新问题。

图7-1 工作周报示意图

       Sunny公司的开发人员通过对问题进行仔细分析,决定按照如下思路对工作周报模块进行重新设计和实现:

       (1)除了允许用户创建新周报外,还允许用户将创建好的周报保存为模板;

       (2)用户在再次创建周报时,可以创建全新的周报,还可以选择合适的模板复制生成一份相同的周报,然后对新生成的周报根据实际情况进行修改,产生新的周报。

       只要按照如上两个步骤进行处理,工作周报的创建效率将得以大大提高。这个过程让我们想到平时经常进行的两个电脑基本操作:复制和粘贴,快捷键通常为Ctrl + C和Ctrl + V,通过对已有对象的复制和粘贴,我们可以创建大量的相同对象。如何在一个面向对象系统中实现对象的复制和粘贴呢?不用着急,本章我们介绍的原型模式正为解决此类问题而诞生。

7.2 原型模式概述

      在使用原型模式时,我们需要首先创建一个原型对象,再通过复制这个原型对象来创建更多同类型的对象。试想,如果连孙悟空的模样都不知道,怎么拔毛变小猴子呢?原型模式的定义如下:

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

      原型模式的工作原理很简单:将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。由于在软件系统中我们经常会遇到需要创建多个相同或者相似对象的情况,因此原型模式在真实开发中的使用频率还是非常高的。原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。

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

       原型模式的结构如图7-2所示:

图7-2 原型模式结构图

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

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

      ● ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。

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

      原型模式的核心在于如何实现克隆方法,下面将介绍两种在Java语言中常用的克隆实现方法:

1.通用实现方法

      通用的克隆实现方法是在具体原型类的克隆方法中实例化一个与自身类型相同的对象并将其返回,并将相关的参数传入新创建的对象中,保证它们的成员属性相同。示意代码如下所示:

class ConcretePrototype implements Prototype

{

private String  attr; //成员属性

public void  setAttr(String attr)

{

    this.attr = attr;

}

public String  getAttr()

{

    return this.attr;

}

public Prototype  clone() //克隆方法

{

    Prototype  prototype = new ConcretePrototype(); //创建新对象

    prototype.setAttr(this.attr);

    return prototype;

}

}

思考

能否将上述代码中的clone()方法写成:public Prototype clone() { return this; }?给出你的理由。

      在客户类中我们只需要创建一个ConcretePrototype对象作为原型对象,然后调用其clone()方法即可得到对应的克隆对象,如下代码所示:

Prototype obj1  = new ConcretePrototype();

obj1.setAttr("Sunny");

Prototype obj2  = obj1.clone();

      这种方法可作为原型模式的通用实现,它与编程语言特性无关,任何面向对象语言都可以使用这种形式来实现对原型的克隆。

2. Java语言提供的clone()方法

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

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

class ConcretePrototype implements  Cloneable

{

……

public Prototype  clone()

{

  Object object = null;

  try {

     object = super.clone();

  } catch (CloneNotSupportedException exception) {

     System.err.println("Not support cloneable");

  }

  return (Prototype )object;

}

……

}

      在客户端创建原型对象和克隆对象也很简单,如下代码所示:

Prototype obj1  = new ConcretePrototype();

Prototype obj2  = obj1.clone();

      一般而言,Java语言中的clone()方法满足:

(1) 对任何对象x,都有x.clone() != x,即克隆对象与原型对象不是同一个对象;

(2) 对任何对象x,都有x.clone().getClass() == x.getClass(),即克隆对象与原型对象的类型一样;

(3) 如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。

      为了获取对象的一份拷贝,我们可以直接利用Object类的clone()方法,具体步骤如下:

(1) 在派生类中覆盖基类的clone()方法,并声明为public;

(2) 在派生类的clone()方法中,调用super.clone();

(3)派生类需实现Cloneable接口。

      此时,Object类相当于抽象原型类,所有实现了Cloneable接口的类相当于具体原型类。

7.3 完整解决方案

      Sunny公司开发人员决定使用原型模式来实现工作周报的快速创建,快速创建工作周报结构图如图7-3所示:

图7-3 快速创建工作周报结构图

      在图7-3中,WeeklyLog充当具体原型类,Object类充当抽象原型类,clone()方法为原型方法。WeeklyLog类的代码如下所示:

//工作周报WeeklyLog:具体原型类,考虑到代码的可读性和易理解性,只列出部分与模式相关的核心代码

class WeeklyLog implements Cloneable

{

       private  String name;

       private  String date;

       private  String content;

       public  void setName(String name) {

              this.name  = name;

       }

       public  void setDate(String date) {

              this.date  = date;

       }

       public  void setContent(String content) {

              this.content  = content;

       }

       public  String getName() {

              return  (this.name);

       }

       public  String getDate() {

              return  (this.date);

       }

       public  String getContent() {

              return  (this.content);

       }

     //克隆方法clone(),此处使用Java语言提供的克隆机制

       public WeeklyLog clone()

       {

              Object obj = null;

              try

              {

                     obj = super.clone();

                     return (WeeklyLog)obj;     

              }

              catch(CloneNotSupportedException e)

              {

                     System.out.println("不支持复制!");

                     return null;

              }

       }

}

编写如下客户端测试代码:

class Client

{

       public  static void main(String args[])

       {

              WeeklyLog log_previous = new WeeklyLog();  //创建原型对象

              log_previous.setName("张无忌");

              log_previous.setDate("第12周");

              log_previous.setContent("这周工作很忙,每天加班!");

             

              System.out.println("****周报****");

              System.out.println("周次:" +  log_previous.getDate());

              System.out.println("姓名:" +  log_previous.getName());

              System.out.println("内容:" +  log_previous.getContent());

              System.out.println("--------------------------------");

             

              WeeklyLog  log_new;

              log_new  = log_previous.clone(); //调用克隆方法创建克隆对象

              log_new.setDate("第13周");

              System.out.println("****周报****");

              System.out.println("周次:" + log_new.getDate());

              System.out.println("姓名:" + log_new.getName());

              System.out.println("内容:" + log_new.getContent());

       }

}

      编译并运行程序,输出结果如下:

****周报****

周次:第12周

姓名:张无忌

内容:这周工作很忙,每天加班!

--------------------------------

****周报****

周次:第13周

姓名:张无忌

内容:这周工作很忙,每天加班!

      通过已创建的工作周报可以快速创建新的周报,然后再根据需要修改周报,无须再从头开始创建。原型模式为工作流系统中任务单的快速生成提供了一种解决方案。

 

思考

如果在Client类的main()函数中增加如下几条语句:

System.out.println(log_previous == log_new);

System.out.println(log_previous.getDate() == log_new.getDate());

System.out.println(log_previous.getName() == log_new.getName());

System.out.println(log_previous.getContent() == log_new.getContent());

预测这些语句的输出结果。

7.4 带附件的周报

      通过引入原型模式,Sunny软件公司OA系统支持工作周报的快速克隆,极大提高了工作周报的编写效率,受到员工的一致好评。但有员工又发现一个问题,有些工作周报带有附件,例如经理助理“小龙女”的周报通常附有本周项目进展报告汇总表、本周客户反馈信息汇总表等,如果使用上述原型模式来复制周报,周报虽然可以复制,但是周报的附件并不能复制,这是由于什么原因导致的呢?如何才能实现周报和附件的同时复制呢?我们在本节将讨论如何解决这些问题。

      在回答这些问题之前,先介绍一下两种不同的克隆方法,浅克隆(ShallowClone)和深克隆(DeepClone)。在Java语言中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类型。浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制,下面将对两者进行详细介绍。

 

1.浅克隆

      在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制,如图7-4所示:

图7-4 浅克隆示意图

      在Java语言中,通过覆盖Object类的clone()方法可以实现浅克隆。为了让大家更好地理解浅克隆和深克隆的区别,我们首先使用浅克隆来实现工作周报和附件类的复制,其结构如图7-5所示:

图7-5 带附件的周报结构图(浅克隆)

      附件类Attachment代码如下:

//附件类

class Attachment

{

       private  String name; //附件名

       public  void setName(String name)

       {

              this.name  = name;

       }

       public  String getName()

       {

              return  this.name;

       }

     public void download()

     {

            System.out.println("下载附件,文件名为" + name);

     }

}

      修改工作周报类WeeklyLog,修改后的代码如下:

//工作周报WeeklyLog

class WeeklyLog implements Cloneable

{

     //为了简化设计和实现,假设一份工作周报中只有一个附件对象,实际情况中可以包含多个附件,可以通过List等集合对象来实现

       private Attachment attachment;

private String name;

       private  String date;

       private  String content;

    public void setAttachment(Attachment  attachment) {

              this.attachment = attachment;

       }

       public  void setName(String name) {

              this.name  = name;

       }

       public  void setDate(String date) {

              this.date  = date;

       }

       public  void setContent(String content) {

              this.content  = content;

       }

public Attachment  getAttachment(){

              return (this.attachment);

       }

       public  String getName() {

              return  (this.name);

       }

       public  String getDate() {

              return  (this.date);

       }

       public  String getContent() {

              return  (this.content);

       }

     //使用clone()方法实现浅克隆

       public WeeklyLog clone()

       {

              Object obj = null;

              try

              {

                     obj = super.clone();

                     return (WeeklyLog)obj;

              }

              catch(CloneNotSupportedException  e)

              {

                     System.out.println("不支持复制!");

                     return null;

              }

       }

}

      客户端代码如下所示:

class Client

{

       public  static void main(String args[])

       {

              WeeklyLog  log_previous, log_new;

              log_previous  = new WeeklyLog(); //创建原型对象

              Attachment  attachment = new Attachment(); //创建附件对象

              log_previous.setAttachment(attachment);  //将附件添加到周报中

              log_new  = log_previous.clone(); //调用克隆方法创建克隆对象

              //比较周报

              System.out.println("周报是否相同? " + (log_previous ==  log_new));

              //比较附件

              System.out.println("附件是否相同? " +  (log_previous.getAttachment() == log_new.getAttachment()));

       }

}

      编译并运行程序,输出结果如下:

周报是否相同?  false

附件是否相同? true

       由于使用的是浅克隆技术,因此工作周报对象复制成功,通过“==”比较原型对象和克隆对象的内存地址时输出false;但是比较附件对象的内存地址时输出true,说明它们在内存中是同一个对象。

 

2.深克隆

      在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制,如图7-6所示:

图7-6 深克隆示意图

      在Java语言中,如果需要实现深克隆,可以通过序列化(Serialization)等方式来实现。序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。下面我们使用深克隆技术来实现工作周报和附件对象的复制,由于要将附件对象和工作周报对象都写入流中,因此两个类均需要实现Serializable接口,其结构如图7-7所示:

图7-7 带附件的周报结构图(深克隆)

      修改后的附件类Attachment代码如下:

import  java.io.*;

 

//附件类

class  Attachment implements Serializable

{

       private  String name; //附件名

       public  void setName(String name)

       {

              this.name  = name;

       }

       public  String getName()

       {

              return  this.name;

       }

     public void download()

     {

            System.out.println("下载附件,文件名为" + name);

     }

}

      工作周报类WeeklyLog不再使用Java自带的克隆机制,而是通过序列化来从头实现对象的深克隆,我们需要重新编写clone()方法,修改后的代码如下:

import  java.io.*;

 

//工作周报类

class  WeeklyLog implements Serializable

{

       private  Attachment attachment;

       private  String name;

       private  String date;

       private  String content;

       public  void setAttachment(Attachment attachment) {

              this.attachment  = attachment;

       }

       public  void setName(String name) {

              this.name  = name;

       }

       public  void setDate(String date) {

              this.date  = date;

       }

       public  void setContent(String content) {

              this.content  = content;

       }

       public  Attachment getAttachment(){

              return  (this.attachment);

       }

       public  String getName() {

              return  (this.name);

       }

       public  String getDate() {

              return  (this.date);

       }

       public  String getContent() {

              return  (this.content);

       }

   //使用序列化技术实现深克隆

       public WeeklyLog deepClone() throws  IOException, ClassNotFoundException, OptionalDataException

       {

              //将对象写入流中

              ByteArrayOutputStream bao=new  ByteArrayOutputStream();

              ObjectOutputStream oos=new  ObjectOutputStream(bao);

              oos.writeObject(this);

             

              //将对象从流中取出

              ByteArrayInputStream bis=new  ByteArrayInputStream(bao.toByteArray());

              ObjectInputStream ois=new  ObjectInputStream(bis);

              return  (WeeklyLog)ois.readObject();

       }

}

      客户端代码如下所示:

class Client

{

       public  static void main(String args[])

       {

              WeeklyLog  log_previous, log_new = null;

              log_previous  = new WeeklyLog(); //创建原型对象

              Attachment  attachment = new Attachment(); //创建附件对象

              log_previous.setAttachment(attachment);  //将附件添加到周报中

              try

              {

                     log_new =  log_previous.deepClone(); //调用深克隆方法创建克隆对象                  

              }

              catch(Exception e)

              {

                     System.err.println("克隆失败!");

              }

              //比较周报

              System.out.println("周报是否相同? " + (log_previous ==  log_new));

              //比较附件

              System.out.println("附件是否相同? " +  (log_previous.getAttachment() == log_new.getAttachment()));

       }

}

      编译并运行程序,输出结果如下:

周报是否相同?  false

附件是否相同?  false

       从输出结果可以看出,由于使用了深克隆技术,附件对象也得以复制,因此用“==”比较原型对象的附件和克隆对象的附件时输出结果均为false。深克隆技术实现了原型对象和克隆对象的完全独立,对任意克隆对象的修改都不会给其他对象产生影响,是一种更为理想的克隆实现方式。

扩展

Java语言提供的Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口,标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,如是否支持克隆、是否支持序列化等。

 

7.5 原型管理器的引入和实现

      原型管理器(Prototype Manager)是将多个原型对象存储在一个集合中供客户端使用,它是一个专门负责克隆对象的工厂,其中定义了一个集合用于存储原型对象,如果需要某个原型对象的一个克隆,可以通过复制集合中对应的原型对象来获得。在原型管理器中针对抽象原型类进行编程,以便扩展。其结构如图7-8所示:

                             

图7-8 带原型管理器的原型模式

      下面通过模拟一个简单的公文管理器来介绍原型管理器的设计与实现:

Sunny软件公司在日常办公中有许多公文需要创建、递交和审批,例如《可行性分析报告》、《立项建议书》、《软件需求规格说明书》、《项目进展报告》等,为了提高工作效率,在OA系统中为各类公文均创建了模板,用户可以通过这些模板快速创建新的公文,这些公文模板需要统一进行管理,系统根据用户请求的不同生成不同的新公文。

       我们使用带原型管理器的原型模式实现公文管理器的设计,其结构如图7-9所示:

 

 

图7-9 公文管理器结构图

      以下是实现该功能的一些核心代码,考虑到代码的可读性,我们对所有的类都进行了简化:

import Java.util.*;

 

//抽象公文接口,也可定义为抽象类,提供clone()方法的实现,将业务方法声明为抽象方法

interface OfficialDocument extends  Cloneable

{

       public  OfficialDocument clone();

       public  void display();

}

 

//可行性分析报告(Feasibility Analysis Report)类

class FAR implements OfficialDocument

{

       public  OfficialDocument clone()

      {

              OfficialDocument  far = null;

              try

              {

                     far  = (OfficialDocument)super.clone();

              }

              catch(CloneNotSupportedException  e)

              {

                      System.out.println("不支持复制!");

              }

              return  far;

       }

      

       public  void display()

       {

              System.out.println("《可行性分析报告》");

       }

}

 

//软件需求规格说明书(Software Requirements Specification)类

class SRS implements OfficialDocument

{

       public  OfficialDocument clone()

       {

              OfficialDocument  srs = null;

              try

              {

                     srs  = (OfficialDocument)super.clone();

              }

              catch(CloneNotSupportedException  e)

              { 

                     System.out.println("不支持复制!");

              }

              return  srs;

       }

      

       public  void display()

       {

              System.out.println("《软件需求规格说明书》");

       }

}

 

//原型管理器(使用饿汉式单例实现)

class  PrototypeManager

{

       //定义一个Hashtable,用于存储原型对象

       private Hashtable ht=new Hashtable();

       private static PrototypeManager pm =  new PrototypeManager();

      

       //为Hashtable增加公文对象   

     private  PrototypeManager()

     {

              ht.put("far",new  FAR());

              ht.put("srs",new  SRS());               

     }

  

     //增加新的公文对象

       public void addOfficialDocument(String  key,OfficialDocument doc)

       {

              ht.put(key,doc);

       }

 

       //通过浅克隆获取新的公文对象

       public OfficialDocument  getOfficialDocument(String key)

       {

              return  ((OfficialDocument)ht.get(key)).clone();

       }

      

       public static PrototypeManager  getPrototypeManager()

       {

              return pm;

       }

}

      客户端代码如下所示:

class Client

{

       public  static void main(String args[])

       {

              //获取原型管理器对象

              PrototypeManager pm =  PrototypeManager.getPrototypeManager();  

             

              OfficialDocument  doc1,doc2,doc3,doc4;

             

              doc1  = pm.getOfficialDocument("far");

              doc1.display();

              doc2  = pm.getOfficialDocument("far");

              doc2.display();

              System.out.println(doc1  == doc2);

             

              doc3  = pm.getOfficialDocument("srs");

              doc3.display();

              doc4  = pm.getOfficialDocument("srs");

              doc4.display();

              System.out.println(doc3  == doc4);

       }

}

      编译并运行程序,输出结果如下:

《可行性分析报告》

《可行性分析报告》

false

《软件需求规格说明书》

《软件需求规格说明书》

false

      在PrototypeManager中定义了一个Hashtable类型的集合对象,使用“键值对”来存储原型对象,客户端可以通过Key(如“far”或“srs”)来获取对应原型对象的克隆对象。PrototypeManager类提供了类似工厂方法的getOfficialDocument()方法用于返回一个克隆对象。在本实例代码中,我们将PrototypeManager设计为单例类,使用饿汉式单例实现,确保系统中有且仅有一个PrototypeManager对象,有利于节省系统资源,并可以更好地对原型管理器对象进行控制。

 

思考

如果需要增加一种新类型的公文,如《项目进展报告》(Project    Progress Report, PPR),公文管理器系统源代码如何修改,动手实践你的修改方案。

 

7.6 原型模式总结

      原型模式作为一种快速创建大量相同或相似对象的方式,在软件开发中应用较为广泛,很多软件提供的复制(Ctrl + C)和粘贴(Ctrl + V)操作就是原型模式的典型应用,下面对该模式的使用效果和适用情况进行简单的总结。

1.主要优点

      原型模式的主要优点如下:

(1) 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。

(2) 扩展性较好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响。

(3) 原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。

(4) 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。

2.主要缺点

      原型模式的主要缺点如下:

(1) 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”。

(2) 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。

3.适用场景

        在以下情况下可以考虑使用原型模式:

(1) 创建新对象成本较大(如初始化需要占用较长的时间,占用太多的CPU资源或网络资源),新的对象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修改。

(2) 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现。

(3) 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。

 

练习

设计并实现一个客户类Customer,其中包含一个名为客户地址的成员变量,客户地址的类型为Address,用浅克隆和深克隆分别实现Customer对象的复制并比较这两种克隆方式的异同。

【作者:刘伟http://blog.youkuaiyun.com/lovelion】

分类:  Java
Java设计模式——原型模式 关键代码: 1、实现克隆操作,在 JAVA 实现 Cloneable 接口,重写 clone(),在 .NET 中可以使用 Object 类的 MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。设计模式,允许一个对象在创建另外一个可定制的对象,无需知道如何创建的细节。5、一个对象多个修改者的场景。 阅读详情

相关推荐

用STM32的SPI模式2搞定SSI绝对值编码器通信(附完整代码与避坑点)

本文详细解析了如何使用STM32的SPI模式2实现SSI绝对值编码器通信,包括硬件设计、SPI配置、数据处理算法及稳定性优化技巧。通过完整的代码示例和避坑指南,帮助工程师解决时序匹配、数据解析等常见问题,提升工业自动化系统中的编码器通信可靠性。

weixin_27252001的博客 265

创建型模式之原型模式(创建工作周报)

类图 package cn.factory4; public class JobWeekly implements Cloneable { private String time; private String weekly; private String keypoint; private String mainbody; publi

zenglinshan的博客 2839

Codex Skill:AI编程助手的专项能力扩展与实战指南

在AI编程助手领域,Skill(技能)是一种标准化的能力扩展机制,它通过封装特定任务所需的指令、资源和脚本,解决AI模型在专项任务上的能力局限。其核心原理是将专业知识和工作流程模块化,让通用AI助手能够像领域专家一样执行复杂任务。从技术价值看,Skill不仅提升了代码生成的准确性,更重要的是实现了业务流程的自动化集成,如代码规范检查、API文档生成等具体场景。这种机制特别适用于需要结合内部工具链和特定业务逻辑的企业环境,能够显著提升开发效率和质量。通过安装PPT生成、SpringBoot项目创建等专项Ski

weixin_33672109的博客 413

原型模式

原型模式一、原型模式二、原型模式实例之快速创建工作周报1.实例说明2.实例类图3.实例代码参考文献 一、原型模式 原型模式(Prototype Pattern)是一种对象创建型模式。 定义:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。 使用频率:3星 原型模式结构图: 优点: 1、Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。

weixin_43594279的博客 1349

在Proteus中用80C51模拟4线SPI点亮OLED屏幕

在Proteus里用80C51模拟4线SPI控制OLED屏幕使其显示图案(汇编实现)。

Connor3093的博客 5004

大语言模型(LLM)在组合优化中的新突破:LLM-LNS与FunSearch性能对比

本文深入探讨了大语言模型(LLM)在组合优化中的创新应用,重点对比了LLM-LNS与FunSearch两种方法的性能差异。研究表明,LLM-LNS在物流路径规划、资源调度等场景中展现出显著优势,其双层自演化架构能有效平衡探索与利用,相比传统方法提升效率约43%。文章还提供了实践选择指南,帮助开发者根据具体需求选用合适的技术方案。

weixin_42526415的博客 406

原型模式 -- 拷贝当前对象的副本

张纪中版《西游记》以出乎意料的造型和雷人的台词遭到广大观众朋友的热议,我们在此对该话题不作过多讨论。但无论是哪个版本的《西游记》,孙悟空都是其中的一号雄性主角,关于他(或它)拔毛变小猴的故事几乎人人皆知,孙悟空可以用猴毛根据自己的形象,复制(又称“克隆”或“拷贝”)出很多跟自己长得一模一样的“身外身”来。在设计模式中也存在一个类似的模式,可以通过一个原型对象克隆出多个一模一样的对象,该模式称之为原

智多牛 - 俯首甘为孺子牛! 1363

BUCK-BOOST升降压电路Multisim电路仿真分析

本文分析了三极管型BUCK-BOOST升降压开关电源的工作原理。该电路结合了BUCK降压和BOOST升压拓扑,通过控制三极管Q1的高频通断,使电感L1周期性储能和释能,实现输出电压可高于或低于输入电压且极性相反。详细阐述了Q1导通和关断阶段的工作过程,推导了输出电压与占空比的关系式Vo=-D/(1-D)*Vi,并分析了不同占空比下的工作模式(降压、升压)。通过Multisim仿真验证了理论分析,当D=25%时输出降压至1.6V,D=60%时输出升压至7.5V。电路具有升降压灵活、极性反转、效率高等特点,适用

XU157303764的博客 1436

设计模式,简单工厂,工厂和克隆

1. 简单工厂模式。 用面向对象思想实现一个简单计算器,绘制类图并编程实现。2. 工厂方法模式日志记录器。某系统日志记录器要求支持多种日志记录方式,如文件日志记录(FileLog)、数据库日志记录(DatabaseLog)等,且用户可以根据要求动态选择日志记录方式,现使用工厂模式设计该系统。3. 抽象工厂模式之数据库操作工厂。某系统为了改进数据库操作的性能,自定义数据为连接对象(Connectio...

qq_35301188的博客 795

uniapp - 实现苹果App打包上架App Store应用商店详细教程,详解Hbuilder打包编译ipa发布上传到苹果ios软件商店全流程,提供云打包/离线本地打包ios、申请IOS苹果证书方法

uni-app,苹果ios,苹果app,打包编译,appstore应用商店,ipa发布到软件商店,uniapp的app苹果应用商店上架,上架苹果APPStore,uniApp打包的ipa文件上传到商店,上架苹果商城,审核失败,被拒绝,UniApp打包ipa文件,上架iOSAppStore流程指南,最新详细教程最全,Hbuilder打包发布iosapp,苹果云打包上线全过程,云打包IPA并上传到商店,打包iOSipa文件后怎么发布到应用商店,苹果证书申请教程,ios证书申请,mac电脑,审核很久

🏆 前端领域优质创作者 1万+

Java设计模式——原型模式【ProtoType Pattern】

Java设计模式、原型模式

scorpio的博客 496

YOLOv8改进 | 主干网络 | 将主干网络替换为轻量化的ShuffleNetv2【原理 + 完整代码】

yolov8,yolov8创新,yolov8改进

kay_545 2565

Java设计模式 六 原型模式 (Prototype Pattern)

原型模式是解决对象重复创建问题的强大工具,特别适合那些创建成本高或需要快速生成对象的场景。根据具体需求,可以选择浅拷贝或深拷贝的实现方式。尽管实现上可能会稍复杂,但它提供了灵活性和高效性,是值得掌握的重要设计模式之一。

空 1117

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Java 设计模式——原型模式(Prototype Pattern)

Java 设计模式——原型模式(Prototype Pattern) 一.基础概念 1.定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 2.主要概念 Java中的克隆方法: 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝 二: 具体实现 1.浅...

a158s168的博客 457

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

设计模式(Design Pattern) - 创建型模式(Creational Pattern) - 原型模式(Prototype) - Java实现

设计模式(Design Pattern) - 创建型模式(Creational Pattern) - 原型模式(Prototype) 用原型实例指定创建对象的种类,并且通过复制这些原型,创建新的对象。

Z 551

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

设计模式:原型模式(Prototype Pattern)java版本

摘要 原型模式是一种通过克隆现有对象来创建新实例的设计模式,适用于需要高效生成相似对象的场景。核心思想是利用原型对象的clone()方法快速复制,避免重复初始化开销。文章通过带附件的邮件示例展示了深拷贝实现,并针对在线考试系统的组卷需求,对比了传统方式和原型模式在性能上的差异。典型应用场景包括:对象创建成本高、需要大量相似实例、需保持对象独立性等。实现时需注意区分浅拷贝与深拷贝,可通过序列化或工具类简化深拷贝过程。源码示例见GitHub项目CodeDesignWork。

qq_61875563的博客 882

设计模式之原型模式(Prototype Pattern),java实现

文章目录1、原型模式的定义2、原型模式UML3、 java 中实现克隆4、原型模式的优缺点4.1、优点:4.2、 缺点:5、浅拷贝6、浅克隆与深克隆 的区别7、深度克隆 1、原型模式的定义 原型模式是: 创建型的设计模式 使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。 分为 深度克隆 和 浅克隆。 2、原型模式UML 原型模式要求对象实现一个可以“克隆”自身的 接口 ,...

猎人在吃肉 835

java设计模式---原型模式(prototype pattern)

java设计模式—原型模式(prototype pattern) 我们在程序开发过程中,都会遇到为一个类创建多个实例的情况,实例的创建开销很大,而且这些实例内部成员往往完全相同或仅有细微的差异,这种情况下如果能通过复制一个已创建的对象实例来重复创建多个相同的对象,这就可以大大减少创建对象的开销,此时就需要使用到原型模式。 1. 定义 原型模式是可以通过一个对象实例确定创建对象的种类,并且通...

渔潭的博客 361

Java设计模式-创建型设计模式-原型模式

Java设计模式-创建型设计模式-原型模式

Fatter$hday的博客 1576

详解Java设计模式之原型模式(Prototype Pattern)

在软件系统中,有时候需要多次创建某一类型的对象,为了简化创建过程,可以只创建一个对象,然后再通过克隆的方式复制出多个相同的对象,这就是原型模式的设计思想。

Elegance never goes out of fashion 9051

JAVA设计模式之 原型模式【Prototype Pattern】

一、概述: 使用原型实例指定创建对象的种类,而且通过拷贝这些原型创建新的对象。简单的说就是对象的拷贝生成新的对象(对象的克隆),原型模式是一种对象创建型模式。 二、使用场景: 创建新的对象能够通过对已有对象进行复制来获得,假设是相似对象,则仅仅需对其成员变量稍作改动。 三、UML结构图: 四、參与者 (1)...

diandingyin9417的博客 105

Java设计模式之原型模式

Java设计模式之原型模式 时间隔了好几天了,今天写一下设计模式中的原型模式,示例代码几天前已经瞧好了,想做一些优化,方便大家对这个模式的理解更加深入一点,另外也需要找更多详细点的资料 一 : 含义 什么是原型模式 ?     使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。 二 : 解决的问题          多个相同对象重复创建的较大开

weixin_47301645的博客 1319

JAVA设计模式-原型模式

原型模式是一种创建型模式,用于创建重复的对象,并且保证性能。原型模式创建的对象是由原型对象自身创建的,是原型对象的一个克隆,和原型对象具有相同的结构和相同的值。

胥平勇的博客 452
上一篇: 原型模式的java实现例子
下一篇: jsonp
javaPie
博客等级 码龄16年 1171粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值