简介

原型模式是属于创建型模式的一种,是通过拷贝原型对象来创建新的对象.

万能的Java超类Object提供了clone()方法来实现对象的拷贝.

可以在以下场景中使用原型模式:

  1. 构造函数创建对象成本太大(性能或安全成本)
  2. 要保存对象的状态, 且状态变化较小, 不会过多占用内存时(状态变化较大的使用状态模式会更合适)

意图

使用原型实例指定要创建的对象类型,并通过拷贝这个原型来创建新对象。

类图

实现

一. 浅拷贝和深拷贝的概念

Object.clone()方法实现的是对象的浅拷贝, 所谓浅拷贝就是当对象中有复杂引用类型的域变量时, 只拷贝该域变量的引用而不是内容, 当有任一方法修改域变量的状态时会同时影响原型对象及拷贝对象, 实际上他们共用了同一个堆内存. 深拷贝创建的对象即是对原对象的完全拷贝,对任一对象的操作不会影响其他对象的状态.

java中提供了Cloneable接口, 约定实现接口Cloneable且重写Object.clone()方法的类可以用来拷贝自身. Cloneable是一个标记接口, 其中没有定义任何方法.

二. 下面的代码演示了使用clone()方法实现的深拷贝,这种方式更适合用于比较简单的对象,否则clone()方法的实现可能会变得异常复杂.

import lombok.AllArgsConstructor;
import lombok.Data; @Data
@AllArgsConstructor
public class CarProperty implements Cloneable { private String power;
private double maxSpeed;
private double oilPerKm; public Object clone(){
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}

import lombok.AllArgsConstructor;
import lombok.Data; @Data
@AllArgsConstructor
public class Car implements Cloneable { private String brand;
private double price;
private CarProperty carProperty; /**
* 深拷贝在此实现,对于复杂的应用类型, 这里的代码可能会相当复杂,如果类有修改(新增成员变量等),这里也需要相应修改
* @return
*/
public Object clone(){
Object car = null;
try {
car = super.clone();
CarProperty carPropertyClone = (CarProperty)this.getCarProperty().clone();
((Car)car).setCarProperty(carPropertyClone);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return car;
} public static void main(String[] args) { CarProperty carProperty = new CarProperty("8匹",250,30);
Car car= new Car("BMW",200,carProperty); Car copy = (Car) car.clone();
System.out.println("copy最大速度为: "+copy.getCarProperty().getMaxSpeed());
System.out.println("原型最大速度为: "+car.getCarProperty().getMaxSpeed());
car.getCarProperty().setMaxSpeed(360);
System.out.println("copy最大速度为: "+copy.getCarProperty().getMaxSpeed());
System.out.println("原型最大速度为: "+car.getCarProperty().getMaxSpeed());
} }

三. 深拷贝的其他实现方式: 除了上面的方法,还可以使用反射机制创建对象的深拷贝, 另外一种更简单的方式是使用序列化;

下面的代码使用序列化方式实现对象的深拷贝,需实现Serializable接口.

import java.io.*;

public class DeepCloneBase implements Serializable {
public Object deepClone() {
ByteArrayOutputStream byteArrayOutputStream = null;
ObjectOutputStream objectOutputStream = null;
ByteArrayInputStream byteArrayInputStream = null;
ObjectInputStream objectInputStream = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
objectInputStream = new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
byteArrayOutputStream.close();
objectOutputStream.close();
byteArrayInputStream.close();
objectInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
} }
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable; @Data
@AllArgsConstructor
public class MyCar extends DeepCloneBase { private String brand;
private double price;
private CarProperty carProperty; public static void main(String[] args) throws Exception{
// 注意CarProperty也需要实现Serializable接口,代码不再单独列出
CarProperty carProperty = new CarProperty("8匹",250,30);
MyCar car= new MyCar("BMW",200,carProperty); MyCar copy = (MyCar)car.deepClone(); if (copy!=null){
System.out.println("copy最大速度为: "+copy.getCarProperty().getMaxSpeed());
System.out.println("原型最大速度为: "+car.getCarProperty().getMaxSpeed());
car.getCarProperty().setMaxSpeed(360);
System.out.println("copy最大速度为: "+copy.getCarProperty().getMaxSpeed());
System.out.println("原型最大速度为: "+car.getCarProperty().getMaxSpeed());
}else{
System.out.println("对象没拷贝成功....");
}
}
}

总结

优点: 1. 如果对象创建比较复杂, 可以简化创建过程, 提高效率;2. 可以保留对象状态;

缺点: 对于clone()方式,如果类有修改则需要修改clone()的实现,不符合开闭原则; 复杂对象的clone逻辑可能较复杂;

JDK

java.lang.Object#clone()

设计模式五: 原型模式(Prototype)的更多相关文章

  1. 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)

    原文:乐在其中设计模式(C#) - 原型模式(Prototype Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:weba ...

  2. 二十四种设计模式:原型模式(Prototype Pattern)

    原型模式(Prototype Pattern) 介绍用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象.示例有一个Message实体类,现在要克隆它. MessageModel usin ...

  3. [设计模式] 4 原型模式 prototype

    设计模式:可复用面向对象软件的基础>(DP)本文介绍原型模式和模板方法模式的实现.首先介绍原型模式,然后引出模板方法模式. DP书上的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创 ...

  4. 设计模式 笔记 原型模式 prototype

    //---------------------------15/04/07---------------------------- //prototype 原型模式--对象创建型模式 /* 1:意图: ...

  5. python 设计模式之原型模式 Prototype Pattern

    #引入 例子1: 孙悟空拔下一嘬猴毛,轻轻一吹就会变出好多的孙悟空来. 例子2:寄个快递下面是一个邮寄快递的场景:“给我寄个快递.”顾客说.“寄往什么地方?寄给……?”你问.“和上次差不多一样,只是邮 ...

  6. 【UE4 设计模式】原型模式 Prototype Pattern

    概述 描述 使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.如孙悟空猴毛分身.鸣人影之分身.剑光分化.无限剑制 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象, ...

  7. 【设计模式】—— 原型模式Prototype

    前言:[模式总览]——————————by xingoo 模式意图 由于有些时候,需要在运行时指定对象时哪个类的实例,此时用工厂模式就有些力不从心了.通过原型模式就可以通过拷贝函数clone一个原有的 ...

  8. 创建型设计模式之原型模式(Prototype)

    结构   意图 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 适用性 当要实例化的类是在运行时刻指定时,例如,通过动态装载:或者 为了避免创建一个与产品类层次平行的工厂类层次时:或 ...

  9. 设计模式之原型模式(prototype)

    原理:拷贝自身对象实际上就是调用的拷贝构造函数,注意事项是这里的拷贝是深拷贝,即需要拷贝指针所指的内容 #include <stdio.h> #include <memory> ...

随机推荐

  1. Java HttpClient4.5.2发送post请求示例

    public static Map<String, Object> invokeCapp(String URL, Map paramMap) throws Exception { Map ...

  2. 深入研究EF Core AddDbContext 引起的内存泄露的原因

    前两天逛园子,看到 @Jeffcky 发的这篇文章<EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下>. 一开始只是粗略的扫了一遍没仔细看,只是觉得是多次 ...

  3. win10设置操作备忘

    添加密码, 更改密码: Win键-->左侧用户图标-->更改帐户设置-->登陆选项-->添加密码 | 更改密码

  4. 【zabbix教程系列】二、zabbix特点

    一.度量收集 从任何设备,系统,应用上收集指标,收集指标的方法有: 多平台zabbix代理 SNMP and IPMI 代理 无代理监控用户服务 自定义方法 计算和聚合 用户端web监控  二.问题发 ...

  5. webpack加载postcss,以及autoprefixer的loader

    webpack2.0加载postcssloader以及autoprefixer实现自动根据兼容性的需求给css加私有前缀的功能,给开发带来便利, 下面是我的配置信息,亲测有效: 1.webpack.c ...

  6. Java多线程10:join()方法

    一.前言 通过一个简单的例子引入join()方法 public class Thread01 extends Thread{ @Override public void run() { for(int ...

  7. 简单实现计算机上多个jdk环境切换

    实现多个jdk环境切换,大致有两种方式 安装两个jdk,并配置相应的环境变量,在java的控制面板中修改设置 非主要的jdk仅仅是用来测试,并不常用,故只要让ide配置对应的jdk位置就可以了,属于懒 ...

  8. java extends和implements区别

    一.作用说明 extends 是继承某个类, 继承之后可以使用父类的方法, 也可以重写父类的方法; implements 是实现多个接口, 接口的方法一般为空的, 必须重写才能使用 二.补充 JAVA ...

  9. 【nginx】nginx的工作模式和信号量控制

    nginx是一个多进程/多线程高性能web服务器,在linux系统中,nginx启动后会以后台守护进程(daemon)的方式去运行,后台进程包含一个master进程和多个worker进程(这个数量可以 ...

  10. django restframework 跨域访问

    场景介绍: 在Django开发过程中,使用前后端分离设计的站点越来越多,如Django+VUE.Django+Angular.在使用DjangoRestFramework开发API的过程中,由于前端站 ...