Hi.
We are now at the second video of this section Chain of Responsibility design pattern in the previous
video.
We looked at strategy design pattern in this video.
We will develop a multi logo solution that we can change in the way we want.
We will use two different console loggers and one general purpose logger.
Our next pattern is called Chain of Responsibility as its name implies it consists of a chain and in
our case each link of the chain follows the Single Responsibility Principle.
The Single Responsibility Principle implies that the type function method or any similar abstraction
must have one single responsibility only and it must do it quite well.
This way we can apply many functions that achieve one specific thing each to some struct slice map and
so on when we apply many of these abstractions in a logical way very often we can chain them to execute
in order such as for example a logging chain a logging chain is a set of types that logs the output
of some program to more than one Io dot writer interface.
We could have a type that logs to the console a type that logs to a file a type that logs to a remote
server you can make three calls every time you want to do some logging but it's more elegant to make
only one and provoke a chain reaction.
But also we can have a chain of checks and in case one of them fails break the chain and return something.
This is how the authentication and authorization middleware works.
The objective of the chain of responsibility is to provide to the developer a way to chain actions at
runtime.
The actions are changed to each other and each link will execute some action and pass the request to
the next link.
Here we have listed the objectives followed by this pattern.
First is dynamically change the actions at runtime based on some input and secondly pass a request through
a chain of processors until one of them can process it.
In which case the chain could be stopped.
Here we are going to develop a multi logger solution that we can change in a way we want.
We will use two different console loggers and one general purpose logger.
For this we need a simple logger that logs the text of a request with a prefix first logger and passes
it to the next link in the chain.
Next a second logger will write on the console if the incoming text has the word hello and pass the
request to a third logger.
But if not the chain will be broken and it will return immediately.
Then a third logger type is a general purpose logger called writer her logger that uses an IO dot writer
interface to log.
Lastly a concrete implementation of the right or logger rights to a file and represents the third link
in the chain.
The implementation of these steps is described in the figure on your screen.
It gives a clear flow and explanation of what we intend to do.
Now we move on to see how we are going to write the unit test for this example.
As you can see I have already created file chain.
Dot go and change underscore test dot go let's open the chain dot go file the very first thing to do
for the chain is as usual to define the interface a chain of responsibility interface will usually have
at least a next method.
The next method is the one that executes the next link in the chain.
Of course the next method on our examples interface takes the message we want to log and passes it to
the link in the chain.
As I said earlier we need three loggers.
So let me add the three loggers the first logger and second logger types have exactly the same structure
both implement chain logger and have a next chain field that points to the next chain logger the right
or logger type is equal to the first logger and second logger types but also has a field to write its
data too.
So you can pass any Io dot writer interface to it as we have done before will implement an IO dot writer
struct to use in our testing in our test file.
We defined the following struct let's save the file and go to change test Dot.
Go first.
We write the package name followed by the import statement and define the struct.
We will pass an instance of my test writer struct to right logger so we can track what's being logged
on testing the my test writer class implements the common right byte in comma error method from the
IO dot writer interface.
Remember if it has the right method it can be used as Io dot writer the right method simply stored the
string arguments to the received message field so we can check later its value on tests.
Now let's begin the first test function.
Here's the code for it wait let me describe these few lines a bit as they are quite important.
We create a variable with a default.
My test write or type that we'll use as an IO dot writer interface.
In the last link of our chain then we create the last piece of the link chain the writer logger interface.
When implementing the change you usually start with the last piece on the link and in our case it is
a writer logger the writer logger writes to an IO dot writer.
So we pass my writer as Io dot writer interface.
Then we have created a second logger the middle link in our chain with a pointer to the writer logger.
As we mentioned before second logger just logs and passes the message in case it contains the word hello
in a production app.
It could be an error only logger finally the first link in the chain has the variable name chain.
It points to the second logger so to resume our chain looks like first logger followed by second logger
and then the writer logger.
Let me add a few more lines of code here.
So this is going to be our default setup for our tests continuing with go one point seven or later testing
signatures.
We define an inner test with this description three loggers two of them right to console.
The second only if it finds the word hello.
The third rights to some variable if the second found Hello.
It's quite descriptive and very easy to understand if someone else has to maintain this code.
First we use a message on the next method that will not reach the third link in the chain as it doesn't
contain the word hello we check the contents of the received message variable that by default is empty
to see if it has changed because it shouldn't.
Next we use the chain variable again.
Our first link in the chain and passed the message.
Hello.
According to the description of the test it should log using first logger.
Then in second logger and finally in right or logger because it contains the word hello.
And the second logger will let it pass the test.
Checked that my writer the last link in the chain that stored the past message in a variable called
received message has the word that we passed first in the chain.
Hello.
Let's run it.
So it fails.
Save the file and move on to the terminal type the command.
Go test hyphen V.
The test passed for the first check of the test and didn't for the second check.
Well ideally no checks should pass before any implementation is done.
Remember that in test driven development test must fail on the first launch because the code they are
testing isn't implemented yet goes 0 initialization misleads us with this past check on the test.
We can solve this in two ways first by making the signature of the chain logger to return an error that
is next string error.
This way we would break the chain returning an error.
This is a much more convenient way in general but it will introduce quite a lot of boilerplate right
now.
And secondly by changing the received message build to a pointer a default value of a pointer is nil.
Instead of an empty string we will use the second option now as it is much simpler and quite effective
to so let's change the signature of my test writer struct open the change test dot to go file let let's
modify the received message and add a pointer that is temp message
check that the type of received message has the asterisk now to indicate that it is a pointer to a string.
The right function needed to change too.
Now we have to check the contents of the received message field because as every pointer It's initialized
to nil then we have to store the message in a variable first so we can take the address in the next
line on the assignment.
M dot received message equals Ampersand temp message.
So now our test code should change a bit too.
Let's replace this code now we are checking that my writer daughter received messages actually nil.
So no content has been written for sure on the variable.
Also we have to change the second half to check first that the member isn't nil before checking its
contents or it can throw a panic on test.
Let's test it again.
Save the file and move back to the terminal and run the test.
Whoops we forgot to remove the comment and include the F.M. t package.
Let's go back and do it.
Cool.
Save the file again and let's run the test again.
It fails again and again the first half of the test passes correctly without implemented code.
So what should we do now.
We have to change the signature of the my right or type to make the test fail in both checks and again
just fail in the second.
Well in this case we can pass this small issue when writing tests.
We must be very careful not to get too crazy about them.
Unit tests are tools to help us write and maintain code.
But our target is to write functionality not tests.
This is important to keep in mind as you can get really crazy engineering unit tests now that we have
written the unit test.
Let us move on to the implementation.
Now we have to implement the first second and third loggers called First logger.
Second logger and writer logger respectively the first logger is the easiest one as described in the
first acceptance criterion.
We need a simple logger that logs the text of a request with a prefix first logger and passes it to
the next link in the chain.
So let's do it.
Let's open our chain dot go file.
I've already added these lines of code but we need to make a few changes here.
Let's first modify the function f OK.
The implementation is quite easy.
Using the F.M. T dot print f method to format and print the incoming string we appended to the text
first logger text then we check that the next change type has actually some content and pass the control
to it by calling its next string method.
The tests shouldn't pass yet so we'll continue with the second logger.
So let's make similar changes to the second logger.
As mentioned in the second acceptance criterion the second logger description is a second logger will
write on the console if the incoming text has the word hello and pass the request to a third logger.
First of all it checks whether the incoming text contains the text.
Hello.
If it's true it prints the message to the console appending the text.
Second logger and passes the message to the next link in the chain check previous instance that the
third link exists but if it doesn't contain the text Hello the chain is broken and it prints the message.
Finishing in second logging.
Now we'll finalize the writer logger type.
Here's the writer logger.
Let us modify it.
Call the writer logger struct next.
Method checks that there is an existing Io dot writer interface stored in the writer member and writes
there the incoming message appending the text writer or logger to it.
Then like the previous links check that there are more links to pass the message.
Now the tests will pass successfully.
Let's save this file and move on to the terminal.
Now run the test the first half of the test print two messages the first logger message that breaks
the chain which is the expected message for the first logger but it halts and the second logger because
no hello word has been found on the incoming message.
That's why it prints the finishing in second logging string the second half of the test receives the
message.
Hello.
So the first logger prints and the second logger prints to the third logger.
Doesn't print to console at all but to our my writer dot received message line defined in the test.
Now before we end this video we need to ask one thing.
What about a closure.
Sometimes it can be useful to define an even more flexible link in the chain for quick debugging.
We can use closures for this so that the link functionality is defined by the caller.
What does a closure link look like.
It's similar to the writer logger.
Let's open the chain dot go file now.
At the end of the file add these lines of code the closure chain type has a next chain as usual and
a closure member.
Look at the signature of the closure func string.
This means it is a function that takes a string and returns nothing the next string method for closure
chain checks that the closure member is stored and executes it with the incoming string.
As usual the link checks for more links to pass the message as every link in the chain.
So how do we use it now.
We'll define a new test to show its functionality.
Let's save this file and open chain underscore test dot go.
And here we add this test function.
The description of this test makes it clear to loggers second uses the closure implementation.
We simply use to chain logger implementations and we use the closure logger.
In the second link we have created a new my test writer to store the contents of the message.
When defining the closure chain we defined an anonymous function directly on the closure member.
When creating closure logger it prints my closure logger message with the incoming message replacing
modulus s part.
Then we store the incoming message on my writer to check later after defining this new link.
We use the third link from the previous test.
The closure is the fourth link and passed the message Hello closure logger.
We use the word hello at the beginning so that we ensure that the message will pass.
The second logger.
Finally the contents of my writer dot received the message must contain the past.
Text Hello closure logger.
This is quite a flexible approach with one drawback when defining a closure like this we cannot test
its contents in a very elegant way.
Let us run the tests again so move back to the terminal and run the test command.
Go test hyphen v look at the third run.
The message passes correctly through the first second and third links to arrive at the closure that
prints the expected my closure logger message Hello closure logger message.
It's very useful to add a closure method implementation to some interfaces as it provides quite a lot
of flexibility when using the library the handle func function which we used previously in the structural
patterns is to define a handler for an H TTP.
Now let me put it together what we learned in this video.
So we learned a powerful tool to achieve dynamic processing of actions and State handling.
The chain of responsibility pattern is widely used also to create finite state machines.
That is FSM.
It is also used interchangeably with the decorator pattern with the different that when you decorate
you change the structure of an object while with the chain you define a behavior for each link in the
chain that can break it too.
In this video we saw in detail about chain of responsibility design pattern in the next video.
We will be learning about command design pattern.