Skip to content

Commit 1c51f95

Browse files
committed
Update readme with introduction, installation, and usage.
1 parent 9a2749a commit 1c51f95

File tree

1 file changed

+220
-2
lines changed

1 file changed

+220
-2
lines changed

README.md

+220-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,220 @@
1-
# simple-chain
2-
Lightweight library for implementing chain of responsibility in C#.
1+
# Simple Chain
2+
3+
Lightweight library for implementing simplified version of [chain of responsibility](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern) in C#.
4+
5+
The inspiration for this library came from figuring out a way to break up `if/else` chains
6+
into loosely coupled, separate units to improve maintainability through separation of concerns.
7+
8+
Example of `if/else` chain:
9+
10+
```c#
11+
public class Main
12+
{
13+
public string Run()
14+
{
15+
var result = ProcessAnimal("dog", "red", 100);
16+
17+
return result;
18+
}
19+
20+
private string ProcessAnimal(string animal, string color, int height)
21+
{
22+
string result;
23+
24+
if (animal == "cat") {
25+
result = "animal is a cat!";
26+
} else if (color == "red" {
27+
result = "animal is red!";
28+
} else {
29+
result = "it is an animal";
30+
}
31+
32+
return result;
33+
}
34+
}
35+
```
36+
37+
As we can see from the example, all the blocks of conditions and processing are stuck together
38+
in the `if/else` construct within the same class. The `if/else` construct itself adds a bit of noise.
39+
It is difficult to view and change high level concerns only, such as order of each case.
40+
41+
# Getting Started
42+
43+
## Installation
44+
45+
Add the library via NuGet to the project(s) that you want to use Simple Chain:
46+
47+
- Either via Project > Manage NuGet Packages... / Browse / search for simple-chain / Install
48+
- Or by running a command in the Package Manager Console
49+
50+
```c#
51+
Install-Package SimpleChain
52+
```
53+
54+
## Usage
55+
56+
Create a type to hold the input:
57+
58+
```c#
59+
public class AnimalProcessorInput
60+
{
61+
public required string Animal { get; set; }
62+
public required string Color { get; set; }
63+
public required int Height { get; set; }
64+
}
65+
```
66+
67+
Derive a processor interface from `IProcessor`:
68+
69+
```c#
70+
using SimpleChain;
71+
72+
public interface IAnimalProcessor : IProcessor<AnimalProcessorInput, string>
73+
{
74+
}
75+
76+
```
77+
78+
Create each of the processors derived from the interface you just created:
79+
80+
```c#
81+
public class CatsOnlyProcessor : IAnimalProcessor
82+
{
83+
public bool Condition(AnimalProcessorInput input) => input.Animal == "cat";
84+
85+
public string Process(AnimalProcessorInput input)
86+
{
87+
return "animal is a cat!";
88+
}
89+
}
90+
```
91+
92+
```c#
93+
public class RedOnlyProcessor : IAnimalProcessor
94+
{
95+
public bool Condition(AnimalProcessorInput input) => input.Color == "red";
96+
97+
public string Process(AnimalProcessorInput input)
98+
{
99+
return "animal is red!";
100+
}
101+
}
102+
```
103+
104+
```c#
105+
public class DefaultProcessor : IAnimalProcessor
106+
{
107+
public bool Condition(AnimalProcessorInput input) => true;
108+
109+
public string Process(AnimalProcessorInput input)
110+
{
111+
return "it is an animal";
112+
}
113+
}
114+
```
115+
116+
Now you can create the chain and use it with the input:
117+
118+
```c#
119+
using SimpleChain;
120+
121+
public class Main
122+
{
123+
public string Run()
124+
{
125+
var result = ProcessAnimal("dog", "red", 100);
126+
127+
return result;
128+
}
129+
130+
private string ProcessAnimal(string animal, string color, int height)
131+
{
132+
var processors = new List<IAnimalProcessor>
133+
{
134+
new CatsOnlyProcessor(),
135+
new RedOnlyProcessor(),
136+
new DefaultProcessor(),
137+
};
138+
var animalProcessor = new Chain<IAnimalProcessor, AnimalProcessorInput, string>(processors);
139+
140+
var input = new AnimalProcessorInput
141+
{
142+
Animal = "dog",
143+
Color = "red",
144+
Height = 100,
145+
};
146+
147+
var result = animalProcessor.Run(input);
148+
149+
return result;
150+
}
151+
}
152+
```
153+
154+
Be aware that the order of the processors in the list matters:
155+
the first processor whose condition returns `true` will handle returning the output.
156+
157+
### Dependency Injection
158+
159+
Using a dependency injection framework, the processor list and chain instance can be
160+
defined separately from the main class via the dependency injection framework.
161+
162+
Using Microsoft.Extensions.DependencyInjection the `Main` class can be refactored:
163+
164+
```c#
165+
using SimpleChain;
166+
167+
public class Main
168+
{
169+
private readonly IChain<AnimalProcessorInput, string> animalProcessor;
170+
171+
public Main(IChain<AnimalProcessorInput, string> animalProcessor)
172+
{
173+
this.animalProcessor = animalProcessor;
174+
}
175+
176+
public string Run()
177+
{
178+
var input = new AnimalProcessorInput
179+
{
180+
Animal = "dog",
181+
Color = "red",
182+
Height = 100,
183+
};
184+
185+
var result = animalProcessor.Run(input);
186+
187+
return result;
188+
}
189+
}
190+
```
191+
192+
The main, processors, and chain classes can be registered with the DI framework:
193+
194+
```c#
195+
using Microsoft.Extensions.DependencyInjection;
196+
using SimpleChain;
197+
198+
internal static class ServiceRegistrations
199+
{
200+
public static void AddServices(this IServiceCollection services)
201+
{
202+
// processor registration order matters
203+
services.AddTransient<IAnimalProcessor, CatsOnlyProcessor>();
204+
services.AddTransient<IAnimalProcessor, RedOnlyProcessor>();
205+
services.AddTransient<IAnimalProcessor, DefaultProcessor>();
206+
207+
services.AddTransient<IChain<AnimalProcessorInput, string>, Chain<IAnimalProcessor, AnimalProcessorInput, string>>();
208+
209+
services.AddTransient<Main>();
210+
}
211+
}
212+
```
213+
214+
The end result is improved separation of concerns such that the main class no longer needs to change due to any modifications related to processors:
215+
216+
- Adding or removing processors from the chain.
217+
- Reordering processors in the chain.
218+
- Changing implementation details of a processor.
219+
220+
Also, each processor is completely separate from each other and the chain.

0 commit comments

Comments
 (0)