设计模式-原型模式

原型模式

其它创建型模式链接:

  1. 设计模式-简单工厂模式
  2. 设计模式-工厂方法模式
  3. 设计模式-抽象工厂模式
  4. 设计模式-单例模式
  5. 设计模式-建造者模式

1.原型模式概述

原型模式:使用原型实例指定待创对象的类型,并且通过复制这个原型来创建新的对象。

原型模式是一种对象创建型模式,它的工作原理很简单:将一个原型对象传给要发动创建的(即客户端对象),这个要发动创建的对象通过请求原型对象复制自己来实现创建过程。由于在软件系统中经常会遇到创建多个相同或相似对象的情况,因此原型模式在实际开发中有较高的使用频率。原型模式是一种另类的创建型模式,创建新对象的工厂就是原型类本身,工厂方法由负责原型对象的克隆方法来实现。

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

2.原型模式结构

image-20210520110403133

原型模式包含三个角色:

  1. Prototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,它可以是抽象类也可以是接口,甚至还可以是具体实现类。
  2. ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回一个自己的一个克隆对象。
  3. Client(客户类):在客户类中,让一个原型对象克隆自身从而创建一个新的对象,只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户端针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或者更换都很方便。

3.浅克隆与深克隆

3.1浅克隆

在浅克隆中,如果原型对象的成员变量为值类型(int,double,byte等),将复制一份给克隆对象;如果原型对象的成员变量是引用类型(类,接口,数组等),则将引用对象的地址复制一份给克隆对象,也就是说原型对象和成员变量指向相同的内存地址。

image-20210520115239712

3.2深克隆

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。

image-20210520143612587

4.案例

在使用某OA系统时,有些岗位的员工发现他们每周的工作都大同小异,因此在填写工作周报时很多内容都是重复的,为了提高工作周报的效率,大家迫切希望有一种机制能够快速创建相同或相似的周报,包括创建周报的附件。

使用原型模式对该OA系统中的工作周报创建模块进行改进。

4.1结构图

image-20210520145004211

4.2代码实现

Attachment:附件类
@Data
public class Attachment {
    private String name;

    public void download() {
        System.out.println("下载附件,文件名为" + name);
    }
}
WeeklyLog:工作周报类
@Data
public class WeeklyLog implements Cloneable {
    private Attachment attachment;
    private String name;
    private String date;
    private String content;

    @Override
    public WeeklyLog clone() {
        Object obj;
        try {
            obj = super.clone();
            return (WeeklyLog) obj;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}
Client:客户端类
public class Client {
    public static void main(String[] args) {
        WeeklyLog log_previous,log_new;
        log_previous=new WeeklyLog();
        Attachment attachment=new Attachment();
        attachment.setName("附件一");
        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

5.深克隆解决方案

在java语言中可以通过序列化等方式来实现深克隆,写到流中的对象是原有对象的一个复制,而原对象仍然存在内存中。不仅可以复制对象本身,还可以复制其引用的成员变量。

需要注意复制的类必须实现Serializable接口。

5.1结构图

image-20210520145217596

5.2Attachment:附件类

@Data
public class Attachment implements Serializable {
    private String name;

    public void download() {
        System.out.println("下载附件,文件名为" + name);
    }
}

5.3WeeklyLog:工作周报类

@Data
public class WeeklyLog implements  Serializable {
    private Attachment attachment;
    private String name;
    private String date;
    private String content;

    public WeeklyLog deepClone() throws IOException, ClassNotFoundException {
        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();
    }
}

5.4Client:客户端类

public class Client {
    public static void main(String[] args) {
        WeeklyLog log_previous,log_new;
        log_previous=new WeeklyLog();
        Attachment attachment=new Attachment();
        attachment.setName("附件一");
        log_previous.setAttachment(attachment);
        try {
            log_new=log_previous.deepClone();
        } catch (IOException | ClassNotFoundException e) {
            log_new=null;
            e.printStackTrace();
        }
        //比较周报
        System.out.println("周报是否相同?:"+(log_previous==log_new));
        //比较附件
        System.out.println("附件是否相同?:"+(log_previous.getAttachment()==log_new.getAttachment()));
    }
}

5.5结果展示

周报是否相同?:false
附件是否相同?:false

6.原型模式优缺点与适用环境

6.1原型模式优点

原型模式优点如下:

  1. 当创建对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有的实例可以提高新实例的创建效率。
  2. 扩展性好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统没有任何影响。
  3. 原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样搞,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无需专门的工厂类来创建产品。
  4. 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(例如恢复到某一历史状态),可以辅助实现撤销操作。

6.2原型模式缺点

原型模式缺点如下:

  1. 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时需要修改源代码,违背了开闭原则。
  2. 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较复杂。

6.3原型模式适用环境

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

  1. 创建新对象成本较大(例如要占用较长时间、占用太多CPU资源等),新对象可以通过复制已有对象来获得,如果是相似对象,则可以对其成员变量稍作修改。
  2. 系统要保存对象的状态,而对象的状态变换很小。
  3. 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值