Hi.
Welcome to the next section of this course visitor state mediator and observer design patterns in this
section.
We are going to look at four more design patterns.
We'll start with the visitor pattern which is very useful when you want to abstract away some functionality
from a set of objects.
Then we'll move on to the state design pattern used commonly to build finite state machines.
That is F S M and also we will learn about the mediated design pattern.
Finally we will look into the observer pattern which is commonly used in event driven architectures
and is gaining a lot of traction again especially in the micro services world.
Now we move on to the first video of the section that deals with visitor design patterns in this video.
We are going to delegate some logic of an object type to an external type called the visitor that will
visit our object to perform operations on it.
Then we will emulate an online shop with a few products in the visitor design pattern.
We are trying to separate the logic needed to work with a specific object outside of the object itself.
So we could have many different visitors to do some things to specific types.
For example imagine that we have a log writer that writes to console.
We could make the logger visible so that you can prepare and any text to each log.
We could write a visitor pattern that prepares the date the time and the host to a field stored in the
object with behavioral design patterns.
We are mainly dealing with algorithms visitor patterns are not an exception.
The objectives that we are trying to achieve are listed here.
The first and foremost is to separate the algorithm of some type from its implementation within some
other type.
Secondly to improve the flexibility of some types by using them with little or no logic at all.
So all new functionality can be added without altering the object structure.
And lastly to fix a structure or behavior that would break the open or closed principle and a type you
might be thinking what the open or closed principle is in computer science the open or closed principles
states that entity should be open for extension but closed for modification.
This simple statement has lots of implications that allows building more maintainable software and less
prone to errors and the visitor pattern helps us to delegate some commonly changing algorithm from a
type that we need to be stable to an external type that can change often without affecting our original
one.
We are going to develop a simple log Pender as an example of the visitor pattern following the approach
we have had in previous sections.
We will start with an extremely simple example to clearly understand how the visitor design pattern
works before jumping to a more complex one.
We have already developed similar examples modifying texts too but in slightly different ways.
But this particular example we will create a visitor.
That depends.
Different information to the types it visits to effectively use the visitor design pattern.
We must have two roles a visitor and a visitor all the visitors is the type that will act within a visitor
all type so a visitor will interface implementation has an algorithm detached to the visitor type.
So we need to message loggers.
Message A and message be that will print a message with an A or B respectively before the message.
Also we need a visitor to be able to modify the message to be printed.
It will append the text visited a or visited B to them respectively.
Now that you are well aware of what we want to do in this video let's start with the unit tests.
As we mentioned before we will need a role for the visitor and the visitor visible interfaces.
They will be interfaces.
We also need the message a and message be struct.
I have created two files visit dot go and visitor underscore test dot go within the folder named visitor
open the file visitor dot go.
Let us start adding the code first we add the package name import statement struct and interfaces the
types message a and message be struct.
Both have a message field to store the text that they will print the output Io dot writer will implement
the OS dot FTD out interface by default or a new Io dot writer interface like the one we will use to
check the contents are correct the visitor interface has a visit method one for each visitor all interfaces
message J and message B type the visitor will interface has a method called accept visitor that will
execute the decoupled algorithm like in previous examples we will create a type that implements the
IO dot write a package so that we can use it in tests close this file and move to the visitor test dot
go file.
Here we first at the package name testing helper struct and a function test helper the test helper struct
implements the IO dot writer interface its functionality is quite simple it stores the written bytes
on the received field later we can check the contents of received to test against our expected value
we will write just one test that will check the overall correctness of the code within this test we
will write two sub tests one for message J and one for message B types.
Let's do this okay.
We will use a test helper struct and the message visitor struct on each test for each message type.
Now let's first add the code to test the message a type done.
This is the full first test.
We created message a struct giving it a value hello world for the message field and the pointer to test
helper which we created at the beginning of the test.
Then we execute its accept method inside the accept visitor method on the message a struct the visitor
a message a method is executed to alter the contents of the message field.
That's why we passed the pointer to visit a method without a pointer.
The contents won't be persisted to test if the visitor type has done its job within the accept method
we must call the print method on the message a type later this way the message a struct must write to
the contents of MSA G to the provided I O dot right to interface that is our test helper the last part
of the test is the check according to the description of the acceptance criteria to the output text
of message a type must be prefixed with the text a the stored message and the text visited just at the
end.
So for the message a type the expected text must be in double quotes a call on hello world and in parentheses
visited.
This is the check that we did in the F section the message B type has a very similar implementation.
Let's write the code for message B type.
In fact we have just changed the type from message to message B and the expected text now is B.
Hello world in parentheses visited b the message field is also hello world and we also use the test
helper type.
We still like the correct implementations of the interfaces to compile the code and run the tests the
message and message be struct have to implement the accept visitor method let's save this file and to
go back to our visitor dot go file so here let us had the code to implement the accept visitor method
for message and message be perfect.
Next we add the code for the implementations of the visitor method for message a and message be that
are declared on the visitor interface.
The message visitor interface is the type that must implement them.
Finally we will create a print method for each message type.
This is the method that we will use to test the contents of the message field on each type.
Let's do it
now we can run the tests to really check if they are failing yet.
So let's save the file.
Open the terminal run the command go test hyphen v the outputs of the tests are clear the expected messages
were incorrect because the contents were empty.
It's time to create the implementations.
We will start completing the implementation of the visit a message a and visit B message B methods open
the visitor dot go file in the type of message visitors struct.
Add this line similarly we do this for message B Co.
Its functionality is quite straightforward.
The FMC dot Sprint f method returns a formatted string with the actual contents of m dot message.
A white space and the message visited this string will be stored on the message field overriding the
previous contents.
Now let us develop the accept method for each message type that must execute the corresponding visitor.
This small code has some implications on it.
In both cases we are using a visitor which in our example is exactly the same as the message visitor
interface but they could be completely different.
The key is to understand that the visitor pattern executes an algorithm in its visit method that deals
with the vegetable object.
What could the visitor be doing in this example.
It alters the visitor will object but it could be simply factoring information from it.
For example we could have a person type with lots of fields names or name age address City postal code
and so on.
We could write a visitor to fetch the name and the surname from a person as a unique string of visitors
to fetch the address info for different sections of an app and so on.
Finally there is the print method which will help us to test the types we mentioned before that it must
print to the SD out called by default.
Let's add the print method next it first checks the contents of the output field to assign the output
of the OS dot SD out call in case it is no in our tests we are storing a pointer there to our test helper
type.
So this line is never executed in our test.
Finally each message type prints to the output field.
The full message stored in the message field.
This is done by using the F print f method which takes an IO dot write the package as the first argument
and the text to format as the next argument before you save the file.
Remember to include LMT and OS in the import statement.
Our implementation is now complete and we can run the test again to see if they all pass.
Now save the file move to the terminal and run the command go test hyphen V.
Everything is okay.
The visitor pattern has done its job flawlessly and the message contents were altered after calling
their visit methods.
The very important thing here is that we can add more functionality to both the struct message and message
B without altering their types.
We can just create a new visitor type that does everything on the vegetable.
For example we can create a visitor to add a method that prints the contents of the message failed.
Let's go back to visit a dot go at the end of the code.
Add the message field visitor printer struct.
So we have just added some functionality to both types without altering their contents.
That's the power of the visitor design pattern.
Now we will develop a second example.
This one a bit more complex.
In this case we will emulate an online shop with a few products.
The products will have plane types with just fields and we will make a couple of visitors to deal with
them in the group.
First of all we will develop the interfaces open visitor dot go and the interfaces the product info
retrieval type has a method to get the price and the name of the product the visitor interface like
before has a visit method that accepts the product info retrieval type finally visible interface is
exactly the same.
It has an accept method that takes a visitor type as an argument.
All the products on the online shop must implement the product info retrieve or type.
Also most products will have some common fields such as name or price.
Now we'll create the product type implements the product info receiver and visitor will interfaces and
embed it on each product.
Let's do it.
Now we have a very generic product type that can store the information about almost any product of the
shop.
For example we could have a rice and pasta product.
So next we add the rice and pasta struct.
Each has the product type embedded.
Now we need to create a couple of visitors interfaces one that sums the price of all products and one
that prints the name of each product.
Here we are the code for it.
The price visitors struct takes the value of the price variable of the product info retrieval type past
as an argument and it adds it to the sum field.
The name printers struct stores the name of the product info retrieve type past as an argument and appends
it to a new line on the product list field time for the main function.
Let's add the main function we create a slice of two visible objects.
A rice and pass the type with some arbitrary names.
Then we iterate for each of them using a price visitor instance as an argument.
We print the total price after the range for.
Finally we repeat this operation with the name printer and print the resulting product list.
Let's go ahead and check the output of this main function save the file and move to the terminal type.
The command go run visitor dot go.
Okay this is a nice example of the visitor pattern.
But what if there are special considerations about a product.
For example what do we need to some twenty to the total price of a fridge type.
Okay let's write the fridge structure.
Let's go back to visit her daughter go and write the fridge structure over here the idea here is just
to override the get price method to return the product's price plus 20.
For this we add the next lines of code.
Unfortunately this isn't enough for our example.
The fridge structure is not of a visitor will type the product struct is of a visible type and the fridge
struct has a product struct embedded.
But as we mentioned in earlier sections a type that embeds a second type cannot be considered.
Of that latter type even when it has all its fields and methods the solution is to implement the accept
visitor method so that it can be considered as a vegetable.
Let's rewrite the main function to add this new fridge product to the slice since we added a third product.
Let's change this value to 3 everything else continues the same.
Running this new main function produces a different output.
Let's run the test and check it first save the file and move to the terminal run the command go run
visitor dot go as expected the total price is higher now outputting the sum of the rice the pasta and
the fridge.
We could be adding visitors forever to these products but the idea is clear we decoupled some algorithms
outside of the types to the visitors.
Superb.
In this video we have seen a powerful abstraction to add new algorithms to some types using the visitor
design patterns in the next video.
We will learn about the state design patterns.