27.1 策略模式 VS 命令模式

27.1.1 策略模式实现压缩算法

//行为型模式大PK——策略模式和命令模式
//实例:用策略模式实现压缩算法
#include <iostream>
#include <string> using namespace std; //抽象压缩算法
class Algorithm
{
public:
//压缩算法
virtual bool compress(string source, string to) = ;
//解压算法
virtual bool uncompress(string source, string to) = ; virtual ~Algorithm(){}
}; //具体算法:zip算法
class Zip : public Algorithm
{
public:
//zip格式的压缩算法
bool compress(string source, string to)
{
cout << source << " --> " << to <<" ZIP压缩成功" <<endl;
return true;
}
//zip格式的解压缩算法
bool uncompress(string source, string to)
{
cout << source <<" --> " << to <<" ZIP解压缩成功" <<endl;
return true;
}
}; //具体算法:Gzip
class Gzip : public Algorithm
{
public:
//gzip格式的压缩算法
bool compress(string source, string to)
{
cout << source << " --> " << to <<" GZIP压缩成功" <<endl;
return true;
}
//gzip格式的解压缩算法
bool uncompress(string source, string to)
{
cout << source <<" --> " << to <<" GZIP解压缩成功" <<endl;
return true;
}
}; //环境角色
class Context
{
private:
Algorithm* al; //指向抽象算法
public:
Context(Algorithm* al)
{
this->al = al;
} //设置算法策略
void setAlgorithm(Algorithm* al)
{
this->al = al;
} //执行压缩算法
bool compress(string source, string to)
{
return al->compress(source ,to);
} //执行解压缩算法
bool uncompress(string source, string to)
{
return al->uncompress(source ,to);
}
}; int main()
{
//创建两种算法策略
Algorithm* alZip = new Zip();
Algorithm* alGzip = new Gzip(); //创建环境角色,并执行zip算法
Context ctx(alZip); cout <<"========执算zip算法========" << endl;
//执行压缩算法
ctx.compress("c:\\window", "d:\\windows.zip");
//执行解压缩算法
ctx.compress("c:\\window.zip", "d:\\windows"); //"========执算gzip算法========"
cout <<"========执算gzip算法========" << endl;
//更换算法
ctx.setAlgorithm(alGzip); //执行压缩算法
ctx.compress("c:\\window", "d:\\windows.zip");
//执行解压缩算法
ctx.compress("c:\\window.zip", "d:\\windows"); delete alZip;
delete alGzip;
return ;
};
/*输出结果:
========执算zip算法========
c:\window --> d:\windows.zip ZIP压缩成功
c:\window.zip --> d:\windows ZIP压缩成功
========执算gzip算法========
c:\window --> d:\windows.zip GZIP压缩成功
c:\window.zip --> d:\windows GZIP压缩成功
*/

27.1.2 命令模式实现压缩算法


注意:图中的接收者是按功能设计的

//行为型模式大PK——策略模式和命令模式
//实例:用命令模式实现压缩算法
#include <iostream>
#include <string> using namespace std; //抽象接收者
class IReceiver
{
public:
//压缩
virtual bool compress(string source, string to) = ;
//解压
virtual bool uncompress(string source, string to) = ; virtual ~IReceiver(){}
}; //zip接收者
class ZipReceiver : public IReceiver
{
public:
//压缩
bool compress(string source, string to)
{
cout << source << " --> " << to <<" ZIP压缩成功" <<endl;
return true;
}
//解压
bool uncompress(string source, string to)
{
cout << source << " --> " << to <<" ZIP解压缩成功" <<endl;
return true;
}
}; //Gzip接收者
class GzipReceiver : public IReceiver
{
public:
//压缩
bool compress(string source, string to)
{
cout << source << " --> " << to <<" GZIP压缩成功" <<endl;
return true;
}
//解压
bool uncompress(string source, string to)
{
cout << source << " --> " << to <<" GZIP解压缩成功" <<endl;
return true;
}
}; //抽象压缩命令
class AbstractCmd
{
protected:
//持有接收者的引用
IReceiver* zip;
IReceiver* gzip;
public:
AbstractCmd()
{
zip = new ZipReceiver();
gzip = new GzipReceiver();
}
virtual ~AbstractCmd()
{
delete zip;
delete gzip;
} //抽象方法,命令的具体单元
virtual bool execute(string source, string to) = ;
}; //zip压缩命令
class ZipCompressCmd : public AbstractCmd
{
public:
bool execute(string source, string to)
{
return zip->compress(source, to);
}
}; //zip解压缩命令
class ZipUncompressCmd : public AbstractCmd
{
public:
bool execute(string source, string to)
{
return zip->uncompress(source, to);
}
}; //gzip压缩命令
class GzipCompressCmd : public AbstractCmd
{
public:
bool execute(string source, string to)
{
return gzip->compress(source, to);
}
}; //gzip解压缩命令
class GzipUncompressCmd : public AbstractCmd
{
public:
bool execute(string source, string to)
{
return gzip->uncompress(source, to);
}
}; //调用者
class Invoker
{
private:
//持有抽象命令的引用
AbstractCmd* cmd;
public:
Invoker(AbstractCmd* cmd)
{
this->cmd = cmd;
} //执行命令
bool execute(string source, string to)
{
return cmd->execute(source, to);
} void setCommand(AbstractCmd* cmd)
{
this->cmd = cmd;
}
}; int main()
{
//定义一个命令,压缩一个文件
AbstractCmd* cmd = new ZipCompressCmd();
//AbstractCmd* cmd = new ZipUncompressCmd();//换命令 //定义调用者
Invoker* invoker = new Invoker(cmd);
//执行压缩命令
cout <<"========执行压缩命令========"<<endl; invoker->execute("c:\\windows", "d:\\windows.zip"); return ;
};
/*输出结果:
========执行压缩命令========
c:\windows --> d:\windows.zip ZIP压缩成功
*/

//另一种实现(只提供类图)

注意:图中的接收者是按对象设计的

27.1.3 小结

(1)当命令模式退化时,比如无接收者(接收者非常简单,无需专门编写一个接收者),在这种情况下,命令模式和策略模式的类图完全一样,代码实现也比较类似

(2)关注点不同

  ①策略模式关注的是算法替换的问题,一个新的算法投产,旧算法退体,或者提供多种算法由调用者自己选择使用,算法的自由更替是它实现的要点。换句话说,策略模式关注的是算法的完整性、封装性,只有具备了这两个条件才能保证其可以自由切换。

  ②命令模式则关注的是解耦问题,如何让请求者和执行者解耦是它需要首先解决的,解耦的要求就是把请求的内容封装为一个个命令,由接收者执行。由于封装成命令,就同时可以对命令进行多种处理,例如撤销、记录等。

(3)角色功能不同

  ①策略模式中的具体算法是负责一个完整算法逻辑,它是不可再拆分的原子业务,一旦变更就是对算法整体的变更。

  ②命令模式关注命令的实现,也就是功能的实现。接收者对命令负责,而与请求者无关。

(4)使用场景不同

  策略模式适用于算法要求变换的场景,而命令模式适用于解耦两个有紧耦合的对象。

27.2 策略模式 VS 状态模式

27.2.1 策略模式实现人生

(1)孩童时期的工作就是玩耍;成人时期的主要工作是养活自己,然后为社会做贡献;老年时期的主要工作是享受天伦之乐。

(2)策略模式,把三种不同的工作方式看作三个不同时期的算法,随着时间推移工作内容随之更替。

【编程实验】用策略模式实现人生

//行为型模式大PK——策略模式和状态模式
//实例:用策略模式实现人生
#include <iostream>
#include <string> using namespace std; //抽象工作算法
class WorkAlgorithm
{
public:
//每个年龄段都必须完成的工作
virtual void work() = ; virtual ~WorkAlgorithm(){}
}; //孩童工作
class ChildWork : public WorkAlgorithm
{
public:
void work()
{
cout << "儿童的工作是玩耍!" << endl;
}
}; //成人工作
class AdultWork : public WorkAlgorithm
{
public:
void work()
{
cout << "成人的工作就是养活自己,然后为社会贡献!" << endl;
}
}; //老人工作
class OldWork : public WorkAlgorithm
{
public:
void work()
{
cout << "老年人的工作就是享受天伦之乐!" << endl;
}
}; //环境角色
class Context
{
private:
WorkAlgorithm* workAlgorithm;
public:
void setWork(WorkAlgorithm* work)
{
workAlgorithm = work;
} //每个算法都必须具有的功能
void work()
{
workAlgorithm->work();
}
}; int main()
{
//定义一个环境角色
Context ctx;
cout <<"=====儿童的主要工作====="<< endl;
WorkAlgorithm* child= new ChildWork();
ctx.setWork(child);
ctx.work(); cout <<"=====成年人的主要工作====="<< endl;
WorkAlgorithm* adult= new AdultWork();
ctx.setWork(adult);
ctx.work(); cout <<"=====老人的主要工作====="<< endl;
WorkAlgorithm* oldman= new OldWork();
ctx.setWork(oldman);
ctx.work(); delete child;
delete adult;
delete oldman; return ;
};
/*输出结果:
=====儿童的主要工作=====
儿童的工作是玩耍!
=====成年人的主要工作=====
成人的工作就是养活自己,然后为社会贡献!
=====老人的主要工作=====
老年人的工作就是享受天伦之乐!
*/

27.2.2 状态模式实现人生

(1)孩童时期的工作就是玩耍;成人时期的主要工作是养活自己,然后为社会做贡献;老年时期的主要工作是享受天伦之乐。

(2)状态模式,认为人的状态(孩童、成人、老人)产生了不同的行为结果,这里的行为都相同,都是工作,但是它们的实现方式不同。

【编程实验】用状态模式实现人生

//行为型模式大PK——策略模式和状态模式
//实例:用状态模式实现人生
#include <iostream>
#include <string> using namespace std; //**************************************接口声明******************************
class Human; //前置声明,相当于环境角色类 //人的抽象状态
class HumanState
{
protected:
Human* human;
public:
void setHuman(Human* human)
{
this->human = human;
} //每一种状态对应一种行为
virtual void work() = ;
}; //环境角色
class Human
{
private:
HumanState* state; //当前状态
public:
static HumanState* CHILD_STATE;
static HumanState* ADULT_STATE;
static HumanState* OLD_STATE;
public:
void setState(HumanState* state)
{
this->state = state;
state->setHuman(this);
} //人类的工作
void work()
{
state->work();
}
}; //*********************************具体的状态类*************************************
//孩童状态
class ChildState : public HumanState
{
public:
void work()
{
cout << "儿童的工作是玩耍!" << endl;
//设置下一个状态
human->setState(Human::ADULT_STATE);
}
}; //成人状态
class AdultState : public HumanState
{
public:
void work()
{
cout << "成人的工作就是养活自己,然后为社会贡献!" << endl;
//设置下一个状态
human->setState(Human::OLD_STATE);
}
}; //老人状态
class OldState : public HumanState
{
public:
void work()
{
cout << "老年人的工作就是享受天伦之乐!" << endl;
}
}; HumanState* Human::CHILD_STATE = new ChildState();
HumanState* Human::ADULT_STATE = new AdultState();
HumanState* Human::OLD_STATE = new OldState(); int main()
{
//定义一个环境角色
Human human; //状态的切换由环境角色和状态共同决定,是由内部引起的状态变化
//而策略模式引起策略的变化是外部选择的结果,这两者存在很大差别 //设置一个人的初始状态
human.setState(Human::CHILD_STATE);
cout <<"=====儿童的主要工作====="<< endl;
human.work(); cout <<"=====成年人的主要工作====="<< endl;
human.work(); cout <<"=====老人的主要工作====="<< endl;
human.work(); return ;
};
/*输出结果:
=====儿童的主要工作=====
儿童的工作是玩耍!
=====成年人的主要工作=====
成人的工作就是养活自己,然后为社会贡献!
=====老人的主要工作=====
老年人的工作就是享受天伦之乐!
*/

27.2.3 小结

(1)解决问题的重点不同

  ①策略模式封装算法,并保证算法可以自由地切换。但是算法之间是没有交互的

  ②状态模式旨在指解决内在的状态的改变而引起的行为改变的问题,它的出发点是事物的状态,封装状态而暴露行为,一个对象的状态改变,从外界来看就好象是行为改变。

(2)解决问题的方法不同

  ①策略模式只是确保算法可自由切换,但是什么时候用什么算法它决定不了

  ②而状态模式对外暴露的是行为,状态的变化一般是由环境角色和具体状态共同完成的,也就是说状态模式封装了状态的变化而暴露了不同的行为或行为结果。

(3)环境角色的职责不同

  ①策略模式的环境角色只是一个委托作用,负责算法的替换。

  ②状态模式的环境角色不仅仅是委托行为,它还具有登记状态变化的功能,与具体的状态类协作,共同完成状态切换行为随之切换的任务。

27.3 观察者模式和责任链模式

27.3.1 责任链模式实现DNS解析过程

(1)首先定义3个DNS服务器:上海DNS服务器(区域服务器)、中国顶级DNS服务器、全球顶级DNS服务器。

(2)假设请求者发出请求,由上海DNS进行解析,如果能够解析,则返回结果,若不能解析,则提交给父服务器(中国顶级DNS)进行解析,若还不能解析,则提交到全球顶级DNS进行解析,若还不能解析,那就返回该域名无法解析。

【编程实验】责任链模式实现DNS解析的类图

  ①Recorder对象,它记录DNS服务器解析后的结果,包括域名、IP地址、属主(即由谁解析的)。

  ②DnsServer抽象类中的resolve方法是一个基本方法,每个DNS服务器都必须拥有该方法,它对DNS进行解析。具体是由echo方法来实现的,每个DNS服务器独自实现。

//行为型模式大PK——观察者模式和责任链模式
//实例:用责任链模式实现DNS解析过程
#include <iostream>
#include <string>
#include <ctime>
#include <sstream> //for ostreamstring using namespace std; //********************************辅助类***************************************
//解析记录
class Recorder
{
private:
string domain; //域名,如www.baidu.com
string ip; //ip
string owner; //属主,即解析者
public:
string getDomain(){return domain;}
void setDomain(string value)
{
domain = value;
} string getIp(){return ip;}
void setIp(string value)
{
ip = value;
} string getOwner(){return owner;}
void setOwner(string value)
{
owner = value;
} string toString()
{
string str; str = "域名:" + domain;
str += "\nIP地址:" + ip;
str += "\n解析者:" + owner; return str;
}
}; //抽象域名服务器
class DnsServer
{
private:
//上级DNS是谁
DnsServer* upperServer; //即一下个服务器
//随机产生一个IP地址,工具类
string genIpAddress()
{
string ip;
ostringstream oss; oss << rand() % <<"." << rand() % <<"."
<< rand() % <<"." << rand() % ; ip =oss.str();
return ip;
}
protected:
//每个域名都在一个Zone内,判断是否在本区中。
virtual bool isLocal(string domain) = ; //每个服务器都必须实现解析任务
virtual Recorder echo(string domain)
{
Recorder rec;
//获得IP地址
rec.setIp(genIpAddress());
rec.setDomain(domain); return rec;
} //辅助函数,判断字符串是否以另一个字符串结尾
bool endWith(string source, string tail)
{
if (source.size() < tail.size())
return false; return ( == source.compare(source.size() - tail.size(), tail.size(), tail));
}
public:
DnsServer()
{
srand((int)time(NULL));
} void setUpperServer(DnsServer* upperServer)
{
this->upperServer = upperServer;
} //解析域名
Recorder resolve(string domain)
{
Recorder rec; if(isLocal(domain))
{
rec = echo(domain); //在本区,直接返回
}
else
{
//本服务器不能解析,则提交上级服务器
rec = upperServer->resolve(domain);
} return rec;
}
virtual ~DnsServer(){}
}; //具体的服务器:上海DNS服务器
class SHDnsServer : public DnsServer
{
public:
Recorder echo(string domain)
{
Recorder rec = DnsServer::echo(domain);
rec.setOwner("上海DNS服务器");
return rec;
} //定义上海的服务器能处理的级别,即域名后缀为.sh.cn
bool isLocal(string domain)
{
return endWith(domain, ".sh.cn");
}
}; //具体的服务器:中国顶级DNS服务器
class CNDnsServer : public DnsServer
{
public:
Recorder echo(string domain)
{
Recorder rec = DnsServer::echo(domain);
rec.setOwner("中国顶级DNS服务器");
return rec;
} //定义中国顶级DNS服务器能处理的级别,即域名后缀为.cn
bool isLocal(string domain)
{
return endWith(domain, ".cn");
}
}; //具体的服务器:全球顶级DNS服务器
class TopDnsServer : public DnsServer
{
public:
Recorder echo(string domain)
{
Recorder rec = DnsServer::echo(domain);
rec.setOwner("全球顶级DNS服务器");
return rec;
} //定义中国顶级DNS服务器能处理的级别,即域名后缀为.cn
bool isLocal(string domain)
{
return true; //所有的域名最终的解析地点
}
}; int main()
{
//上海域名服务器
DnsServer* sh = new SHDnsServer();
//中国顶级域名服务器
DnsServer* cn = new CNDnsServer();
//全球顶级域名服务器
DnsServer* top = new TopDnsServer();
//定义查询路径
cn->setUpperServer(top);
sh->setUpperServer(cn); //解析域名
cout << "=====域名解析模拟器=====" << endl; string domain = "www.abc.com";
Recorder rec = sh->resolve(domain);
cout << rec.toString() << endl;
cout << "=====域名解析结束=====" << endl; delete sh;
delete cn;
delete top; return ;
};
/*输出结果:
=====域名解析模拟器=====
域名:www.abc.com
IP地址:4.40.34.78
解析者:全球顶级DNS服务器
=====域名解析结束=====
*/

27.3.2 观察者模式实现DNS解析过程

(1)责任链模式模拟DNS解析过程的缺陷责任链实现的DNS解析其返回的最终解析者是不同的。但实际中的DNS解析,其解析者都是相同的,准确地说都是由本机配置的DNS服务器来做的解析,即返回的解析者是一样的。

(2)真实DNS的解析过程

  A.首先由请求者发送一个请求,然后由上海DNS服务器尝试解析,若不能解析再通过路径②转发给中国顶级DNS进行解析,解析结果通过路径⑤返回给上海DNS,然后由上海DNS服务器通过路径⑥返回给请求者。

  B.同样,若中国顶级DNS不能解析,则通过路径③转由全球顶级DNS进行解析,通过路径④把结果返给中国顶级DNS,然后再通过路径⑤返回给上海DNS。

  C.注意标号⑥,不管一个域名最终由谁解析,最终反馈给请求者的还是第一个节点,也就是说首节点负责请求者应答,其他节点都不与请求者交互,而只与自己的左右节点交互。

  D.实际的DNS服务器就是如此处理的。

【编程实验】观察者模式模拟DNS的解析过程

  ①每个DNS可以看成即是被观察者,又是观察者。以中国DNS为例,当作为被观察者时,其观察者是上一级的全球DNS,他要实现的功能是当中国DNS出现了不能解析的域名,就通知观察者(全球DNS)。如果作为观察者时,其观察的是上海DNS,当上海DNS出现不能处理的问题时,就交由他来处理。即所有的DNS服务器都具备双重身份:即是观察者也是被观察者。

  ②setUpperServer的作用是设置父DNS,也就是设置自己的观察者

  ③SubmitToUppererServer:向父DNS请求解析,也就是通知观察者。handleRequest是函数本DNS服务器用来处理请求,也就是接到事件后的处理。

//行为型模式大PK——观察者模式和责任链模式
//实例:用观察者模式实现DNS解析过程
#include <iostream>
#include <string>
#include <ctime>
#include <sstream> //for ostreamstring using namespace std; //********************************辅助类***************************************
//解析记录
class Recorder
{
private:
string domain; //域名,如www.baidu.com
string ip; //ip
string owner; //属主,即解析者
public:
string getDomain(){return domain;}
void setDomain(string value)
{
domain = value;
} string getIp(){return ip;}
void setIp(string value)
{
ip = value;
} string getOwner(){return owner;}
void setOwner(string value)
{
owner = value;
} string toString()
{
string str; str = "域名:" + domain;
str += "\nIP地址:" + ip;
str += "\n解析者:" + owner; return str;
}
}; //观察者接口
class Observer
{
public:
virtual void handleRequest(Recorder* recorder) = ;
}; //主题对象,被观察者
class Observable
{
protected:
//当该对象行为变更时,要通知的观察者
Observer* observer;
void setObserver(Observer* obs)
{
observer = obs;
}
public:
//通知观察者
void notifyObserver(Recorder* rec)
{
observer->handleRequest(rec);
}
}; //抽象域名服务器(从被观察者继承,并实现观察接口)
//即每个服务器即做为观察者,观察来自前一级DNS的请求,
//同时被观察者,在不能处理时向后一级DNS提交请求
class DnsServer : public Observable, public Observer
{
private:
//随机产生一个IP地址,工具类
string genIpAddress()
{
string ip;
ostringstream oss; oss << rand() % <<"." << rand() % <<"."
<< rand() % <<"." << rand() % ; ip =oss.str();
return ip;
}
protected:
//每个DNS服务器签名上自己的名字
virtual void sign(Recorder* recorder) = ; //每个域名都在一个Zone内,判断是否在本区中。每个服务器都必须定义自己的处理级别
virtual bool isLocal(Recorder* recorder) = ; //辅助函数,判断字符串是否以另一个字符串结尾
bool endWith(string source, string tail)
{
if (source.size() < tail.size())
return false; return ( == source.compare(source.size() - tail.size(), tail.size(), tail));
} public:
//作为被观察者,允许增加观察者,这里是上一级DNS,一般只有一个
void setUpperServer(DnsServer* dnsServer)
{
setObserver(dnsServer);
} //处理请求,也就是接到事件后的处理
void handleRequest(Recorder* recorder)
{
//如果并本能解析
if(isLocal(recorder))
{
recorder->setIp(genIpAddress());
}
else //本服务器不能解析,则提交到上级DNS
{
SubmitToUpperServer(recorder); //通知观察者(上一级DNS)处理
} //处理完后,签上自己的名字
sign(recorder);
}
//向父DNS请求解析,也就是通知观察者
void SubmitToUpperServer(Recorder* recorder)
{
notifyObserver(recorder);
} virtual ~DnsServer(){}
}; //上海DNS服务器
class SHDnsServer : public DnsServer
{
protected:
void sign(Recorder* recorder)
{
recorder->setOwner("上海DNS服务器");
} //定义上海的DNS服务器的处理级别
bool isLocal(Recorder* recorder)
{
return endWith(recorder->getDomain(),".sh.cn");
}
}; //中国顶级DNS服务器
class CNDnsServer : public DnsServer
{
protected:
void sign(Recorder* recorder)
{
recorder->setOwner("中国顶级DNS服务器");
} //定义上海的DNS服务器的处理级别
bool isLocal(Recorder* recorder)
{
return endWith(recorder->getDomain(),".cn");
}
}; //全球顶级DNS服务器
class TopDnsServer : public DnsServer
{
protected:
void sign(Recorder* recorder)
{
recorder->setOwner("全球顶级DNS服务器");
} //定义上海的DNS服务器的处理级别
bool isLocal(Recorder* recorder)
{
return true; //所有的域名最终的解析地点
}
}; int main()
{
//上海域名服务器
DnsServer* sh = new SHDnsServer();
//中国顶级域名服务器
DnsServer* cn = new CNDnsServer();
//全球顶级域名服务器
DnsServer* top = new TopDnsServer();
//定义查询路径
cn->setUpperServer(top);
sh->setUpperServer(cn); //解析域名
cout << "=====域名解析模拟器=====" << endl; string domain = "www.abc.com";
Recorder rec;
rec.setDomain(domain);
sh->handleRequest(&rec);
cout << rec.toString() << endl; cout << "=====域名解析结束=====" << endl; delete sh;
delete cn;
delete top; return ;
};
/*输出结果:
=====域名解析模拟器=====
域名:www.abc.com
IP地址:132.190.35.41
解析者:上海DNS服务器 //注意这里,永远都是由上海DNS服务器返回结果
=====域名解析结束=====
*/

27.3.3 小结:

(1)相同:都是链结构。观察者模式是可看作是一个触发链。

(2)链中的消息对象不同

  ①两个链中传递的消息对象不同。责任链模式基本上不改变消息对象的结构,虽然每个节点都可以参与消费(一般是不参与消费),但是它的结构不会改变。

  ②而观察者模式中链中传递的对象可以自由变化只要上下级节点对传递的对象了解即可

(3)上下节点的关系不同

  ①在责任链中,上下节点没有关系,都是接收同样的对象有,所有传递的对象都是从链首传递过来

  ②触发链模式它的上下级关系很亲密,链中的任意两个相邻节点都是一个牢固的独立团体

(4)消息的分销渠道不同

  ①责任链模式中,一个消息从链首传递进来后,就开始沿着链条向链尾运动,方向是单一的、固定的

  ②触发链模式则不同,由于它采用的是观察者模式,所以很灵活,一个消息传递到链首后,具体怎么传递是不固定的,可以以广播方式传递,也可以以跳跃方式传递,这取决于处理消息的逻辑。

第28章 行为型模式大PK的更多相关文章

  1. 第26章 创建型模式大PK

    26.1 工厂方法模式 VS 建造者模式 26.1.1 按工厂方法建造超人 (1)产品:两类超人,成年超人和未成年超人. (2)工厂:这里选择简单工厂 [编程实验]工厂方法建造超人 //创建型模式大P ...

  2. 第27章 结构型模式大PK

    27.1 代理模式 VS 装饰模式 27.1.1 代理模式 (1)场景:客人找运动员代理要求安排运动员参加比赛 (2)说明:代理人有控制权,可以拒绝客人的要求,也可以答应安排,甚至自己下去跑(因为有些 ...

  3. 设计模式之行为类模式大PK

                                        行为类模式大PK 行为类模式包括责任链模式.命令模式.解释器模式.迭代器模式.中介者模式.备忘录模式.观察者模式.状态模式.策略 ...

  4. 设计模式之结构类模式大PK

                                      结构类模式大PK 结构类模式包括适配器模式.桥梁模式.组合模式.装饰模式.门面模式.享元模式和代理模式.之所以称其为结构类模式,是因 ...

  5. 设计模式之创建类模式大PK

                                        创建类模式大PK 创建类模式包括工厂方法模式.建造者模式.抽象工厂模式.单例模式和原型模式,他们能够提供对象的创建和管理职责.其 ...

  6. 创建类模式大PK(总结)

    创建类模式包含工厂方法模式.建造者模式.抽象工厂模式.单例模式和原型模式,它们都可以提供对象的创建和管理职责.当中的单例模式和原型模式很easy理解,单例模式是要保持在内存中仅仅有一个对象,原型模式是 ...

  7. INSPIRED启示录 读书笔记 - 第28章 创业型公司的产品管理

    产品设计方式 第一步:创业初期只设三个职位,产品经理.交互设计师和原型开发人员(职位可以兼任) 第二步:快速展开产品设计(高保真原型),邀请真实的目标用户验证产品原型,迭代修改 第三步:随着迭代的深入 ...

  8. 第29章 跨战区大PK

    29.1 策略模式 VS 桥梁模式 29.1.1 策略模式 [编程实验]用策略模式实现邮件发送 (1)有文本和超文本两种格式的邮件,将这两种格式封装成两种不同的发送策略. (2)文本邮件和超文本邮件分 ...

  9. Php设计模式(三):行为型模式part1

    原文详见:http://www.ucai.cn/blogdetail/7023?mid=1&f=5 可以在线运行查看效果哦! 在上一篇我们讲了结构型模式,结构型模式是讨论类和对象的结构的.总共 ...

随机推荐

  1. css伪元素实现tootip提示框

    先看效果 废话不说,直接上图(请把鼠标移到我的头像上),看看今天要做的是什么: 实现原理 这些提示框原理都是一样的,且只需一个div标签就能实现,当然也有笨方法,比如用多个标签相互重叠.遮盖,或者干脆 ...

  2. JavaScript语言精粹学习笔记

    0.JavaScript的简单数据类型包括数字.字符创.布尔值(true/false).null和undefined值,其它值都是对象. 1.JavaScript只有一个数字类型,它在内部被表示为64 ...

  3. FeatureLayer,FeatureDataset,FeatureClass,Feature的概念

    刚学AE,其中很多概念都模糊不清.经过一段时间的摸索总结,对FeatureLayer,FeatureDataset,FeatureClass,Feature几个概念有了一点认识.拿出来分享一下,有错误 ...

  4. ECMAScript对文件夹图片幻灯片播放

    代码如下: var curContext = null; var curWeb = null; var picListTitle = "PictureLib"; var folde ...

  5. php服务器版本更新工具up2server

    为什么要做这个工具 之前做php 开发的时候,每次版本更新,要把修改的文件同步到服务器,都要小心翼翼,如果漏掉了文件,那就完蛋了,KPI,奖金什么的都没了. 所以写了这个工具.代码在github  h ...

  6. 计算c字符的长度,保证不超过2^30

    来自sqlite3源码 /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits ...

  7. Android 隐式意图和显示意图的使用场景

    本文实现一个隐式意图的应用,激活短信应用 public void click4(View view) { Intent intent = new Intent(); intent.setAction( ...

  8. 用Reveal分析第三方App的UI

    文章出自:听云博客 Reveal简介: 这是个神奇的工具,它能常透彻地分析个App的UI结构. 这个工具包括两部分,部分是在PC上运行的一个独立应用,即Reveal.app,另一部分代码在你要分析的某 ...

  9. 【读书笔记】iOS网络-使用Bonjour实现自组织网络

    Bonjour就是这样一种技术:设备可以通过它轻松探测并连接到相同网络中的其他设备,整个过程只需要很少的用户参与或是根本就不需要用户参与.该框架提供了众多适合于移动的使用场景,如基于网络的游戏,设备间 ...

  10. iOS 学习 - 12.NSMutableAttributedString 计算高度

    计算 NSMutableAttributedString 高度,必须要有两个属性 -(void)test{ UILabel *label = [[UILabel alloc]initWithFrame ...