写点什么

【设计模式】原型模式:猴头,我叫你一声你敢答应吗?

作者:游坦之
  • 2022-11-14
    山东
  • 本文字数:3529 字

    阅读完需:约 12 分钟

【设计模式】原型模式:猴头,我叫你一声你敢答应吗?

1 原型模式

1.1 概述

原型模式是一种对象创建型模式,用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。原型模式允许一个对象再创建另外一个可定制的对象,无须知道任何创建的细节。

原型模式的基本工作原理是通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象复制原型来实现创建过程。

1.2 结构

原型模式包含三个角色:


  • 抽象原型类:规定了具体原型对象必须实现的 clone()方法。

  • 具体原型类:实现抽象原型类的 clone 方法,它是可被复制的对象。

  • 访问类:使用具体原型类中的 clone()方法来复制新的对象。

1.3 用例图

2 例子

2.1 浅克隆

2.1.1 解释

浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本数据类型属性,仍指向原有属性所指向的对象的内存地址。


Java 的基本数据类型有 8 种,分别是:byte(位)、short(短整数)、int(整数)、long(长整数)、float(单精度)、double(双精度)、char(字符)和 boolean(布尔值),那么除此之外的数据类型就是非基本数据类型。


这段话的意思是,如果你克隆的对象里面不含有基本数据类型,那么你克隆的对象 2 和原来的对象指向同一个地址,一个对象里面的值会随着另一个对象的改变而改变。


这很容易让我们想到西游记里的一个例子:


"猴头,我叫你一声你敢答应吗?"



银角大王和孙悟空酣战,孙悟空不敌,于是又幻化成者行孙、行者孙前来挑战,不管是者行孙、孙行者、还是行者孙都是孙悟空被尊,所以每一个角色被抓,都意味着真正的孙悟空被抓住了。这和浅克隆非常的类似。

2.1.2 代码

在这个例子里,抽象原型类是就是 Cloneable 接口,具体原型类就是 Monkey 类(孙悟空类),至于访问类就是 Qazxcdew 类(银角大王类)


/* 孙悟空 */public class Monkey implements Cloneable{    /* 猴子的姓名 */    private  String monkeyName;    /* 猴子的情况 */    private Status status = new Status();
public String getMonkeyName() { return monkeyName; }
public void setMonkeyName(String monkeyName) { this.monkeyName = monkeyName; }
public Status getStatus() { return status; }
public void setStatus(Status status) { this.status = status; }
/* 我叫你一声你敢答应吗? */ public void call() { System.out.println(monkeyName+",我叫你一声你敢答应吗?"); } /* 回应 */ public void respond() { System.out.println("爷爷在此~"+status.getAction()); }
@Override protected Monkey clone() throws CloneNotSupportedException { return (Monkey)super.clone(); }}
复制代码


状态类


public class Status {     String action;
public String getAction() { return action; }
public void setAction(String action) { this.action = action; }
@Override public String toString() { return "Status{" + "action='" + action + '\'' + '}'; }}
复制代码


银角大王类


public class Qazxcdew {    public static void main(String[] args) throws CloneNotSupportedException {        /* 1、创建原型对象:孙行者 */        /* 猴子本尊*/        Monkey monkey = new Monkey();        monkey.setMonkeyName("孙行者");        monkey.getStatus().setAction("孙悟空跑掉了");        /* 2、猴子克隆对象 */        /* 者姓孙 */        Monkey monkey1 = monkey.clone();        /* 行者孙 */        Monkey monkey2 = monkey.clone();
monkey1.setMonkeyName("者姓孙"); monkey2.setMonkeyName("行者孙");
monkey1.getStatus().setAction("孙悟空打赢了"); monkey2.getStatus().setAction("孙悟空被抓了");
/* 3、展示对象 */ monkey.call(); monkey.respond(); monkey1.call(); monkey1.respond(); monkey2.call(); monkey2.respond(); System.out.println((monkey==monkey1)+":"+(monkey==monkey2));
}}
复制代码

2.1.3 效果图


为什么先是孙行者逃走了、再是者姓孙打赢了、最后行者孙被抓了,最终的结果却变成了孙行者、者行孙、行者孙都被抓了呢?

这和浅克隆就非常的相似,浅克隆是指的克隆的非基本类型和原型共用一个值,一个改变其他的也都改变。

而行者孙、者行孙、孙行者都是孙悟空一个猴,所以行者孙最后被抓了,也就意味着其他的三个都被抓了。

2.2 深克隆

2.2.1 解释

深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不在指向原有对象地址。


这句话的意思是:深克隆的对象,除了值和原来的一样,其他没有任何关系。


这也可以让我们想到《西游记》里的一个例子:孙悟空吹猴毛


2.2.2 代码

银角大王类


public class Qazxcdew {    public static void main(String[] args) throws CloneNotSupportedException {        /* 1、创建原型对象:孙行者 */        /* 猴子本尊*/        Monkey monkey = new Monkey();        monkey.setMonkeyName("孙行者");        monkey.getStatus().setAction("孙悟空跑掉了");        /* 2、猴子克隆对象 */        /* 者姓孙 */        Monkey monkey1 = monkey.clone();        /* 行者孙 */        Monkey monkey2 = monkey.clone();
monkey1.setMonkeyName("孙悟空一号"); monkey2.setMonkeyName("孙悟空二号");
monkey1.getStatus().setAction(monkey1.getMonkeyName()+"打赢了"); monkey2.getStatus().setAction(monkey2.getMonkeyName()+"被抓了");

/* 3、展示对象 */ monkey.call(); monkey.respond(); monkey1.call(); monkey1.respond(); monkey2.call(); monkey2.respond(); System.out.println((monkey==monkey1)+":"+(monkey==monkey2));
}}
复制代码


孙悟空类


/* 孙悟空 */public class Monkey implements Cloneable{    /* 猴子的姓名 */    private  String monkeyName;    /* 猴子的情况 */    private Status status = new Status();
public String getMonkeyName() { return monkeyName; }
public void setMonkeyName(String monkeyName) { this.monkeyName = monkeyName; }
public Status getStatus() { return status; }
public void setStatus(Status status) { this.status = status; }
/* 我叫你一声你敢答应吗? */ public void call() { System.out.println(monkeyName+",我叫你一声你敢答应吗?"); } /* 回应 */ public void respond() { System.out.println("爷爷在此~"+status.getAction()); }
@Override protected Monkey clone() throws CloneNotSupportedException {
Object object = super.clone(); Monkey m = (Monkey)object; /* 将属性也克隆一遍 */ m.status = (Status) this.status.clone();

return m; }}
复制代码


状态类


public class Status implements Cloneable{     String action;
public String getAction() { return action; }
public void setAction(String action) { this.action = action; }
@Override public String toString() { return "Status{" + "action='" + action + '\'' + '}'; }
@Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }}
复制代码

2.2.3 效果图


猴毛脱离猴子身上之后,就变成了一个独立的个体,虽然幻化出来的猴子和孙悟空有着相同的特点、属性,但是猴毛怎么样,和孙悟空是无关的。

2.2.4 深克隆的方法

  1. 序列化、反序列化

  2. 克隆的时候,连同属性一块克隆(本例使用,方便实现)

3 优缺点

3.1 优点

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

  • 可以动态增加或减少产品类;

  • 提供了简化的创建结构;

  • 可以用深克隆的方式保存对象的状态

3.2 缺点

  • 需要为每一个类配备一个克隆方法,而且这个克隆方法需要对类的功能进行全盘考虑。

  • 改造已有的类变得复杂,而且需要改变其源代码,违法了开闭原则。怎么样,和孙悟空是无关的。

发布于: 刚刚阅读数: 4
用户头像

游坦之

关注

还未添加个人签名 2022-10-14 加入

还未添加个人简介

评论

发布
暂无评论
【设计模式】原型模式:猴头,我叫你一声你敢答应吗?_11月月更_游坦之_InfoQ写作社区