#590 Add explanation for Chain of Responsibility

This commit is contained in:
Ilkka Seppälä 2017-09-19 23:20:40 +03:00
parent 1b0f55c3eb
commit eb36033b83
9 changed files with 127 additions and 275 deletions

View File

@ -16,7 +16,131 @@ Avoid coupling the sender of a request to its receiver by giving
more than one object a chance to handle the request. Chain the receiving more than one object a chance to handle the request. Chain the receiving
objects and pass the request along the chain until an object handles it. objects and pass the request along the chain until an object handles it.
![alt text](./etc/chain_1.png "Chain of Responsibility") ## Explanation
Real world example
> The Orc King gives loud orders to his army. The closest one to react is the commander, then officer and then soldier. The commander, officer and soldier here form a chain of responsibility.
In plain words
> It helps building a chain of objects. Request enters from one end and keeps going from object to object till it finds the suitable handler.
Wikipedia says
> In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain.
**Programmatic Example**
Translating our example with orcs from above. First we have the request class
```
public class Request {
private final RequestType requestType;
private final String requestDescription;
private boolean handled;
public Request(final RequestType requestType, final String requestDescription) {
this.requestType = Objects.requireNonNull(requestType);
this.requestDescription = Objects.requireNonNull(requestDescription);
}
public String getRequestDescription() { return requestDescription; }
public RequestType getRequestType() { return requestType; }
public void markHandled() { this.handled = true; }
public boolean isHandled() { return this.handled; }
@Override
public String toString() { return getRequestDescription(); }
}
public enum RequestType {
DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX
}
```
Then the request handler hierarchy
```
public abstract class RequestHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RequestHandler.class);
private RequestHandler next;
public RequestHandler(RequestHandler next) {
this.next = next;
}
public void handleRequest(Request req) {
if (next != null) {
next.handleRequest(req);
}
}
protected void printHandling(Request req) {
LOGGER.info("{} handling request \"{}\"", this, req);
}
@Override
public abstract String toString();
}
public class OrcCommander extends RequestHandler {
public OrcCommander(RequestHandler handler) {
super(handler);
}
@Override
public void handleRequest(Request req) {
if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) {
printHandling(req);
req.markHandled();
} else {
super.handleRequest(req);
}
}
@Override
public String toString() {
return "Orc commander";
}
}
// OrcOfficer and OrcSoldier are defined similarly as OrcCommander
```
Then we have the Orc King who gives the orders and forms the chain
```
public class OrcKing {
RequestHandler chain;
public OrcKing() {
buildChain();
}
private void buildChain() {
chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null)));
}
public void makeRequest(Request req) {
chain.handleRequest(req);
}
}
```
Then it is used as follows
```
OrcKing king = new OrcKing();
king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); // Orc commander handling request "defend castle"
king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); // Orc officer handling request "torture prisoner"
king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); // Orc soldier handling request "collect tax"
```
## Applicability ## Applicability
Use Chain of Responsibility when Use Chain of Responsibility when

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,109 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
realizations="true" associations="true" dependencies="false" nesting-relationships="true">
<class id="1" language="java" name="com.iluwatar.chain.Request" project="chain"
file="/chain/src/main/java/com/iluwatar/chain/Request.java" binary="false" corner="BOTTOM_RIGHT">
<position height="196" width="228" x="168" y="182"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.chain.OrcOfficer" project="chain"
file="/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java" binary="false" corner="BOTTOM_RIGHT">
<position height="124" width="194" x="168" y="609"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.chain.OrcCommander" project="chain"
file="/chain/src/main/java/com/iluwatar/chain/OrcCommander.java" binary="false" corner="BOTTOM_RIGHT">
<position height="124" width="217" x="402" y="609"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="4" language="java" name="com.iluwatar.chain.RequestHandler" project="chain"
file="/chain/src/main/java/com/iluwatar/chain/RequestHandler.java" binary="false" corner="BOTTOM_RIGHT">
<position height="141" width="218" x="451" y="418"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="5" language="java" name="com.iluwatar.chain.OrcSoldier" project="chain"
file="/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java" binary="false" corner="BOTTOM_RIGHT">
<position height="124" width="194" x="659" y="609"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="6" language="java" name="com.iluwatar.chain.OrcKing" project="chain"
file="/chain/src/main/java/com/iluwatar/chain/OrcKing.java" binary="false" corner="BOTTOM_RIGHT">
<position height="124" width="188" x="451" y="182"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<enumeration id="7" language="java" name="com.iluwatar.chain.RequestType" project="chain"
file="/chain/src/main/java/com/iluwatar/chain/RequestType.java" binary="false" corner="BOTTOM_RIGHT">
<position height="142" width="243" x="168" y="418"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</enumeration>
<association id="8">
<end type="SOURCE" refId="6" navigable="false">
<attribute id="9" name="chain"/>
<multiplicity id="10" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="11">
<end type="SOURCE" refId="4" navigable="false">
<attribute id="12" name="next"/>
<multiplicity id="13" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="14">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="4"/>
</generalization>
<association id="15">
<end type="SOURCE" refId="1" navigable="false">
<attribute id="16" name="requestType"/>
<multiplicity id="17" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="7" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<generalization id="18">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="4"/>
</generalization>
<generalization id="19">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="4"/>
</generalization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

View File

@ -1,61 +0,0 @@
@startuml
package com.iluwatar.chain {
class App {
+ App()
+ main(args : String[]) {static}
}
class OrcCommander {
+ OrcCommander(handler : RequestHandler)
+ handleRequest(req : Request)
+ toString() : String
}
class OrcKing {
~ chain : RequestHandler
+ OrcKing()
- buildChain()
+ makeRequest(req : Request)
}
class OrcOfficer {
+ OrcOfficer(handler : RequestHandler)
+ handleRequest(req : Request)
+ toString() : String
}
class OrcSoldier {
+ OrcSoldier(handler : RequestHandler)
+ handleRequest(req : Request)
+ toString() : String
}
class Request {
- handled : boolean
- requestDescription : String
- requestType : RequestType
+ Request(requestType : RequestType, requestDescription : String)
+ getRequestDescription() : String
+ getRequestType() : RequestType
+ isHandled() : boolean
+ markHandled()
+ toString() : String
}
abstract class RequestHandler {
- LOGGER : Logger {static}
- next : RequestHandler
+ RequestHandler(next : RequestHandler)
+ handleRequest(req : Request)
# printHandling(req : Request)
+ toString() : String {abstract}
}
enum RequestType {
+ COLLECT_TAX {static}
+ DEFEND_CASTLE {static}
+ TORTURE_PRISONER {static}
+ valueOf(name : String) : RequestType {static}
+ values() : RequestType[] {static}
}
}
RequestHandler --> "-next" RequestHandler
Request --> "-requestType" RequestType
OrcKing --> "-chain" RequestHandler
OrcCommander --|> RequestHandler
OrcOfficer --|> RequestHandler
OrcSoldier --|> RequestHandler
@enduml

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

View File

@ -473,6 +473,8 @@
<param>decorator</param> <param>decorator</param>
<param>facade</param> <param>facade</param>
<param>flyweight</param> <param>flyweight</param>
<param>proxy</param>
<param>chain</param>
</skipForProjects> </skipForProjects>
</configuration> </configuration>
</plugin> </plugin>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1,72 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.11" icons="true" automaticImage="PNG" always-add-relationships="false"
generalizations="true" realizations="true" associations="true" dependencies="false" nesting-relationships="true"
router="FAN">
<class id="1" language="java" name="com.iluwatar.proxy.WizardTowerProxy" project="proxy"
file="/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java" binary="false" corner="BOTTOM_RIGHT">
<position height="149" width="191" x="388" y="271"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.proxy.Wizard" project="proxy"
file="/proxy/src/main/java/com/iluwatar/proxy/Wizard.java" binary="false" corner="BOTTOM_RIGHT">
<position height="113" width="102" x="619" y="271"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<interface id="3" language="java" name="com.iluwatar.proxy.WizardTower" project="proxy"
file="/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java" binary="false" corner="BOTTOM_RIGHT">
<position height="77" width="116" x="388" y="460"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="4" language="java" name="com.iluwatar.proxy.IvoryTower" project="proxy"
file="/proxy/src/main/java/com/iluwatar/proxy/IvoryTower.java" binary="false" corner="BOTTOM_RIGHT">
<position height="113" width="116" x="761" y="271"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="5" language="java" name="com.iluwatar.proxy.App" project="proxy"
file="/proxy/src/main/java/com/iluwatar/proxy/App.java" binary="false" corner="BOTTOM_RIGHT">
<position height="95" width="114" x="917" y="271"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<association id="6">
<end type="SOURCE" refId="1" navigable="false">
<attribute id="7" name="tower"/>
<multiplicity id="8" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="3" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="9">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="3"/>
</realization>
<realization id="10">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="3"/>
</realization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

View File

@ -1,32 +0,0 @@
@startuml
package com.iluwatar.proxy {
class App {
+ App()
+ main(args : String[]) {static}
}
class IvoryTower {
- LOGGER : Logger {static}
+ IvoryTower()
+ enter(wizard : Wizard)
}
class Wizard {
- name : String
+ Wizard(name : String)
+ toString() : String
}
interface WizardTower {
+ enter(Wizard) {abstract}
}
class WizardTowerProxy {
- LOGGER : Logger {static}
- NUM_WIZARDS_ALLOWED : int {static}
- numWizards : int
- tower : WizardTower
+ WizardTowerProxy(tower : WizardTower)
+ enter(wizard : Wizard)
}
}
WizardTowerProxy --> "-tower" WizardTower
IvoryTower ..|> WizardTower
WizardTowerProxy ..|> WizardTower
@enduml