关于如何创建类的新实例的模式(creational patterns)
工厂方法模式(factory method pattern)
什么时候使用
- 当客户端代码不知道要创建哪个类的实例的时候
- 当客户端不想在client代码中指明具体的要创建的类时
给出一个例子
现在这里有一个接口Trace,FileTrace和SystemTrace分别是继承这个接口的两个子类,正常使用如下:
Trace log = **new SystemTrace();**
log.debug( "entering log" );
现在使用工厂模式将这个类包装起来,
interface TraceFactory { public Trace getTrace(); public Trace getTrace(String type); void otherOperation(){}; }
public class Factory1 implements TraceFactory { public Trace getTrace() { *return new SystemTrace();* } }
public class Factory2 implements TraceFactory { public getTrace(String type) { if(type.equals(“file”) *return new FileTrace();* else if (type.equals(“system”) *return new SystemTrace();* } }
那么使用的时候就是这样的了
Trace log1 = new Factory1().getTrace();
当然你也可以将类中的方法设置成static类型的。
优点
还是举个例子好了,针对上面给出的例子,Trace接口的两个实现分别是FileTrace和SystemTrace,一开始你在客户端代码里面用的全部都是SysemTrace,结果后来需要使用使用fileTrace,如果这个时候你的代码里面有100个systemTrace,一个一个改可能就要疯掉了,但是如果使用的工厂模式的话,就没有这样的问题,你直接在工厂里面改一句话就解决问题了。
下面给出比较官方的言论(学校老师PPt里面说的)
Eliminates the need to bind application-specific classes to your code
Code deals only with the Product interface (Trace), so it can work with any user-defined ConcreteProduct(FileTrace, SystemTrace)
符合开闭原则:对扩展开放,对修改关闭,说白了就是不用更改客户端代码,也能够实现功能的转换。
缺点
实际上工厂方法将所有的功能模块进行了划分,如果你想要产出一个新的功能,那就必须写一个新的关于接口的实现类,比较麻烦
抽象工厂模式(abstract factory pattern)
提供接口以创建一组相关/相互依赖的对象, 但不需要指明其具体类。
什么时候使用
- 一个UI, 包含多个窗口控件,这些控件在不同的OS中实现不同
- 一个仓库 类,要控制多个设备,这些设备的制造商各有不同,控制接口有差异
模型的结构
其实这个模型的特点还是非常鲜明的:
首先是你要实现一个东西X,X有n个属性a1,a2,a3……,每一个属性又有着不同的选择,最终这些属性形成不同的组合。(简单吧)
和工厂方法进行对比
抽象工厂里面有多个方法,
可以创建多个不同类型的对象,
使用的是组合和委派
构造器模式(builder)
什么时候使用
创建包含多个组成部分的复杂对象
模型的结构
附上一张图,先创建一个空的builder实例(有一个关于product的rep),然后将其作为delegation传给director,director调用自己的construct函数(其实内部调用的是builder的buildPart方法)设置相关属性,然后通过get方法得到目标object。
但是需要注意的是,每一个对builder的实现只能构造出一种product。
和抽象工厂进行对比
抽象工厂创建的是一个搭配,而在搭配内部的数量配比关系并没有约定,而是随机的,也就是说最终给出的不是一个确定的产品。
但是builder就不一样了,直接整出来一个完整的产品。
优点
可能扩展和维护起来比较灵活吧~~~~毕竟本节讲的是面向可维护性!
Structural patterns
桥接模式(bridge)
模型的结构
不要被这模型硕大的身躯吓到,举个非常简单的例子,就相当于你平时写代码的时候通过delegation的方法调用了一个第三方的api,建立起来的delegation是永久的,讲解完毕(真的不知道为这么简单的常规操作起这么高大上又晦涩难懂的名字有什么意义)
代理模式(proxy)
什么时候使用
某个对象比较“敏感”/“私密”/“贵重”,不希望被client直接访问 到,故设置proxy,在二者之间建立防火墙。
举个例子
本来是没有proxyImage就能使用的,但是现在不愿意将接口直接暴漏给client,这样可能便于维护,这个其实是和工厂模式是一样的道理,在实现类和客户端之间添加了一个类,也就是在使用的时候直接调用proxyImage就可以了,那么在维护的时候,如果你想要更改一些关于realImage的一些内容,那么直接改源代码就好了,不需要更改客户端代码,这是满足开闭原则的。
全程是通过delegation实现的,也就是在代理模式中可以灵活的处理代码结构。
优点
隔离了对复杂对象的访问,降低了难度、代价,定位在“访问/使用行为”
通过delegation将原对象隔离之后,所有的操作可以重新组合,这对复杂对象来说是一种很好的处理方式。
组合模式(composite)
什么时候使用
目的是在同类型的对象之间建立起树形层次结构,一个上层对象可包含多个下层对象。
模型结构
在使用这种模式的时候,你可能需要递归调用
举个例子
公司现在有CEO和clerk两个职位,现在需要描述他们的基本个人信息(结构化的数据),并且要记录他们之间的领导关系。
首先对应上图的component接口是一个人物的接口,两个实现类CEO和clerk,clerk是叶子,因为他没有人可以领导,而CEO就不一样了,他可以领导clerk,所有他可以调用addElement函数来增加元素,这就对应了spec中的领导关系。
Behavioral patterns
观察者模式(observer)
什么时候使用
一对多进行信息传递
模型结构
使用的时候首先必须创建一个subject类的实例,然后创建多个observer 的实例,在执行observer的构造函数的时候将自己添加到subject的观察者列表中,当subject自身有消息的时候,就依次调用自己的所有的observer的update方法从而将信息传递给observer。
优点
低耦合
可以动态地增删观察者
java api提供了观察者接口
拜访者模式(Visitor Pattern)
什么时候使用
将数据和作用于数据上的某种/些特定操作分离开来
模型结构
简单理解,比如现在在类A中有一个方法func,你给这个方法传入一个visitor实例,然后再方法内部调用visitor.visit(this)方法,注意要将A自己作为参数传过去,让你后你就可以再visit方法中进行各种操作了。
但是有一点需要注意,为了保证表示不变性,所有的rep应该射为private,所以想在类的外面访问变量,需要添加足够数量的get方法。
优点
扩展起来很方便,只需要将设计一个新的visit方法就可以了
基于状态的构建(State-based construction)
根据当前状态决定下一步要执行什么,执行之后要跳转到什么状态
自动机编程
程序的执行被分解为一组自动执 行的步骤
各步骤之间的通讯 通过“状态变量”进行
程序执行就可看作是各自动 步骤的不断循环
使用枚举类型enum定义状态
使用二维数组定义 状态转换表
这不是重点
状态模式 (state pattern)
模型结构
context是你的ADT,维护了一个state的delegation,可以通过调用state的状态转换方法实现state的不同实现类的实例之间来回转换,这就完成了状态的转换。
优点
可以灵活的添加状态
备忘录模式 (Memento Pattern)
什么时候使用
记住对象的历史状态,以便于回滚
模型结构
理解起来非常简单,将originator当前的状态(关于state的delegation)保存起来,保存在另外单独的一个数据结构caretaker中,吐过想要回复之前的状态,只需要调用caretaker的相关方法就行
欢迎关注公众号BBIT
让我们共同学习共同进步!