设计模式解密(18)- 原型模式

1、简介

定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。

功能:①是通过克隆来创建新的对象实例;②是为克隆出来的新的对象实例复制原型实例属性的值。

本质:通过克隆来创建新的对象实例。

英文:Prototype

类型:创建型

2、类图及组成

(引)类图:

组成:

  Prototype:声明一个克隆自身的接口,用来约束想要克隆自己的类,要求它们都要实现这里定义的克隆方法。

  ConcretePrototype:实现Prototype接口的类,这些类真正实现了克隆自身的功能。

  Client:使用原型的客户端,首先要获取到原型实例对象,然后通过原型实例克隆自身来创建新的对象实例。

标准代码结构:

/**
 * 先来看看原型接口的定义 : 声明一个克隆自身的接口
 */
public interface Prototype {
    /**
     * 克隆自身的方法
     * @return 一个从自身克隆出来的对象
     */
    public Prototype clone();
}

/**
 * 具体的原型实现对象 : 克隆的具体实现对象
 * 为了跟上面原型模式的结构示意图保持一致,因此这两个具体的原型实现对象,都没有定义属性。事实上,在实际使用原型模式的应用中,原型对象多是有属性的,克隆原型的时候也是需要克隆原型对象的属性的,特此说明一下。
 */
public class ConcretePrototype implements Prototype {
    public Prototype clone() {
       //最简单的克隆,新建一个自身对象,由于没有属性,就不去复制值了
       Prototype prototype = new ConcretePrototype();
       return prototype;
    }
}

/**
 * 使用原型的客户端
 */
public class Client {
    /**
     * 持有需要使用的原型接口对象
     */
    private Prototype prototype;

    /**
     * 构造方法,传入需要使用的原型接口对象
     * @param prototype 需要使用的原型接口对象
     */
    public Client(Prototype prototype){
        this.prototype = prototype;
    }

    /**
     * 示意方法,执行某个功能操作
     */
    public void operation(){
        //会需要创建原型接口的对象
        Prototype newPrototype = prototype.clone();
    }
}

上面的代码组成是为了让大家清楚了解原型模式的结构,Java中已经提供Cloneable接口,所以在实现原型模式不必在实现Prototype原型接口,直接实现Cloneable接口即可;

Java实现原型模式的两个要点:

1、实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException异常。

2、重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。

Java原型模式代码结构:

public class PrototypeClass implements Cloneable{
    //覆写父类Object方法
    @Override
    public PrototypeClass clone(){
        PrototypeClass prototypeClass = null;
        try {
            prototypeClass = (PrototypeClass)super.clone();
        } catch (CloneNotSupportedException e) {
            //异常处理
        }
        return prototypeClass;
    }
} 

注意:

  Object类的clone方法只会拷贝java中的8中基本类型以及他们的封装类型,另外还有String类型。对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。

3、浅拷贝与深拷贝

浅拷贝(shallow copy)

  被复制对象的所有变量都含有与原来的对象相同的值(仅对于简单的值类型数据),而所有的对其他对象的引用都仍然指向原来的对象。换言之,只负责克隆按值传递的数据(比如:基本数据类型、String类型)。

深拷贝 (deep copy)

  被复制对象的所有的变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,除了浅度克隆要克隆的值外,还负责克隆引用类型的数据,基本上就是被克隆实例所有的属性的数据都会被克隆出来。

浅拷贝实例↓↓↓:

package com.designpattern.Prototype.shallowcopy;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 计划
 * @author Json<<[email protected]>>
 */
public class Plan implements Cloneable {
    //计划名称
    private String name;
    //任务级别
    private int level;
    //开始时间
    private Date startdate;
    //截止时间
    private Date enddate;
    //执行人员
    private List<String> executors = new ArrayList<String>();

    @Override
    public Plan clone(){
        try {
            return (Plan) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getName() {
        return name;
    }

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

    public Date getStartdate() {
        return startdate;
    }

    public void setStartdate(Date startdate) {
        this.startdate = startdate;
    }

    public Date getEnddate() {
        return enddate;
    }

    public void setEnddate(Date enddate) {
        this.enddate = enddate;
    }

    public List<String> getExecutors() {
        return executors;
    }

    public void setExecutors(List<String> executors) {
        this.executors = executors;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    @Override
    public String toString() {
        return "[name=" + name + ", level=" + level + ", startdate=" + startdate + ", enddate=" + enddate
                + ", executors=" + executors + "]";
    }
}

测试:

package com.designpattern.Prototype.shallowcopy;

import java.util.ArrayList;
import java.util.List;

import com.designpattern.utils.DateFormatUtil;

/**
 * 测试
 * @author Json<<[email protected]>>
 */
public class Client {
    public static void main(String[] args) {
        List<String> executors = new ArrayList<String>();
        executors.add("张三");
        executors.add("李四");

        Plan plan = new Plan();
        plan.setName("重构前端登录界面");
        plan.setLevel(1);
        plan.setStartdate(DateFormatUtil.stringToDate(DateFormatUtil.YYYYMMDD,"2017-08-07"));
        plan.setEnddate(DateFormatUtil.stringToDate(DateFormatUtil.YYYYMMDD,"2017-08-09"));
        plan.setExecutors(executors);

        Plan plan2 = plan.clone();
        plan2.setName("后端接口改造");
        plan2.setLevel(2);
        plan2.setStartdate(DateFormatUtil.stringToDate(DateFormatUtil.YYYYMMDD,"2017-08-10"));
        plan2.setEnddate(DateFormatUtil.stringToDate(DateFormatUtil.YYYYMMDD,"2017-08-12"));

        System.out.println("地址是否一样?"+(plan == plan2));
        System.out.println("plan.getName() == plan2.getName() "+(plan.getName() == plan2.getName()));
        System.out.println("plan.getLevel() == plan2.getLevel() "+(plan.getLevel() == plan2.getLevel()));
        System.out.println("plan.getStartdate() == plan2.getStartdate() "+(plan.getStartdate() == plan2.getStartdate()));
        System.out.println("plan.getEnddate() == plan2.getEnddate() "+(plan.getEnddate() == plan2.getEnddate()));
        System.out.println("plan.getExecutors() == plan2.getExecutors() "+(plan.getExecutors() == plan2.getExecutors()));
        System.out.println("plan:"+plan.toString());
        System.out.println("plan2:"+plan2.toString());

        //plan任务比较重,在给plan添加一个人
        executors.add("王五");
        plan.setExecutors(executors);

        System.out.println();
        System.out.println("地址是否一样?"+(plan == plan2));
        System.out.println("plan.getName() == plan2.getName() "+(plan.getName() == plan2.getName()));
        System.out.println("plan.getLevel() == plan2.getLevel() "+(plan.getLevel() == plan2.getLevel()));
        System.out.println("plan.getStartdate() == plan2.getStartdate() "+(plan.getStartdate() == plan2.getStartdate()));
        System.out.println("plan.getEnddate() == plan2.getEnddate() "+(plan.getEnddate() == plan2.getEnddate()));
        System.out.println("plan.getExecutors() == plan2.getExecutors() "+(plan.getExecutors() == plan2.getExecutors()));
        System.out.println("plan:"+plan.toString());
        System.out.println("plan2:"+plan2.toString());
    }
}

结果:

地址是否一样?false
plan.getName() == plan2.getName() false
plan.getLevel() == plan2.getLevel() false
plan.getStartdate() == plan2.getStartdate() false
plan.getEnddate() == plan2.getEnddate() false
plan.getExecutors() == plan2.getExecutors() true
plan:[name=重构前端登录界面, level=1, startdate=Mon Aug 07 00:00:00 CST 2017, enddate=Wed Aug 09 00:00:00 CST 2017, executors=[张三, 李四]]
plan2:[name=后端接口改造, level=2, startdate=Thu Aug 10 00:00:00 CST 2017, enddate=Sat Aug 12 00:00:00 CST 2017, executors=[张三, 李四]]

地址是否一样?false
plan.getName() == plan2.getName() false
plan.getLevel() == plan2.getLevel() false
plan.getStartdate() == plan2.getStartdate() false
plan.getEnddate() == plan2.getEnddate() false
plan.getExecutors() == plan2.getExecutors() true
plan:[name=重构前端登录界面, level=1, startdate=Mon Aug 07 00:00:00 CST 2017, enddate=Wed Aug 09 00:00:00 CST 2017, executors=[张三, 李四, 王五]]
plan2:[name=后端接口改造, level=2, startdate=Thu Aug 10 00:00:00 CST 2017, enddate=Sat Aug 12 00:00:00 CST 2017, executors=[张三, 李四, 王五]]

通过上面的实例,大家发现没有,对于引用类型List,修改plan的List,plan2也随着变化了,这就是浅拷贝带来的问题;

深拷贝实例(修改浅拷贝实例)↓↓↓:

package com.designpattern.Prototype.deepcopy;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 计划
 * @author Json<<[email protected]>>
 */
public class Plan implements Cloneable {
    //计划名称
    private String name;
    //任务级别
    private int level;
    //开始时间
    private Date startdate;
    //截止时间
    private Date enddate;
    //执行人员
    private List<String> executors = new ArrayList<String>();

    @Override
    public Plan clone(){
        try {
            Plan plan = (Plan) super.clone();
            //引用类型的属性,需要处理
            if(this.getExecutors() != null){
                List<String> _executors = new ArrayList<String>();
                for(String s : this.getExecutors()){
                    _executors.add(s);
                }
                plan.setExecutors(_executors);
            }

            return plan;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getName() {
        return name;
    }

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

    public Date getStartdate() {
        return startdate;
    }

    public void setStartdate(Date startdate) {
        this.startdate = startdate;
    }

    public Date getEnddate() {
        return enddate;
    }

    public void setEnddate(Date enddate) {
        this.enddate = enddate;
    }

    public List<String> getExecutors() {
        return executors;
    }

    public void setExecutors(List<String> executors) {
        this.executors = executors;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    @Override
    public String toString() {
        return "[name=" + name + ", level=" + level + ", startdate=" + startdate + ", enddate=" + enddate
                + ", executors=" + executors + "]";
    }

}

测试(没改动):

package com.designpattern.Prototype.deepcopy;

import java.util.ArrayList;
import java.util.List;

import com.designpattern.utils.DateFormatUtil;

/**
 * 测试
 * @author Json<<[email protected]>>
 */
public class Client {
    public static void main(String[] args) {
        List<String> executors = new ArrayList<String>();
        executors.add("张三");
        executors.add("李四");

        Plan plan = new Plan();
        plan.setName("重构前端登录界面");
        plan.setLevel(1);
        plan.setStartdate(DateFormatUtil.stringToDate(DateFormatUtil.YYYYMMDD,"2017-08-07"));
        plan.setEnddate(DateFormatUtil.stringToDate(DateFormatUtil.YYYYMMDD,"2017-08-09"));
        plan.setExecutors(executors);

        Plan plan2 = plan.clone();
        plan2.setName("后端接口改造");
        plan2.setLevel(2);
        plan2.setStartdate(DateFormatUtil.stringToDate(DateFormatUtil.YYYYMMDD,"2017-08-10"));
        plan2.setEnddate(DateFormatUtil.stringToDate(DateFormatUtil.YYYYMMDD,"2017-08-12"));

        System.out.println("地址是否一样?"+(plan == plan2));
        System.out.println("plan.getName() == plan2.getName() "+(plan.getName() == plan2.getName()));
        System.out.println("plan.getLevel() == plan2.getLevel() "+(plan.getLevel() == plan2.getLevel()));
        System.out.println("plan.getStartdate() == plan2.getStartdate() "+(plan.getStartdate() == plan2.getStartdate()));
        System.out.println("plan.getEnddate() == plan2.getEnddate() "+(plan.getEnddate() == plan2.getEnddate()));
        System.out.println("plan.getExecutors() == plan2.getExecutors() "+(plan.getExecutors() == plan2.getExecutors()));
        System.out.println("plan:"+plan.toString());
        System.out.println("plan2:"+plan2.toString());

        //plan任务比较重,在给plan添加一个人
        executors.add("王五");
        plan.setExecutors(executors);

        System.out.println();
        System.out.println("地址是否一样?"+(plan == plan2));
        System.out.println("plan.getName() == plan2.getName() "+(plan.getName() == plan2.getName()));
        System.out.println("plan.getLevel() == plan2.getLevel() "+(plan.getLevel() == plan2.getLevel()));
        System.out.println("plan.getStartdate() == plan2.getStartdate() "+(plan.getStartdate() == plan2.getStartdate()));
        System.out.println("plan.getEnddate() == plan2.getEnddate() "+(plan.getEnddate() == plan2.getEnddate()));
        System.out.println("plan.getExecutors() == plan2.getExecutors() "+(plan.getExecutors() == plan2.getExecutors()));
        System.out.println("plan:"+plan.toString());
        System.out.println("plan2:"+plan2.toString());
    }
}

结果:

地址是否一样?false
plan.getName() == plan2.getName() false
plan.getLevel() == plan2.getLevel() false
plan.getStartdate() == plan2.getStartdate() false
plan.getEnddate() == plan2.getEnddate() false
plan.getExecutors() == plan2.getExecutors() false
plan:[name=重构前端登录界面, level=1, startdate=Mon Aug 07 00:00:00 CST 2017, enddate=Wed Aug 09 00:00:00 CST 2017, executors=[张三, 李四]]
plan2:[name=后端接口改造, level=2, startdate=Thu Aug 10 00:00:00 CST 2017, enddate=Sat Aug 12 00:00:00 CST 2017, executors=[张三, 李四]]

地址是否一样?false
plan.getName() == plan2.getName() false
plan.getLevel() == plan2.getLevel() false
plan.getStartdate() == plan2.getStartdate() false
plan.getEnddate() == plan2.getEnddate() false
plan.getExecutors() == plan2.getExecutors() false
plan:[name=重构前端登录界面, level=1, startdate=Mon Aug 07 00:00:00 CST 2017, enddate=Wed Aug 09 00:00:00 CST 2017, executors=[张三, 李四, 王五]]
plan2:[name=后端接口改造, level=2, startdate=Thu Aug 10 00:00:00 CST 2017, enddate=Sat Aug 12 00:00:00 CST 2017, executors=[张三, 李四]]

到这里,大家对原型模式的浅拷贝、深拷贝有了一定了解了吧!

4、优缺点

优点:

  1、对客户端隐藏具体的实现类型:原型模式的客户端,只知道原型接口的类型,并不知道具体的实现类型,从而减少了客户端对这些具体实现类型的依赖。

  2、在运行时动态改变具体的实现类型:原型模式可以在运行期间,由客户来注册符合原型接口的实现类型,也可以动态的改变具体的实现类型,看起来接口没有任何变化,但其实运行的已经是另外一个类实例了。因为克隆一个原型就类似于实例化一个类。

缺点:

  深度克隆方法实现会比较困难:原型模式最大的缺点就在于每个原型的子类都必须实现clone的操作,尤其在包含引用类型的对象时,clone方法会比较麻烦,必须要能够递归的让所有的相关对象都要正确的实现克隆。

5、应用场景

  1、一个系统想要独立于它想要使用的对象时,可以使用原型模式,让系统只面向接口编程,在系统需要新的对象的时候,可以通过克隆原型来得到;

  2、需要实例化的类是在运行时刻动态指定时,可以使用原型模式,通过克隆原型来得到需要的实例;

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

  .......

6、扩展总结

Java中对象的创建:

clone就是复制,在Java中,clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。

那么在java语言中,有几种方式可以创建对象呢?

  1、使用new操作符创建一个对象

  2、使用clone方法复制一个对象

那么这两种方式有什么相同和不同呢?

  ? new操作符的本意是分配内存。程序执行到new操作符时,首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。

  ? clone在第一步是和new相似的,都是分配内存,调用clone方法时,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。

clone 方法需要满足的几个原则:

  x.clone() != x,两个对象不是一个;

  x.clone().getClass() == x.getClass(),两个对象的class是同一个;

  x.clone().equals(x),两个应该满足equals()方法条件;

注意:

  Object类的clone方法只会拷贝java中的8中基本类型以及他们的封装类型,另外还有String类型。对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。

PS: 要使用clone方法,类的成员变量上不要增加final关键字。

PS:深拷贝和浅拷贝建议不要混合使用,特别是是在涉及到类的继承,父类有多个引用的情况就非常的复杂,建议的方案是深拷贝和浅拷贝分开实现。

PS:java实现真正的深拷贝,可以通过实现Serializable接口用序列化的办法来实现。

PS:源码地址   https://github.com/JsonShare/DesignPattern/tree/master

PS:原文地址 http://www.cnblogs.com/JsonShare/p/7300124.html

  

时间: 2024-08-03 04:25:49

设计模式解密(18)- 原型模式的相关文章

设计模式学习05—原型模式

一.动机与定义 之前学习原型模式一直以为原型模式目的是为了方便的创建相同或相似对象,用复制对象的方式替换new的方式,还研究了深克隆和浅克隆.最近仔细看了GOF的设计模式,发现原型模式的本意并不仅仅是复制对象这么简单. 复制对象确实是一方面,当我们需要大量相似,甚至相同对象的时候,除了一个个的new之外,还可以根据一个原型,直接复制出更多的对象.但是如果把原型模式认为只是复制对象这么简单就错了. 创建型模式主要讲如何创建对象,通常包含何时创建,谁来创建,怎么创建等.GOF书里面写的意图是,用原型

Java描述设计模式(05):原型模式

一.原型模式简介 1.基础概念 原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象. 2.模式结构 原型模式要求对象实现一个可以"克隆"自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例.这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建. 3.代码实现 1).UML关系图 Java描述设计模

php设计模式 四 (观察者 原型模式 迭代器模式)

观察者模式 观察者模式(有时又被称为发布-订阅Subscribe>模式.模型-视图View>模式.源-收听者Listener>模式或从属者模式)是软件设计模式的一种.在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知.这通常透过呼叫各观察者所提供的方法来实现.此种模式通常被用来实现事件处理系统. 当一个对象的状态发生改变时,依赖他的对象全部会接到通知,并自动更新.观察者模式实现了低耦合 非入侵式的通知与更新机制. 观察者模式示例: 首先创建一个事

设计模式学习-原型模式

1.定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 2.类图 3.代码示例 1 package com.zhaoyangwoo.prototype; 2 3 /** 4 * Created by john on 16/5/8. 5 */ 6 public class Prototype { 7 public static void main(String[] args){ 8 Product p1= new Product(); 9 p1.setName("nihao&q

《Java设计模式》之原型模式

原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象.这就是原型模式的用意. 原型模式的结构 原型模式要求对象实现一个可以"克隆"自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例.这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建. 原型模式有两种表现形式:(1)简单形式.(2)登记形式,这两

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

引言: 原型模式是什么?它是在什么场景下被提出的呢?本章节,我们将详细了解下原型模式. 在软件系统中,当创建一个类的实例过程过于昂贵或复杂,并且我们需要创建多个这样类的实例时,如果我们通过new来创建类实例,这就会增加创建类的复杂度和创建过程与客户代码复杂的耦合度.如果采用工厂模式来创建这样的实例对象的话,随着产品类的不断增加,导致子类的数量不断增多,也导致了相应工厂类的增加,维护的代码维度增加了,因为有产品和工厂两个维度了,反而增加了系统复杂程度,所以在这里使用工厂模式来封装类创建过程并不合适

从王者荣耀看设计模式(十七.原型模式)

从王者荣耀看设计模式(原型模式) 一.简介 王者荣耀包含有很多的玩法,其中有一种游戏模式只在周六和周日开放,那就是--克隆模式.与常规的游戏双方阵营只允许出现单一英雄不同,在克隆模式中,双方各选择一个英雄进行克隆,换句话说,一个阵营的五个人操作的五个相同的英雄 二.模式动机 在软件系统中,有些对象的创建过程比较复杂,而且有时候需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的意图所在. 三.原型模式 原型模式

java设计模式 GOF23 04 原型模式

一.原型模式介绍 因为java中new一个新对象比clone一个对象需要花费等多的资源,所以一般需要 在短时间内创建大量对象并且new对象的过程需要耗费比较多的资源使用原型模式. 想要clone一个类需要这个类实现Cloneable接口,重载clone方法,这个接口在底层 通过内存拷贝实现clone对象,因此效率很高. package com.lz.prototype; import java.util.Date; public class ShallowClone implements Clo

设计模式(5)之原型模式

什么是原型模式? Prototype模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例.使用Prototype模式创建的实例,具有与原型一样的数据. 原型模式的特点 <1> 由原型对象自身创建目标对象.也就是说,对象创建这一动作发自原型对象本身. <2> 目标对象是原型对象的一个克隆.也就是说,通过Prototype模式创建的对象,不仅仅与原型对象具有相同的结构,还与原型对象具有相同的值. <3> 根据对象克隆深度层次的不同,有浅度克隆与深度克隆. 原型

设计模式之六:原型模式(Prototype)

原型模式:使用原型实例来指定创建对象的种类,并通过拷贝这个对象的值来创建新的对象. Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype. UML图: 主要包括 Prototype:定义了一个包含克隆自身的接口 ConcretePrototype:具体的原型类,实现了克隆自身函数的类 Client:通过一个具体的原型