责任链模式
将请求中设计的步骤分离,并采用统一的接口(称作处理器吧),可以采用树形或者链表等的方式来添加处理器。
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
函数。又或者使得某个管理类(成为备忘录类)来获取状态类的所有状态,交给他来保存。
总结如下:
一个类实现
save-load
函数一个类将需要保存的状态提取出成为一个单独的类,在这个状态类中实现
save-load
,或者可以由某个类来获取内部状态保存和恢复状态的功能就叫做备忘录模式
观察者模式
观察者模式就是发布-订阅模式,是一种通知机制。它非常像中介模式,但是中介模式注重于切断所有组件之间的通信,而观察者模式注重于所有组件之间的某个需求通信模式,并不在乎组件之间有没有其他的通信。
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
的状态等等。
总结
- 责任链模式:将请求研究链传递,单个请求只注重单个请求的功能
- 命令模式:将请求通过命令来实现,调用者专注于命令编写即可
- 解释器模式:将请求定义成一种匹配模式等等,调用者专注于匹配模式编写即可
- 迭代器模式:为容器提供统一接口遍历
- 中介者模式:降低多个组件之间的耦合,将多对多通信改成一对一通信
- 观察者模式:发布-订阅模式
- 备忘录模式:状态保存-恢复模式
- 状态模式:有限状态机
- 模板方法模式:基类只专注于整体执行流程,子过程定义成虚函数交给子类来实现
- 访问者模式:将不相干的方法或新增的方法与对象分离,不影响已有对象的结构