创造性模式(Creational patterns)一共分为三种模式,分别是:
下面对这三种模式一一进行描述
工厂方法模式也被称为虚拟构造器。当client不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法。 定义一个用于创建对象的接口,让其子类来决定实例化哪一个类,从而使一个类的实例化延迟到其子类。
下面来看一个具体例子:
首先定义一个接口,可视为Abstract product
public interface Trace{
// turn on and off debugging
public void setDebug( boolean debug );
// write out a debug message
public void debug( String message );
// write out an error message
public void error( String message );
}
接着实现这个接口,这个接口下有两个具体的product
Concrete product 1:
public class FileTrace implements Trace {
private PrintWriter pw;
private boolean debug;
public FileTrace() throws IOException {
pw = new PrintWriter( new FileWriter( "t.log" ) );
}
public void setDebug( boolean debug ) {
this.debug = debug;
}
public void debug( String message ) {
if( debug ) {
pw.println( "DEBUG: " + message );
pw.flush();
}
}
public void error( String message ) {
pw.println( "ERROR: " + message );
pw.flush();
}
}
Concrete product 2
public class SystemTrace implements Trace {
private boolean debug;
public void setDebug( boolean debug ) {
this.debug = debug;
}
public void debug( String message ) {
if( debug )
System.out.println( "DEBUG: " + message );
}
public void error( String message ) {
System.out.println( "ERROR: " + message );
}
}
客户端代码:
//... some code ...
Trace log = new SystemTrace();
log.debug( "entering log" );
Trace log2 = new FileTrace();
log.debug(“...”);
这么写会使得客户代码与具体产品紧密结合。如果采用工厂方法模式,可将两者分离开。
首先定义工厂的接口
interface TraceFactory {
public Trace getTrace();
public Trace getTrace(String type);
void otherOperation(){};
}
不仅包含 factory method, 还可以实现其他功能。
接下来具体实现工厂,每一个具体产品对应一个工厂。
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();
}
}
根据类型创建哪个具体产品,有新的具体产品类加入时,可以在工 厂类里修改或增加新的工厂函数 (OCP),不会影响客户端代码。
客户端:
Trace log1 = new Factory1().getTrace();
log1.setDebug(true);
log1.debug( "entering log" );
Trace log2 = new Factory2().getTrace("system");
log2.setDebug(false);
log2.debug("...");
Client使用 “工厂方法” 来创建实例, 得到实例的类型是抽象接口而非具体类。
除此之外,还有静态工厂方法,它既可以在ADT 内部实现,也可以构造单独的工厂类。
public class TraceFactory1 {
public static Trace getTrace() {
return new SystemTrace();
}
}
public class TraceFactory2 {
public static Trace getTrace(String type) {
if(type.equals(“file”)
return new FileTrace();
else if (type.equals(“system”)
return new SystemTrace();
}
}
客户端可以这样调用:
//... some code ...
Trace log1 = TraceFactory1.getTrace();
log1.setDebug(true);
log1.debug( "entering log" );
Trace log2 = TraceFactory2.getTrace(“system”);
log2.setDebug(true);
log2.debug(“...”);
优点:
Trace
),因此它可以与任何用户的ConcreteProduct(FileTrace
,SystemTrace
)一起使用。考虑两个实际应用场景:
我们可以使用抽象工厂模式处理这种情况。
抽象工厂模式:提供接口以创建一组相关/相互依赖的对象, 但不需要指明其具体类。
以下面窗口滚动条为例:
客户端想要一个产品,由窗口和滚动条组成。于是可以交给一个抽象工厂来做,这个工厂负责将产品的组件组装起来成一个完整的产品。不同的产品继承这个抽象工厂接口,实现自己的工厂方法。
客户端根据要创建的“组合产品”的类型, 构建不同的抽象工厂子类,然后再利用辅助类进行具体构建。
创建的不是一个完整产品,而是“产品族”(遵循 固定搭配规则的多类产品的实例),得到的结果是:多个不同产品的 object,各产品创建过程对client可见,但“搭配”不能改变。
本质上,Abstract Factory是把多类产品的factory method组合在一起。
构造器模式:创建复杂对象,包含多个组成部分 。
例如:将文档转换为多种不同的格式 。写出文档的步骤是相同的,但每个步骤的细节取决于格式。
就像你在麦当劳点一种组合食品一样。
注意:client要的不是一堆零散的objects (abstract factory那样的结果),而是一个完整的产品,client不关心其中的细节组成部分是什么、如何创建。
具体例子如下:
我们需要一个Pizza的产品,该Pizza产品的part是以三个属性的形式体现,其builder就相当于给三个属性赋值(也可更复杂)。
/* "Product" */
class Pizza {
private String dough = "";
private String sauce = "";
private String topping = "";
public void setDough(String dough) {
this.dough = dough;
}
public void setSauce(String sauce) {
this.sauce = sauce;
}
public void setTopping(String topping) {
this.topping = topping;
}
}
Builder抽象类有三个抽象方法,分别创建三个 part。
/* "Abstract Builder" */
abstract class PizzaBuilder {
protected Pizza pizza;
public Pizza getPizza() {
return pizza;
}
public void createNewPizzaProduct() {
pizza = new Pizza();
}
public abstract void buildDough();
public abstract void buildSauce();
public abstract void buildTopping();
}
具体的builder子类中,override三个抽象方法,分别构建三个parts,这里我们实现两个不同的builder子类,构建三个parts的内容有差异。
/* "ConcreteBuilder 1" */
class SpicyPizzaBuilder extends PizzaBuilder {
public void buildDough() {
pizza.setDough("pan baked");
}
public void buildSauce() {
pizza.setSauce("hot");
}
public void buildTopping() {
pizza.setTopping("pepperoni+salami");
}
}
/* "ConcreteBuilder 2" */
class HawaiianPizzaBuilder extends PizzaBuilder {
public void buildDough() {
pizza.setDough("cross");
}
public void buildSauce() {
pizza.setSauce("mild");
}
public void buildTopping() {
pizza.setTopping("ham+pineapple");
}
}
Director中定义要delegate的builder
/* "Director" */
class Waiter {
private PizzaBuilder pizzaBuilder;
public void setPizzaBuilder(PizzaBuilder pb) {
pizzaBuilder = pb;
}
//得到最终builder的结果
public Pizza getPizza() {
return pizzaBuilder.getPizza();
}
//在这里具体构建各部分
public void constructPizza() {
pizzaBuilder.createNewPizzaProduct();
pizzaBuilder.buildDough();
pizzaBuilder.buildSauce();
pizzaBuilder.buildTopping();
}
}
Client使用不同的builder子类创建不同的产品
/* A customer ordering a pizza. */
public class PizzaBuilderDemo {
public static void main(String[] args) {
Waiter waiter = new Waiter();
PizzaBuilder hawaiianPizzabuilder = new HawaiianPizzaBuilder();
PizzaBuilder spicyPizzaBuilder = new SpicyPizzaBuilder();
//建立waiter和builder之间的delegation关系
waiter.setPizzaBuilder( hawaiianPizzabuilder );
//Bingo! 构建细节完全对client隐藏
waiter.constructPizza();
Pizza pizza = waiter.getPizza();
}
}
Abstract Factory创建的不是一个完整产品,而是“产品族”(遵循 固定搭配规则的多类产品实例),得到的结果是:多个不同产品的实例object,各产品创建过程对client可见,但“搭配”不能改变。
Builder创建的是一个完整的产品,有多个部分组成,client不需了解每个部分是怎么创建、各个部分怎么组合,最终得到一个产品的完整 object 。
Template Method: a behavioral pattern 目标是为了复用算法的公共结构(次序)。
Builder: a creationalpattern 目标是“创建复杂对象”,灵活扩展
Bridge是OOP最基本的structural pattern,通过delegation+inheritance 建立两个具体类之间的关系(DIP依赖转置,抽象依赖于抽象)。
例子:
各种Shape,希望具备绘图功能, delegate到专门的绘图。
Createbridge implementerinterface:
//绘图类的抽象接口
public interface DrawAPI {
public void drawCircle(int radius, int x, int y);
}
Create concrete bridge implementer classes:
//绘图类的各种实现
public class DrawRedCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println(“Color: red " + radius + x + y);
}
}
public class DrawGreenCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println(“Color: green " + radius + x + y);
}
}
Create an abstract class Shape using the DrawAPI interface:
public abstract class Shape {
//永久保存delegation并在其他场合使用
protected DrawAPI drawAPI;
//动态传入,建立delegation link
protected Shape(DrawAPI drawAPI){
this.drawAPI = drawAPI;
}
public abstract void draw();
}
Create concrete class implementingthe Shape interface
public class Circle extends Shape {
private int x, y, radius;
public Circle(int x, int y, int radius, DrawAPI drawAPI) {
super(drawAPI);
this.x = x;
this.y = y;
this.radius = radius;
}
//将draw这个责任delegate到drawAPI这个接口的具体实例去执行
public void draw() {
drawAPI.drawCircle(radius,x,y);
}
}
Use the Shape and DrawAPI classes to draw different colored circles
public class BridgePatternDemo {
public static void main(String[] args) {
//Circle是上面左侧继承树中的子类
//DrawRedCircle 和 DrawGreenCircle右侧继承树中的子类
//运行时在二者之间动态建立delegation关系
Shape redCircle = new Circle(100,100, 10, new DrawRedCircle());
Shape greenCircle = new Circle(100,100, 10, new DrawGreenCircle());
//这里调用draw()已经不再是本模式关注的重点了
redCircle.draw();
greenCircle.draw();
}
}
Bridge: structural pattern 强调双方的run time delegation linking :
一个类A的对象中有其他类B的对象作为其组成部分,但A的对象具体绑定到B的哪个具体子类的实现?在运行时通过delegation加以组合, 并永久保存这种delegation关系。
Bridge强调结构关系的动态绑定,举例来说,类A需要完成“通讯”功能,它有一个组成部分Device类,这是个抽象接口(模式中的implementor),有多种实现方式(PC, tablet, phone,即ConcreteImplementor),该模式在运行时为类A的具体子类型实例设定具体的Device的具体实现(强调对A和Device两个抽象类的各自实例之间如何建立delegation),进而在其他各项功能中利用其通讯功能(这不再是bridge关注的目标)。
Strategy: behavioral pattern 强调一方run-time使用另一方的“算法” :
“算法”通常实现为“类的某个方法”的形式,strategy的目的并非在“调用算法的类”与“被调用算法所在的类”之间建立起永久联系,而只是帮助前者临时使用后者中的“算法”,前者无需永久保存后者的实例。
举例子来说,Strategy强调算法的动态调用,但前提是动态绑定。使用螺丝刀的时候,针对不同的工作任务,选取不同的“刀头”,但目的并非将 螺丝刀与刀头组合起来建立永久的delegation, 而只是临时通过delegation完成任务(即调用刀 头的“算法”),然后二者再无联系。
针对的情况是某个对象比较“敏感”/“私密”/“贵重”,不希望被client直接访问到,故设置proxy,在二者之间建立防火墙。
Adaptor: structural pattern
目的:消除不兼容,目的是B以客户端期望的统一的方式与A建立起联系。
Proxy: behavioral pattern
目的:隔离对复杂对象的访问,降低难度/代价,定位在“访问/使用行为”。
意图:将对象组成树结构来表示整体部分的层次结构。
例子:
public class Employee {
private String name;
private List subordinates;
public Employee(String name) {
this.name = name;
subordinates = new ArrayList();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
public List getSubordinates(){
return subordinates;
}
public String toString(){
…
}
}
Employee CEO = new Employee("John");
Employee headSales = new Employee("Robert");
Employee clerk1 = new Employee("Laura");
Employee clerk2 = new Employee("Bob");
CEO.add(headSales);
CEO.add(headMarketing);
clerk1.add(clerk1);
clerk1.add(clerk2);
System.out.println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
System.out.println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
System.out.println(employee);
}
}
Composite: structural pattern
目的是在同类型的对象之间建立起树型层次结构,一个上层对象可包含多个下层对象 。
Decorator: structural pattern
强调的是同类型对象之间的“特性增加”问题, 它们之间是平等的,区别在于 “拥有特性”的多少,每次decoration 只能作用于一个object。
举例子来说,“粉丝”对“偶像”感兴趣,希望随时得知偶像的一举一动 ,因此粉丝可以到偶像那里注册,偶像一旦有新闻发生,就推送给已注册的粉丝 (回调callback粉丝的特定功能)。
对特定类型的object的特定操作(visit),在运行时将二者动态绑定到一起,该操作可以灵活更改,无需更改被visit的类。本质上:将数据和作用于数据上的某种/些特定操作分离开来。
Iterator: behavioral pattern
迭代器:以遍历的方式访问集合数据而无需暴露其内部表示,将“遍历”这项功能delegate到外部的iterator对象。
Visitor: behavioral pattern
在特定ADT上执行某种特定操作,但该操作不在ADT内部实现,而是delegate到独立的visitor对象,客户端可灵活扩展/改变visitor的操作算法,而不影响ADT。
二者都是通过delegation建立两个对象的动态联系
区别:visitor是站在外部client的角度,灵活增加对ADT的各种不同操作(哪怕ADT没实现该操作),strategy则是站在内部ADT的角度, 灵活变化对其内部功能的不同配置。
多个对象之间要进行交互,不是直接交互,而是通过mediator,实现消息的广播,从而将对象之间松散耦合,利于变化 。
Observer pattern:
一组object对另一个object B的状 态变化感兴趣(1对多),所以对其进行观察。B维持着一个对自己感 兴趣的object list,一旦自己发生变化,就通知这些object。双方地位 并不对等,一方只是“通知”,另一方接到通知后执行特定行为。
Mediator pattern:
一组同类型的object,彼此之间相互 发/收消息(多对多),不存在谁观察谁的问题,所有object都对等。 每个object都维持一个mediator,将通讯的任务delegate给它,自己只 负责send和receive即可,无需了解其他object,实现了“解耦”。
Observer:自己来广播,其他对象接收;
Mediator:第三方中介负责广播(类似于“邮件列表”)。
将“指令”封装为对象,指令的所有细节对client隐藏,在指令 内对具体的ADT发出动作(调用ADT的细节操作),解决的问题是客户端希望执行指令,但不想知道指令的 细节,也不想知道指令的具体作用对象。
将所有对client提供的指令与内部执行的ADT操作彻底分开,指令对外看来是“黑盒”——进一 步“变本加厉”的封装!
均强调对某个复杂系统内部提供的功能的“封装”,对外提供简单的 调用接口,简化client的使用,“隐藏”细节 。
Command:强调将指令封装为了“对象”,提供了统一的对外接口 (execute) 。
Façade:没有显式的“对象”,仍然通过类的方法加以调用。
针对一个请求,可能有多个处理模块 。各种不确定情况存在,不能以“硬编码”的方式指明按何种次序调用处理模块 。
避免在请求方和各handler之间的紧耦合:构造流水线,请求在其上传递,直到被处理为止。
客户端只需在流水线的入口发出请求即可,请求自动在流水线上传递,直到被处理。
处理器对象的数目和类型事先都不确定,需要动态配置。使用递归组合的方式,将多个 handlers连成“职责链” 。请求的发起者无需维护所有可能的handler对象,只需记录职责链的 入口;每个handler只需要维护“下一个”handler即可,从而简化了各 handlers之间的连接关系。
不是像传统类设计中将操作与数据捆绑在一起形成ADT,这两个设计模式都是将“数据”与“作用于数据上的客户端定制操作”分离开来。
原因:操作可以灵活增加、运行时使用的操作可以动态配置、多个操作的执行次序可以动态变化。
区别1:visitor只定义了一个操作,chain of responsibility定义了一组操作及其之间的次序。
区别2:visitor中,client创建visitor之后传入ADT,ADT再将执行权 delegate到visitor;chain of responsibility中,控制权完全在各个 handler之间流转,ADT(request对象)完全感受不到。