行为模式
- 1. Chain of Responsibility Pattern(责任链模式)
- 2.Command Pattern(命令模式)
- 3.Interpreter Pattern(解释器模式)▲
- 4.Iterator(迭代器模式)
- 5.Mediator(中介者模式)
- 6. Memento(备忘录模式)
- 7.Observer Pattern(观察者模式)
- 8. State Pattern(状态模式)
- 9. Strategy Pattern(策略模式)
- 10.Template Method Pattern(模板方法模式)
- 11.Visitor Pattern(访问者模式)
- 模式之间关系
1. Chain of Responsibility Pattern(责任链模式)
模式定义
责任链模式允许将请求沿着处理者链进行传递,直到有一个处理者能够处理该请求。每个处理者都包含对下一个处理者的引用,形成一个链条。
模式构成
- Handler(处理者):定义一个处理请求的接口,通常包含一个指向下一个处理者的引用。处理者可以决定自己是否处理请求,或者将请求传递给下一个处理者。
- ConcreteHandler(具体处理者):实现处理请求的具体处理逻辑。如果自己能够处理请求,则处理请求;否则将请求传递给下一个处理者。
- Client(客户端):创建请求对象并将其传递给责任链的第一个处理者,从而启动责任链的处理过程。
模式特点
- 降低耦合度:请求发送者和接收者之间解耦,每个处理者只需关注自己的责任范围。
- 灵活性:可以动态地改变责任链的组成,增加或删除处理者,或者改变处理者之间的顺序。
- 可扩展性:易于新增处理者,扩展新的业务逻辑,不需要修改已有代码。
常见的应用场景包括
- 有多个对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
- 在不明确指定接受者的情况下,向多个对象中的一个提交一个请求。
- 可处理一个请求的对象集合应能被动态指定。
抽象场景
考虑一个图形用户界面中的窗口上下文有关的帮助机制,用户在界面的任意部分点击就可以获取到帮助信息,所提供的帮助信息依赖于点击的是界面的那一部分以及其上下文。下面是一个界面以及不同窗口上下文的嵌套关系:
实现上,每个在链上的对象都要有一致的处理请求和访问链上后继者的接口,因此,可以定义一个带有HandleHelp操作的HelpHandler类作为基类来处理链式请求。例如,按钮、对话框和应用类都使用HelpHandler类来处理获取帮助的请求(HelpHandler作为他们的基类),HelpHandler的HandlerHelp操作缺省是将请求转发给后继者,子类可重定义这一操作以在条件符合的情况下提供帮助,否则可使用缺省实现转发该请求。
示例伪代码
//定义一个处理者类
class HelpHandler {
public:
HelpHandler (HelpHandler * successor) : _successor(successor) {} //构造函数中将后继者链接到链上,即给后继者成员变量赋值
virtual void HandleHelp(const std::string& request) //默认将请求转发给后继者
{
if (_successor)
{
_successor->HandleHelp(request);
}
}
protected:
HelpHandler* _successor; //后继者
};
//具体的处理者类
class Widget : public HelpHandler
{
public:
Widget(HandleHelp* h) : HelpHandler(h){} //加后继者
Widget(){}//不加后继者
void HandleHelp(const std::string& request)
{
if(match(request))
{
//do..
}
else
{
HelpHandler::HandleHelp();
}
}
}
class Button : public HelpHandler
{
//与Widget实现一样
}
class Dialog : public HelpHandler
{
//...
}
//test
Dialog * d = new Dialog();
Button * b = new Button(d);
Widget * w = new Widget(b);
w->HandleHelp("dialog");
2.Command Pattern(命令模式)
模式定义
将请求封装成一个对象,从而允许客户端通过不同的请求参数化对象,并支持请求队列、请求日志记录、撤销请求等功能。这种方式可以将命令的发送者与命令的接收者解耦,从而实现更松散的耦合。
模式构成
- Command(命令):定义命令的接口,包括执行命令的方法 execute()。
- ConcreteCommand(具体命令):实现命令接口,封装了命令的具体行为和接收者。
- Receiver(接收者):知道如何实施与执行一个请求相关的操作。
- Invoker(调用者):要求命令执行请求的对象,通过命令对象来处理请求。
- Client(客户端):创建具体命令对象并设置其接收者,然后将命令对象传递给调用者。
模式工作原理
该模式通过命令类来降低发送者和接收者的耦合度。具体的行为有:
(1)客户端需要创建一个具体命令对象,并指定它的接收者对象。
(2)调用者对象保存该具体命令对象,并通过调用命令对象的Execute操作来提交一个请求。
(3)最终,具体命令对象会调用接受者对象的方法来执行该请求。
模式特点
- 解耦:发送者和接收者之间解耦,发送者只需知道如何发送命令,而不需要知道如何执行命令。
- 可扩展性:易于添加新的命令,无需修改已有代码。
- 支持撤销和重做:可以轻松实现命令的撤销和重做功能(命令类中新增对应接口即可)。
常见的应用场景包括
- GUI应用程序中,命令模式常用于实现菜单和工具栏按钮的操作。每个菜单项或按钮可以被表示为一个命令对象,从而实现操作的撤销、重做以及动态变化。
- 文本编辑器中,命令模式可以用于实现撤销、重做、复制、粘贴、剪切等操作,每个操作都可以被封装成一个命令对象。
- 实现日志系统,每个日志记录操作可以被封装成一个命令对象,方便记录日志并支持撤销。
- 实现事务系统,一个事务封装了多个命令对象。
- …
抽象场景
用命令模式可以很容易为应用实现菜单,每一个菜单选项配置一个具体的Command子类的实例,当用户选择一个菜单选项时,应用会调用它的Command对象的Execute方法,由Execute执行相应的操作,Execute操作将调用某个接收者的一个或多个操作。
例如,一个粘贴的菜单项,配置一个PasteCommand类的实例,它的接收者是一个文档对象,Execute操作将调用文档对象的Paste操作。对应的打开、关闭、复制等操作都同理。示例伪代码
class Command
{
public:
virtual void Execute() = 0;
}
class OpenCommand : public Command
{
public:
OpenCommand(string str, Document *doc) : _str(str), _doc(doc){}
void Execute()
{
if(_doc) _doc->open(_str);
}
private:
Document *_doc;
string _str;
}
class PasteCommand : public Command
{...}
class Application
{
public:
void setOpenCommand(Command *c){_command = c;} //设置命令
void openMenu()//执行打开菜单
{
if(_command) _command->Execute();
}
...
private:
Command *_command;
}
//test open menu
Document *doc = new Document(); //接收者
OpenCommand *open = new OpenCommand("file", doc); //具体命令
Application *app = new Application();
app->setOpenCommand(open);
app->openMenu();
3.Interpreter Pattern(解释器模式)▲
模式定义
解释器模式用于定义一个语言的文法,并定义一个解释器,该解释器使用文法解释语言中的句子。它为解决特定类型的问题提供了一种灵活的解决方案,可以用于解释和执行特定语言或规则。
模式构成
- 抽象表达式(Abstract Expression):定义解释器的接口,包括一个 interpret() 方法,用于解释语言中的表达式。这个接口为抽象语法树中所有节点所共享。
- 终结符表达式(Terminal Expression):实现抽象表达式中的 interpret() 方法,用于解释语言中的终结符,可以理解为一条规则产生的最终结果。
- 非终结符表达式(Non-terminal Expression):通常是复合表达式,包含多个子表达式,每个子表达式可以是终结符表达式或其他非终结符表达式。文法中每条规则都需要一个非终结符表达式。
- 环境类(Context):包含解释器解释的全局信息或状态,通常用于传递表达式中的信息。
模式工作原理
- 解释器模式通过定义一种语言的语法表示,并解释语法表示的句子,将句子翻译成实际操作。
- 客户端创建并配置特定的表达式对象,形成一个表示特定语言的表达式树。
- 解释器按照表达式树的结构,逐步解释表达式。从根节点开始,递归地解释每个子表达式。
模式特点
- 灵活,可以轻松地扩展语言或规则,而不必修改现有代码。
- 可维护,将语法规则表达为类结构,使得维护和理解代码更加容易。
- 解释器模式在处理复杂的语法规则时非常有用,但对于简单的情况可能会增加代码复杂性。
常见的应用场景包括
- SQL解释器
- 编程语言解释器
- 正则表达式引擎
- …
抽象场景
实现一个布尔表达式求值器。布尔表达式拥有的运算符有:and、or、not,其规则定义如下:
boolExp = orExp | andExp | notExp | constant | varExp;
orExp = boolExp or boolExp ;
andExp = boolExp and boolExp ;
notExp = not boolExp ;
varExp = a-z | 0-9 | A-Z…;
constant = true | false;
其中constant为规则中的终结符表达式,即布尔常量true和false;其它几种均为非终结符表达式。
(1== 2)or(2 ==2)and 3 and(true)
示例伪代码
//抽象表达式boolExp
class BooleanExp
{
public:
virtual bool interpret() = 0;
}
//非终结符表达式varExp
class VariableExp: public BooleanExp
{
public:
VariableExp(char* var):_var(var){}
bool interpret(){ return context(_var) ;} //context是用来解释_var是真是假的函数,即它会返回一个终结符表达式true或者false
char* _var;
}
//非终结符表达式andExp
class AndExp: public BooleanExp
{
public:
AndExp(BooleanExp *b1, BooleanExp *b2):_b1(b1), _b2(b2){}
bool interpret(){ return _b1.interpret() && _b2.interpret();}
BooleanExp *_b1;
BooleanExp *_b2;
}
class OrExp : BooleanExp{...} //同AndExp相似
class NotExp : BooleanExp{...}//同AndExp相似
//test : (true or'x' ) and (y and or'x')
VariableExp vx = new VariableExp('x');
VariableExp vy = new VariableExp('y');
BooleanExp *exp = new AndExp(new OrExp(vx, constant(true)) , new OrExp(vy, new NotExp(vx)));
exp->interpret();
4.Iterator(迭代器模式)
模式定义
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。即通过迭代器模式,可以在不暴露集合底层数据结构的情况下访问集合中的元素。
模式构成
- 迭代器(Iterator):定义了访问和遍历元素的接口,包括获取下一个元素、判断是否还有元素等方法。
- 具体迭代器(Concrete Iterator):实现迭代器接口,在具体集合上进行遍历和访问元素。
- 聚合对象(Aggregate):定义创建迭代器的接口,通常包含一个返回迭代器的方法。
- 具体聚合对象(Concrete Aggregate):实现创建迭代器接口,返回具体的迭代器。
模式特点
- 分离集合对象的遍历行为:迭代器模式将集合对象和遍历行为分离,使得可以独立地改变集合的遍历方式。
- 简化集合接口:通过迭代器模式,集合类可以专注于自身的数据结构和操作,而将遍历操作交给迭代器来实现。
- 支持多种遍历方式:迭代器模式可以针对同一个集合对象实现多种不同的遍历方式,提高灵活性。
抽象场景
实现一个简单List(列表)类的迭代器。
示例伪代码
//具体聚合对象(以标准库中的list为例)
template <class Item>
class List
{
public:
List(long size){...}
int size(){...}
Item &Get(long index) const {...}
Iterator *CreateIterator(){...}
//...
private:
//...
};
//迭代器
template <class Item>
class Iterator
{
public :
virtual void first() = 0;
virtual void next() = 0;
virtual void end() = 0;
virtual Item CurrentItem() const = 0;
//....
};
//具体迭代器
template <class Item>
class ListIterator
{
public:
ListIterator(const List<Item>* list):_list(list), _current(0) {}
void first() { _current = 0 ;}
void next() { _current++ ;}
bool end() { if(_current >= _list->size() ) return true; return false;}
Item CurrentItem() const { if(!end()) _list->Get(_current) ;}
private:
const List<Item>* _list;
long _current;
}
//test 假设有一个Node类的集合需要被遍历
List<Node> li;
ListIterator *it = li.CreateIterator();
for(it.first(); !it.end(); it.next)
{
do(it.CurrentItem());
}
delete it;
//代码中没有完整体现到模式构造图的各部分,完整的体现应该是给List一个抽象的父类class AbstractList,这样如果有一个SkipList类型的集合,也可以继承该类,以体现多态的原则。
5.Mediator(中介者模式)
模式定义
中介者模式通过一个中介对象来封装一系列对象交互。这样,对象之间就不需要显式地引用彼此,从而使其耦合度降低,同时也更容易维护和扩展系统。
模式构成
- 中介者(Mediator):定义了一个接口用于与各个同事对象通信。
- 具体中介者(Concrete Mediator):实现了中介者接口,协调各个同事对象的行为。
- 同事对象(Colleague):每个同事对象都知道中介者对象,与其他同事通过中介者进行通信。
- 具体同事对象(Concrete Colleague):实现各自的行为,需要与其他同事对象协同工作时,通过中介者进行通信。
模式特点
- 减少耦合:对象之间通过中介者通信,相互之间不直接引用,降低了对象之间的耦合度。
- 集中控制:中介者可以集中控制对象之间的交互行为,更容易管理和维护系统。
- 易扩展:增加新的同事对象或者改变同事对象的交互行为时,只需要修改中介者即可,不影响其他对象。
常见的应用场景包括
- 一组对象以复杂的方式进行通信,产生相互依赖关系混乱且难以理解。
- 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用。
- 想定制一个分布在多个类中的行为,而又不想生成太多子类。
抽象场景
实现上述对话框。对话框中各组件存在依赖关系,例如,当输入栏为空时,下面的OK按钮组件不能使用;输入文本匹配列表会匹配输入栏中输入的文本并展示一些相近的可供选择的文本条目;一旦输入栏有内容,调整文本格式的按钮组件就可以使用等等。
不同的对话框会有不同的窗口组件间的依赖关系。因此即使对话框显示相同类型的窗口组件,也不能简单的直接重用已有的窗口组件类型,而必须定制它们以反映特定对话框的依赖关系。由于涉及很多类,用逐个生成子类的方式来定制它们就会显得很冗余。此时,就可以通过将集体行为封装在一个单独的中介者对象中以避免这个问题。
结构如下:
示例伪代码
//定义一个中介者类
class DialogDirector
{
public:
virtual void ShowDialog();
virtual void WidgetChanged(Widget*) = 0;
virtual void CreateWidget() = 0;
};
//定义一个抽象同事类
class Widget
{
public:
virtual void Changed()
{_derector->WidgetChange(this);}
virtual void HandleMouset(MousetEvent& event);
//...
private:
DialogDirector* _derector;
}
//具体的同事类(通过MousetEvent驱动)
//输入文本匹配列表
class ListBox : public Widget
{
public:
const char *GetSelection(){}//获取某一条文本
void SetList(List<char*>* listItems){} //设置展示的文本列表
void HandleMouset(MousetEvent& event){changed();}//鼠标事件,例如双击选择某条文本
//...
}
//输入栏
class EntryField : public Widget
{
public:
void SetText(const char *text){} //设置文本内容
const char *GetText(){}//获取文本内容
void HandleMouset(MousetEvent& event){changed();}//鼠标事件
//...
}
//按钮
class Button : public Widget
{
public:
void HandleMouset(MousetEvent& event){changed();} //鼠标点击后通知其他组件改动
//...
}
//具体的中介类
class FontDialogDirector : public DialogDirecotr
{
public:
void CreateWidget() //创建需要展示的组件,这里也可以将各个组件以参数的形式传进来,这里为了简单直接函数中创建
{
_ok = new Button(this); _cancel = new Button(this);
_List = new ListBox(this);
_Field = new EntryField(this);
//...
}
void WidgetChanged(Widget* w) //核心,协调各组件之间的行为
{
if(w == _List){ _field->SetTxt(_List->GetSelection());}
else if(w == _ok) {...}
//...
}
private:
Button* _ok, *_cancel;
ListBox* _List;
EntryField* _Field;
}
//test 创建显示组件即可
FontDialogDirector d = new FontDialogDirector();
d->CreateWidget();
sleep();//等待鼠标或键盘等事件
6. Memento(备忘录模式)
模式定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。后续可以将该对象恢复到原先保存的状态。这种模式通常用于需要实现撤销操作或者保存对象历史状态的场景。
模式构成
- Originator(原发器):负责创建一个备忘录对象,用于记录当前时刻的内部状态,并可以使用备忘录对象恢复其状态。
- Memento(备忘录):存储Originator对象的内部状态。除原发器以外的对象都无法访问备忘录,只能将其递送给其他对象。
- Caretaker(管理者):负责保存备忘录,但不对备忘录的内容进行操作。
模式工作原理
管理器向原发器请求一个备忘录,保存在自己内部,在需要的时候将其送回给原发器。
模式特点
- 封装性,原发器对象的状态被封装在备忘录中,保证了数据的隐私性。
- 简化原发器,如果将原发器状态保存在自身内部,那么所有存储管理的重任都集中在原发器,使其过于复杂。
- 使用备忘录的代价可能会很高,如果原生器在生成备忘录时必须拷贝并存储大量信息时,这种模式可能并不适合。
值得注意的是,如果在原发器中通过接口让其他对象直接得到其状态,将会暴露原发器对象的实现细节,破坏其封装性。
常见的应用场景包括
备忘录模式在需要保存和恢复对象状态的情况下非常有用,例如文本编辑器的撤销功能、游戏中的进度保存等。
抽象场景
这个模式容易理解,没给出实际的例子,在示例代码给出了大致的框架。
示例伪代码
//Originator
class Originator
{
public:
Memento* Creatememento(){return new Memento().setState(_state);}
void setMemento(const Memento* m){_state = m->getState();}
//other interface...
private:
state* _state;
};
// Memento
class Memento
{
private:
friend class Originator; //因为备忘录是私有接口,将原发器设置为友元类,可以访问或更新状态。
state *getState(){return _state;}
void setState(state *s){ _state = s;}
state* _state;
};
// Caretaker
class Caretaker
{
public:
Caretaker(Originator *og):_og(og){}
void execute() //执行某个操作时,原发器保存自己的状态到备忘录
{
_memento = _og->Creatememento();
//do somethings...
}
void unexecute() //撤销操作时,原发器从备忘录回滚状态
{
_og->setMemento(_memento);