`

二十八:原始模型模式

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

一:克隆满足的条件.
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:代理模式;....

    合纵连横:大模型的模型融合技术全解析

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    风驰电掣:大模型硬件加速技术全解析

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    驾驭巨龙:大模型的监控与维护策略

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    揭开黑箱:大模型的解释性方法全攻略

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    贝叶斯的光辉:大模型中的概率推理艺术

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    多面手的挑战:大模型的多任务评估策略

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    探索未知的艺术:大模型的半监督学习实现

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    分布式训练的艺术:大模型的高效学习之旅

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    跨界巨头:大模型在多领域知识应用的革命

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    大数据的舞蹈:大模型数据加载与预处理的最佳实践

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    正义的算法:大模型在法律领域的革命性应用

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    声音的解码者:大模型在语音识别领域的革命性应用

    与参数较少、层数较浅的小模型相比,大模型虽然需要更多的计算资源和时间来训练和推理,但它们展现出了一种被称为“涌现能力”的特性,即从原始训练数据中自动学习并发现新的、更高层次的特征和模式。 大模型的发展...

    Advanced Concepts_代理模型_

    1. **代理的类型**:代理模型通常分为多种类型,包括虚拟代理(用于延迟加载或缓存)、远程代理(处理网络通信)、保护代理(控制对原始对象的访问权限)和智能引用代理(提供额外的元数据或操作)等。每种类型的...

    浅析23种软件设计模式

    4. 原始模型模式:原始模型模式的主要思想是通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。这种模式的优点是允许动态的增加或减少产品类,产品类不需要...

    关于-23种设计模式的有趣见解.doc

    本文将深入解析五个常见的设计模式,分别是工厂模式、建造者模式、工厂方法模式、原始模型模式和单例模式。 1. **工厂模式**: 工厂模式是一种简单的创建型模式,它定义了一个创建对象的接口,但由子类决定要实例化...

    数据挖掘:概念、模型、方法和算法

    4. 序列模式:挖掘时间序列数据中的模式,如用户行为序列。例如,Markov模型和LSTM神经网络可用于此类问题。 5. 异常检测:识别数据中的离群值或异常事件,如信用卡欺诈检测。常用方法有基于统计的方法、基于聚类的...

    Java的23种设计模式.pdf

    4. 原始模型模式(Prototype模式):原始模型模式允许通过复制原型对象来创建新对象,提供了动态地增加或减少产品类的能力,且无需关心产品的具体实现细节。 5. 单例模式:单例模式保证一个类只有一个实例,并提供...

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

    ### BEG模型原始论文:C-Pack: Packaged Resources To Advance General Chinese Embedding #### 概述 本论文介绍了一个名为C-Pack的资源包,旨在推动通用中文嵌入领域的进展。C-Pack包含三个关键组成部分:一个...

    RFM模型分析及原模型

    在提供的"RFM模型(原始数据聚类).xml"文件中,很可能包含了原始的RFM数据,通过导入到SPSS中,我们可以进行数据的清洗、转换、聚类分析等一系列操作,最终得出有价值的客户群体划分。 通过RFM模型分析,企业不仅...

Global site tag (gtag.js) - Google Analytics