设计模式总纲——原型设计模式

   接上文,在小陈合理的推广下,小陈的铺货量也是越来越大,越来越多,订单数量也是越来越多,大到上市公司,运动品牌专卖店,小到街边饰品小店等都来向小陈这边进货,但是随着订单的越来越多,现在每天的订单量都要将近10万量的计数,这时候一件让小陈头疼的事情来了,因为订单量过于多,现在小陈那边的出单机的速率已经跟不上了,因为原本出单机是一单单的打出来的,但是因为订单量过于庞大,速度实在跟不上了,而且小陈认真的观察了一下,订单的格式都是差不多的,都是订单时间,订单数量,订单的产品信息,预定公司的信息,基本上都是信息都是大同小异,我们先来看看街边小店的订单格式:

 1 public class OrderInfo {
 2     private String shopName;
 3     
 4     private String shopAddr;
 5     
 6     private String shopMobile;
 7     
 8     private String productName;
 9     
10     private String productNumber;
11 }//因为篇幅原因,setter/getter方法就不写了

街边小店的订单格式比较简单,就基本信息和地址就是了,但是别看这种街边小店,现在有将近3K家小店跟小陈的公司有合作,每个月的营收可不少,有的时候小店的名字、地址、手机一样,就产品和数量不一样,又要重新生成一个订单,有的是小店的开了分店,地址不一样,但是其他的信息又一样,但是每个订单新生成的速度忒慢(如果每个订单都要生成,既浪费虚拟机的性能,而且每次填写相同的信息也要花费很多时间),这个时候,小陈想既然都一样,干脆复制得了,然后再重新填上区别的信息,这样就可以一个订单范本多家公司使用了。(大家可以想想为什么这里不用工厂模式来生成订单?)

  接下来我们就来看看怎么来进行复制:

 1 public class OrderInfo implements Cloneable{
 2     private String shopName;
 3     private String shopAddr;
 4     private String shopMobile;
 5     private String productName;
 6     private String productNumber;
 7 
 8     @Override
 9     protected Object clone() {
10         try {
11             return super.clone();
12         } catch (CloneNotSupportedException e) {
13             e.printStackTrace();
14         }
15         return null;
16     }
17 }//因为篇幅原因,setter/getter方法就不写了

在这里OrderInfo必须实现Cloneable,让虚拟机知道该对象是可复制的,在这里必须说明一点,clone对象不是Cloneable中的方法,如果我们点进源代码里面看的话,会发现这个接口是个空接口,那这个clone方法是哪里来的呢?Java里面所有的对象都继承自Object,所以clone方法是Object对象中的方法,我们这里是重写了他的方法。好了,这样的话,我们来看看客户端来怎么生成,一家小店有两家分店,除了地址其他的信息都一样的订单了。

 1     @Test
 2     public void clien() {
 3         OrderInfo orderInfo = new OrderInfo("饰品小店", "北京王府井大街", "13888888888", "骑士徽章", "10000");
 4         
 5         OrderInfo cloneInfo = (OrderInfo)orderInfo.clone();
 6         
 7         cloneInfo.setShopAddr("北京王府井大街2号");
 8         
 9         System.out.println(orderInfo);
10         System.out.println(cloneInfo);
11     }

OrderInfo [shopName=饰品小店, shopAddr=北京王府井大街, shopMobile=13888888888, productName=骑士徽章, productNumber=10000]
OrderInfo [shopName=饰品小店, shopAddr=北京王府井大街2号, shopMobile=13888888888, productName=骑士徽章, productNumber=10000]

这里的话,我们可以看到除了店面的地址不一样,其他的信息全部一样了,这样子就省去了很多成本了,订单的生产速度也快了不止一个级别,小陈非常开心,但是这个时候,订单出单员小A发现了个问题了,当订单是街边小店的订单的时候,订单会没有问题,但是当是上市公司的订单,或者品牌大店的订单的时候,出单就会有问题,小陈观察了一下,原来上市集团的大单的订单格式是下面这种方式的:

 1 public class OrderInfo implements Cloneable{
 2     private Product productInfo;
 3     private Company company;
 4     
 5     @Override
 6     protected Object clone() {
 7         try {
 8             return super.clone();
 9         } catch (CloneNotSupportedException e) {
10             e.printStackTrace();
11         }
12         return null;
13     }
14 }
15 
16 class Product {
17     private String productName;
18     private String productBuildeAddr;
19     private String productBuildeDate;
20     //...等一系列关于产品的信息,因为篇幅略过
21 }
22 
23 class Company {
24     private String companyName;
25     private String companyAddr;
26     private String companyMobile;
27     //...等一系列关于公司的信息,因为篇幅略过
28 }

大型公司的信息都比较完善,而且他们都把信息封装到了一个个的对象中去了,订单中展示出了一个个的对象,那为什么我们clone的时候不起作用了呢?我们再来看看客户端的调用:

 1     @Test
 2     public void clien() {
 3         OrderInfo orderInfo = new OrderInfo(new Product("骑士徽章","克利夫兰","20160601"),new Company("骑士公司", "北京骑士大道", "13666666666"));
 4         
 5         OrderInfo cloneInfo = (OrderInfo)orderInfo.clone();
 6         
 7         cloneInfo.getCompany().setCompanyAddr("北京骑士大道2号");
 8         
 9         System.out.println(orderInfo);
10         System.out.println(cloneInfo);
11     }

OrderInfo [productInfo=Product [productName=骑士徽章, productBuildeAddr=克利夫兰, productBuildeDate=20160601], company=Company [companyName=骑士公司, companyAddr=北京骑士大道2号, companyMobile=13666666666]]
OrderInfo [productInfo=Product [productName=骑士徽章, productBuildeAddr=克利夫兰, productBuildeDate=20160601], company=Company [companyName=骑士公司, companyAddr=北京骑士大道2号, companyMobile=13666666666]]

我们这里就可以看到了clone订单更改了地址之后,原对象也更改了,这里就涉及到了jvm虚拟中的对象的引用了。(说的简单点就是,其实java中的每个对象在jvm中算是一个引用,引用指向了堆中的对象,而我们在克隆一个对象时,如果进行了浅拷贝的话,那只是将这个引用复制了一次,而没有对这个对象进行克隆,所以会导致,对象的克隆失败,因为两个引用都指向了堆中的同一个对象,所以在这里我们就需要进行deep Clone),这里的deep Clone有两种方式:

一种就是对需要克隆的对象里面的对象,全部实现clone的方法,那样的话,clone的时候,他就会全部进行复制了,类似上文中的方法

 1 class Product implements Cloneable{
 2     @Override
 3     protected Object clone() {
 4         try {
 5             return super.clone();
 6         } catch (CloneNotSupportedException e) {
 7             e.printStackTrace();
 8         }
 9         return null;
10     }
11 }
12 
13 class Company implements Cloneable{
14     @Override
15     protected Object clone() {
16         try {
17             return super.clone();
18         } catch (CloneNotSupportedException e) {
19             e.printStackTrace();
20         }
21         return null;
22     }
23 }

这里必须注意到的是,如果对象里面还有对象的话,那就必须一直实现Cloneable。

接下来我们重点介绍第二种方法:流式克隆

在流式克隆中,我们这边需要使用到Serializable这个接口,这个接口跟Cloneable一样,也是标识接口,这个接口也是空方法,实现了该接口的对象被标识为可序列化的

 1 public class OrderInfo implements Cloneable,Serializable{
 2     private Product productInfo;
 3     private Company company;
 4     
 5     @Override
 6     protected Object clone() {
 7         try {
 8             ByteArrayOutputStream baos = new ByteArrayOutputStream();
 9             ObjectOutputStream oos = new ObjectOutputStream(baos);
10             oos.writeObject(this);
11             ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
12             ObjectInputStream ois = new ObjectInputStream(bais);
13             return ois.readObject();
14         } catch (IOException e) {
15             e.printStackTrace();
16         } catch (ClassNotFoundException e) {
17             e.printStackTrace();
18         }
19         return null;
20     }
21 }

这里对其进行IO操作是使用JVM中的将对象写到流中是对象的一个拷贝的这一个特性,而源对象仍然保留在了jvm中,所以重新读取出来的话又是一条好对象啦,但是这里必须注意的是如果使用流式拷贝的话,那内部所有的被拷贝的对象都要实现Serializable这个接口,只需要实现Serializable接口即可,不需要其他的操作。然后我们再执行客户端的操作就可以看到,客户端得到的是两个不同的对象了。

现在小陈又解决了一个问题了,现在订单的问题也解决了,也不再会被大量的订单导致出货慢,让客户投诉了,在创业的这段时间遇到了不同的问题,但是小陈有自信可以一步步的解决掉,下回小陈还会遇到什么样的问题呢?

  欲知下回如何,且听下文分解。

原文地址:https://www.cnblogs.com/algorithm-cpp/p/5553531.html