首页
Web开发
Windows程序
编程语言
数据库
移动开发
系统相关
微信
其他好文
会员
首页
>
其他好文
> 详细
设计模式读书笔记——行为型模式
时间:
2016-04-16 19:24:17
阅读:
171
评论:
0
收藏:
0
[点我收藏+]
标签:
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节讲的非常重要,要多看几遍。
设计模式读书笔记——行为型模式
标签:
原文地址:http://blog.csdn.net/zhaoguoguang/article/details/51162216
踩
(
0
)
赞
(
0
)
举报
评论
一句话评论(
0
)
登录后才能评论!
分享档案
更多>
2021年07月29日 (22)
2021年07月28日 (40)
2021年07月27日 (32)
2021年07月26日 (79)
2021年07月23日 (29)
2021年07月22日 (30)
2021年07月21日 (42)
2021年07月20日 (16)
2021年07月19日 (90)
2021年07月16日 (35)
周排行
更多
分布式事务
2021-07-29
OpenStack云平台命令行登录账户
2021-07-29
getLastRowNum()与getLastCellNum()/getPhysicalNumberOfRows()与getPhysicalNumberOfCells()
2021-07-29
【K8s概念】CSI 卷克隆
2021-07-29
vue3.0使用ant-design-vue进行按需加载原来这么简单
2021-07-29
stack栈
2021-07-29
抽奖动画 - 大转盘抽奖
2021-07-29
PPT写作技巧
2021-07-29
003-核心技术-IO模型-NIO-基于NIO群聊示例
2021-07-29
Bootstrap组件2
2021-07-29
友情链接
兰亭集智
国之画
百度统计
站长统计
阿里云
chrome插件
新版天听网
关于我们
-
联系我们
-
留言反馈
© 2014
mamicode.com
版权所有 联系我们:gaon5@hotmail.com
迷上了代码!