`

二十八:原始模型模式

阅读更多
孙悟空在与黄风怪的战斗中,使用了一个身外身的手段:把毫毛揪下一把,用口嚼和粉碎,往一喷叫声'变',变有百十个行者,都是一样的打扮!..老孙的这种身外身的手段在面向对象的设计领域里叫做原始模式.

一:克隆满足的条件.
A:对任何的对象x,都有:x.clone()!=x,换言之,克隆对象与原对象不是同一个对象.
B:对任何的对象x,都有:x.clone().getClass() == x.getClass(),换言之,克隆对象与原始对象的类型一样C:如果对象x的equals()方法定义恰当的话,那么x.clone.equals(x)应当是成立的.
在Java语言的API中,凡是提供了clone()方法的类,都满足上面的这些条件,Java语言的设计师在设计自己的clone方法时也应当遵守这三个条件。一般来说,上面的三个条件的前两个是必需的,而第三个是可选的,但建议遵守上面三个条件.
疑问:clone是产生了一个新的对象,那直接用new关键字产生好了(但这时候的对象里的数据都是原始数据而已),clone有何重要作用.

附:equals()方法的讨论
上面所给出的三个条件中,第三个条件常常被忽视,很多读者误认为可以通过继承得到java.object.Object对象的equals()方法就足够了,而这是不对的,先看下java.lang.Object的源码:
public boolean equals(Object obj){ 
return (this==obj); 
} 


也就是说,当两个变量指向同一个对象时,equals方法才会返回true,很显然,这并不适合所有需要被克隆的对象,假设被克隆的对象按照它们的内部状态是否可变,划分为可变对象和不变对象(String就是不变对象,详情看不变模式一章)的话,那么可变对象和不变对象所提供的equals方法的工作方式应当是不同的.可变对象只有当它们是同一个对象时,equals才会返回true,所以这样的类型可以直接从java.lang.Object继承这个方法,不变对象必须含有相同的状态才可能满足这个条件,因此不变类型必须自行实现这个equals方法.一个典型的例子就是java的String对象,它是一个不变对象,而不变对象具有不会改变的内部状态,具有相同内部状态的String对象对于客户端就没有区别.这样一来,两个String对象的比较就应当是它们的内部状态的比较。

二:原始模型模式的结构
原始模型模式有两种表现形式,第一种是简单形式,第二种是登记形式.
A:简单形式的原始模型模式:这种形式涉及到三种角色:
(1)客户端(Client)角色:客户类提出创建对象的请求
(2)抽象原型(Prototype)角色:这是一个抽象角色,通常由一个java接口或java抽象类实现,此角色给出所有的具体原型所需的接口
(3)具体原型(Concrete Prototype)角色:被复制的对象,此角色需要实现抽象的原型角色里要求的接口.
下面是示意源码:
package cai.milenfan.basic.test; 

public class Client { 
private Prototype prototype; 
public void operation(Prototype example){ 
Prototype p = (Prototype)example.clone(); 
} 
} 



package cai.milenfan.basic.test; 

public interface Prototype extends Cloneable{ 
Prototype clone(); 
} 



package cai.milenfan.basic.test; 

public class ConcretePrototype implements Prototype{ 
public Prototype clone(){ 
try { 
return (Prototype)super.clone(); 
} catch (CloneNotSupportedException e) { 
return null; 
} 
} 
} 


B:登记形式的原始模型模式
它有如下角色:
(1)客户端(Client)角色:客户端类向管理员提出创建对象的请求
(2)抽象原型(Prototype)角色:这是一个抽象角色,通常由一个接口或抽象类实现,此角色给出所有的具体原型类所需要的接口.
(3)具体原型(Concrete Prototype)角色:被复制的对象,需要实现抽象的原型角色所要求的接口
(4)原型管理器(Prototype Manager)角色:创建具体原型类的对象,并记录每一个被创建的对象.
下面给出源码:
package cai.milenfan.basic.test; 
public interface Prototype extends Cloneable{ 
public Object clone(); 
} 


package cai.milenfan.basic.test; 
public class ConcretePrototype implements Prototype{ 
public synchronized Object clone(){ 
Prototype temp = null; 
try { 
temp = (Prototype)super.clone(); 
return temp; 
} catch (CloneNotSupportedException e) { 
System.out.println("clone failed......................"); 
}finally{ 
return temp; 
} 
} 
} 


package cai.milenfan.basic.test; 
import java.util.Vector; 
public class PrototypeManager { 
private Vector objects = new Vector(); 
public void add(Prototype object){ 
objects.add(object); 
} 
public Prototype get(int i){ 
return (Prototype)objects.get(i); 
} 
public int getSize(){ 
return objects.size(); 
} 
} 



package cai.milenfan.basic.test; 
public class Client { 
private PrototypeManager mgr; 
private Prototype prototype; 
public void registerPrototype(){ 
prototype = new ConcretePrototype(); 
Prototype copytype = (Prototype)prototype.clone(); 
mgr.add(copytype); 
} 
} 


两种形式的比较:
如果需要创建的原型对象数目较少而且比较固定的话,可以采取第一种形式,这种情况下原型对象由客户端自己保存.如果要创建的原型对象数目不固定的话可以采取第二种形式,这种情况下,客户端并不保存原型对象的引用,这个任务被交给管理员对象,在复制一个原型对象之前,客户端可以查看管理员对象是否已经有一个满足要求的原型对象,如果有,可以直接从管理员类取得这个对象的引用,如果没有,客户端就需要自行复制此原型对象.

三:模式的实现:深复制和浅复制
复制或克隆有两种方式,这两种方式分别叫做浅复制(浅克隆)和深复制(深克隆)
(1)浅复制(浅克隆)
被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象,换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象
(2)深复制(深克隆)
被复制的对象所有的变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新的对象,而不是原有的那些被引用的对象,换言之,深复制把复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。深复制要深入到多少层,是一个不易确定的问题,在决定以深复制的方式复制一个对象的时候,必须决定对间接复制的对象是采取浅复制还是继续采用深复制,因此,在采取深复制时,需要决定多深才算深,此外,在深复制过程中,很可能会出现循环引用的问题,必须小心处理.

四:利用串行化来做深复制
把对象写到流里的过程是串行化(Serilization)过程,也被称为"冷冻"过程,而把对象从流中读出来的并行化过程则叫做"解冻"过程,应当指出的是,写到流里的是对象的一个拷贝,而原对象仍然存在于jvm里面.
在java语言里面深复制一个对象,常常可以先使其实现Serializable接口,然后把对象(实际只是对象的一个拷贝)写到一个流里,然后再从流里读出来,代码如下:
package cai.milenfan.basic.test; 

import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 

public class MyDeepClone { 
public Object deepClone() throws IOException, ClassNotFoundException{ 
//将对象写对流里
ByteArrayOutputStream bo = new ByteArrayOutputStream(); 
ObjectOutputStream oo = new ObjectOutputStream(bo); 
oo.writeObject(this); 
//从流里读出来
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); 
ObjectInputStream oi = new ObjectInputStream(bi); 
return(oi.readObject()); 
} 
} 


以上做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则就需要仔细考察那些不可串行化的对象可否transient,从而将之排除在复制过程之外.
浅复制显然比深复制更容易实现,因为java语言里的所有类都继承了clone方法,而这个clone方法所做的正是浅复制.有一些对象比如线程(thread)对象或Socket对象,是不能简单复制或共享的,不管使用深复制还是浅复制,只要涉及这样的间接对象,就必须把间接对象设成transient而不给复制.

五:孙悟空的变身术
TheGreatestSage类扮演客户角色,Monkey是大圣,Monkey用Object的clone方法进行浅复制的代码如下:
package cai.milenfan.basic.test; 

public class GoldRingedStaff { 
private float height = 100.0f; 
private float diameter = 10.0f;//半径

public void grow(){ 
this.height *= 2; 
this.diameter *= 2; 
} 
public void shrink(){ 
this.height /= 2; 
this.diameter /= 2; 
} 
public void move(){} 
public float getHeight() { 
return height; 
} 
public void setHeight(float height) { 
this.height = height; 
} 
public float getDiameter() { 
return diameter; 
} 
public void setDiameter(float diameter) { 
this.diameter = diameter; 
} 
} 

package cai.milenfan.basic.test; 

import java.util.Date; 

public class Monkey implements Cloneable{ 
private int height; 
private int weight; 
private GoldRingedStaff staff; 
private Date birthDate; 

public Monkey(){ 
this.birthDate = new Date(); 
} 

public Object clone(){ 
Monkey temp = null; 
try { 
temp = (Monkey)super.clone(); 
return temp; 
} catch (CloneNotSupportedException e) { 
System.out.println("clone failed......................."); 
}finally{ 
return temp; 
} 
} 

public int getHeight() { 
return height; 
} 
public void setHeight(int height) { 
this.height = height; 
} 
public int getWeight() { 
return weight; 
} 
public void setWeight(int weight) { 
this.weight = weight; 
} 
public GoldRingedStaff getStaff() { 
return staff; 
} 
public void setStaff(GoldRingedStaff staff) { 
this.staff = staff; 
} 
public Date getBirthDate() { 
return birthDate; 
} 
public void setBirthDate(Date birthDate) { 
this.birthDate = birthDate; 
} 
} 

package cai.milenfan.basic.test; 

public class TheGreatestSage { 
private Monkey monkey = new Monkey(); 
public void change(){ 
Monkey copyMonkey; 
//空循环一会
for(int i=0;i<2000;i ){} 
//变身
copyMonkey = (Monkey)monkey.clone(); 
System.out.println("Monkey King's birthdate = " monkey.getBirthDate()); 
System.out.println("Copy monkey's birthdate = " copyMonkey.getBirthDate()); 
System.out.println("Monkey King == Copy Monkey?" (monkey==copyMonkey)); 
System.out.println("Monkey King's Staff==Copy Monkey's Staff?" (monkey.getStaff()==copyMonkey.getStaff())); 
} 
public static void main(String[] args){ 
TheGreatestSage sage = new TheGreatestSage(); 
sage.change(); 
} 
} 

输出结果: 
Monkey King's birthdate = Fri Aug 14 12:55:50 CST 2009 
Copy monkey's birthdate = Fri Aug 14 12:55:50 CST 2009 
Monkey King == Copy Monkey?false 
Monkey King's Staff==Copy Monkey's Staff?true 


可以看出,首先,复制的大圣本尊具有和原始的大圣本尊对象一样的birthDate,而本尊对象并不相等,这表示他们二者是克隆关系,其次,复制的大圣本尊所持有的金刚棒和原始的大圣本尊所持有的金刚棒是一样的,而不是两根...这和电视剧里的并不太一样,要纠正这点,就需要用深复制。

深复制的源码如下:
package cai.milenfan.basic.test; 

import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.io.Serializable; 
import java.util.Date; 

public class Monkey implements Cloneable,Serializable{ 
private int height; 
private int weight; 
private GoldRingedStaff staff; 
private Date birthDate; 

public Monkey(){ 
this.birthDate = new Date(); 
this.staff = new GoldRingedStaff(); 
} 

public Object deepClone() throws IOException, ClassNotFoundException{ 
//将对象写对流里
ByteArrayOutputStream bo = new ByteArrayOutputStream(); 
ObjectOutputStream oo = new ObjectOutputStream(bo); 
oo.writeObject(this); 
//从流里读出来
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); 
ObjectInputStream oi = new ObjectInputStream(bi); 
return(oi.readObject()); 
} 

public Object clone(){ 
Monkey temp = null; 
try { 
temp = (Monkey)super.clone(); 
return temp; 
} catch (CloneNotSupportedException e) { 
System.out.println("clone failed......................."); 
}finally{ 
return temp; 
} 
} 

public int getHeight() { 
return height; 
} 
public void setHeight(int height) { 
this.height = height; 
} 
public int getWeight() { 
return weight; 
} 
public void setWeight(int weight) { 
this.weight = weight; 
} 
public GoldRingedStaff getStaff() { 
return staff; 
} 
public void setStaff(GoldRingedStaff staff) { 
this.staff = staff; 
} 
public Date getBirthDate() { 
return birthDate; 
} 
public void setBirthDate(Date birthDate) { 
this.birthDate = birthDate; 
} 
} 



package cai.milenfan.basic.test; 

import java.io.Serializable; 

public class GoldRingedStaff implements Cloneable,Serializable{ 
private float height = 100.0f; 
private float diameter = 10.0f;//半径

public void grow(){ 
this.height *= 2; 
this.diameter *= 2; 
} 
public void shrink(){ 
this.height /= 2; 
this.diameter /= 2; 
} 
public void move(){} 
public float getHeight() { 
return height; 
} 
public void setHeight(float height) { 
this.height = height; 
} 
public float getDiameter() { 
return diameter; 
} 
public void setDiameter(float diameter) { 
this.diameter = diameter; 
} 
} 


package cai.milenfan.basic.test; 

import java.io.IOException; 

public class TheGreatestSage { 
private Monkey monkey = new Monkey(); 
public void change() throws IOException, ClassNotFoundException{ 
Monkey copyMonkey; 
//空循环一会
for(int i=0;i<2000;i ){} 
//变身
copyMonkey = (Monkey)monkey.deepClone(); 
System.out.println("Monkey King's birthdate = " monkey.getBirthDate()); 
System.out.println("Copy monkey's birthdate = " copyMonkey.getBirthDate()); 
System.out.println("Monkey King == Copy Monkey?" (monkey==copyMonkey)); 
System.out.println("Monkey King's Staff==Copy Monkey's Staff?" (monkey.getStaff()==copyMonkey.getStaff())); 
} 
public static void main(String[] args) throws IOException, ClassNotFoundException{ 
TheGreatestSage sage = new TheGreatestSage(); 
sage.change(); 
} 
} 

输出结果: 
Monkey King's birthdate = Fri Aug 14 14:13:50 CST 2009 
Copy monkey's birthdate = Fri Aug 14 14:13:50 CST 2009 
Monkey King == Copy Monkey?false 
Monkey King's Staff==Copy Monkey's Staff?false 


从运行的结果看出,大圣的金刚棒和他本身都是不同的对象,这是因为使用了深复制,从而把大圣本尊所引用的对象也都复制了一遍.

六:在什么情况下使用原始模型
假设一个系统的产品类是动态加载的,而且产品类具有一定的等级结构,这个时候如果采取工厂模式的话,工厂类就不得不具有一个相应的等级结构,而产品类的等级结构一旦变化,工厂类的等级结构就不得不有一个相应的变化,这对于产品结构可能会有经常性的变化的系统来说,采用工厂模式就有不方便之处,这时如果采取原始模型模式,给每一个产品类配备一个克隆的方法(大多数的时候只需要给产品类等级结构的根类配备一个克隆的方法),便可以避免使用工厂模式带来的具有固定等级结构的工厂类。这样,一个使用了原始模型模式的系统与它的产品对象是怎么创建出来的,以及这些产品对象之间的结构是怎么样的,以及这个结构会不会发生变化是没有关系的。
????????-->不明白

七:原始模型模式的优点和缺点
抽象工厂模式有许多与原始模型模式和建造模式相同的效果,包括客户端不知道具体的产品类,而只知道抽象产品类,客户端不需要知道这么多的具体产品名称,如果有新的产品类加入,客户端不需要进行改造就可以直接使用.
原始模型模式有其特有的优点:
(1)原始模型模式允许动态地增加或减少产品类,由于创建产品类实例的方法是产品类内部具有的,因此,增加新产品对整个结构没有影响。
(2)原始模型模式提供简化的创建结构,工厂方法模式常常需要一个与产品类等级结构相同的等级结构,而原始模型模式就不需要这样.???
(3)具有给一个应用软件动态加载新功能的能力.
(4)产品类不需要非得有任何事先确定的等级结构,因为原始模型模式适用于任何等级结构.
原始模型模式的缺点主要是每一个类都必须配备有一个克隆的方法,配备克隆的方法需要对类的功能进行通盘的考虑,这么于全新的类来说不是很难,而对于已经有的类不一定很容易,特别是当一个类引用不支持串行化的间接对象或者引用含有循环结构的时候.
分享到:
评论

相关推荐

    java与模式

    1:模式的简史和形而上学;2:统一建模语言UML简介;...20:原始模型模式;21:javabean冷藏和解冻;22:适配器模式;23:缺省适配器模式;24:合成模式;25:装饰模式;26:设计模式在JAVA I/O设计原则;28:代理模式;....

    源码:阎宏设计模式光盘

    com.javapatterns.prototype 原始模型模式 com.javapatterns.proxy 代理模式 com.javapatterns.proxy.smartproxy 专题:智能引用代理 com.javapatterns.proxy.imageloader 专题:虚拟代理的例子 ...

    BEG模型原始论文:C-Pack: Packaged Resources To Advance General Chinese

    BGE(北京智源人工智能研究院提出的embedding模型)可能是指北京智源人工智能研究院开发的一种新型的嵌入(embedding)技术或模型。在自然语言处理(NLP)中,嵌入技术是用来将词汇、句子或文档转换为高维空间中的...

    通货膨胀超出T模型和原始B模式

    我们描述了扩展的理论,它们具有T模型的尺度变换对称性,并且以T模型以及Starobinsky模型为特例。 我们推导了两个慢滚参数之间的一般关系,发现可以嵌入一大类模型。 这样的模型包括更一般的Starobinsky式通货膨胀,...

    java和设计模式ppt教程

    java和设计模式ppt包含工厂模式、建造模式、原始模型模式、单例模式、结构模式、适配器、桥梁模式、合成模式、装饰模式、门面模式、享元模式、代理模式、行为模式、解释器模式、迭代子模式、调停者模式、备忘录模式...

    C#23种设计模式_示例源代码及PDF

    原始模型模式: 原始模型模式 通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原 型对象的方法创建出更多同类型的对象。 原始模型模式允许动态的增加或减少产品类, 产品 类不需要非得有任何事先...

    XLM:PyTorch跨语言模型预训练的原始实现

    PyTorch训练的原始实现。 包括: XLM支持多GPU和多节点训练,并包含以下代码: 语言模型预训练: 因果语言模型(CLM) 屏蔽语言模型(MLM) 翻译语言模型(TLM) 胶微调 XNLI微调 有监督/无监督的机器翻译...

    ts-schema:从TypeScript中提取类型,并可用于创建json模式

    原始模型,它是可序列化的格式(考虑使用从TypeScript包或项目中生成此格式)。 对类型的引用存储为{moduleName:string,name:string}对象。 完全解析的模型,其中所有引用都替换为该类型的实际实例。 这是最...

    Diffusion Models: 生成扩散模型数学推导

    对于扩散过程,在满足马尔科夫链的条件下,我们应用了重参数技巧证明了基于原始数据我们 可以 对任意步进行采样 ,并且可以使用数学表达式 进行 表示;对于逆扩散过程, 我们应用了贝叶斯公式和高斯分布的性质进行...

    Stable Diffusion 商业变现与绘画大模型多场景实战(附课件+软件包)

    Stable Diffusion 是 Diffusion 扩散模型中最先进的模式( Diffusion 有一些早期版本,比如: 原始Diffusion、Latent Diffusion)。它采用了更加稳定、可控和高效的方法来生成高质量图像。在生成图像的质量、速度和...

    java的23种设计模式

    1、工厂模式 2、建造模式 3、工厂方法模式 4、原始模型模式 5、单例模式 6、适配器(变压器)模式 7、桥梁模式

    23种设计模式.txt

     原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,...

    Java与模式.清晰.rar

    Java与模式.清晰.rar 第一部分 第1章 模式的简史和形而上学 第2章 统一建模语言UML简介 ...第20章 原始模型(Prototype)模式 第21章 专题:JavaBean的“冷藏”和“解冻” 第四部分 第22章 适配器(Adapter)模式[1]

    JAVA与模式

    <br>第一部份、UML和模式介绍 com.javapatterns.uml 统一建模语言UML... com.javapatterns.prototype 原始模型模式 com.javapatterns.serializable 专题:JavaBean的“冷藏”和“解冻”

    Axion夸克金块暗物质模型:尺寸分布和生存模式

    我们认为,该模型提供了广泛的参数空间,在其中AQN可以解释所观察到的暗物质质量密度,自然地解释了“暗”和“可见”分量之间的相似性,即Ω暗〜Ω可见,并且还提供了 解释许多其他长期存在的难题,例如“原始锂...

    人工智能:模型与算法——练习题.pdf

    ⼈⼯智能:模型与算法——练习题 第⼀周 第⼀周 ⼈⼯智能概述 ⼈⼯智能概述 1如果⼀个问题或者任务不可计算,那么对这个问题或任务的描述哪⼀句是正确的( ) A.该问题或任务所需计算时间是⾮线性增加的 B.⽆法将该...

    Java与设计模式,大话设计

    工厂模式 工厂方法模式 建造模式 原始模型模式 单例模式 設計模式之Adapter -适配器

    浅析java常用的设计模式(doc-23页).doc

    4、原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复 制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少 产品类,产品类不需要非得有任何事先确定的等级结构...

    Matlab官方给出的机器人CAD导出模型xml可直接用

    该压缩包包含XML多实体描述文件和一组STEP零件几何文件示例,您可以在默认导入模式下使用Simscape Multibody sm...该框图使用Simscape Multibody块重新创建原始CAD装配模型。数据文件提供模型中使用的块参数的数值。

Global site tag (gtag.js) - Google Analytics