`
java苹果+番茄
  • 浏览: 67994 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

原始模型(Prototype【创建模式第七篇】

阅读更多
原始模型(Prototype):
属于对象的创建模式。通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。

java.lang.Object.clone()方法,所有javaBean都必须实现Cloneable接口,才具有clone功能;

Java语言提供的Cloneable接口只起一个作用,就是在运行时期通知java虚拟机可以安全地在这个类上使用clone()方法。通过调用
这个clone()方法可以得到一个对象的复制。由于Object类本身并实现Cloneable接口,因此如果所考虑的类没有实现Cloneable接
口时,调用clone()方法会抛出CloneNotSupportedException异常。
//PandaToClone类
	public class PandaToClone implements Cloneable{
		private int height, weight, age;
		
		public PandaToClone(int height, int weight){
			this.age = 0;
			this.weight = weight;
			this.height = height;
		}
		
		public void setAge(int age){
			this.age = age;	
		}
		
		public int getAge(){
			return age;
		}
		//其他重复
		
		//克隆方法
		public Object clone(){
			//创建一个本类对象并返回给调用者
			PandaToClone temp = new PandaToClone(height,weight);
			temp.setAge(age);
			return (Object)temp;
		}
	}
	
	//客户端
	public class Client{
		private static PandaToClone thisPanda, thatPanda;
		
		public static void main(String args[]){
			thisPanda = new PandaToClone(15,25);
			thisPanda.setAge(3);
			
			thatPanda = (PandaToClone)thisPanda.clone();
			
			System.out.println("Age of this panda :" + thisPanda.getAge());
			System.out.println("Height : " + thisPanda.getHeight());
			System.out.println("Weigth : " + this.Panda.getWeight());
			System.out.println("Age of that panda :" + thatPanda.getAge());
			System.out.println("Height : " + thatPanda.getHeight());
			System.out.println("Weigth : " + that.Panda.getWeight());
		}
	}


二、克隆满足的条件:
1、clone()方法将对象复制了一份并返还给调用者。一般而言,克隆方法满足以下条件:
1):对任何的对象x,都有:x.clone() != x。换言之,克隆对象与原始对象不是同一个对象。
2):对任何的对象x,都有:x.clone().getClass() == x.getClass(),换言之,克隆对象与原始对象的类型一样。
3):如果对象x的equals()方法定义恰当的话,那么x.clone().equals(x)应当是成立的。

三、原始模型模式的结构;
1、简单形式的原始模型模式
1):这种形式涉及到三个角色:
。 客户(Client)角色:客户类提出创建对象的请求。
。 抽象原始(Prototype)角色:这是一个抽象角色,通常由一个java接口或java抽象类实现。此角色给出
   所有的具体原始类所需要的接口。
。 具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
//客户端
			public class Client{
				private Prototype prototype;
				
				public void operation(Prototype example){
					Prototype p = (Prototype)example.clone();
				}
			}
			
			//抽象原型角色
			public interface Prototype implements Cloneable{
				Prototype clone();
			}
			
			//具体原型角色
			public class ConcretePrototype implements Prototype{
				//克隆方法
				public Object clone(){
					try{
						return super.clone();
					}catch(CloneNotSupportedException e){
						return null;
					}
				}
			}
			
		2、登记形式的原始模型模式
			1):有如下角色:
				。 客户端(Client)角色:客户端类向管理员提出创建对象的请求。
				。 抽象原型(Prototype)角色:这是一个抽象角色,通常由一个接口或抽象类实现。
				   此角色给出所有的具体原型类所需要的接口。
				。 具体原型(Concrete Prototype)角色:被复制的对象。需要实现抽象原型角色所要求的接口。
				。 原型管理器(Prototype Manager)角色:创建具体原型类的对象,并记录每一个被创建的对象。
			
			//抽象原型角色
			public interface Prototype inplements Cloneable{
				public Object clone();
			}
			
			//具体原型角色
			public class ConcretePrototype implements Prototype{
				//克隆方法
				public synchronizer Object clone(){
					Prototype temp = null;
					try{
						temp  = (Prototype)super.clone();
						return (Object)temp;
					}catch(CloneNotSupportedException e){
						System.out.println("Clone failed.");
					}finally{
						return (Object)temp;
					}
				}
			}
			
			//原型管理器角色保持一个聚集,作为多所有原型对象的登记,这个惧色提供必要的方法,
			  供外界增加新的原型对象和取得已经登记过的原型对象。
			//原型管理器类
			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();
				}
			}
			
		//客户端
		public class Client{
			private PrototypeManager mgr;
			private Prototype prototype;
			public void registerPrototype(){
				prototype = new ConcretePrototype();
				Prototype copytype = (Prototype)prototype.clone();
				mgr.add(copytype);
			}
		}


3、两种形式的比较
简单形式和登记形式的原始模型模式各有其长处和短处。
1)如果需要创建的原型对象数目较少而且比较固定的话,可以采取第一种形式,也即简单形式的原始模型模式。
   在这种情况下,原型对象的引用可以由客户端自己保存。
2)如果要创建的原型对象数目不固定的话,可以采取第二种形式,也即登记形式的原始模型模式。
   在这种情况下,客户端并不保存对原型对象的引用,这个任务被交给管理员对象。在复制一个原型对象之前,客户端
   可以查看管理员对象是否已经有一个满足要求的原型对象。如果有,可以直接从管理员类取得这个对象引用;如果没有,
   客户端就需要自行复制此原型对象。
  
4、模式的实现
1):浅复制(浅克隆)
被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象,换言之,
浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。

2):深复制(深克隆)
被复制对象的所有的变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将
指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了
一遍,而这种对被引用到的对象的复制叫做间接复制。

3):利用串行化来作深复制
把对象写到流里的过程是串行化(Serilization)过程,写到流里的是对象的一个拷贝,而原来对象仍然存在于JVM
里面,因此“腌成咸菜”(串行化)的只是对象的一个拷贝,java咸菜(并行化Deserialization)还可以回鲜。

在java语言里深复制一个对象,常常可以使一个对象实现Serialization接口,然后把对象(实际上只是对象的一个拷贝)
写到一个流里(腌成咸菜),再成流里读回来(把咸菜回鲜),便可以重建对象。

//深复制
public Object deepClone(){
//将对象写到流里
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,从而将之排除在复制过程之外。
5、模式的选择
1):如果客户角色所使用的原型对象只有固定的几个,那么使用单独的变量来引用每一个原型对象就很方便,
    这时候简单形式的原始模型模式就比较合适。
2):而如果客户端所创建的原型对象的个数不是固定的,而是动态地变化的,那么就不妨使用一个Vector类型的变量
     用来动态地存储和释放原型对象。这时候,使用第二中形式的原始模型模式就比较合乎需要。
//孙大圣本人类
		public class TheGreatestSage{		//GreatestSage(大圣)
			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 birth date = " + monkey.getBirthDate());
				System.out.println("Copy monkey's birth date = " + 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();
			}
		}
		
		////////////////
		import java.util.Date;
		
		public class Monkey inplements Colenable{
			private int height;
			private int weight;
			private GoldRingedStaff staff;
			private Date birthDate;
			
			public Monkey(){
				this.birhtDate = new Date();
			}
			
			public Object clone(){
				Monkey temp = null;
				try{
					temp  = (Monkey)super.clone();
				}catch(CloneNotSupportedException e){
					System.out.println("Clone failed.");
				}finally{
					return (Object)temp;
				}
			}
			
			public int getHeight(){
				return height;
			}
			
			public void setHeight(int height){
				this.height = height;
			}
			//其他重复
			//生日的取值方法
			public Date getBirthDate(){
				return birthDate;
			}
			
			public void setBirthDate(Date birhtDate){
				this.birthDate = birhtDate;
			}
		}
		
		//金箍棒类
		public class GoldRingedStaff{
			private float height = 100.0F;
			private float diameter = 10.0F;
			
			public GoldRingedStaff(){
				//write your code here;
			}
		}
		
		//增长行为,每次调用长度和半径增加一倍
		public void grow(){
			this.diameter *= 2.0;
			this.height *= 2;
		}
		//缩小行为,每次调用长度和半径减少一半
		public void shrink(){
			this.diameter /= 2;
			this.height /= 2;
		}
		//移动
		public void move(){
			//write you code
		}
		
		public float getHeight(){
			return height;
		}
		public void setHeight(float heigth){
			this.height = height;
		}
		//半径的取值。赋值方法。。。。。。。。
	
	//深复制,必须要实现serializable接口
		import java.util.Date;
		import java.io.IOException;
		import java.lang.ClassNotFoundException;
		
		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 birth date = " + monkey.getBirthDate());
				System.out.println("Copy monkey's birth date = " + 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();
			}
		}
		
		//金箍棒类
		import java.util.Date;
		import java.io.Serializable;
		
		public class GoleRingedStaff implements Cloneable,Serializable{
			private float height = 100.0F;
			private float diameter = 10.0F;
			
			public GoldRingedStaff(){
				//write ..............
			}
			//跟上一样
		}
		


6、在什么情况下使用原始模型模式
假设一个系统的产品类是动态加载的,而且产品类有一定的等级结构。
这是采取原始模型模式,给每一个产品类配备一个克隆方法(大多数的时候只需要给产品类等级结构的根类配备一个克隆方法)
便可以避免使用工厂模式所带来的具有固定等级结构的工厂类。

7、原始模型模式的优点与缺点
1):特有的优点
/:原始模型模式原许动态地增加或减少产品类。由于创建产品类实例的方法是产品类内部具有的,因此,增加新产品对
    整个结构没有影响。
/:原始模型模式提供简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的等级结构,
    而原始模型模式就不需要这样。
/:具有给一个应用软件动态加载新功能的能力。例如,一个分析Web服务器的记录文件的应用软件,针对每一种记录文件
    格式,都可以由一个相应的“格式类”负责。如果出现了应用软件所不支持的新的Web服务器,只需要提供一个格式类
    的克隆,并在客户端登记即可,而不必给每个软件的用户提供一个全新的软件包。
/:产品类不需要非得有任何事先确定的等级结构,因为原始模型模式适合用于任何的等级结构。
2):主要缺点
/:每一个类都必须配备一个克隆方法,配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难,
    而对于已经有的类不一定很容易,特别是当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
   
8、原始模型模式与其他模式的关系
1)原始模型模式与合成模式的关系
原始模型模式经常与合成模式一同使用,因为原型对象经常是合成对象。
2)原始模型模式与抽象工厂模式的区别
如果系统不需要动态地改变原型对象,抽象工厂模式可以成为原始模型模式的替代品
3)原始模型模式与门面(Facade)模式的区别
原型模型模式的客户端通常可以将系统的其他对象与参与原始模型模式的对象分割开,起到一个门面对象的作用。
4)原始模型模式与工厂犯法尬,模式的关系
如果原型对象只有一种,而且不会增加的话,工厂方法模式可以成为一种替代模式。
5)原始模型模式与装饰(Decorator)模式的关系
原始模型模式常常与装饰模式一同使用。
分享到:
评论

相关推荐

    Java与模式.清晰.rar

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

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

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

    java设计模式详细解说

    #### 第7章:门面模式【FACADE PATTERN】 **定义与作用:** 门面模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 **实现原理:** - 定义一个门面类...

    java面试800题

    factory工厂模式、prototype原始模型模式、singleton单例模式、builder建造模式 结构模式 facade门面模式、proxy代理模式、adapter适配器(变压器)模式、composite合成模式、decorator装饰模式、bridge桥梁模式、...

    Spring.3.x企业应用开发实战(完整版).part2

    第7章 基于@AspectJ和Schema的AOP 7.1 Spring对AOP的支持 7.2 JDK 5.0注解知识快速进阶 7.2.1 了解注解 7.2.2 一个简单的注解类 7.2.3 使用注解 7.2.4 访问注解 7.3 着手使用@AspectJ 7.3.1 使用前的准备 7.3.2 一个...

    Spring3.x企业应用开发实战(完整版) part1

    第7章 基于@AspectJ和Schema的AOP 7.1 Spring对AOP的支持 7.2 JDK 5.0注解知识快速进阶 7.2.1 了解注解 7.2.2 一个简单的注解类 7.2.3 使用注解 7.2.4 访问注解 7.3 着手使用@AspectJ 7.3.1 使用前的准备 7.3.2 一个...

    超级有影响力霸气的Java面试题大全文档

    当客户机第一次调用一个Stateful Session Bean 时,容器必须立即在服务器中创建一个新的Bean实例,并关联到客户机上,以后此客户机调用Stateful Session Bean 的方法时容器会把调用分派到与此客户机相关联的Bean实例...

    JavaScript v2.0 参考大全

    JavaScript的基础语法包括变量声明(var、let、const)、数据类型(原始类型:number、string、boolean、null、undefined,以及引用类型:object、array、function)、运算符(算术、比较、逻辑、位运算符)、流程...

    javascript源码14

    通过构造函数和`new`关键字可以创建对象实例,`prototype`属性用于原型继承。 7. 异步编程:事件循环和回调函数是JavaScript异步的基础,Promise和async/await提供更优雅的异步处理方式。 8. 模块系统:ES6引入了...

    超级实用且不花哨的js代码大全

    其中,Undefined和Null是特殊的空值,Boolean表示逻辑值,Number用于数值计算,BigInt用于处理大整数,String用于文本字符串,而Symbol是ES6引入的新的原始数据类型,用于创建唯一的标识。 三、对象与数组 对象是...

    javascirpt源码学习

    1. **基础语法**:JavaScript的基础包括变量(var, let, const)、数据类型(原始类型如字符串、数字、布尔值,以及引用类型如对象、数组)、运算符(算术、比较、逻辑、赋值等)、流程控制(条件语句if...else, ...

    javascript源码11

    7. **原型与继承**:JavaScript使用原型链实现继承,每个对象都有一个隐含的_proto_属性,指向其构造函数的原型对象。通过`Object.create()`或`prototype`实现继承。 8. **闭包**:当一个函数可以记住并访问它的...

    vue面试题第二部分整理

    深拷贝是创建一个新对象,其属性值是原始对象的副本,包括嵌套的对象和数组。可以使用JSON方法、Object.assign()或者第三方库如lodash的_.cloneDeep()实现。 20. **typeof与instanceof区别** `typeof`用于检测...

    常用面试题及答案.pdf

    这种继承模型使得属性查找沿着原型链进行,直到找到相应的属性或到达原型链的终点(通常是 `Object.prototype`)。 ### 4. null、undefined 与未声明的变量的区别 - **null** 表示一个空值或者不存在的对象值,...

    Javascript 权威指南 第五版

    《JavaScript 权威指南》第五版是一本深受程序员喜爱的经典著作,由David Flanagan撰写,是深入理解JavaScript语言的重要参考资料。这本书旨在为开发者提供全面、准确且深入的JavaScript知识,帮助他们有效地利用这...

    计算机专业常用英语PPT学习教案.pptx

    41. **Prototype**:原型是产品或系统的第一版,用于测试和验证概念,通常在正式版本发布之前。 这些术语和概念构成了计算机科学的基础,对于理解计算机系统的工作原理、编程语言和硬件设计至关重要。深入学习和...

    悟透JavaScript

    它有七种原始数据类型(Boolean、Null、Undefined、Number、BigInt、String和Symbol)和一种复杂数据类型(Object)。 3. **作用域与闭包**:JavaScript的作用域分为全局作用域和局部作用域,以及ES6引入的块级作用...

    Hackathon1

    它有七种原始数据类型:Undefined、Null、Boolean、Number、BigInt、String和Symbol,以及一种复杂数据类型——Object。 3. **函数与闭包** 函数是JavaScript中的第一类对象,可以作为参数传递,也可以作为返回值...

Global site tag (gtag.js) - Google Analytics