`

原型设计模式prototype

阅读更多
基于《研磨设计模式》,没什么实质突破,简单的记录一下
问题引人:考虑一个订单系统。该系统里有一个保存订单的业务功能,在这个功能里,用户有这样的需求当订单的预定产品数量超过1000的时候,就需要把订单拆成两份订单来保存,如果拆分后还是超过1000,继续拆分,直到每份订单的预定产品数量不超过1000。
一个订单,有个人订单和公司订单。不管什么类型的订单,都要能正常的处理,如何实现???不用模式的解决方案见notusingMode包,该解决方案的订单业务处理对象
必须知道订单 的具体实现类,才能进行处理,也难以扩展新的订单类型,
package notusingMode;

//既然要实现通用的订单处理,而不关心具体实现,需要定义一个订单的接口
public interface OrderApi {
//获取订单产品数量
	public int getOrderProductNum();
	
	//设置订单产品数量
	public void setOrderProductNum(int num);
}

package notusingMode;

public class PersonalOrder implements OrderApi {
//	产品数量
	private int productNum;
	
	private String customerName; //订购人员姓名
	
	private String productId; // 订购的产品编号

	@Override
	public int getOrderProductNum() {
		return productNum;
	}

	@Override
	public void setOrderProductNum(int num) {

		this.productNum = num;
	}

	public String getCustomerName() {
		return customerName;
	}

	public void setCustomerName(String customerName) {
		this.customerName = customerName;
	}

	public String getProductId() {
		return productId;
	}

	public void setProductId(String productId) {
		this.productId = productId;
	}
	
	public String toString(){
		return "本个人订单的订购人是="+this.customerName+",订购产品是="+this.productId+",订购数量是="+this.productNum;
	}

}


package notusingMode;

public class CompanyOrder implements OrderApi {
	// 产品数量
	private int productNum;

	private String companyName; // 订购公司名称

	private String productId; // 订购的产品编号

	@Override
	public int getOrderProductNum() {
		return productNum;
	}

	@Override
	public void setOrderProductNum(int num) {
		this.productNum = num;
	}

	public String getCompanyName() {
		return companyName;
	}

	public void setCompanyName(String companyName) {
		this.companyName = companyName;
	}

	public String getProductId() {
		return productId;
	}

	public void setProductId(String productId) {
		this.productId = productId;
	}
	
	public String toString(){
		return "本公司订单的公司名称是="+this.companyName+",订购产品是="+this.productId+",订购数量是="+this.productNum;
	}

}

业务逻辑
package notusingMode;
//处理订单的业务对象
public class OrderBusiness {

	//保存订单
	public void saveOrder(OrderApi order){
		//等具体实现
		
//		1。		判断订单的产品数量是否大于1000?
		while(order.getOrderProductNum()>1000){
			//2, 如果大于,继续拆分
//			2.1  再创建一个订单,跟传入的订单除了数量不一样外,其它都一样
			OrderApi newOrder = null;
			
			//问题产生了,如何新建一个订单????传入的参数是订单接口,根本不知道具体实现,一个简单的解决方案,使用
//			instanceof,仍然有问题,业务处理知道了订单的具体实现
			
			if(order instanceof PersonalOrder){
				PersonalOrder p1 = (PersonalOrder) order;
				PersonalOrder p2 = new PersonalOrder();
				p2.setCustomerName(p1.getCustomerName());
				p2.setProductId(p1.getProductId());
				p2.setOrderProductNum(1000); //产品数量为1000
				
				newOrder = p2;
			}
			
			else if(order instanceof CompanyOrder){
				CompanyOrder c1 = (CompanyOrder) order;
				CompanyOrder c2 = new CompanyOrder();
				c2.setCompanyName(c1.getCompanyName());
				c2.setProductId(c1.getProductId());
				c2.setOrderProductNum(1000); //产品数量为1000
				
				newOrder = c2;
			}
			
			//原来的订单保留,把数量设置成减少1000
			
			order.setOrderProductNum(order.getOrderProductNum()-1000);
			
			//然后是业务功能处理,省略了,打印输出,看一下
			System.out.println("拆分生成订单=="+newOrder);
			
			
		}
		//不超过1000,就直接进入业务处理
		System.out.println("订单=="+order);
		
	}
	
}

客户端:
package notusingMode;

public class Client {

	public static void main(String[] args) {

		PersonalOrder po = new PersonalOrder();
		
		
//		设置订单数据
		po.setOrderProductNum(2980);
		po.setCustomerName("John");
		po.setProductId("产品001");
		
		//这里简化了,连业务接口都没有
		OrderBusiness ob = new OrderBusiness();
		ob.saveOrder(po);
	}

}


上面的问题描述一下:已经有了某个对象实例后,如何能够快速简单地创建出更多的这种对象??如上面问题,就是已经有了接口类型的对象实例,然后在方法中需要创建出
更多的这种对象。怎么解决??
原型模式(prototype)
定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

原型模式会要求对象实现一个可以”克隆“自身的接口,这样就可以通过拷贝或者可同一个实例对象本身来创建一个新的实例。
这样一来,通过原型实例创建新的对象,就不需要关心这个实例本身的类型,也不关心它的具体实现,只要它实现类克隆自身的方法,就可以
通过这个方法来获取新的对象,无需再去通过new 创建对象。

原型模式的结构:
Prototype:声明一个克隆自身的接口,用来约束要想克隆自己的类,要求他们都要实现这里定义的克隆方法
ConcretePrototype:实现Prototype接口的类,这些类真正实现类克隆自身的功能
Client:使用原型的客户端,首先要获取到原型实例对象,然后通过原型实例克隆自身来创建新的对象实例

package prototype;

public interface Prototype {

//	克隆自身的方法
	public Prototype clone();
}


package prototype;

public class ConcretePrototype1 implements Prototype {

	@Override
	public Prototype clone(){
		//创建一个自身对象
		Prototype p = new ConcretePrototype1();
		return p;
	}
}


package prototype;

public class ConcretePrototype2 implements Prototype {
	@Override
	public Prototype clone(){
		//创建一个自身对象
		Prototype p = new ConcretePrototype2();
		return p;
	}
}


package prototype;

public class Client {
	private Prototype prototype; // 持有需要使用的原型接口对象

	// 传入需要使用的原型接口对象,如果是业务处理,传入需要使用的订单接口对象
	public Client(Prototype prototype) {
		this.prototype = prototype;
	}

	public void oeration() {
		// 需要创建原型接口对象
		Prototype newPrototype = prototype.clone();

	}
}


用原型模式解决问题,订单接口就相当于Prototype,而PersonalOrder与CompanyOrder就相当于ConcretePrototype,业务处理对象
OrderBusiness就相当于使用原型的客户端。
package prototypeSolve;

//既然要实现通用的订单处理,而不关心具体实现,需要定义一个订单的接口
package prototypeSolve;

public class PersonalOrder implements OrderApi {
//	产品数量
	private int productNum;
	
	private String customerName; //订购人员姓名
	
	private String productId; // 订购的产品编号

	@Override
	public int getOrderProductNum() {
		return productNum;
	}

	@Override
	public void setOrderProductNum(int num) {

		this.productNum = num;
	}

	public String getCustomerName() {
		return customerName;
	}

	public void setCustomerName(String customerName) {
		this.customerName = customerName;
	}

	public String getProductId() {
		return productId;
	}

	public void setProductId(String productId) {
		this.productId = productId;
	}
	
	public String toString(){
		return "本个人订单的订购人是="+this.customerName+",订购产品是="+this.productId+",订购数量是="+this.productNum;
	}
	public OrderApi clone(){
		PersonalOrder personalOrder = new PersonalOrder();
		personalOrder.setCustomerName(this.customerName);
		personalOrder.setOrderProductNum(this.productNum);
		personalOrder.setProductId(this.productId);
		return personalOrder;
//		如果直接是return (this);返回的是同一个对象引用,对克隆对象的操作会影响到原操作
	}
}


package prototypeSolve;

public class CompanyOrder implements OrderApi {
	// 产品数量
	private int productNum;

	private String companyName; // 订购公司名称

	private String productId; // 订购的产品编号

	@Override
	public int getOrderProductNum() {
		return productNum;
	}

	@Override
	public void setOrderProductNum(int num) {
		this.productNum = num;
	}

	public String getCompanyName() {
		return companyName;
	}

	public void setCompanyName(String companyName) {
		this.companyName = companyName;
	}

	public String getProductId() {
		return productId;
	}

	public void setProductId(String productId) {
		this.productId = productId;
	}
	
	public String toString(){
		return "本公司订单的公司名称是="+this.companyName+",订购产品是="+this.productId+",订购数量是="+this.productNum;
	}
	public OrderApi clone(){
		CompanyOrder companyOrder = new CompanyOrder();
		companyOrder.setCompanyName(this.companyName);
		companyOrder.setOrderProductNum(this.productNum);
		companyOrder.setProductId(this.productId);
		return companyOrder;
//		如果直接是return (this);返回的是同一个对象引用,对克隆对象的操作会影响到原操作
	}
}


package prototypeSolve;

//处理订单的业务对象
public class OrderBusiness {

	// 保存订单
	public void saveOrder(OrderApi order) {
		// 等具体实现

		// 1。 判断订单的产品数量是否大于1000?
		while (order.getOrderProductNum() > 1000) {
			// 2, 如果大于,继续拆分
			// 2.1 再创建一个订单,跟传入的订单除了数量不一样外,其它都一样
			OrderApi newOrder = order.clone();
			newOrder.setOrderProductNum(1000);

			// 原来的订单保留,把数量设置成减少1000

			order.setOrderProductNum(order.getOrderProductNum() - 1000);

			// 然后是业务功能处理,省略了,打印输出,看一下
			System.out.println("拆分生成订单==" + newOrder);

		}
		// 不超过1000,就直接进入业务处理
		System.out.println("订单==" + order);

	}

}

package prototypeSolve;

public class Test {

	public static void main(String[] args) {

		OrderApi po = new PersonalOrder();

		po.setOrderProductNum(100);

		System.out.println("这是第一次获取的对象实例====" + po.getOrderProductNum());
		OrderApi po1 = po.clone();   //接口上定义的clone方法
		po1.setOrderProductNum(80);
		System.out.println("输出克隆出来的对象实例====" + po1.getOrderProductNum());
		System.out.println("再次输出原型实例====" + po.getOrderProductNum());
	}

}

原型模式功能:
(1)通过克隆来创建新的对象实例
(2)为克隆出来的新的对象实例复制原型实例属性的值
克隆和new,克隆方法得到的一个实例,通常属性是有值的,new一个实例对象,通常没有值或是默认值。

原型实例和克隆出来的实例,本质上是不同的实例,克隆完成后,它们之间没有关联的,即克隆的实例的属性值的改变不会影响原型实例
java中的克隆方法,是Object对象的方法protected Object clone()  throws CloneNotSupportedException
如何使用java中的克隆方法来实现原型模式??见包javaClonePrototype
需要克隆功能的类,只需实现Cloneable接口即可。
无论是自己实现克隆方法,还是采用java中的克隆方法,都存在深度克隆和浅度克隆的问题
深度克隆:除了浅度克隆,还负责克隆引用类型的数据,基本上是被克隆实例的属性数据都被克隆出来。
浅度克隆:只负责克隆按值传递的数据(比如基本数据类型,String类型)
深度克隆还有一个特定,如果被克隆的对象里面的属性数据是引用类型,则需要一直递归地克隆下去。意味着深度克隆成功,必须
整个克隆所涉及的对象都有正确实现克隆方法,如果其中有一个方法没有正确实现克隆,那么就会导致克隆失败。
在调用super.clone()方法进行克隆时,java先开辟一块内存空间,然后把实例对象的值原样拷贝过去,对于基本数据类型可以,但对于引用类型
拷贝的就是内存地址,所以克隆后的对象实例和原型对象指向同一块地址空间,要想正确地执行深度拷贝,必须手工对每个引用类型的属性进行克隆。
如果一个系统中的原型的数目不固定,比如系统中的原型可以被动态地创建和销毁,就需要维护一个档期可用的原型的注册表,这个注册表就称为原型管理器。
也相当于一个资源管理器,即一个缓冲资源的实现。
原型模式的优缺点:
优点:对客户端隐藏具体的实现类型,在运行时动态改变具体的实现类型
缺点:每个原型的子类都必须实现clone的操作,尤其在包含引用类型的对象时,clone方法会比较麻烦
本质:克隆生成对象。
原型模式通常被归类为创建型的模式,因为它可以生成新的对象实例。
全部代码见附件:
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics