#590 Add explanation for Chain of Responsibility
This commit is contained in:
parent
1b0f55c3eb
commit
eb36033b83
126
chain/README.md
126
chain/README.md
@ -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.
|
||||||
|
|
||||||

|
## 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 |
@ -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>
|
|
@ -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 |
2
pom.xml
2
pom.xml
@ -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 |
@ -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>
|
|
@ -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
|
|
Loading…
x
Reference in New Issue
Block a user