第28章 行为型模式大PK

时间:2023-03-08 18:20:19

27.1 策略模式 VS 命令模式

27.1.1 策略模式实现压缩算法

第28章 行为型模式大PK

//行为型模式大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 命令模式实现压缩算法

第28章 行为型模式大PK

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

//行为型模式大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压缩成功
*/

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

第28章 行为型模式大PK

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

27.1.3 小结

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

(2)关注点不同

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

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

(3)角色功能不同

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

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

(4)使用场景不同

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

27.2 策略模式 VS 状态模式

27.2.1 策略模式实现人生

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

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

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

第28章 行为型模式大PK

//行为型模式大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)状态模式,认为人的状态(孩童、成人、老人)产生了不同的行为结果,这里的行为都相同,都是工作,但是它们的实现方式不同。

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

第28章 行为型模式大PK

//行为型模式大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进行解析,若还不能解析,那就返回该域名无法解析。

第28章 行为型模式大PK

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

第28章 行为型模式大PK

  ①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的解析过程

第28章 行为型模式大PK

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

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

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

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

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

第28章 行为型模式大PK

  ①每个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)消息的分销渠道不同

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

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