namespaceRefactoringGuru.DesignPatterns.ChainOfResponsibility.Conceptual { // The Handler interface declares a method for building the chain of // handlers. It also declares a method for executing a request. publicinterfaceIHandler { IHandler SetNext(IHandler handler); objectHandle(object request); }
// The default chaining behavior can be implemented inside a base handler // class. abstractclassAbstractHandler : IHandler { private IHandler _nextHandler;
public IHandler SetNext(IHandler handler) { this._nextHandler = handler; // Returning a handler from here will let us link handlers in a // convenient way like this: // monkey.SetNext(squirrel).SetNext(dog); return handler; } publicvirtualobjectHandle(object request) { if (this._nextHandler != null) { returnthis._nextHandler.Handle(request); } else { returnnull; } } }
classClient { // The client code is usually suited to work with a single handler. In // most cases, it is not even aware that the handler is part of a chain. publicstaticvoidClientCode(AbstractHandler handler) { foreach (var food innew List<string> { "Nut", "Banana", "Cup of coffee" }) { Console.WriteLine($"Client: Who wants a {food}?");
var result = handler.Handle(food);
if (result != null) { Console.Write($" {result}"); } else { Console.WriteLine($" {food} was left untouched."); } } } }
classProgram { staticvoidMain(string[] args) { // The other part of the client code constructs the actual chain. var monkey = new MonkeyHandler(); var squirrel = new SquirrelHandler(); var dog = new DogHandler();
monkey.SetNext(squirrel).SetNext(dog);
// The client should be able to send a request to any handler, not // just the first one in the chain. Console.WriteLine("Chain: Monkey > Squirrel > Dog\n"); Client.ClientCode(monkey); Console.WriteLine();
Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Monkey: I'll eat the Banana. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
Subchain: Squirrel > Dog
Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Banana was left untouched. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
/** * The Handler interface declares a method for building the chain of handlers. * It also declares a method for executing a request. */ classHandler { public: virtual Handler *SetNext(Handler *handler)= 0; virtual std::string Handle(std::string request)= 0; }; /** * The default chaining behavior can be implemented inside a base handler class. */ classAbstractHandler : public Handler { /** * @var Handler */ private: Handler *next_handler_;
public: AbstractHandler() : next_handler_(nullptr) { } Handler *SetNext(Handler *handler)override{ this->next_handler_ = handler; // Returning a handler from here will let us link handlers in a convenient // way like this: // $monkey->setNext($squirrel)->setNext($dog); return handler; } std::string Handle(std::string request)override{ if (this->next_handler_) { returnthis->next_handler_->Handle(request); }
return {}; } }; /** * All Concrete Handlers either handle a request or pass it to the next handler * in the chain. */ classMonkeyHandler : public AbstractHandler { public: std::string Handle(std::string request)override{ if (request == "Banana") { return"Monkey: I'll eat the " + request + ".\n"; } else { return AbstractHandler::Handle(request); } } }; classSquirrelHandler : public AbstractHandler { public: std::string Handle(std::string request)override{ if (request == "Nut") { return"Squirrel: I'll eat the " + request + ".\n"; } else { return AbstractHandler::Handle(request); } } }; classDogHandler : public AbstractHandler { public: std::string Handle(std::string request)override{ if (request == "MeatBall") { return"Dog: I'll eat the " + request + ".\n"; } else { return AbstractHandler::Handle(request); } } }; /** * The client code is usually suited to work with a single handler. In most * cases, it is not even aware that the handler is part of a chain. */ voidClientCode(Handler &handler){ std::vector<std::string> food = {"Nut", "Banana", "Cup of coffee"}; for (const std::string &f : food) { std::cout << "Client: Who wants a " << f << "?\n"; const std::string result = handler.Handle(f); if (!result.empty()) { std::cout << " " << result; } else { std::cout << " " << f << " was left untouched.\n"; } } } /** * The other part of the client code constructs the actual chain. */ intmain(){ MonkeyHandler *monkey = new MonkeyHandler; SquirrelHandler *squirrel = new SquirrelHandler; DogHandler *dog = new DogHandler; monkey->SetNext(squirrel)->SetNext(dog);
/** * The client should be able to send a request to any handler, not just the * first one in the chain. */ std::cout << "Chain: Monkey > Squirrel > Dog\n\n"; ClientCode(*monkey); std::cout << "\n"; std::cout << "Subchain: Squirrel > Dog\n\n"; ClientCode(*squirrel);
delete monkey; delete squirrel; delete dog;
return0; }
Output.txt: 执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Chain: Monkey > Squirrel > Dog
Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Monkey: I'll eat the Banana. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
Subchain: Squirrel > Dog
Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Banana was left untouched. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
/** * Builds chains of middleware objects. */ public Middleware linkWith(Middleware next) { this.next = next; return next; }
/** * Subclasses will implement this method with concrete checks. */ publicabstractbooleancheck(String email, String password);
/** * Runs check on the next object in chain or ends traversing if we're in * last object in chain. */ protectedbooleancheckNext(String email, String password) { if (next == null) { returntrue; } return next.check(email, password); } }
/** * Please, not that checkNext() call can be inserted both in the beginning * of this method and in the end. * * This gives much more flexibility than a simple loop over all middleware * objects. For instance, an element of a chain can change the order of * checks by running its check after all other checks. */ publicbooleancheck(String email, String password) { if (System.currentTimeMillis() > currentTime + 60_000) { request = 0; currentTime = System.currentTimeMillis(); }
/** * ConcreteHandler. Checks whether a user with the given credentials exists. */ publicclassUserExistsMiddlewareextendsMiddleware { private Server server;
/** * Client passes a chain of object to server. This improves flexibility and * makes testing the server class easier. */ publicvoidsetMiddleware(Middleware middleware) { this.middleware = middleware; }
/** * Server gets email and password from client and sends the authorization * request to the chain. */ publicbooleanlogIn(String email, String password) { if (middleware.check(email, password)) { System.out.println("Authorization have been successful!");
/** * Demo class. Everything comes together here. */ publicclassDemo { privatestaticBufferedReaderreader=newBufferedReader(newInputStreamReader(System.in)); privatestatic Server server;
privatestaticvoidinit() { server = newServer(); server.register("admin@example.com", "admin_pass"); server.register("user@example.com", "user_pass");
// All checks are linked. Client can build various chains using the same // components. Middlewaremiddleware=newThrottlingMiddleware(2); middleware.linkWith(newUserExistsMiddleware(server)) .linkWith(newRoleCheckMiddleware());
// Server gets a chain from client code. server.setMiddleware(middleware); }
/** * The Handler interface declares a method for building the chain of handlers. * It also declares a method for executing a request. */ interfaceHandler { publicfunctionsetNext(Handler $handler): Handler;
publicfunctionhandle(string$request): ?string; }
/** * The default chaining behavior can be implemented inside a base handler class. */ abstractclassAbstractHandlerimplementsHandler { /** * @var Handler */ private$nextHandler;
publicfunctionsetNext(Handler $handler): Handler { $this->nextHandler = $handler; // Returning a handler from here will let us link handlers in a // convenient way like this: // $monkey->setNext($squirrel)->setNext($dog); return$handler; }
publicfunctionhandle(string$request): ?string { if ($this->nextHandler) { return$this->nextHandler->handle($request); }
returnnull; } }
/** * All Concrete Handlers either handle a request or pass it to the next handler * in the chain. */ classMonkeyHandlerextendsAbstractHandler { publicfunctionhandle(string$request): ?string { if ($request === "Banana") { return"Monkey: I'll eat the " . $request . ".\n"; } else { returnparent::handle($request); } } }
/** * The client code is usually suited to work with a single handler. In most * cases, it is not even aware that the handler is part of a chain. */ functionclientCode(Handler $handler) { foreach (["Nut", "Banana", "Cup of coffee"] as$food) { echo"Client: Who wants a " . $food . "?\n"; $result = $handler->handle($food); if ($result) { echo" " . $result; } else { echo" " . $food . " was left untouched.\n"; } } }
/** * The other part of the client code constructs the actual chain. */ $monkey = newMonkeyHandler(); $squirrel = newSquirrelHandler(); $dog = newDogHandler();
$monkey->setNext($squirrel)->setNext($dog);
/** * The client should be able to send a request to any handler, not just the * first one in the chain. */ echo"Chain: Monkey > Squirrel > Dog\n\n"; clientCode($monkey); echo"\n";
Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Monkey: I'll eat the Banana. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
Subchain: Squirrel > Dog
Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Banana was left untouched. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
/** * The classic CoR pattern declares a single role for objects that make up a * chain, which is a Handler. In our example, let's differentiate between * middleware and a final application's handler, which is executed when a * request gets through all the middleware objects. * * The base Middleware class declares an interface for linking middleware * objects into a chain. */ abstractclassMiddleware { /** * @var Middleware */ private$next;
/** * This method can be used to build a chain of middleware objects. */ publicfunctionlinkWith(Middleware $next): Middleware { $this->next = $next;
return$next; }
/** * Subclasses must override this method to provide their own checks. A * subclass can fall back to the parent implementation if it can't process a * request. */ publicfunctioncheck(string$email, string$password): bool { if (!$this->next) { returntrue; }
return$this->next->check($email, $password); } }
/** * This Concrete Middleware checks whether a user with given credentials exists. */ classUserExistsMiddlewareextendsMiddleware { private$server;
publicfunctioncheck(string$email, string$password): bool { if (!$this->server->hasEmail($email)) { echo"UserExistsMiddleware: This email is not registered!\n";
returnfalse; }
if (!$this->server->isValidPassword($email, $password)) { echo"UserExistsMiddleware: Wrong password!\n";
returnfalse; }
returnparent::check($email, $password); } }
/** * This Concrete Middleware checks whether a user associated with the request * has sufficient permissions. */ classRoleCheckMiddlewareextendsMiddleware { publicfunctioncheck(string$email, string$password): bool { if ($email === "admin@example.com") { echo"RoleCheckMiddleware: Hello, admin!\n";
/** * This Concrete Middleware checks whether there are too many failed login * requests. */ classThrottlingMiddlewareextendsMiddleware { private$requestPerMinute;
/** * Please, note that the parent::check call can be inserted both at the * beginning of this method and at the end. * * This gives much more flexibility than a simple loop over all middleware * objects. For instance, a middleware can change the order of checks by * running its check after all the others. */ publicfunctioncheck(string$email, string$password): bool { if (time() > $this->currentTime + 60) { $this->request = 0; $this->currentTime = time(); }
/** * This is an application's class that acts as a real handler. The Server class * uses the CoR pattern to execute a set of various authentication middleware * before launching some business logic associated with a request. */ classServer { private$users = [];
/** * @var Middleware */ private$middleware;
/** * The client can configure the server with a chain of middleware objects. */ publicfunctionsetMiddleware(Middleware $middleware): void { $this->middleware = $middleware; }
/** * The server gets the email and password from the client and sends the * authorization request to the middleware. */ publicfunctionlogIn(string$email, string$password): bool { if ($this->middleware->check($email, $password)) { echo"Server: Authorization has been successful!\n";
// All middleware are chained. The client can build various configurations of // chains depending on its needs. $middleware = newThrottlingMiddleware(2); $middleware ->linkWith(newUserExistsMiddleware($server)) ->linkWith(newRoleCheckMiddleware());
// The server gets a chain from the client code. $server->setMiddleware($middleware);
// ...
do { echo"\nEnter your email:\n"; $email = readline(); echo"Enter your password:\n"; $password = readline(); $success = $server->logIn($email, $password); } while (!$success);
from __future__ import annotations from abc import ABC, abstractmethod from typing importAny, Optional
classHandler(ABC): """ The Handler interface declares a method for building the chain of handlers. It also declares a method for executing a request. """
classAbstractHandler(Handler): """ The default chaining behavior can be implemented inside a base handler class. """
_next_handler: Handler = None
defset_next(self, handler: Handler) -> Handler: self._next_handler = handler # Returning a handler from here will let us link handlers in a # convenient way like this: # monkey.set_next(squirrel).set_next(dog) return handler
@abstractmethod defhandle(self, request: Any) -> str: if self._next_handler: return self._next_handler.handle(request)
returnNone
""" All Concrete Handlers either handle a request or pass it to the next handler in the chain. """
classMonkeyHandler(AbstractHandler): defhandle(self, request: Any) -> str: if request == "Banana": returnf"Monkey: I'll eat the {request}" else: returnsuper().handle(request)
classSquirrelHandler(AbstractHandler): defhandle(self, request: Any) -> str: if request == "Nut": returnf"Squirrel: I'll eat the {request}" else: returnsuper().handle(request)
classDogHandler(AbstractHandler): defhandle(self, request: Any) -> str: if request == "MeatBall": returnf"Dog: I'll eat the {request}" else: returnsuper().handle(request)
defclient_code(handler: Handler) -> None: """ The client code is usually suited to work with a single handler. In most cases, it is not even aware that the handler is part of a chain. """
for food in ["Nut", "Banana", "Cup of coffee"]: print(f"\nClient: Who wants a {food}?") result = handler.handle(food) if result: print(f" {result}", end="") else: print(f" {food} was left untouched.", end="")
if __name__ == "__main__": monkey = MonkeyHandler() squirrel = SquirrelHandler() dog = DogHandler()
monkey.set_next(squirrel).set_next(dog)
# The client should be able to send a request to any handler, not just the # first one in the chain. print("Chain: Monkey > Squirrel > Dog") client_code(monkey) print("\n")
Client: Who wants a Nut? Squirrel: I'll eat the Nut Client: Who wants a Banana? Monkey: I'll eat the Banana Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
Subchain: Squirrel > Dog
Client: Who wants a Nut? Squirrel: I'll eat the Nut Client: Who wants a Banana? Banana was left untouched. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
# The Handler interface declares a method for building the chain of handlers. It # also declares a method for executing a request. classHandler # @abstract # # @param [Handler] handler defnext_handler=(handler) raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end
# @abstract # # @param [String] request # # @return [String, nil] defhandle(request) raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end end
# The default chaining behavior can be implemented inside a base handler class. classAbstractHandler < Handler # @return [Handler] attr_writer:next_handler
# @param [Handler] handler # # @return [Handler] defnext_handler(handler) @next_handler = handler # Returning a handler from here will let us link handlers in a convenient # way like this: # monkey.next_handler(squirrel).next_handler(dog) handler end
# All Concrete Handlers either handle a request or pass it to the next handler # in the chain. classMonkeyHandler < AbstractHandler # @param [String] request # # @return [String, nil] defhandle(request) if request == 'Banana' "Monkey: I'll eat the #{request}" else super(request) end end end
classSquirrelHandler < AbstractHandler # @param [String] request # # @return [String, nil] defhandle(request) if request == 'Nut' "Squirrel: I'll eat the #{request}" else super(request) end end end
classDogHandler < AbstractHandler # @param [String] request # # @return [String, nil] defhandle(request) if request == 'MeatBall' "Dog: I'll eat the #{request}" else super(request) end end end
# The client code is usually suited to work with a single handler. In most # cases, it is not even aware that the handler is part of a chain. defclient_code(handler) ['Nut', 'Banana', 'Cup of coffee'].each do |food| puts "\nClient: Who wants a #{food}?" result = handler.handle(food) if result print " #{result}" else print " #{food} was left untouched." end end end
monkey = MonkeyHandler.new squirrel = SquirrelHandler.new dog = DogHandler.new
monkey.next_handler(squirrel).next_handler(dog)
# The client should be able to send a request to any handler, not just the first # one in the chain. puts 'Chain: Monkey > Squirrel > Dog' client_code(monkey) puts "\n\n"
Client: Who wants a Nut? Squirrel: I'll eat the Nut Client: Who wants a Banana? Monkey: I'll eat the Banana Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
Subchain: Squirrel > Dog
Client: Who wants a Nut? Squirrel: I'll eat the Nut Client: Who wants a Banana? Banana was left untouched. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
在 Swift 中使用模式
复杂度: ★★☆
流行度: ★☆☆
使用示例: 责任链模式在 Swift 程序中并不常见, 因为它仅在代码与对象链打交道时才能发挥作用。
/// The Handler interface declares a method for building the chain of handlers. /// It also declares a method for executing a request. protocolHandler: class {
/// Returning a handler from here will let us link handlers in a /// convenient way like this: /// monkey.setNext(handler: squirrel).setNext(handler: dog) return handler }
/// The client code is usually suited to work with a single handler. In most /// cases, it is not even aware that the handler is part of a chain. classClient { // ... staticfuncsomeClientCode(handler: Handler) {
let food = ["Nut", "Banana", "Cup of coffee"]
for item in food {
print("Client: Who wants a "+ item +"?\n")
guardlet result = handler.handle(request: item) else { print(" "+ item +" was left untouched.\n") return }
print(" "+ result) } } // ... }
/// Let's see how it all works together. classChainOfResponsibilityConceptual: XCTestCase { functest() {
/// The other part of the client code constructs the actual chain.
let monkey =MonkeyHandler() let squirrel =SquirrelHandler() let dog =DogHandler() monkey.setNext(handler: squirrel).setNext(handler: dog)
/// The client should be able to send a request to any handler, not just /// the first one in the chain.
funcisNotificationsEnabled() -> Bool { returnfalse/// Calls special method } }
enumAuthError: LocalizedError {
case emptyFirstName case emptyLastName
case emptyEmail case emptyPassword
case invalidEmail case invalidPassword case differentPasswords
case locationDisabled case notificationsDisabled
var errorDescription: String? { switchself { case .emptyFirstName: return"First name is empty" case .emptyLastName: return"Last name is empty" case .emptyEmail: return"Email is empty" case .emptyPassword: return"Password is empty" case .invalidEmail: return"Email is invalid" case .invalidPassword: return"Password is invalid" case .differentPasswords: return"Password and repeated password should be equal" case .locationDisabled: return"Please turn location services on" case .notificationsDisabled: return"Please turn notifications on" } } }
protocolRequest {
var firstName: String? { get } var lastName: String? { get }
var email: String? { get } var password: String? { get } var repeatedPassword: String? { get } }
extensionRequest {
/// Default implementations
var firstName: String? { returnnil } var lastName: String? { returnnil }
var email: String? { returnnil } var password: String? { returnnil } var repeatedPassword: String? { returnnil } }
structSignUpRequest: Request {
var firstName: String? var lastName: String?
var email: String? var password: String? var repeatedPassword: String? }
let loginHandler =LoginHandler(with: LocationHandler()) let loginController =LoginViewController(handler: loginHandler)
loginController.loginButtonSelected()
print("\nClient: Let's test SignUp flow!")
let signUpHandler =SignUpHandler(with: LocationHandler(with: NotificationHandler())) let signUpController =SignUpViewController(handler: signUpHandler)
signUpController.signUpButtonSelected() } }
Output.txt: 执行结果
1 2 3 4 5 6 7 8
Client: Let's test Login flow! Login View Controller: User selected Login button Login View Controller: Preconditions are successfully validated
Client: Let's test SignUp flow! SignUp View Controller: User selected SignUp button SignUp View Controller: something went wrong SignUp View Controller: Error -> Please turn notifications on
/** * The Handler interface declares a method for building the chain of handlers. * It also declares a method for executing a request. */ interfaceHandler { setNext(handler: Handler): Handler;
handle(request: string): string; }
/** * The default chaining behavior can be implemented inside a base handler class. */ abstractclassAbstractHandlerimplementsHandler { privatenextHandler: Handler;
publicsetNext(handler: Handler): Handler { this.nextHandler = handler; // Returning a handler from here will let us link handlers in a // convenient way like this: // monkey.setNext(squirrel).setNext(dog); return handler; }
publichandle(request: string): string { if (this.nextHandler) { returnthis.nextHandler.handle(request); }
returnnull; } }
/** * All Concrete Handlers either handle a request or pass it to the next handler * in the chain. */ classMonkeyHandlerextendsAbstractHandler { publichandle(request: string): string { if (request === 'Banana') { return`Monkey: I'll eat the ${request}.`; } returnsuper.handle(request);
} }
classSquirrelHandlerextendsAbstractHandler { publichandle(request: string): string { if (request === 'Nut') { return`Squirrel: I'll eat the ${request}.`; } returnsuper.handle(request); } }
classDogHandlerextendsAbstractHandler { publichandle(request: string): string { if (request === 'MeatBall') { return`Dog: I'll eat the ${request}.`; } returnsuper.handle(request); } }
/** * The client code is usually suited to work with a single handler. In most * cases, it is not even aware that the handler is part of a chain. */ functionclientCode(handler: Handler) { const foods = ['Nut', 'Banana', 'Cup of coffee'];
for (const food of foods) { console.log(`Client: Who wants a ${food}?`);
const result = handler.handle(food); if (result) { console.log(` ${result}`); } else { console.log(` ${food} was left untouched.`); } } }
/** * The other part of the client code constructs the actual chain. */ const monkey = newMonkeyHandler(); const squirrel = newSquirrelHandler(); const dog = newDogHandler();
monkey.setNext(squirrel).setNext(dog);
/** * The client should be able to send a request to any handler, not just the * first one in the chain. */ console.log('Chain: Monkey > Squirrel > Dog\n'); clientCode(monkey); console.log('');
Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Monkey: I'll eat the Banana. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
Subchain: Squirrel > Dog
Client: Who wants a Nut? Squirrel: I'll eat the Nut. Client: Who wants a Banana? Banana was left untouched. Client: Who wants a Cup of coffee? Cup of coffee was left untouched.
Serching recursively for keyword rose in folder Folder2 Searching for keyword rose in file File2 Searching for keyword rose in file File3 Serching recursively for keyword rose in folder Folder1 Searching for keyword rose in file File1