设计模式速查手册
作者: 刘鹏整理
日期: 2009-04-16
本文简单明了地总结了 23 种设计模式,介绍了每种模式的意图、适用环境,并给出了类图和 C++ 代码,是个不错的速查手册。

创建型模式1

单例模式 (Singleton)

名称:Singleton(单件模式);

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点;

适用环境

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时;
  • 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

C++实现代码


class Singleton {
public:
    static Singleton* Instance();
protected:
    Singleton(){}
private:
    static Singleton* _instance;
};

Singleton* Singleton::_instance = 0;

Singleton* Singleton::Instance () {
    if (_instance == 0) {
        _instance = new Singleton;
    }
    return _instance;
}

工厂方法模式 (Factory Method)

名称:Factory Method(工厂模式)

意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。

适用环境

  • 当一个类不知道它所必须创建的对象的类的时候;
  • 当一个类希望由它的子类来指定它所创建的对象的时候;
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一 个帮助子类;
  • 是代理者这一信息局部化的时候。

简单工厂 VS 工厂方法

简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。

工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现具体产品,工厂 方法把简单工厂的内部逻辑判断移到了客户端代码来进行。想要加功能,本来是 改工厂类的,而现在是修改客户端。

C++实现


class Product {};

class ConcreteProduct : public Product
{
public:
    ConcreteProduct()
    {
        cout<<"ConcreteProduct"<<endl;
    }
};

class ConcreteProduct2 : public Product
{
public:
    ConcreteProduct2()
    {
        cout<<"ConcreteProduct2"<<endl;
    }
};

class Creator
{
public:
    virtual Product * FactoryMethod()=0;
};

class ConcreteCreator : public Creator
{
public:
    Product * FactoryMethod()
    {
        return new ConcreteProduct;
    }
};

class ConcreteCreator2 : public Creator
{
public:
    Product * FactoryMethod()
    {
        return new ConcreteProduct2;
    }
};

int main()
{
    Product *product;
    bool flag = false; //逻辑判断

    if(flag)
    {
        ConcreteCreator concreteCreator;
        product = concreteCreator.FactoryMethod();
    }
    else
    {
       ConcreteCreator2 concreteCreator;
       product = concreteCreator.FactoryMethod();
    }
}

运行结果

ConcreteProduct2

抽象工厂模式 (Abstract Factoy)

建造者模式 (Buidler)

名称:Builder(生成器模式)

意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不 同的表示。

适用环境

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时;
  • 当构造过程必须允许被构造的对象有不同的表示时。

建造者模式主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常 是稳定的,但对象内部的构建通常面临着复杂的变化。

C++实现


#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Product
{
public:
    void Add(string part)
    {
        parts.push_back(part);
    }

    void show()
    {
        cout<<"--- Product builder ---"<<endl;
        for(vector<string>::iterator iter = parts.begin(); iter != parts.end(); ++iter)
            cout<<*iter<<endl;
    }

    private:
        vector<string> parts;
};


class Builder
{
public:
    virtual void BuilderPartA()=0;
    virtual void BuilderPartB()=0;
    virtual Product * GetResult()=0;
};

class ConcreteBuilder1 : public Builder
{
public:
    ConcreteBuilder1()
    {
        product = new Product;
    }

    void BuilderPartA()
    {
        product->Add("Part A");
    }

    void BuilderPartB()
    {
        product->Add("Part B");
    }

    Product * GetResult()
    {
        return product;
    }

private:
    Product *product;
};

class ConcreteBuilder2 : public Builder
{
public:
    ConcreteBuilder2()
    {
        product = new Product;
    }

    void BuilderPartA()
    {
        product->Add("Part X");
    }

    void BuilderPartB()
    {
        product->Add("Part Y");
    }

    Product * GetResult()
    {
        return product;
    }

private:
    Product *product;
};

class Director
{
public:
    void Construct(Builder *builder)
    {
        builder->BuilderPartA();
        builder->BuilderPartB();
    }
};

int main()
{
    Director *director = new Director;
    Builder *b1 = new ConcreteBuilder1;
    Builder *b2 = new ConcreteBuilder2;

    director->Construct(b1);
    Product *p1 = b1->GetResult();
    p1->show();

    director->Construct(b2);
    Product *p2 = b2->GetResult();
    p2->show();
}

运行结果


--- Product builder ---
Part A
Part B
--- Product builder ---
Part X
Part Y

原型模式 (Prototype)

名称:Prototype(原型模式)

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

适用环境

  • 当一个系统应该独立于它的产品创建、构成和表示时,要使用Prototype模式;
  • 当要实例化的类是在运行时刻指定时,例如,通过动态装载;或者为了避免创建一个与产品类层次平行的工厂类层次时;或者 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们;
  • 可能比每次用合适的状态手工实例化该类更方便一些。

原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何 创建的细节。

C++实现


class Prototype
{
public:
    virtual Prototype * Clone()=0;
};


class Object : public Prototype
{
public:
    Object(){}

    Object(int m)
    {
        n = m;
    }

    Object(Object &object)
    {
        SetValue(object.GetValue());
    }

    void SetValue(int n)
    {
        this->n = n;
    }

    int GetValue()
    {
        return n;
    }

    Prototype * Clone()
    {
        return new Object(*this);
    }

    void show()
    {
        cout<<n<<endl;
    }

private:
    int n;
};

class ConcretePrototype1 : public Prototype
{
public:
    ConcretePrototype1(){}

    ConcretePrototype1(Object *obj)
    {
        SetObject(obj);
    }

    ConcretePrototype1(ConcretePrototype1 &concretePrototype1)
    {
        //SetObject(concretePrototype1.GetObject());      //浅拷贝
        SetObject((Object *)(concretePrototype1.GetObject()->Clone())); //深拷贝
    }

    Prototype * Clone()
    {
        return new ConcretePrototype1(*this);
    }

    void SetValue(int n)
    {
        object->SetValue(n);
    }

    void show()
    {
        object->show();
    }

    void SetObject(Object *obj)
    {
        object = obj;
    }

    Object * GetObject()
    {
        return object;
    }

private:
    Object *object;
};

int main()
{
/*
    ConcretePrototype1 concretePrototype1(new Object(1));
    //concretePrototype1.SetObject(new Object(10));
    ConcretePrototype1 concretePrototype2 = *(ConcretePrototype1 *)concretePrototype1.Clone();
    concretePrototype1.show();
    concretePrototype2.show();

    concretePrototype1.SetValue(2);
    concretePrototype1.show();
    concretePrototype2.show();
*/
    Prototype *prototype = new ConcretePrototype1(new Object(1));
    ConcretePrototype1 * prototype1 = (ConcretePrototype1 *)prototype;
    //prototype1->SetObject(new Object(10));
    ConcretePrototype1 * prototype2 = (ConcretePrototype1 *)prototype->Clone();
    prototype1->show();
    prototype2->show();

    prototype1->SetValue(2);
    prototype1->show();
    prototype2->show();
}

浅拷贝运行结果


1
1
2
2

深拷贝运行结果

1
1
2
1

结构型模式

适配器模式 (Adapter)

名称:Adapter(适配器模式);

意图:将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适用环境

  • 你想使用一个已经存在的类,而它的接口不符合你的需求;
  • 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作;
  • (仅适用于对象Adapter)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。

C++实现代码


class Target
{
public:
    //virtual void Request()=0;
    virtual void Request()
    {
        cout<<"Common request."<<endl;
    }
};

class Adaptee
{
public:
    void SpecificRequest()
    {
       cout<<"Specific request."<<endl;
    }
};

class Adapter : Target
{
public:
    Adapter()
    {
       adaptee = new Adaptee;
    }

    void Request()
    {
        adaptee->SpecificRequest();
    }

private:
    Adaptee *adaptee;
};

int main()
{
    Target *target = (Target *)new Adapter;
    target->Request();
}

运行结果

Specific request.

代理模式 (proxy)

名称:proxy(代理模式)

意图:为其他对象提供一种代理以控制对这个对象的访问。

适用环境

在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。下面是一些可以使用Proxy模式常见情况:

  1. 远程代理(Remote Proxy )为一个对象在不同的地址空间提供局部代表。
  2. 虚代理(Virtual Proxy )根据需要创建开销很大的对象。
  3. 保护代理(Protection Proxy )控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
  4. 智能指引(Smart Reference )取代了简单的指针,它在访问对象时执行一些附加操作。它的典型用途包括:
    1. 对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它。
    2. 当第一次引用一个持久对象时,将它装入内存。
    3. 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能 改变它。
  5. android NDK 就是典型的 proxy 模式

C++实现代码


class Subject
{
public:
virtual void Request()=0;
};

class RealSubject : public Subject
{
public:
void Request()
{
   cout<<"Test."<<endl;
}
};

class Proxy : public Subject
{
public:
Proxy()
{
   realSubject = NULL;
}

void Request()
{
   if(!realSubject)
    realSubject = new RealSubject;

  realSubject->Request();
}

private:
RealSubject *realSubject;
};

int main()
{
Proxy *proxy = new Proxy;
proxy->Request();
}

行为型模式

GOF 未提到的设计模式

委托模式 (delegation)2

名称:delegation(委托模式)

意图:一个对象在外界来看好像实现了一些行为,但实际上是委托给相关的其他类来实现行为的。

适用环境

在不可以使用继承,而采用聚合时,必须使用这种技术。

缺点:

这个模式是典型的牺牲性能提高抽象程序的清晰程度. (或者说提高代码可读性)

一个简单的Java例子

这个例子中,C 拥有调用 A 中 f() 和 g() 的插口,看起来 C 好像有 A 的功能。


class A {
    void f() { system.out.println("A: doing f()"; }
   void g() { system.out.println("A: doing g()"; }
}

class C {
   // delegation
   A a = new A();
   void f() { a.f(); }
   void g() { a.g(); }
   // normal attributes
   X x = new X();
   void y() { /* do stuff */ }
}

void main() {
   C c = new C();
   c.f();
   c.g();
}

一个复杂些的 Java 例子

使用接口+委托可以提高程序灵活性,和类型的安全性.这个例子中,C代理了A,B二 者之一.C可以在A,B之间切换.由于A,B都必须通过实现接口以实现功能,这就提高 了了类型安全性.作为折中,当然也需要写更多的代码。


interface I {
    void f();
   void g();
}

class A implements I {
   void f() { system.out.println("A: doing f()"; }
   void g() { system.out.println("A: doing g()"; }
}

class B implements I {
   void f() { system.out.println("B: doing f()"; }
   void g() { system.out.println("B: doing g()"; }
}

class C implements I {
   // delegation
   I i = new A();
   void f() { i.f(); }
   void g() { i.g(); }
   // normal attributes
   void toA() { i = new A(); }
   void toB() { i = new B(); }
}

void main() {
   C c = new C();
   c.f();
   c.g();
}

Reference

  1. 设计模式总结
  2. delegation模式