【Chain of Resposibility】C++设计模式——职责链


    C++设计模式大全,23种设计模式合集详解—👉(点我跳转)

一、设计流程探讨

  假如你正在开发一个在线订购系统。你希望对系统访问进行限制,只允许认证用户创建订单。此外,拥有管理权限的用户也拥有所有订单的完全访问权限。
  简单规划后,你会意识到这些检查必须依次进行。只要接收到包含用户凭据的请求,应用程序就可尝试对进入系统的用户进行认证。但如果由于用户凭据不正确而导致认证失败,那就没有必要进行后续检查了。
在这里插入图片描述
在接下来的几个月里,你实现了后续的几个检查步骤。
 ● 一位同事认为直接将原始数据传递给订购系统存在安全隐患。因此你新增了额外的验证步骤来处理请求中的数据。
 ● 过了一段时间,有人注意到系统无法抵御暴力密码破解方式的攻击。为了防范这种情况,你立刻添加了一个检查步骤来过滤来自同一IP地址的重复错误请求。
 ● 又有人提议你可以对包含同样数据的重复请求返回缓存中的结果,从而提高系统响应速度。因此,你新增了一个检查步骤,确保只有没有满足条件的缓存结果时请求才能通过并被发送给系统。
在这里插入图片描述
  检查代码本来就已经混乱不堪,而每次新增功能都会使其更加臃肿。修改某个检查步骤有时会影响其他的检查步骤。最糟糕的是,当你希望复用这些检查步骤来保护其他系统组件时,你只能复制部分代码,因为这些组件只需部分而非全部的检查步骤。
  系统会变得让人非常费解,而且其维护成本也会激增。你在艰难地和这些代码共处一段时间后,有一天终于决定对整个系统进行重构。
解决方案:
  与许多其他行为设计模式一样,责任链会将特定行为转换为被称作处理者的独立对象。在上述示例中,每个检查步骤都可被抽取为仅有单个方法的类,并执行检查操作。请求及其数据则会被作为参数传递给该方法。
  模式建议你将这些处理者连成一条链。链上的每个处理者都有一个成员变量来保存对于下一处理者的引用。除了处理请求外,处理者还负责沿着链传递请求。请求会在链上移动,直至所有处理者都有机会对其进行处理。
  最重要的是:处理者可以决定不再沿着链传递请求,这可高效地取消所有后续处理步骤。
  在我们的订购系统示例中,处理者会在进行请求处理工作后决定是否继续沿着链传递请求。如果请求中包含正确的数据,所有处理者都将执行自己的主要行为,无论该行为是身份验证还是数据缓存。
在这里插入图片描述
  不过还有一种稍微不同的方式(也是更经典一种),那就是处理者接收到请求后自行决定是否能够对其进行处理。如果自己能够处理,处理者就不再继续传递请求。因此在这种情况下,每个请求要么最多有一个处理者对其进行处理,要么没有任何处理者对其进行处理。在处理图形用户界面元素栈中的事件时,这种方式非常常见。
  例如,当用户点击按钮时,按钮产生的事件将沿着 GUI 元素链进行传递,最开始是按钮的容器(如窗体或面板),直至应用程序主窗口。链上第一个能处理该事件的元素会对其进行处理。此外,该例还有另一个值得我们关注的地方:它表明我们总能从对象树中抽取出链来。
在这里插入图片描述
  所有处理者类均实现同一接口是关键所在。每个具体处理者仅关心下一个包含 execute执行方法的处理者。这样一来,你就可以在运行时使用不同的处理者来创建链,而无需将相关代码与处理者的具体类进行耦合。

真实世界类比:
在这里插入图片描述
  最近,你刚为自己的电脑购买并安装了一个新的硬件设备。身为一名极客,你显然在电脑上安装了多个操作系统,所以你会试着启动所有操作系统来确认其是否支持新的硬件设备。Windows 检测到了该硬件设备并对其进行了自动启用。但是你喜爱的 Linux 系统并不支持新硬件设备。抱着最后一点希望,你决定拨打包装盒上的技术支持电话。
  首先你会听到自动回复器的机器合成语音,它提供了针对各种问题的九个常用解决方案,但其中没有一个与你遇到的问题相关。过了一会儿,机器人将你转接到人工接听人员处。
  这位接听人员同样无法提供任何具体的解决方案。他不断地引用手册中冗长的内容,并不会仔细聆听你的回应。在第 10 次听到 “你是否关闭计算机后重新启动呢?” 这句话后,你要求与一位真正的工程师通话。
  最后,接听人员将你的电话转接给了工程师,他或许正缩在某幢办公大楼的阴暗地下室中,坐在他所深爱的服务器机房里,焦躁不安地期待着同一名真人交流。工程师告诉了你新硬件设备驱动程序的下载网址,以及如何在 Linux 系统上进行安装。问题终于解决了!你挂断了电话,满心欢喜。

二、模式介绍

(1)模式动机
  在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显式指定,将必不可少地带来请求发送者与接受者的紧耦合。
  如何使请求的发送者不需要指定具体的接受者,让请求的接受者自己在运行时决定来处理请求,从而使两者解耦
(2)模式定义
  使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
(3)要点总结
a). Chain of Responsibility 模式的应用场合在于 “一个请求可能有多个接受者,但是最后真正的接受者只有一个”,这时候请求发送者与接受者的耦合有可能出现 “变化脆弱” 的症状,职责链的目的就是将二者解耦,从而更好地应对变化。
b). 应用了Chain of Responsibility 模式后,对象的职责分派将更具灵活性,我们可以在运行时动态添加 / 修改请求的处理职责。
c). 如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任。

三、代码实现

  下面将用伪代码实现职责链模式,但该模式在平常使用不多,下图是方便大家理解的类图结构。
在这里插入图片描述

enum class RequestType{
	REQ_HANDLER1,
	REQ_HANDLER2,
	REQ_HANDLER3
};
class Request{
	string description;
	RequestType reqType;
public:
	Request(const string &desc, RequestType type) : description(desc),reqType(type) {}
	RequestType getReqType() const { return reqType; }
	const string& getDescription() const { return description; }
};
class ChainHandler{
	ChainHandler *nextChain;		//指针指向自身,多态指针,形成了个多态链表
	void sendRequestToNextHandler(const Request &req){
		if (nextChain != nullptr)
			nextChain->handle(req);
	}
protected:
	virtual bool canHandleRequest(const Request &req) = 0;		//这里是虚函数,相当于运行时来判断
	virtual void processRequest(const Request &req) = 0;
public:
	ChainHandler() { nextChain = nullptr; }
	void setNextChain(ChainHandler *next) { nextChain = next; }
	void handle(const Request &req){
		if (canHandleRequest(req))
			processRequest(req);
		else
			sendRequestToNextHandler(req);
	}
};
class Handler1 : public ChainHandler{
protected:
	bool canHandleRequest(const Request &req) override{
			//这为了简单就采用枚举类,实际上是会复杂点(一般根据业务缺点)
			return req.getReqType() == RequestType::REQ_HANDLER1;
	}
	void processRequest(const Request &req) override{
		cout << "Handler1 is handle request: " << req.getDescription() << endl;
	}
};
class Handler2 : public ChainHandler{
protected:
	bool canHandleRequest(const Request &req) override{
			//这为了简单就采用枚举类,实际上是会复杂点(一般根据业务缺点)
			return req.getReqType() == RequestType::REQ_HANDLER2;
	}
	void processRequest(const Request &req) override{
		cout << "Handler2 is handle request: " << req.getDescription() << endl;
	}
};
class Handler3 : public ChainHandler{
	//... 与 Handler1 和 Handler2 同理
};
int main(){
	Handler1 h1;
	Handler2 h2;
	Handler3 h3;
	h1.setNextChain(&h2);
	h2.setNextChain(&h3);

	Request req("process task ...", RequestType::REQ_HANDLER3);
	h1.hand(req);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
httpd2.2.12.tar编码工具APACHE 2.x VERSIONING ===================== [$LastChangedDate: 2005-10-17 17:17:21 +0000 (Mon, 17 Oct 2005) $] INTRODUCTION ------------ The Apache HTTP Server project must balance two competing and disjoint objectives: maintain stable code for third party authors, distributors and most importantly users so that bug and security fixes can be quickly adopted without significant hardship due to user-visible changes; and continue the development process that requires ongoing redesign to correct earlier oversights and to add additional features. The Apache HTTP Server, through version 2.0, used the Module Magic Number (MMN) to reflect API changes. This had the shortcoming of often leaving users hunting to replace binary third party modules that were now incompatible. This also left module authors searching through the API change histories to determine the exact cause for the MMN change and whether their module was affected. With the simultaneous release of Apache 2.2-stable and Apache 2.3-development, the Apache HTTP Server project is moving towards a more predictable stable release cycle, while allowing forward progress to occur without concern for breaking the stable branch. This document explains the rationale between the two versions and their behavior. STABLE RELEASES, 2.{even}.{revision} ------------------------------------ All even numbered releases will be considered stable revisions. Stable revisions will retain forward compatiblity to the maximum possible extent. Features may be added during minor revisions, and features may be deprecated by making appropriate notations in the documentation, but no features may be removed. In essence, that implies that you can upgrade from one minor revision to the next with a minimum of trouble. In particular, this means: * The Module API will retain forward compatibility. It will not be necessary to update modules to work with new revisions of the stable tree. * The run-time configuration will be forward compatible. No configuration changes will be necessary to work with new revisions of the stable tree. * Compile-time configuration will be forward compatible. The configure command line options that work in one release of the stable tree will also work in the next release. As always, it will be necessary to test any new release to assure that it works correctly with a particular configuration and a particular set of modules, but every effort will be made to assure that upgrades are as smooth as possible. In addition, the following development restrictions will aid in keeping the stable tree as safe as possible: * No 'Experimental' modules; while it may be possible (based on API changes required to support a given module) to load a 2.3-development module into a 2.2-stable build of Apache, there are no guarantees. Experimental modules will be introduced to the 2.3-development versions and either added to 2.2-stable once they are proven and compatible, or deferred to the 2.4-stable release if they cannot be incorporated in the current stable release due to API change requirements. * The stable subversion tree should not remain unstable at any time. Atomic commits aught be used to introduce code from the development version to the stable tree. At any given time a security release may be in preparation, unbeknownst to other contributors. At any given time, testers may be checking out SVN trunk to confirm that a bug has been corrected. And as all code was well-tested in development prior to committing to the stable tree, there is really no reason for this tree to be broken for more than a few minutes during a lengthy commit. In order to avoid 'skipped' release numbers in the stable releases, the Release Manager will generally roll a release candidate (APACHE_#_#_#_RC#) tag. Release Candidate tarballs will be announced to the [email protected] for the stable tree. Then, the participants will vote on the quality of the proposed release tarball. The final APACHE_#_#_# tag will not exist until the APACHE_#_#_#_RC# candidate has passed the usual votes to release that version. Only then is the final tarball packaged, removing all -rc# designations from the version number, and tagging the tree with the release number. DEVELOPMENT RELEASES, 2.{odd}.{revision} ----------------------------------------- All odd numbered releases designate the 'next' possible stable release, therefore the current development version will always be one greater than the current stable release. Work proceeds on development releases, permitting the modification of the MMN at any time in order to correct deficiencies or shortcomings in the API. This means that modules from one development release to another may not be binary compatible, or may not successfully compile without modification to accomodate the API changes. The only 'supported' development release at any time will be the most recently released version. Developers will not be answering bug reports of older development releases once a new release is available. It becomes the resposibility of the reporter to use the latest development version to confirm that any issue still exists. Any new code, new API features or new ('experimental') modules may be promoted at any time to the next stable release, by a vote of the project contributors. This vote is based on the technical stability of the new code and the stability of the interface. Once moved to stable, that feature cannot change for the remainder of that stable release cycle, so the vote must reflect that the final decisions on the behavior and naming of that new feature were reached. Vetos continue to apply to this choice of introducing the new work to the stable version. At any given time, when the quality of changes to the development branch is considered release quality, that version may become a candidate for the next stable release. This includes some or all of the API changes, promoting experimental modules to stable or deprecating and eliminating older modules from the last stable release. All of these choices are considered by the project as a group in the interests of promoting the stable release, so that any given change may be 'deferred' for a future release by the group, rather than introduce unacceptable risks to adopting the next stable release. Third party module authors are strongly encouraged to test with the latest development version. This assures that the module will be ready for the next stable release, but more importantly, the author can react to shortcomings in the API early enough to warn the [email protected] community of the shortcomings so that they can be addressed before the stable release. The entire burden is on the module author to anticipate the needs of their module before the stable release is created. Once a new stable release cycle has begun, that API will be present for the lifetime of the stable release. Any desired changes in the stable versions must wait for inclusion into the next release cycle. When deciding to promote a development tree to being stable, a determination should be made whether the changes since the last stable version warrant a major version bump. That is, if 2.2 is the current stable version and 2.3 is 'ready' to become stable, the group needs to decide if the next stable version is 2.4 or 3.0. One suggested rule of thumb is that if it requires too much effort to port a module from 2.2 to 2.4, then the stable version should be labeled 3.0. In order to ease the burden of creating development releases, the process for packaging a development releases is less formal than for the stable release. This strategy reflects the fact that while in development, versions are cheap. Development releases may be classified as alpha, beta, or GA to reflect the group's perceived stability of the tree. Development releases may be made at any time by any committer. Please read the following link for a more detailed description of the development release strategy: http://httpd.apache.org/dev/release.html
delphi socket call php socket 例子,可根据需要扩展写成聊天室、网站助理类似淘宝助理,有订单提醒。 <?php //确保在连接客户端时不会超时 set_time_limit(0); $port = 10081 ; $ip = '192.168.1.102'; // create a streaming socket, of type TCP/IP $sock = socket_create ( AF_INET , SOCK_STREAM , SOL_TCP ); // set the option to reuse the port socket_set_option ( $sock , SOL_SOCKET , SO_REUSEADDR , 1 ); // "bind" the socket to the address to "localhost", on port $port // so this means that all connections on this port are now our resposibility to send/recv data, disconnect, etc.. socket_bind ( $sock , $ip , $port ); // start listen for connections socket_listen ( $sock ); // create a list of all the clients that will be connected to us.. // add the listening socket to this list $clients = array( $sock ); while ( true ) { // create a copy, so $clients doesn't get modified by socket_select() $read = $clients ; // get a list of all the clients that have data to be read from // if there are no clients with data, go to next iteration if ( socket_select ( $read , $write = NULL , $except = NULL , 0 ) < 1 ) continue; // check if there is a client trying to connect if ( in_array ( $sock , $read )) { // accept the client, and add him to the $clients array $clients [] = $newsock = socket_accept ( $sock ); // send the client a welcome message socket_write ( $newsock , "这是一个delphi(客户端) socket 与 PHP_socket(服务器) 通信的例子 测试,交流QQ:410578660。 but ill make an exception :)\n" . "There are " .( count ( $clients ) - 1 ). " client(s) connected to the server\n" ); socket_getpeername ( $newsock , $ip ); echo "New client connected: { $ip } \n" ; // remove the listening socket from the clients-with-data array $key = array_search ( $sock , $read ); unset( $read [ $key ]); } // loop through all the clients that have data to read from foreach ( $read as $read_sock ) { // read until newline or 1024 bytes // socket_read while show errors when the client is disconnected, so silence the error messages $data = @ socket_read ( $read_sock , 1024 , PHP_NORMAL_READ ); // check if the client is disconnected if ( $data === false ) { // remove client for $clients array $key = array_search ( $read_sock , $clients ); unset( $clients [ $key ]); echo "client disconnected.\n" ; // continue to the next client to read from, if any continue; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ac君

在你们的鼓励下我会多多分享代码

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值