设计模式读书笔记——行为型模式

1. chain of responsibility 职责链

使用: · 有多个对象可以处理一个请求, 而具体由哪个对象处理请求需要在运行时刻自动确定。

· 可以处理一个请求的对象集合应被动态指定

原理: 在链上的每个对象都有一致的处理请求的接口, 和访问链上后继者的接口。

从链上的第一个对象开始,要么亲自处理他,要么转发给链中的下一个候选者。

提交请求的客户并不明确知道哪一个对象会处理他,提交请求的可以并不直接引用最终响应他的对象。

实现:class HelpHandler ///////// 基类,维护链的父节点引用(successor)

{

HelpHandler(HelpHander * successor):_successor(successor){}

virtual void HandleEvent(Event e)

{

_successor->HandleEvent(e);

}

private:

HelpHandler * _successor;

};

class Bottun: public HelpHandler

{

Bottun(HelpHandler * h):HelpHandler(h){}

void HandleEvent(Event e)

{

if( can deal with event)

{

do some thing;

}

else

{

HelpHandler::HandleEvent(e); //////交给链的下一个对象

}

}

};

class Dialog: public HelpHandler

{

Dialog(HelpHandler * h):HelpHandler(h){}

void HandleEvent(Event e)

{

if( can deal with event)

{

do some thing;

}

else

{

HelpHandler::HandleEvent(e); //////交给链的下一个对象

}

}

};

app:

{

dialog = new Dialog(0); /////他下面没有结点

button = new Button(&dialog);  //链上他的下一结点

button->HandEvent(event); ///// event 可能会被dialog 或 button中的一个处理。

}

2. command 命令

使用: 1. command将调用操作的对象,与知道如何实现该操作的对象解耦。(命令的接收者在声明命令对象的时候装配)

但是这带来的好处是什么呢? 命令作为一个对象可以自己提供execute方法执行自己,省去了响应命令时的switch?或者是

响应命令的总入口(对象)不需要知道自己管理提供的所有功能了,也不需要自己去区分那个命令由那个功能处理了。

可能命令接收者仅能提供一些基础功能,而处理每个命令则需要对基础功能进行排序组合来实现,这样可以放到command里面自己去

实现(使用基础功能)。

2. 把命令作为一个对象处理,他有了一定的声明周期,为回滚提供了可能,只要每个command实现UnExecute方法。

3. 可以将多个命令组合为一个复合命令(组合模式)。

4. 新增command容易,不影响现有的类。

原理:将command封装成一个类,自己提供Execute方法。 同时自己可能会维护一个命令接收者的引用(在声明命令时注册进去),这是提供具体动作的对象。

必须考虑: 命令到底达到何种智能程度,是自己把命令全处理完,还是直接转交给命令接收者。 这里有一个度要根据情况把握。

多个命令还可以组合成一个。

实现:

class Command

{

virtual void Execute() = 0;

};

class OpenCommand : Command

{

  OpenCommand(Application * a):__application(_application){}

void Execute()

{

_application->AddDocument();

}

private:

Application *_application;

};

app:

{

Application app; ////命令的接收者

Command * open = new OpenCommand(&app);

open->Execute();

}

3. Interpreter 解释器

解释器和组合类似,实现中一定会用到组合。 他们的不同一点是组合重在通过一种通用的方式表达对象,即便

是一群对象,表达起来也像是一个对象一样。

解释器是为了解决特定问题,自行设定了一种语音给clientAPP,或用户界面。通过翻译这种语音,确定了解释器树(或列表)

中的每个对象(解释某一特定语法的子类)。

他和组合模式比首先多了一步翻译语音,将语言翻译成为各个解释器子类(构建语法数)。 并且各个子类的行为也可以不一样,比如字符串查找 “以什么开头,以什么结尾,包含什么。。。”

#a  以a开头;   $a  以a结尾   %a  包含a。

则可通过定义三个子类, 并且通过翻译语言 : #ss$b  查找以ss开头以b结尾的字符串.

4. Iterator 迭代器

动机: 提供一个方法来遍历一个聚合对象,而又不暴露该对象的内部表示。

List的iterator就是一个实例, 我们并不知道数据在list中是怎么存储的。

这样还可以支持对同一个聚合对象同时进行多个遍历、不同的遍历。使用不同的迭代器就可以了。

为遍历聚合提供了统一的接口。

实现:

template < class Item>

class Aggregate

{

long count();

Item & getitem(long index); /////实际的聚合对象,他自己提供一个根据下标获取元素的方法,这样iterator就可以不声明为他的友元了。

把迭代器声明为聚合的一个友元,也是一种方法,但是破坏了聚合的封装性。

};

class Iterator  ////抽象迭代器接口

{

virtual void first() = 0;

virtual void next() = 0;

virtual bool isDone() const = 0;

virtual Item CurrentItem() const = 0;

};

class ConceretIterator : public Iterator

{

ConceretIterator(const Aggregate & a):aggregate(a),_current(0){}

void first()

{

_current = 0;

}

void next()

{

_current++;

}

bool isDone() const

{

_current == aggregate.count();

}

virtual Item CurrentItem() const

{

return aggregate.getitem(_current);

}

private:

long _current;

Aggregate & aggregate;

};

上面是一个外部迭代器,就是对item的操作在外面app中进行,迭代器只是把item返回出去了。

app:

{

ConceretIterator i(Aggregate);

for(i.first();!i.IsDone();i.next()

{

item & item = i.CurrentItem();

}

}

还可以在aggregate里面定义工厂,通过工厂方法创建相应的迭代器。★ 因为工厂通常要使用new,所以迭代器也可以再包一个代理,以便迭代器对象的释放。

即 IteratorProxy(CreateIterate());IteratorProxy本身是迭代器的proxy模式,CreateIterate是工厂模式,里面使用new方法。

这时需要IteratorProxy重载->和*方法,把包含的对象吐出来。在IteratorProxy的析构里面delete工厂new出来的对象。

还有一种内部迭代器,即把对象的操作自己搞定。

class ListTraverser{

ListTraverser(Aggregate & a):iter(a){}

bool traverse()

{

for(iter.first();!iter.IsDone();iter.next()

{

ProcessItem( iter.CurrentItem());

}

}

protected :

virtual bool ProcessItem(const Item &); ///// item的处理函数

private:

Iterator & iter;  // 自己持有一个迭代器

};

5. mediator 中介者

意图: 当一组对象以复杂的方式通信,产生的互相依赖关系混乱切难以理解时。

想定义一个分布在多个类中的行为,又不想生成太多的子类。

当一个类引用了很多其他对象,导致其复用性下降。

这时可以通过一个中介者,由中介者来持有和维护一系列对象的交互,协调各个同事对象协作行为。

原理:用一个中介来封装一系列对象,使得各对象不需要显式的互相引用,从而使其松散耦合。

实现:

class DialogDirector

{

void changed() //////中介对外的接口

{

_ok.xxx();

_cancel.xxx();

if(_fontlist changed)

{

xxxx;

}

}

private:

Button & _ok;   /////////维护几个同事对象,协调他们之间工作

Button & _cancel;

ListBox & _fontlist;

};

6. memento 备忘录

意图: 在不破坏封装性的前提下获取一个对象的内部状态,并将其保存在对象之外,以便以后需要时恢复对象原先保存的状态。

原理: 被保存状态的对象叫原发器。 原发器自己提供接口生成一个自己的备忘录对象,并提供恢复状态接口来装入备忘录。

即原发器自己定义自己的备忘录,并提供接口返回出来。 应用程序可以拿到这个备忘录,并且在需要时还给原发器去恢复数据。

实现:

class OriginatorMemento  ///备忘录

{

~Originator();

private:

firend class Originator;

OriginatorMemento();

};

class Originator  ///原发器

{

OriginatorMemento * CreateMemento();

void setMemento(OriginatorMemento & memento);

};

app

{

OriginatorMemento * _state = Originator.CreateMemento();

/////////////////////////////// ........

Originator.setMemento(_state);/////可通过此方式导入备忘录

}

其实就好像以加密的方式把数据备份一个文件出来一样,怎么备份和使用数据全是原发器的事情,对外完全封装。

7. observer 观察者

意图: 定义对象间的一种一对多的关系。 当一个对象的状态发生改变时,所有依赖于他的对象都得到通知并自动做更新。

原理: 观察者的是一个对象,他可以持有被观察对象的引用。 被观察对象提供一个注册方法,把希望观察他的对象们注册到他内部。

所有的观察者都继承自抽象的接口。

当被观察者发生改变时,通过自己的notify接口,依次调用注册到他内部的观察者的update方法,并把自己传过去,以此达到观察者们感知他变化的目的。

实现:

class Observer

{

virtual void Update(Subject *) = 0;

};

class ConceretObserver : public Observer

{

void Update(Subject * s)

{

XXXXXXXXXXXXXXXX;

}

};

class Subject

{

void Register(Observer * o)

{

m_oberList->push_back(o);

}

void Notify()

{

for(i = m_oberList.begin();  i!= m_oberList.end(); i++)

{

(*i)->Update(this);

}

}

void Function()//////////// 目标类某个具体函数。 当里面发生数据改变时,调用Notify触发他的观察者。

{

////////////

Notify();

}

private:

list<Observer*> m_oberList;

};

★ 如果有这种情况:当一个操作涉及多个互相依赖的目标,当这些目标的修改全部完成之后才能通知他们的观察者,这时可以引入一个ChangeManager,负责维护目标和观察者之间的关系和通知时机。

这种情况下,changeManager是观察者和目标之间的Mediator模式。 changeManager通常用单件来实现。

还有可能,观察者仅希望观察特定事件的改变,这时可以通过注册接口定义他感兴趣的观察事件。可以节省一定的无效通知。

8. state 状态

意图: 允许一个对象在其内部状态改变时改变他的行为。对象看起来就像修改了他的类。

原理: Context定义客户感兴趣的接口,并维护一个具体的状态子类的引用,这个状态子类对应Context的当前状态。

通过抽象的state定义一个接口,封装与context一个特定状态相关的行为。 然后通过具体状态子类来实现与一个context状态相关的行为。

context将与状态相关的请求委托给当前的具体state子类。context可以将自己作为参数传递给处理该请求的状态对象,状态对象可以访问context。

客户程序调用的接口还是context的接口。

context和具体state子类都可以决定哪个状态是另外一个状态的后继者。

他将与特定状态相关的行为局部化。

9. strategy 策略

意图: 定义一系列算法,把他们一个一个封装起来,并且他们可以互相替换。使得算法可以独立于使用他的客户而变化。

比如可以有很多个文字换行算法,每一个算法封装成一个策略。

程序在当不同的时候可以用不同的算法。

如果在context里面直接实现算法,则会将context的实现与算法实现混合起来,从而是context难以维护。并且算法是硬编码的,不能动态改变算法。

如果在这种情况下派生context,则会得到一堆相关的类,而唯一的区别就是算法。

这样不如将算法的实现独立于context,使得context可以切换他使用的算法。

实现:可以用strategy作为类模版参数来配置类,但这样可以实现在编译时选择strategy,无法在运行时实现选择strategy。

另一种方式:

class Context

{

Context( strategy * s ):_s(s){}

void dosth()////////// 应用程序实现某个功能

{

_s->compose(); /////////调用算法的抽象接口

}

private:

strategy * _s;

};

class strategy

{

virtual void compose() = 0;

};

class conceretStrategy1 : public strategy

{

void compose()

{

XXXXXXXXXXXXXX;

}

};

class conceretStrategy2 : public strategy

{

void compose()

{

XXXXXXXXXXXXXX;

}

};

app

{

Context * s1Context = new Context( new conceretStrategy1);

Context * s2Context = new Context( new conceretStrategy2);

s1Context->compose();

}

10. template method 模版方法

意图: 定义一个操作中的算法的骨架,并将一些步骤延迟到子类中去。 使得子类可以不改变算法的结构即可重定义算法的某些特定步骤(或具体操作)。

一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。

实现: 一个模版方法调用的原语操作可以定义为protected,保证只被模版方法调用。 必须重定义的原语操作定义为纯虚函数。

模版方法自身不需要被重定义,因此定义为非虚函数。

class AbstractClass

{

void TemplateMethod()  //////////实现一个算法的骨架,里面使用可被重载的方法DoOperate(), 并在他的子类中重载; 子类使用父类的算法骨架,重定义自身的具体操作。

{

XXXXXXXX;

DoOperate1();

XXXXXXXXXXXXX;

DoOperate2();

xxxxxxxxxxx;

}

virtual void DoOperate1() = 0;

virtual void DoOperate2() = 0;

};

class ConceretClass : public AbstractClass

{

virtual void DoOperate1()

{

XXXXXXXXXXXXXXXX;

}

virtual void DoOperate2()

{

XXXXXXXXXXX;

}

};

模版方法基于继承技术,不如strategy灵活。

11. visitor 访问者

意图: 一个作用于某对象结构中的各元素的操作, 可以在不改变各元素类的前提下定义对这些元素的新的操作。

需要对一个对象结构中的对象进行很多不相关的操作,而不想这些操作互相干扰。

访问者使得增加依赖于负责对象的操作变得容易。仅需增加一个新的访问者几个在对象结构上定义一个新操作。

访问者可以集中相关的操作而分离无关的操作。使得相关的操作行为集中在访问者中而不是分布在对象结构中。

增加新的操作变得容易。

他强调的是操作的多样和独立。

原理: 定义一个抽象的visitor,里面确定一个访问对象元素的接口。

用具体的conceretVisitor继承自visitor。

用抽象元素定一个接受访问者的接口,比如Accept操作,以一个访问者为参数。

具体的元素继承自抽象元素,实现accept操作。

实现:

class Equipment

{

virtual int discountPrice();

virtual accept(EquipmentVisitor * v);

};

class FloppyDisk : Equipment

{

accept(EquipmentVisitor * v)

{

v.visitFloppyDisk(this); /////////////每一个具体的Element对象的accept方法实现都不同,使用的是visitor中不同的方法,但是这些方法都是被重载的、有统一接口原型的。即

每个具体的Element对象都可以有多个visitor,他的多个visitor有共同的抽象接口。

}

}

class EquipmentVisitor

{

virtual void visitFloppyDisk(FloppyDisk * f);

virtual void visitBus(Bus * b);

};

class InventoryVisitor: public EquipmentVisitor

{

void visitFloppyDisk(FloppyDisk * f)

{

f->Accumulate(); ////////计算FloppyDisk的存货数量

}

};

★★★★★ GoF的《设计模式》5.12节讲的非常重要,要多看几遍。

时间: 2024-07-28 22:48:47

设计模式读书笔记——行为型模式的相关文章

大话设计模式读书笔记2——策略模式

策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类直接的耦合. UML 图: 根据<大话设计模式>——第二章 商场促销这个案例代码来简单的记录一下策略模式的使用方式: /// <summary> /// 现金收费抽象类 /// </summary> public abstract class CashSuper { /// <summary> ///

设计模式学习笔记-创建型模式总结

一.总结 创建型模式抽象了实例化的过程:它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象 创建型模式将实例化委托给另一个对象. 在这些模式中有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在 一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以及何时创建这些方面给予 了我们很大的灵活性.它们允许

head first 设计模式读书笔记 之 策略模式

作为一个php开发者,深知曾经很多程序员都鄙视php,为什么呢?因为他们认为php的语法是dirty的,并且由于开发者水平参差不齐导致php的代码更加乱上加乱,维护起来简直一坨shit一样.随着php加入了面向对象的阵型之后,很多开发者开始使用了oop思想来写代码,php也变得越来越标准,越来越规范.而其中,设计模式起到了不小的作用.最近老大找我谈话,说php这边的开发模块耦合度过高,代码感觉质量不高,想来一次代码重构行动.我对代码重构也是一知半解,而代码重构的基础就是去了解设计模式,于是我翻起

大话设计模式读书笔记--12.状态模式

定义 状态模式定义: 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来改变了其类 消除庞大的条件分支,将特定状态的行为放入一个对象中 生活中:开灯和关灯是两个状态 模式结构 Context: 上下文环境,维护一个状态实例,定义当前的状态 State: 抽象状态类,定义一个接口,封装与Context的一个特定状态相关的行为 ConcreteState:具体状态.实现Context的一个特定状态相关的行为 代码实现 场景: 12店之前是休闲状态, 之后是忙碌状态 点击下载代码 特点及使用场

大话设计模式读书笔记--23.访问者模式

定义 访问者模式定义: 表示一个作用于某对象结构中的各元素的操作,它使你在不改变各元素的类的前提下,定义作用于这些元素的新操作 把数据结构, 和作用于数据结构上的操作,分离 模式结构 代码实现 场景: 男人和女人谈恋爱,男人的状态和女人的状态 代码实现:点击下载 特点和使用场景 优点:很容易增加新的操作 缺点: 使增加新的数据结构变得困难 使用场景: 数据结构相对稳定的系统

大话设计模式读书笔记--8.外观模式

外观模式的用意是为子系统提供一个集中化和简化的沟通渠道,而不是向子系统加入新的行为 生活中的例子: 一个电源总开关可以控制四盏灯.一个风扇.一台空调和一台电视机的启动和关闭.该电源总开关可以同时控制上述所有电器设备,电源总开关即为该系统的外观模式设计 定义 定义: 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这一接口使得这一子系统更加容易使用 结构图 Facade: 是模式的核心,指导所有子系统的功能, 可根据客户端的需求定制功能组合 SubSystemOne: 实现子系统

大话设计模式读书笔记--6.原型模式

简单的复制粘贴极有可能造成重复代码的灾难, 但是java中提供了克隆的功能, 如果一个对象创建过程复杂,又要频繁使用, 在初始化信息不发生变化的情况下,应当采取克隆而不是new一个对象 定义 原型模式: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象 也就是说, 只需知道原型对象的类型,就可以拷贝, 拷贝分为浅拷贝和深拷贝,可参考浅拷贝和深拷贝 模式结构 Prototype: 抽象原型类,声明克隆接口 ConcretePrototypeA: 具体的原型类, 实现克隆操作 模式实现

大话设计模式读书笔记--18.命令模式

定义 命令模式定义: 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作 比如: 吃烤串时, 客人点烤羊肉,烤鸡肉, 服务员记录下客人的点餐记录,然后通知厨师开始做 烤羊肉,烤鸡肉是请求对象;服务员是命令请求者;厨师是命令实现者 命令模式将功能(请求)封装成对象 目的 对命令请求者(Invoker)和命令实现者(Receiver)的解耦,方便对命令进行各种控制. 模式结构 代码实现 场景: 去饭店吃烤串 代码: 点击下载 特点和使用场

大话设计模式读书笔记--7.模板方法模式

有时候,我们会遇到由一系列步骤构成的过程,这个过程从高层次上看是相同的,但有些步骤的实现可能不同,我们就要考虑使用模板方法 生活中的例子:坐交通工具上班,  A开车,B做公交, 交通工具就是一个抽象模板 定义 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类不改变算法的结构,即可重定义该算法的某些特定步骤 模式结构 AbstractClass:是一个抽象模板类,templateMethod()是模板方法,它是一个顶级逻辑的骨架,primitiveOperation()是一个