• 设计模式10---设计模式之原型模式(Prototype)


    1.场景模式

    考虑这样一个实际应用:订单处理系统
    里面有一个保存订单的功能,当产品数量超过1000份以后,拆成两份订单,再超,那么就再拆。直到每份订单不超过1000为止,订单有两种,一个是个人订单,一个是公司订单,现在需要实现一个通用的订单处理系统。

    2.场景模式代码(代码很简单,不一一讲解了)

    package demo08.prototype.example1;
    
    
    /**
     * 订单的接口
     */
    public interface OrderApi {
    	/**
    	 * 获取订单产品数量
    	 * 
    	 * @return 订单中产品数量
    	 */
    	public int getOrderProductNum();
    
    
    	/**
    	 * 设置订单产品数量
    	 * 
    	 * @param num
    	 *        订单产品数量
    	 */
    	public void setOrderProductNum(int num);
    }
    package demo08.prototype.example1;
    
    
    /**
     * 个人订单对象
     */
    public class PersonalOrder implements OrderApi {
    	/**
    	 * 订购人员姓名
    	 */
    	private String customerName;
    	/**
    	 * 产品编号
    	 */
    	private String productId;
    	/**
    	 * 订单产品数量
    	 */
    	private int orderProductNum = 0;
    
    
    	public int getOrderProductNum() {
    		return this.orderProductNum;
    	}
    
    
    	public void setOrderProductNum(int num) {
    		this.orderProductNum = 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.orderProductNum;
    	}
    }
    package demo08.prototype.example1;
    
    
    /**
     * 企业订单对象
     */
    public class EnterpriseOrder implements OrderApi {
    	/**
    	 * 企业名称
    	 */
    	private String enterpriseName;
    	/**
    	 * 产品编号
    	 */
    	private String productId;
    	/**
    	 * 订单产品数量
    	 */
    	private int orderProductNum = 0;
    
    
    	public int getOrderProductNum() {
    		return this.orderProductNum;
    	}
    
    
    	public void setOrderProductNum(int num) {
    		this.orderProductNum = num;
    	}
    
    
    	public String getEnterpriseName() {
    		return enterpriseName;
    	}
    
    
    	public void setEnterpriseName(String enterpriseName) {
    		this.enterpriseName = enterpriseName;
    	}
    
    
    	public String getProductId() {
    		return productId;
    	}
    
    
    	public void setProductId(String productId) {
    		this.productId = productId;
    	}
    
    
    	public String toString() {
    		return "本企业订单的订购企业是=" + this.enterpriseName + ",订购产品是=" + this.productId + ",订购数量为=" + this.orderProductNum;
    	}
    }
    package demo08.prototype.example1;
    
    
    /**
     * 处理订单的业务对象
     */
    public class OrderBusiness {
    	/**
    	 * 创建订单的方法
    	 * 
    	 * @param order
    	 *        订单的接口对象
    	 */
    	public void saveOrder(OrderApi order) {
    		// 根据业务要求,当订单的预定的产品数量超过1000的时候,就需要把订单拆成两份订单
    		// 当然如果要做好,这里的1000应该做成常量,这么做是为了演示简单
    
    
    		// 1:判断当前的预定产品数量是否大于1000
    		while (order.getOrderProductNum() > 1000) {
    			// 2:如果大于,还需要继续拆分
    			// 2.1再新建一份订单,跟传入的订单除了数量不一样外,其他都相同
    			OrderApi newOrder = null;
    			if (order instanceof PersonalOrder) {
    				// 创建相应的新的订单对象
    				PersonalOrder p2 = new PersonalOrder();
    				// 然后进行赋值,但是产品数量为1000
    				PersonalOrder p1 = (PersonalOrder) order;
    				p2.setCustomerName(p1.getCustomerName());
    				p2.setProductId(p1.getProductId());
    				p2.setOrderProductNum(1000);
    				// 然后再设置给newOrder
    				newOrder = p2;
    			} else if (order instanceof EnterpriseOrder) {
    				// 创建相应的订单对象
    				EnterpriseOrder e2 = new EnterpriseOrder();
    				// 然后进行赋值,但是产品数量为1000
    				EnterpriseOrder e1 = (EnterpriseOrder) order;
    				e2.setEnterpriseName(e1.getEnterpriseName());
    				e2.setProductId(e1.getProductId());
    				e2.setOrderProductNum(1000);
    				// 然后再设置给newOrder
    				newOrder = e2;
    			}
    
    
    			// 2.2原来的订单保留,把数量设置成减少1000
    			order.setOrderProductNum(order.getOrderProductNum() - 1000);
    
    
    			// 然后是业务功能处理,省略了,打印输出,看一下
    			System.out.println("拆分生成订单==" + newOrder);
    		}
    
    
    		// 3:不超过,那就直接业务功能处理,省略了,打印输出,看一下
    		System.out.println("订单==" + order);
    
    
    	}
    
    
    //	public void saveOrder2(OrderApi order){
    //		int oldNum = order.getOrderProductNum();
    //		while(oldNum > 1000){
    //			//定义一个表示被拆分出来的新订单对象
    //			OrderApi newOrder = null;
    //			
    //			if(order instanceof PersonalOrder){
    //				//创建相应的订单对象
    //				PersonalOrder p2 = new PersonalOrder();
    //				//然后进行赋值等,省略了
    //				//然后再设置给newOrder
    //				newOrder = p2;
    //			}else if(order instanceof EnterpriseOrder){
    //				//创建相应的订单对象
    //				EnterpriseOrder e2 = new EnterpriseOrder();
    //				//然后进行赋值等,省略了
    //				//然后再设置给newOrder
    //				newOrder = e2;
    //			}			
    //			//然后进行拆分和其他业务功能处理,省略了
    //		}		
    //	}
    }
    package demo08.prototype.example1;
    
    
    public class OrderClient {
    	public static void main(String[] args) {
    		// 创建订单对象,这里为了演示简单,直接new了
    		PersonalOrder op = new PersonalOrder();
    		// 设置订单数据
    		op.setOrderProductNum(2925);
    		op.setCustomerName("张三");
    		op.setProductId("P0001");
    
    
    		// 这里获取业务处理的类,也直接new了,为了简单,连业务接口都没有做
    		OrderBusiness ob = new OrderBusiness();
    		// 调用业务来保存订单对象
    		ob.saveOrder(op);
    	}
    }

    3.问题所在

    仔细观察可以发现:实际上是关注订单的类型和具体实现的。证据如下:

    		// 1:判断当前的预定产品数量是否大于1000
    		while (order.getOrderProductNum() > 1000) {
    			// 2:如果大于,还需要继续拆分
    			// 2.1再新建一份订单,跟传入的订单除了数量不一样外,其他都相同
    			OrderApi newOrder = null;
    			if (order instanceof PersonalOrder) {
    				// 创建相应的新的订单对象
    				PersonalOrder p2 = new PersonalOrder();
    				// 然后进行赋值,但是产品数量为1000
    				PersonalOrder p1 = (PersonalOrder) order;
    				p2.setCustomerName(p1.getCustomerName());
    				p2.setProductId(p1.getProductId());
    				p2.setOrderProductNum(1000);
    				// 然后再设置给newOrder
    				newOrder = p2;
    			} else if (order instanceof EnterpriseOrder) {
    				// 创建相应的订单对象
    				EnterpriseOrder e2 = new EnterpriseOrder();
    				// 然后进行赋值,但是产品数量为1000
    				EnterpriseOrder e1 = (EnterpriseOrder) order;
    				e2.setEnterpriseName(e1.getEnterpriseName());
    				e2.setProductId(e1.getProductId());
    				e2.setOrderProductNum(1000);
    				// 然后再设置给newOrder
    				newOrder = e2;
    			}
    
    
    			// 2.2原来的订单保留,把数量设置成减少1000
    			order.setOrderProductNum(order.getOrderProductNum() - 1000);
    
    
    			// 然后是业务功能处理,省略了,打印输出,看一下
    			System.out.println("拆分生成订单==" + newOrder);
    		}

    4.解决方案

    是不是觉得上面代码很臃肿,很不爽,很不舒服,对头,这样说明你已经开始理解java设计模式的好处了。
    解决它的方法就是使用原型模式

    5.原型模式

    5.1原型模式定义:

    用原型实例指定创建对象的种类,并通过拷贝这些原型创新新的对象。

    5.2如何解决

    那么如何解决呢?大家有没有想过可以把这些复制的代码放在接口中,返回接口对象,这样就好像是接口创建了接口对象似的。而且,客户端就不用理解对象的一些操作了,知识最少化。

    6.原型模式示例代码

    6.1模式结构图

     

    6.2声明一个克隆自身的接口

    package demo08.prototype.example2;
    /**
     * 声明一个克隆自身的接口
     */
    public interface Prototype {
    	/**
    	 * 克隆自身的方法
    	 * @return 一个从自身克隆出来的对象
    	 */
    	public Prototype clone();
    }

    6.3具体的实现对象

    package demo08.prototype.example2;
    /**
     * 克隆的具体实现对象
     */
    public class ConcretePrototype1 implements Prototype {
    	public Prototype clone() {
    		//最简单的克隆,新建一个自身对象,由于没有属性,就不去复制值了
    		Prototype prototype = new ConcretePrototype1();
    		return prototype;
    	}
    }
    package demo08.prototype.example2;
    
    
    /**
     * 克隆的具体实现对象
     */
    public class ConcretePrototype2 implements Prototype {
    	public Prototype clone() {
    		//最简单的克隆,新建一个自身对象,由于没有属性,就不去复制值了
    		Prototype prototype = new ConcretePrototype2();
    		return prototype;
    	}
    }

    6.4客户端

    package demo08.prototype.example2;
    
    
    /**
     * 使用原型的客户端
     */
    public class Client {
    	/**
    	 * 持有需要使用的原型接口对象
    	 */
    	private Prototype prototype;
    
    
    	/**
    	 * 构造方法,传入需要使用的原型接口对象
    	 * 
    	 * @param prototype
    	 *        需要使用的原型接口对象
    	 */
    	public Client(Prototype prototype) {
    		this.prototype = prototype;
    	}
    
    
    	/**
    	 * 示意方法,执行某个功能操作
    	 */
    	public void operation() {
    		// 会需要创建原型接口的对象
    		Prototype newPrototype = prototype.clone();
    	}
    }

    7.使用原型模式来重写示例代码

    为了方便我就直接写改动的地方

    7.1订单的接口

    package demo08.prototype.example3;
    
    
    
    
    /**
     * 订单的接口,声明了可以克隆自身的方法
     */
    public interface OrderApi {
    	/**
    	 * 获取订单产品数量
    	 * @return 订单中产品数量
    	 */
    	public int getOrderProductNum();
    	/**
    	 * 设置订单产品数量
    	 * @param num 订单产品数量
    	 */
    	public void setOrderProductNum(int num);
    	/**
    	 * 克隆方法
    	 * @return 订单原型的实例
    	 */
    	public OrderApi cloneOrder();
    }

    7.2个人订单

    package demo08.prototype.example3;
    /**
     * 个人订单对象
     */
    public class PersonalOrder implements OrderApi{
    	/**
    	 * 订购人员姓名
    	 */
    	private String customerName;
    	/**
    	 * 产品编号
    	 */
    	private String productId;
    	/**
    	 * 订单产品数量
    	 */
    	private int orderProductNum = 0;
    	
    	public int getOrderProductNum() {
    		return this.orderProductNum;
    	}	
    	public void setOrderProductNum(int num) {
    		this.orderProductNum = 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.orderProductNum;
    	}
    	public OrderApi cloneOrder() {
    		//创建一个新的订单,然后把本实例的数据复制过去
    		PersonalOrder order = new PersonalOrder();
    		order.setCustomerName(this.customerName);
    		order.setProductId(this.productId);
    		order.setOrderProductNum(this.orderProductNum);
    		
    		return order;
    	}
    }

    7.3企业订单

    package demo08.prototype.example3;
    
    
    /**
     * 企业订单对象
     */
    public class EnterpriseOrder implements OrderApi {
    	/**
    	 * 企业名称
    	 */
    	private String enterpriseName;
    	/**
    	 * 产品编号
    	 */
    	private String productId;
    	/**
    	 * 订单产品数量
    	 */
    	private int orderProductNum = 0;
    
    
    	public int getOrderProductNum() {
    		return this.orderProductNum;
    	}
    
    
    	public void setOrderProductNum(int num) {
    		this.orderProductNum = num;
    	}
    
    
    	public String getEnterpriseName() {
    		return enterpriseName;
    	}
    
    
    	public void setEnterpriseName(String enterpriseName) {
    		this.enterpriseName = enterpriseName;
    	}
    
    
    	public String getProductId() {
    		return productId;
    	}
    
    
    	public void setProductId(String productId) {
    		this.productId = productId;
    	}
    
    
    	public String toString() {
    		return "本企业订单的订购企业是=" + this.enterpriseName + ",订购产品是=" + this.productId + ",订购数量为=" + this.orderProductNum;
    	}
    
    
    	public OrderApi cloneOrder() {
    		// 创建一个新的订单,然后把本实例的数据复制过去
    		EnterpriseOrder order = new EnterpriseOrder();
    		order.setEnterpriseName(this.enterpriseName);
    		order.setProductId(this.productId);
    		order.setOrderProductNum(this.orderProductNum);
    
    
    		return order;
    	}
    }

    7.4处理订单的业务对象

    package demo08.prototype.example3;
    /**
     * 处理订单的业务对象
     */
    public class OrderBusiness {
    	/**
    	 * 创建订单的方法
    	 * @param order 订单的接口对象
    	 */
    	public void saveOrder(OrderApi order){
    		//根据业务要求,当订单的预定的产品数量超过1000的时候,就需要把订单拆成两份订单
    		//当然如果要做好,这里的1000应该做成常量,这么做是为了演示简单
    		
    		//1:判断当前的预定产品数量是否大于1000
    		while(order.getOrderProductNum() > 1000){
    			//2:如果大于,还需要继续拆分
    			//2.1再新建一份订单,跟传入的订单除了数量不一样外,其他都相同
    			OrderApi newOrder = order.cloneOrder();
    			//然后进行赋值,产品数量为1000
    			newOrder.setOrderProductNum(1000);
    			
    			//2.2原来的订单保留,把数量设置成减少1000
    			order.setOrderProductNum(order.getOrderProductNum()-1000);
    			
    			//然后是业务功能处理,省略了,打印输出,看一下
    			System.out.println("拆分生成订单=="+newOrder);
    		}		
    		//3:不超过,那就直接业务功能处理,省略了,打印输出,看一下
    		System.out.println("订单=="+order);
    		
    	}
    }

    这样是不是简单清爽了很多呢?

    8.原型模式思考

    8.1原型模式的讲解

    通过克隆创建新的对象实例
    为克隆出来的对象复制原型实例属性的值,不是new而是类似new

    8.2原型模式的调用顺序图

     

    8.3浅度克隆和深度克隆

    浅度克隆:只负责按值传递的数据
    深度克隆:除了浅度克隆复制的数据外,还要负责克隆引用类型的数据,而且,引用类型还要递归克隆
    java也有克隆方法,这里就不说了。
    体现深度克隆的代码如下(稍微更改一下就行了)
    public OrderApi cloneOrder() {
    //创建一个新的订单,然后把本实例的数据复制过去
    PersonalOrder order = new PersonalOrder();
    order.setCustomerName(this.customerName);
    order.setOrderProductNum(this.orderProductNum);
    //对于对象类型的数据,深度克隆的时候需要继续调用这个对象的克隆方法
    order.setProduct((Product)this.product.cloneProduct());
    return order;
    }

    8.4原型模式本质

    克隆生成对象

    8.5原型模式优缺点

    优点:对客户端隐藏具体的实现类型,运行时动态改变具体的实现类型
    缺点:每个原型的子类都要实现clone接口

  • 相关阅读:
    C语言利用按位与、按位或转换大小写字母
    综合布线知识点总结
    C语言 计算阶乘
    C语言位运算符详解
    docker-compose的flask自动部署
    redis集群的布置
    fatal: unable to auto-detect email address (got 'CC@LAPTOP-UPQ1N1VQ.(none)')
    使用ImagesPipeline时候报错为:ModuleNotFoundError: No module named 'scrapy.contrib'
    多任务
    json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (ch
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3163077.html
Copyright © 2020-2023  润新知