责任链模式

将请求中设计的步骤分离,并采用统一的接口(称作处理器吧),可以采用树形或者链表等的方式来添加处理器。

classDiagram

class ProcessorBase{
    <<Interface>>
    +process()bool
}

ProcessorBase<..Processor1
class Processor1{
    +process()bool
}

ProcessorBase<..Processor2
class Processor2{
    +process()bool
}

ProcessorBase<..Processor3
class Processor3{
    +process()bool
}

class Manager{
    +Tree~ProcessorBase~ processers
    +process()bool
}

这样,所有需要处理,或者处理转移的过程就交给Manager来管理,用户只需要在意Manager::process函数调用即可。

命令模式

命令模式是,将请求封装成一个命令,用户只需要在意命令是什么即可,调用函数都是一样的。比如常用的,mysql.excute(string)数据库调用函数、system.excute(string)系统调用函数等等

解释器

解释器也是通过传入匹配模式,解释器会解释传入的匹配模式,依次来运行。比如正则表达式,通过传入正则表达式匹配传,来检查传入的文本中有没有需要的。

迭代器

正像C++的迭代器一样,迭代器就是用来遍历容器的,迭代器会有一个接口,用来指向下一个元素。还需要有一个接口,用来判断是否该停止了。

classDiagram
class Iterator{
    +next()Iteraotr
    +stop()bool
}

中介模式

设想许多的组件类之间需要互相影响,比如一个按钮会影响一些文本组件,一个文本组件也会影响一个按钮组件。但是,如果想要获取对方控件,那样就会形成复杂的图,因此,采用一个中间件来跟所有组件交互,这样逻辑就变的清晰多了。

下面是不用中介模式的

classDiagram
class Button1
class Button2
class Text1
class Text2
class Radio1
class Radio2

Button1-->Button2
Button1-->Radio1
Button2-->Text2
Button2-->Text1
Radio1-->Text1
Radio2-->Text2
Text2-->Button1
Button2-->Radio2

现在可以选择引入一个中介,其他控件只需要注册到中介者即可,中介者会提供一些接口,比如通知某个控件,通知某些控件,通知所有控件做某种事等等,并且如果多个通知还可以汇聚一起,使得只需要通知一次即可。

classDiagram
class Mediator{
    -Components
    +Register(component)
    +Notify(components)
    +Notify()
}

class Button1
class Button2
class Text1
class Text2
class Radio1
class Radio2

Mediator<-->Button1
Mediator<-->Button2
Mediator<-->Text1
Mediator<-->Text2
Mediator<-->Radio1
Mediator<-->Radio2

备忘录模式

备忘录模式是为了保存和恢复状态的,由于对于一些来说,它内部的私有数据是无法被访问的,所以想要保存它的状态还得看它是否提供了获取所有状态的接口,也许有些状态并不能被外界获取等等,因此最简单的备忘录模式是这个类自己实现一对save-load函数。又或者是在类中,将所有状态相关的私有变量提取成一个嵌套类,然后嵌套类实现save-load函数。又或者使得某个管理类(成为备忘录类)来获取状态类的所有状态,交给他来保存。

总结如下:

  1. 一个类实现save-load函数

  2. 一个类将需要保存的状态提取出成为一个单独的类,在这个状态类中实现save-load,或者可以由某个类来获取内部状态

  3. 保存和恢复状态的功能就叫做备忘录模式

观察者模式

观察者模式就是发布-订阅模式,是一种通知机制。它非常像中介模式,但是中介模式注重于切断所有组件之间的通信,而观察者模式注重于所有组件之间的某个需求通信模式,并不在乎组件之间有没有其他的通信。

classDiagram
class Mediator{
    -Components
    +Register(component)
    +Notify(components)
    +Notify()
}

class Button1
class Button2
class Text1
class Text2
class Radio1
class Radio2

Mediator<-->Button1
Mediator<-->Button2
Mediator<-->Text1
Mediator<-->Text2
Mediator<-->Radio1
Mediator<-->Radio2

Button1<-->Button2
Button1<-->Text1
Button1<-->Text2
Button1<-->Radio1
Button1<-->Radio1

状态模式

状态模式就是有限状态机,状态之间的切换叫做转移,状态往往是有限的,只会处于一种状态中,状态都应该是明确的,不会存在未定义的状态。

stateDiagram
A-->B:Condition_A2B
B-->C:Condition_B2C
B-->D:Condition_B2D
B-->E:Condition_B2E
E-->C:Condition_E2C

策略模式

策略模式就是,将函数方法作为参数传入,也就是对于实现逻辑的类中,不应该在乎采取什么策略,只在乎采取什么流程。比如排序中,通过参数可以把排序判断策略通过参数传入。又或者,防火墙只在意拦截,不在乎什么样的IP能够通过,判断就是一种策略,交给外部传入即可。

下面是一个防火墙的例子

classDiagram
class Firewall{
    +Interception(IPv4, Strategy)
}

class Strategy{
    +Pass(IPv4)bool
}

Strategy<|..USIpStrategy
class USIpStrategy{
    +Pass(IPv4)bool
}

Strategy<|..UKIpStrategy
class UKIpStrategy{
    +Pass(IPv4)bool
}

Strategy<|..JPIpStrategy
class JPIpStrategy{
    +Pass(IPv4)bool
}

模板方法模式

这是一个非常简单的模式,如果我们定义了一系列复杂的过程,但是对于中间的某些过程不知道如何处理,又或者中间的过程可能会变化。那么就将这一系列过程定义成一个基类,这其中不确定的子过程定义成虚函数,交给子类实现。

下面以一个烹饪番茄炒鸡蛋为例子:

class ScrambledEggsWithTomato{
public:
    void process(){
        getEggs();        // 获取鸡蛋
        stirringEggs();   // 搅拌鸡蛋
        getTomatos();     // 获取番茄
        cuttingTomatoes();// 切番茄
        turningOnFire();  // 开火
        pourOil();        // 倒油
        stirFrying();     // 翻炒
        serving();        // 盛盘
    }

    // 不知道如何获取鸡蛋和番茄,或者不知道从哪获取鸡蛋和番茄,那么就把他们定义为虚函数
    virtual void getEggs() = 0;
    virtual void getTomatos() = 0;
}

访问者模式

假设已经有一个比较完备的类,想为它添加一些方法。又或者,对于一个类,有一些功能想加入但是又感觉不应该由这个类来管理。那么就可以使用访问者模式,把这个类传给访问者类,然后由访问者实现这些新的功能或者不强相干的功能。

classDiagram
class Visitor{
    <<Interface>>
    +visitA(element: ElementA)
    +visitB(element: ElementB)
    +visitC(element: ElementC)
}
class Element{
    <<Interface>>
    +accept(visitor)
}

Element<|..ElementA
class ElementA{
    +accept(visitor)
}

Element<|..ElementB
class ElementB{
    +accept(visitor)
}
Element<|..ElementC
class ElementC{
    +accept(visitor)
}

Visitor<|..Visitor1
class Visitor1{
    +visitA(element: ElementA)
    +visitB(element: ElementB)
    +visitC(element: ElementC)
}

Visitor<|..Visitor2
class Visitor2{
    +visitA(element: ElementA)
    +visitB(element: ElementB)
    +visitC(element: ElementC)
}

Visitor<|..Visitor3
class Visitor3{
    +visitA(element: ElementA)
    +visitB(element: ElementB)
    +visitC(element: ElementC)
}

注意看到,对于Visitor来说,会执行具体的Visit,并且需要传入实际的类型(如ElementA)而不是抽象的类型(Element),因为他需要获取实际类的成员来进行行为。而我们在Element中也会注册Visitor,让Visitor能够访问ElementA,这是一个双分派的模式。双回调,在ElementA中回调Vistitor,在Visitor中回调ElementA的状态等等。

总结

  • 责任链模式:将请求研究链传递,单个请求只注重单个请求的功能
  • 命令模式:将请求通过命令来实现,调用者专注于命令编写即可
  • 解释器模式:将请求定义成一种匹配模式等等,调用者专注于匹配模式编写即可
  • 迭代器模式:为容器提供统一接口遍历
  • 中介者模式:降低多个组件之间的耦合,将多对多通信改成一对一通信
  • 观察者模式:发布-订阅模式
  • 备忘录模式:状态保存-恢复模式
  • 状态模式:有限状态机
  • 模板方法模式:基类只专注于整体执行流程,子过程定义成虚函数交给子类来实现
  • 访问者模式:将不相干的方法或新增的方法与对象分离,不影响已有对象的结构