| ////////////////////////////////////////// |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| |
| ////////////////////////////////////////// |
| |
| = Chain of Responsibility Pattern |
| |
| In the Chain of Responsibility Pattern, objects using and implementing an interface (one or more methods) are intentionally loosely coupled. A set of objects that __implement__ the interface are organised in a list (or in rare cases a tree). Objects using the interface make requests from the first __implementor__ object. It will decide whether to perform any action itself and whether to pass the request further down the line in the list (or tree). Sometimes a default implementation for some request is also coded into the pattern if none of the implementors respond to the request. |
| |
| == Example using traditional classes |
| |
| In this example, the script sends requests to the `lister` object. The `lister` points to a `UnixLister` object. If it can't handle the request, it sends the request to the `WindowsLister`. If it can't handle the request, it sends the request to the `DefaultLister`. |
| |
| [source,groovy] |
| ---- |
| include::../test/DesignPatternsTest.groovy[tags=chain_of_responsibility,indent=0] |
| ---- |
| |
| The output will be a list of files (with slightly different format depending on the operating system). |
| |
| Here is a UML representation: |
| |
| [plantuml, ChainOfResponsibilityClasses, png] |
| .... |
| !pragma layout smetana |
| skinparam nodesep 200 |
| skinparam ObjectBorderColor<<Hidden>> Transparent |
| skinparam ObjectBackgroundColor<<Hidden>> Transparent |
| skinparam ObjectFontColor<<Hidden>> Transparent |
| skinparam ObjectStereotypeFontColor<<Hidden>> Transparent |
| class UnixLister { |
| nextInLine : Object |
| +listFiles(dir) |
| } |
| object dummy1<<Hidden>> |
| class WindowsLister { |
| nextInLine : Object |
| +listFiles(dir) |
| } |
| object dummy2<<Hidden>> |
| class DefaultLister { |
| +listFiles(dir) |
| } |
| hide DefaultLister fields |
| object script |
| script ..r..> "<<use>>" UnixLister |
| UnixLister --> "forwardIfRequired" WindowsLister |
| UnixLister <-[hidden]- dummy1 |
| WindowsLister <-[hidden]- dummy2 |
| WindowsLister --> "forwardIfRequired" DefaultLister |
| hide <<Hidden>> |
| .... |
| |
| == Example using simplifying strategies |
| |
| For simple cases, consider simplifying your code by not requiring the chain of classes. |
| Instead, use Groovy truth and the elvis operator as shown here: |
| |
| [source,groovy] |
| ---- |
| include::../test/DesignPatternsTest.groovy[tags=chain_of_responsibility_elvis,indent=0] |
| ---- |
| |
| Or Groovy's switch as shown here: |
| |
| [source,groovy] |
| ---- |
| include::../test/DesignPatternsTest.groovy[tags=chain_of_responsibility_switch,indent=0] |
| ---- |
| |
| Alternatively, for Groovy 3+, consider using streams of lambdas as shown here: |
| |
| [source,groovy] |
| ---- |
| include::../test/DesignPatternsTest.groovy[tags=chain_of_responsibility_lambda,indent=0] |
| ---- |
| |
| == When not to use |
| |
| If your use of chain of responsibility involves frequent use of the `instanceof` operator, like here: |
| |
| [source,groovy] |
| ---- |
| include::../test/DesignPatternsTest.groovy[tags=chain_of_responsibility_shape,indent=0] |
| ---- |
| <1> instanceof code smell |
| |
| It could indicate that instead of using the chain of responsibility pattern, you might consider |
| using richer types, perhaps in combination with Groovy's multimethods. For example, perhaps this: |
| |
| [source,groovy] |
| ---- |
| include::../test/DesignPatternsTest.groovy[tags=chain_of_responsibility_shape_multimethods,indent=0] |
| ---- |
| |
| or using more traditional object-oriented style like this: |
| |
| [source,groovy] |
| ---- |
| include::../test/DesignPatternsTest.groovy[tags=chain_of_responsibility_shape_oo,indent=0] |
| ---- |
| |
| == Going further |
| |
| Other variations to this pattern: |
| |
| * we could have an explicit interface in the traditional example, e.g. `Lister`, to statically type the implementations but because of _duck-typing_ this is optional |
| * we could use a chain tree instead of a list, e.g. `if (animal.hasBackbone())` delegate to `VertebrateHandler` else delegate to `InvertebrateHandler` |
| * we could always pass down the chain even if we processed a request (no early return) |
| * we could decide at some point to not respond and not pass down the chain (pre-emptive abort) |
| * we could use Groovy’s meta-programming capabilities to pass unknown methods down the chain, e.g. combine |
| chain of responsibility with the use of `methodMissing` |