Hello.
We are now at the second video of this section decorator design pattern in the previous video.
We looked at proxy design pattern in this video.
We will prepare a pizza type where the core is the pizza and the ingredients on the decorating types.
We will have a couple of ingredients for our pizza onion and meat.
We'll continue this section with the big brother of the proxy pattern and maybe one of the most powerful
design patterns of all the Decorator pattern is pretty simple.
But for instance it provides a lot of benefits when working with legacy code.
The decorator design pattern allows you to decorate an already existing type with more functional features
without actually touching it.
How is it possible.
Well it uses an approach similar to match your shadows where you have a small doll that you can put
inside a doll of the same shape but bigger and so on and so forth.
The decorator type implements the same interface of the type it decorates and stores an instance of
that type in its member.
This way you can stack as many decorators that is Doles as you want by simply storing the old decorator
in a field of the new one.
When you think about extending legacy code without the risk of breaking something you should think of
the Decorator pattern first.
It's a really powerful approach to deal with this particular problem a different field where the decorator
is very powerful may not be so obvious though it reveals itself when creating types with lots of features
based on user inputs preferences or similar inputs like in a Swiss knife.
You have a base type or the frame of the knife and from there you unfold its functionalities so precisely
when are we going to use the Decorator pattern.
Here's the answer to this question when you need to add functionality to some code that you don't have
access to or you don't want to modify to avoid a negative effect on the code and follow the open or
closed principle like legacy code.
When you want the functionality of an object to be created or altered dynamically then the number of
features is unknown and could grow fast.
The acceptance criteria for a decorator pattern is to have common interface and core type.
The one that all layers will be built over.
We must have the main interface that all decorators will implement.
This interface will be called ingredient add and it will have ADD ingredient string method.
We must have a core pizza decorator type the decorator that we will add ingredients to.
We must have an ingredient onion implementing the same ingredient add interface that will add the string
onion to the returned pizza.
We must have an ingredient meat implementing the ingredient add interface that will add the string meat
to the returned pizza when calling add ingredient method on the top object it must return a fully decorated
pizza with the text pizza with the ingredients meat and onion.
Let's start with the unit test to launch our unit tests.
We must first create the basic structures described in accordance with the acceptance criteria to begin
with the interface that all decorating types must implement is as I'm going to show you now.
I have already created a file and named it as pizza underscored decorator.
Don't go.
Let's add the decorator package and import errors
next.
Let's add the code to defined the pizza decorator type which must have ingredient add inside and which
implements ingredient.
Add to after this.
Let's add the definition of the meat type which will be very similar to that of the pizza decorator
structure.
Now we define the onion struct in a similar fashion.
Let's add the code for this.
Let's save the file.
This is enough to implement the first unit tests and to allow the compiler to run them without any compiling
errors.
Now let's open the pizza decorator test dot go.
Here we start with the name of the package and import strings and testing.
And now add the adds ingredient method saved the file.
Now it must compile without problems so we can check that the test fails.
So move to the terminal first.
Navigate to the working directory and run the test and here's the output and you can see that the test
fails our first test is done and we can see that the pizza decorator struct isn't returning anything
yet.
That's why it fails.
We can now move on to the onion type.
The test of the onion type is quite similar to that of the pizza decorator but we must also make sure
that we actually add the ingredient to the ingredient add method and not to a null pointer.
Let's go back to pizza decorator test.
Don't go.
Let's add the code for test onion.
The first half of this test examines the returning error when no ingredient add method is passed to
the onion struct initialized as no pizza is available to add the ingredient.
An error must be returned.
The second part of the onion type test actually passes.
Pizza decorator structure to the initialize.
Then we check whether no error is being returned and also whether the returning string contains the
word onion in it.
This way we can ensure that onion has been added to the pizza.
Finally with the onion type let's see the console output of this test with our current implementation.
So open the terminal and run the test command.
And here's the output the meat ingredient is exactly the same but we changed the type to meet.
Instead of onion let's go to the test.
Don't go and add the lines of code save the file and move back to the terminal.
So the result of the tests must be similar.
Let's run the test again.
Finally we must check the full stack test.
Creating a pizza with onion and meat must return the text pizza with the ingredients meat and onion.
Go back to the pizza decorator test.
Don't go and add the code for full stack test.
Our test creates a variable called Pizza which like the mattress Goodall's embeds types of the ingredient
add method in several levels calling the add ingredient method executes the method at the Onion level
which executes the meat one which finally executes that of the pizza decorator struct after checking
that no error has been returned.
We check whether the returned text follows the needs of the acceptance criteria.
5.
Now save the file and let's go back to the terminal type at the command.
Go test hyphen v hyphen run equal to test.
Pizza decorator underscore a full stack enter cool from this output we can see that the tests now return
an empty string for our decorated type.
This is of course because no implementation has been done yet.
This was the last test to check the fully decorated implementation.
Let's look closely at the implementation then.
Now we are going to start implementing the pizza decorator type.
Its role is to provide the initial text of the full pizza.
Open the pizza decorator dot go file and now let's replace this piece of code.
Now a single line change on the return of the added ingredient method was enough to pass the test save
the file and go to the terminal.
Let us run the test as you can see we have successfully passed the test.
Moving onto the onion struct implementation.
We must take the beginning of our ingredient and return string and add the word onion at the end of
it.
In order to get a composed pizza in return let us modify this code with the return string.
Also don't forget to define f empty in the import checking that we actually have a pointer to ingredient
ad first we use the contents of the inner ingredient ad and check it for errors if no errors occurred.
We receive a new string composed of this content space and the word onion and no errors.
Looks good enough to run the tests save the file go to the terminal and run the test.
Onion
implementation of the meat struct is very similar.
Let's open the pizza decorator and modify the meat.
Function take care of the brace brackets.
The proper opening and closing.
Okay save this file and here goes there test execution.
So we have again passed the test.
Okay so now all the pieces are to be tested separately.
If everything is okay.
The test of the full stacked solution must be passing smoothly.
Let's run the test for full stack also with the decorator pattern we could keep stacking ingredient
ads which call their inner pointer to add functionality to piece a decorator.
We aren't touching the core type either nor modifying or implementing new things all the new features
are implemented by an external type.
Let me share a few words about ghost structural typing.
So go has a feature that most people dislike at the beginning that is structural typing.
This is when your structure defines your type without explicitly writing it.
For example when you implement an interface you don't have to write explicitly that you're actually
implementing it.
Contrary to languages such as Java where you have to write the keyword implements if your method follows
the signature of the interface you are actually implementing the interface.
This can also lead to accidental implementations of interface something that could provoke an impossible
to track mistake but that is very unlikely.
However structural typing also allows you to define an interface.
After defining their implementers.
Imagine a my printer struct as this one.
Now imagine we have been working with my printer type for a few months now but it didn't implement any
interface so it can't be a possible candidate for a decorator pattern.
Or maybe it can.
What if we wrote an interface that matches its print method.
After a few months consider this piece of code that I'm about to add.
It actually implements the printer interface and we can use it to create a decorator solution structural
typing allows a lot of flexibility when writing programs.
If you don't know whether a type should be part of an interface or not you can leave it and at the interface
later when you are completely sure about it.
This way you can decorate types very easily and with little modification in your source code you might
be wondering what's the difference between decorator pattern and the proxy pattern in the decorator
pattern with decorator type dynamically.
This means that the decoration may or may not be there or it may be composed of one or many types.
If you remember the proxy pattern wraps a type in a similar fashion but it does so at compile time and
it's more like a way to access some type at the same time.
A decorator might implement the entire interface that the type it decorates also implements or not.
So you can have an interface with 10 methods and the decorator that just implements one of them.
And it will still be valid a call on a method not implemented by the decorator will be passed to the
decorated type.
This is a very powerful feature but also very prone to undesired behaviors at runtime.
If you forget to implement any interface method in this aspect you may think that the proxy pattern
is less flexible and it is but the Decorator pattern is weaker as you could have errors at runtime which
you can avoid it to compile time by using the proxy pattern.
Just keep in mind that the decorator is commonly used when you want to add functionality to an object
at runtime like our web server.
It's a compromise between what you need and what you want to sacrifice to achieve it in this video.
We dived into the decorator design pattern in the next video.
We will learn about facade design pattern.