Design Pattern —— Prototype /Template Method/Iterator/Composite/Bridge

Prototype /Template Method/Iterator/Composite/Bridge

为什么把这五种设计模式放一起呢,没什么太高大上的原因,就是因为这五种模式JAVA开发最基本的特征或者设计原则,也是开发中最常见,甚至常见到我们都没有认为他是一种设计模式。

一、Prototype

 原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建。

Prototype类需要具备以下两个条件:

  • 实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。

  • 重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。

java中的Prototype是显而易见的。在这里我们需要关注的不是Prototype模式本身,而是要关注深拷贝和浅拷贝

  •  浅度克隆

  只负责克隆按值传递的数据(比如基本数据类型、String类型),而不复制它所引用的对象,换言之,所有的对其他对象的引用都仍然指向原来的对象。

  •  深度克隆

  除了浅度克隆要克隆的值外,还负责克隆引用类型的数据。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深度克隆把要复制的对象所引用的对象都复制了一遍,而这种对被引用到的对象的复制叫做间接复制。

  深度克隆要深入到多少层,是一个不易确定的问题。在决定以深度克隆的方式复制一个对象的时候,必须决定对间接复制的对象时采取浅度克隆还是继续采用深度克隆。因此,在采取深度克隆时,需要决定多深才算深。此外,在深度克隆的过程中,很可能会出现循环引用的问题,必须小心处理。

利用序列化实现深度克隆

  把对象写到流里的过程是序列化(Serialization)过程;而把对象从流中读出来的过程则叫反序列化(Deserialization)过程。应当指出的是,写到流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。

  在Java语言里深度克隆一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的拷贝)写到一个流里(序列化),再从流里读回来(反序列化),便可以重建对象。

 1 public  Object deepClone() throws IOException, ClassNotFoundException{
 2         //将对象写到流里
 3         ByteArrayOutputStream bos = new ByteArrayOutputStream();
 4         ObjectOutputStream oos = new ObjectOutputStream(bos);
 5         oos.writeObject(this);
 6         //从流里读回来
 7         ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
 8         ObjectInputStream ois = new ObjectInputStream(bis);
 9         return ois.readObject();
10     }

这样做的前提就是对象以及对象内部所有引用到的对象都是可序列化的,否则,就需要仔细考察那些不可序列化的对象可否设成transient,从而将之排除在复制过程之外。

 

关于transient

1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。

2)transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。

3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。

 

二、Template Method

模板方法的一般代码结构如下:

 1 abstract class AbstractClass 
 2 {
 3 //模板方法
 4 public void TemplateMethod() 
 5 {
 6         PrimitiveOperation1();
 7         PrimitiveOperation2();
 8         PrimitiveOperation3();
 9 }
10 
11 //基本方法—具体方法
12 public void PrimitiveOperation1() 
13 {
14     //实现代码
15 }
16 
17 //基本方法—抽象方法
18     public abstract void PrimitiveOperation2();    
19 
20 //基本方法—钩子方法
21 public virtual void PrimitiveOperation3()   
22 {  }
23 }

非常常见,普遍。模板方法只需记住一句:先用接口/抽象类确定业务标准,然后由子类做具体的业务实现

 

三、Iterator

迭代器模式有叫做游标(Cursor)模式。定义:提供一种方法访问一个容器(container)对象中的各个元素,而又不暴露该对象的内部细节。

java所有的容器类(Collection,List、Set、Map)都已经实现了容器自己的迭代器

 

 

四、Composite

合成模式非常好理解,就是一个树形结构,树枝和叶子都符合一个标准,树枝要支持叶子的管理。最好的例子就是android中View和ViewGroup

合成模式分为安全型和透明型,安全型就是标准中不包括树枝中叶子的管理,而透明型就是标准中包括了树枝中叶子的管理(树枝和叶子结构完全一致)

安全型

 

 

透明型

 

android中View和Viewgroup结合了安全型和透明型的特点,View是叶子的标准,ViewGroup是树枝的标准,而ViewGroup又是符合View的标准。

五、Bridge

桥梁模式是看起来简单,但其实很讲“思想”的一个模式

我们一般面向对象编程,会比较重视抽象和实现,而一般情况下,抽象和实现都是通过继承来实现。这样的结果就是继承这种强关系下,抽象和实现的耦合性就较强。

而桥梁模式的主要思想就是既实现标准和实现的提取,又实现了抽象和实现的解耦。

桥梁模式的用意是“将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化”。

 

从上图我们可以看到,其实原本我们的目的是RefinedAbstraction类要符合Implementor的标准,但是如果RefinedAbstraction直接去实现Implementor,那么二者的耦合性就比较强,试想,如果我们的项目代码中一个类要实现很多标准的功能,那么这个类就要实现很多的标准,这样下去代码耦合性就太强,且代码也比较乱。

采用桥梁模式,RefinedAbstraction和Implementor的关系是聚合这种弱关系,且附加业务(即implementor标准的实现)在原本的业务类(即RefinedAbstraction)外面实现,实现了解耦合。

 

 

 

 

分别用一句话总结这五种设计模式:

1.原型模式  无需新建,clone自身,注意深浅拷贝

2.模板方法模式  先用接口/抽象类定义业务标准,然后在子类中实现具体业务功能

3.迭代器模式 提供一种方法访问一个容器(container)对象中的各个元素,而又不暴露该对象的内部细节。Java的集合类都已经实现了自己的迭代器。

4.合成模式  树形结构,有树枝和叶子两种节点,安全型和透明型的区别就是标准中有没有包含树枝中管理节点的功能。android View/Viewgroup是最好的例子

5.桥梁模式  一般的抽象和实现都是模板方法方式的采用继承的结构,这样耦合性太强。我们将附加业务的实现采用聚合的方式加入原有基本业务的实现中,即实现了标准和实现的分离,又实现了功能间的解偶。

 

 

参考资料:

《java与模式》

posted @ 2015-12-09 23:05  xerrard  阅读(228)  评论(0编辑  收藏  举报