How to use Chain of Responsibility in TypeScript to solve real world problems in web projects.
Welcome to the Design Patterns in TypeScript series, this series will introduce some useful Design Patterns in web development using TypeScript.
Design Patterns are very important for web developers and we can code better by mastering them. In this article, I will use TypeScript to introduce the Chain of Responsibility .
Design Patterns are very important for web Devs and we can do better coding by mastering them. In this article, I will use TypeScript to introduce the Chain of Responsibility .
Chain of Responsibility
Chain of Responsibility is a way to avoid coupling between the sender
and receiver
of requests by having multiple request handlers. In Chain of Responsibility, multiple objects are connected by a reference from each object to its next object to form a chain (next,next,next…). Requests are passed along the chain until one of the objects in the chain decides to handle the request.
Different positions in the company have different responsibilities and authorities. Take the example of a company’s leave process, when applying for leave only needs to be approved by the leader, not needing to be transferred to superiors and directors. If a link in the Chain of Responsibility cannot handle the current request, if there is a next link, the request will be forwarded to the next link for processing.
During software development, for Chain of Responsibility , a common application scenario is middleware . Let’s see how to use Chain of Responsibility to handle requests.
To better understand the following code, let’s first take a look at the corresponding UML diagram:
In the image above, we define an Interface Handler
. The following two functions are defined in this interface:
- use(h: Handler): Handler => Used to register handler (middleware)
- get(url: string, callback: (data: any) => void): void => Register get request handler
Handler interface
1 2 3 4 5 | <span class="token keyword">interface</span> <span class="token class-name">Handler</span> <span class="token punctuation">{</span> <span class="token function">use</span> <span class="token punctuation">(</span> h <span class="token operator">:</span> Handler <span class="token punctuation">)</span> <span class="token operator">:</span> Handler <span class="token punctuation">;</span> <span class="token function">get</span> <span class="token punctuation">(</span> url <span class="token operator">:</span> string <span class="token punctuation">,</span> <span class="token function-variable function">callback</span> <span class="token operator">:</span> <span class="token punctuation">(</span> <span class="token parameter">data <span class="token operator">:</span> any</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">void</span> <span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Then we define an abstract Class AbstractHandler
, encapsulating the handling logic of Chain of Responsibility . That is, combine different handlers to form a reference string.
AbstractHandler abstract class
1 2 3 4 5 6 7 8 9 10 11 12 13 | abstract <span class="token keyword">class</span> <span class="token class-name">AbstractHandler</span> <span class="token keyword">implements</span> <span class="token class-name">Handler</span> <span class="token punctuation">{</span> next <span class="token operator">!</span> <span class="token operator">:</span> Handler <span class="token punctuation">;</span> <span class="token function">use</span> <span class="token punctuation">(</span> <span class="token parameter">h <span class="token operator">:</span> Handler</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> next <span class="token operator">=</span> h <span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> next <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">get</span> <span class="token punctuation">(</span> url <span class="token operator">:</span> string <span class="token punctuation">,</span> <span class="token function-variable function">callback</span> <span class="token operator">:</span> <span class="token punctuation">(</span> <span class="token parameter">data <span class="token operator">:</span> any</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">void</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> next <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> next <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> url <span class="token punctuation">,</span> callback <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Based on the abstract Class AbstractHandler
, we define AuthMiddleware
and LoggerMidddleware
respectively. AuthMiddleware
middleware is used to handle user authentication and LoggerMidddleware
middleware is used to log each request.
AuthMiddleware class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">class</span> <span class="token class-name">AuthMiddleware</span> <span class="token keyword">extends</span> <span class="token class-name">AbstractHandler</span> <span class="token punctuation">{</span> isAuthenticated <span class="token operator">:</span> boolean <span class="token punctuation">;</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> <span class="token parameter">username <span class="token operator">:</span> string <span class="token punctuation">,</span> password <span class="token operator">:</span> string</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> isAuthenticated <span class="token operator">=</span> <span class="token boolean">false</span> <span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> username <span class="token operator">===</span> <span class="token string">"bytefer"</span> <span class="token operator">&&</span> password <span class="token operator">===</span> <span class="token string">"666"</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> isAuthenticated <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token function">get</span> <span class="token punctuation">(</span> url <span class="token operator">:</span> string <span class="token punctuation">,</span> <span class="token function-variable function">callback</span> <span class="token operator">:</span> <span class="token punctuation">(</span> <span class="token parameter">data <span class="token operator">:</span> any</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">void</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> isAuthenticated <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> url <span class="token punctuation">,</span> callback <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span> <span class="token punctuation">(</span> <span class="token string">"Not Authorized"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
LoggerMiddleware class
1 2 3 4 5 6 7 | <span class="token keyword">class</span> <span class="token class-name">LoggerMiddleware</span> <span class="token keyword">extends</span> <span class="token class-name">AbstractHandler</span> <span class="token punctuation">{</span> <span class="token function">get</span> <span class="token punctuation">(</span> url <span class="token operator">:</span> string <span class="token punctuation">,</span> <span class="token function-variable function">callback</span> <span class="token operator">:</span> <span class="token punctuation">(</span> <span class="token parameter">data <span class="token operator">:</span> any</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">void</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token template-punctuation string">`</span> <span class="token string">Request url is: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> url <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token template-punctuation string">`</span></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> url <span class="token punctuation">,</span> callback <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
With the middleware AuthMiddleware
and LoggerMidddleware
, define a Route class
to register these middleware.
Route class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">class</span> <span class="token class-name">Route</span> <span class="token keyword">extends</span> <span class="token class-name">AbstractHandler</span> <span class="token punctuation">{</span> urlDataMap <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">[</span> key <span class="token operator">:</span> string <span class="token punctuation">]</span> <span class="token operator">:</span> any <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token function">constructor</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> urlDataMap <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token string">"/api/todos"</span> <span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> title <span class="token operator">:</span> <span class="token string">"Learn Design Pattern"</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token string">"/api/random"</span> <span class="token operator">:</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> Math <span class="token punctuation">.</span> <span class="token function">random</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">get</span> <span class="token punctuation">(</span> url <span class="token operator">:</span> string <span class="token punctuation">,</span> <span class="token function-variable function">callback</span> <span class="token operator">:</span> <span class="token punctuation">(</span> <span class="token parameter">data <span class="token operator">:</span> any</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">void</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> url <span class="token punctuation">,</span> callback <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> urlDataMap <span class="token punctuation">.</span> <span class="token function">hasOwnProperty</span> <span class="token punctuation">(</span> url <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> value <span class="token operator">=</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> urlDataMap <span class="token punctuation">[</span> url <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> result <span class="token operator">=</span> <span class="token keyword">typeof</span> value <span class="token operator">===</span> <span class="token string">"function"</span> <span class="token operator">?</span> <span class="token function">value</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> value <span class="token punctuation">;</span> <span class="token function">callback</span> <span class="token punctuation">(</span> result <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
After defining the Route
Route class, we can use it and register the middleware in the following way:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">const</span> route <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Route</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> route <span class="token punctuation">.</span> <span class="token function">use</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">AuthMiddleware</span> <span class="token punctuation">(</span> <span class="token string">"bytefer"</span> <span class="token punctuation">,</span> <span class="token string">"666"</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">use</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">LoggerMiddleware</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> route <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token string">"/api/todos"</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">data</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token constant">JSON</span> <span class="token punctuation">.</span> <span class="token function">stringify</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> data <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> <span class="token number">2</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> route <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token string">"/api/random"</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">data</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> data <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
When you successfully run the above code, the corresponding output is shown in the following figure:
Chain of Responsibility usage scenarios:
- Want to send a request to one of many objects without explicitly specifying the request recipient.
- There are many objects that can handle a request and which object handles the request is determined automatically at runtime and the Client only needs to send the request to the Chain.
Roundup
As always, I hope you enjoyed this article and learned something new.
Thank you and see you in the next posts!
If you find this blog interesting, please give me a like and subscribe to support me. Thank you.
Ref
* https://tuan200tokyo.blogspot.com/2022/11/blog54-design-patterns-chain-of.html