Skip to main content
Loading Explore guides…
Design Patterns Introduction · Acedra
Home
Explore
Computer Science
Design Patterns Introduction
Sign in to save
Chat
If YouTube asks you to sign in, open the video on YouTube.
Watch on YouTube
Reload player
Transcript
361 segments
Copy
Hide video
0:00
hello everyone and welcome to this talk on design patterns now in this first video I want to introduce the concept of design patterns um see what they are and what they are useful for and also why you might want to use them in your coding and in your application or project so what are design patterns design patterns are basically ways of solving similar problems in similar ways okay so during our development um pretty much no matter what language you develop on you will run
0:32
into kind of the same issues and you might want to implement the same Solutions okay so design patterns gives us a way of fixing or solving a problem in a kind of accepted standard way we know that these Solutions work and we want to use them to implement them in our code so design patterns will allow us to do that they will allow us to um have the certainty uh to know that our solution does solve the problem but also introduces some other things that we might want in addition to
1:07
Simply Having a solution whatever that solution may be so these things might be uh scalability where your application or code might be able to grow um when you add further functionality and further um code to it so scalability is one thing testability is another we want our application or project to be tested in a simple way and we can introduce unit tests and more complex tests okay so design patterns um allow us to do that it also introduces separation of
1:40
concerns okay so that means um our code is split into kind of blocks of code or components that each solve their individual problems and this in turn leads to better architecture and better um you know scalability maintainability things like that so that is at the core that is what design patterns are in addition to all that they introduce standard terminology okay so we want to be able to discuss uh the solutions to our problems um in an easy way and speaking
2:16
to let's say developers or um Architects um about your code about your project using design patterns um provides an easy way to kind of communicate what you have done so for for example if you say that you have implemented a biller pattern for a particular class then the person you're talking to will automatically know what you are talking about they introduce best practices so that
2:43
means um what are the most efficient ways the best ways to solve certain problems in your code um in software development we often have one right way to uh fix to solve a particular problem and many kind of wrong ways even though the solution might work you might not get things like uh separation of concerns if you let's say put everything in one code right so that would be an example of
3:10
a um wrong way to solve a problem design patterns introduce best practices the best way uh that we have found to solve particular problems now I want to introduce this term you might have heard of it the gang of four the gang of four is basically a group of four um software development Architects um that have put together the book uh the uh foundational book on design patterns okay and this is what this course is kind of based upon um they describe based on their experience they
3:43
describe the best ways to solve a lot of problems and come up with some solutions to common problems okay they have named these Solutions design patterns um they provide a number of them that we will cover here in this course okay so this is what it's based on um there is a book out by the gang of four so if you want more information um check out that book as well all right so that's it for this introductory video thank you for watching I will see you in the next one all right
4:14
so the types of design patterns now of course we have different types of design patterns depending on what we want to achieve with a particular pattern okay so in this video we're going to go through the three uh types that that we will cover in this course the first one is going to be the creational type of design patterns and the creational design patterns basically relate to how you create specific objects or specific components in your applications okay so why
4:47
do we have this well we have different ways of creating it creating objects or components depending on the requirements we put on those so you can imagine for instance a class that has uh multiple parameters we want to let's say instantiate some parameters and not others um and we want to do this in an efficient way to provide the quote unquote user so the color object um with a lot of options and a lot of possibilities and this is what where for example the Builder
5:19
pattern would come in okay so um different ways of creating objects or creating components in a way that achieves the nice to haves that we talked about the second type of design patterns is the structural and here we talk more about how we kind of structure our whole project how we structure our application um to achieve certain objectives this is sort of related to architecture although
5:47
architecture is kind of a a broader view um which relates to components and which components um are responsible for which uh actions here in the structural design patterns we take a kind of closer view um closer to the individual class or component and see how that component is structured um in order to again achieve some nice to Hales like testability scalability and so on and the final type of design pattern is going to be the behavioral and the behavioral pattern relate to
6:25
how the components that we create behave in our application and interact with each other okay so here is where we will see um how we communicate between components based again on these structural types of design patterns here we see how those components that we have created kind of interact and communicate with each other and here we will have quite a number of design patterns that we need to go through but all are quite useful to know and uh I think you will use most in your
6:56
development and in your projects okay so this is kind of of a broad view of what types of design patterns we have and in this course we have split the uh structure of the course we have sections for each individual type okay so firstly we will talk about all the creational patterns um and we will begin that in the next section okay so thank you for watching this video and I will see you there okay so let's get into it the first design pattern we're going to talk about is
7:27
the Singleton now if you've heard of one design pattern this is probably the one you heard about the Singleton is probably the most well-known and the most talked about design pattern both in a positive way and in a negative way it's not necessarily the most used one um or the most useful but just that people uh do talk about and know about the Singleton and we're going to cover the positives and negatives in this video so let's take kind of a thought experiment um to
7:58
understand why these Singleton might be useful so let's say we have a project with three components and these three components need to access some external resource for example an API um on a network on the internet so a naive solution to this would be to have a network Communicator we instantiate that Network Communicator every time we need to access that resource so for example for component one we have its own instance component two has its own instance as well and we
8:30
communicate with the resource in this way now there are a few problems with this um so first of all this is kind of resource inefficient so you might be um on a uh you know a smaller device that doesn't have enough resources or you might be on a server where you have a lot of um access and you want to conserve your resources in this case creating three instances that do the same thing um that is not a very efficient solution also you might want to have some sorting some
9:03
ordering of requests in this communication okay so um you might want to serve each request as it comes in and order those those requests if that is important okay also for testing purposes this is not very efficient because you cannot um you shouldn't really test multiple instances of the same thing so a better solution would be to have a Singleton which is going to be a single instance of this network communicator and then all the components that require that functionality will
9:38
access that Network Communicator okay so this is kind of the idea behind the single turn put a central resource in a single instance of that resource rather than duplicating it multiple times okay so only one instance it's a single point of access for a resource and we've taken the example of a network communicator but we might have for example a database or a logging system or things like that where you want a single resource okay so here are a few examples but of course it's not
10:10
limited to that now as I mentioned there are some disadvantages to Singletons and people are often um quite against this pattern the main ideas the main reasons why people don't like them are first of all it breaks the single responsibility principle which means that um a Singleton will basically manage its own State and will allow others to create only one instance and this is not ideal this is often a code smells quote unquote code smell which simply means that a particular
10:46
class should um create a component when required whereas the Singleton creates its own instance okay so this is not ideal also testability issues might come in since the Singleton kind of handles multiple situations multiple cases multiple components will access that Singleton by its very nature where you have a single instance of a component that means you couple tightly um the components that need the Singleton with the Singleton instance okay so this kind of reduces
11:22
testability you cannot really mock a singlet ton in a test since you have only one instance and you need to um you know kind of get around that in some way also um you have a state for the lifetime of your application okay so once the Singleton has been instantiated you can only use that particular instance of course you could Implement a solution where you kind of kill that instance in some
11:51
situations and recreate it but that would break the principle behind the Singleton okay so that means once a Singleton is instantiated at you have your state for the lifetime of your application and that causes some issues all right so enough with the theoretical introduction let's go to our editor and start to implement a Singleton in our code now here I am using py charm for development
12:18
but you can use any editor you like and I've just simply created a new project here okay so let me just delete that file all right now we're going to let me just create a python file we're going to implement the Singleton in two different ways the first way is going to be a naive implementation where we Implement a Singleton that works but I will show you a particular case where it doesn't
12:45
work to kind of um tell you straight away what the issue is U it works in a single thread but not in a multi-threaded environment okay so we're going to implement this naive implementation and then we're going to see what the solution is and how we can fix the multi-threaded implementation okay so first of all I'm going to have a naive Singleton so let's implement this naive Singleton
13:09
I'm going to have a class with a singl ton and I'm going to have a type here okay I'm going to have underscore instances is going to be this empty dictionary and I'm going to override the call method So Def uncore underscore call and I have here self ARs and KW ARS now um I'm going to say if um self not in self doore instances so it if we don't have that instance then we will create it so instance equals super do call okay okay here we have arcs and KW
14:03
ARS okay and then we're going to have self doore instances of underscore uh sorry self equals instance okay and outside my if I'm going to say return self do instances of self so what I'm doing here is I'm checking does this instance exist and if it doesn't then I create it and I add it to my dictionary if it does exist then I don't need to create it again and I simply return it okay so that will just create um
14:49
a single instance for my Singleton class okay I'm going to have here a class Network driver with a meta class Singleton okay and here I will just have a method Define log of self and I'm going to print um I'm going to print an F string and I'm going to have self and I want a sln at the end um that should be I put self here twice there we go so um I will just Define a method that prints out my instance of my class okay and then finally I will have a def create
15:46
Singleton okay no parameters here and I want to have Singleton equals Network driver
16:00
singleton. log and I will return Singleton okay so that is my creation function so here I will have a main and I will say let me just put a um comment here single thread okay so we're on a single thread I'm going to say S1 equals create Singleton and S2 2 equals create Singleton okay so I create two Singleton but obviously if this works as it should then only one Singleton will be created okay so let me go ahead and run this the na quote unquote naive Singleton
16:44
and we can see here that the created objects are exactly the same now the problem comes when we uh Implement a multi-threaded environment I've just realized that the text is quite small so let me just um up dat this uh let me search for font font size 16 let's put it to 20 hopefully what is happening okay so hopefully that is easier to see now um so as I mentioned this is a single thread so let's add a um here multi- thread
17:30
all right so for multithread we're going to say uh proc P1 for process one equals I need a thread of course so let me just go at the top here and I'm going to say uh from threading import thread okay so I have my thread let me put a space here okay going down I have uh P1 equals thread and I have Target equals create Singleton okay and I will have P2 equals thread with Target equals create
18:16
Singleton so this will create two Singleton on two threads so let's see how this works if I run this um okay so I need to comment out the creation because the creation once the creation is done of course the Singleton is uh created so we will get the same thing and I'm thinking uh because this creation here can happen really quickly I would need a bit of slowdown so in a real world situation this might be delayed to you know instantiate some resource so I want to
18:55
simulate that I'm going to put here import time if I type this correctly import time and I want to simulate here that this might take let's say um time do sleep for one second so it will take let's say one second to create this Singleton and in this case that will cause some issues okay the way I've the reason I've done this here is because in a real world situation it takes some time to
19:27
instantiate some resource sources on your machine so for example instantiating a connection to a database or to a network Communicator or whatever it is okay so um with that let's run our naive Singleton and and I've just forgotten here to say p1. start and P2 do start okay so running that again this should be a should not be a function call all right we got there in the end okay so um sorry about that there was a bit of
20:09
confusion but in the end um we basically create two threads we start them and as we can see here we create two different um objects of this class right here we have 40 here we have d0 so these are two different instances of our supposedly single instance class okay so that is a problem um in a multi-threaded environment so how do we actually fix this let's go ahead and create a new python file um here we have let's say thread safe
20:49
Singleton okay um I'm just going to copy everything from here and paste it here here um the way we solve this is once we start to implement start to create our Singleton we need to have a lock um so that we lock the thread so that other threads cannot access it here okay so in order to do that I'm going to add lock here uh from threading and in my Singleton class I'm going to have underscore lock is going to be um lock
21:30
all right so I have my lock and in my call uh function I'm going to say with um here I have self. lock okay and I'm going to put my if inside this WID block okay so with that done I think the rest should stay the same and I'm going to just run this and you can see that we have the same instance for both threads so what is happening here is basically whenever we enter this function call we lock the thread to check whether or not there's an instance okay so that is all that we
22:11
need to do once we have that determined then we can simply return the instance that is available so that is how you would implement the Singleton for multi-threaded environments all right so in this video we're going to talk about the factory method meod now here is the problem that this design pattern will try to solve so first of all in our development in our projects often times requirements change and we do need to go into the code and change some particular aspects
22:43
depending on what new functionality is needed or we need to refactor the code that we have to adapt to new changes okay so um this is where Factory method will come in because it will cause kind of a separation between uh the creator of an object and the user of that object right so that means this separation will allow us to change our code more easily without impacting a lot of our code now Dynamic switching is also a quite interesting topic because in a
23:18
real life situation you might have for example you might have multiple data sources or you might have multiple databases and you want to be able to switch between those databases in a dynamic way based on certain requirements um in that particular moment okay so we don't want to have a tight coupling between the user of the object and the creator of the object so that we can change that object dynamically based on other requirements also there is the principle of
23:49
separation of concerns which means that you want to separate the creation from the utilization okay this is kind of a general principle in software development so how does factory method help with all of this first of all the design logic and the creation or instantiation of an object is hidden from the client so the client doesn't really know how to create the object it just knows how to use
24:14
it so that means multiple classes of objects will be able to uh be created and the client will have access to it without knowing which exact type it is actually using we have many subclass types but only one instance right so as I mentioned we can have multiple classes multiple uh types of objects but as long as the utilization or the interface is the same then we only need one instance and
24:44
we don't really care which instance which type it is creation is removed from the client obviously we need another object that creates object or another class that creates objects and then this is useful for frequent code changes so when we want to update our code when we want to introduce another type of uh this uh you know object or this component um this is where the factory method really shines because the separation is done in a way that we can just add or subtract multiple
25:16
types and the code will still work seamlessly without impacting too much so to kind of give you an overview how this will work is here we have for example three types of data databases okay we have instances of these databases these connect to their respective databases and use their particular language however all of these objects that access the database will have the same interface so that means this interface um will allow us to communicate with either one of
25:49
these Without Really caring about which particular instance we are dealing with and then we have the database Factory in this example this is a generic Factory that creates the particular instance that uh at that particular time is required okay so this is where the separation kind of comes in the client doesn't really know or doesn't really particularly care which instance it is using as
26:13
long as it gets an instance with this particular interface so you see the separation here between utilization and creation so the components that are required basically will be an interface or an ract class that defines the common functionality the implementations one or more uh as many as we need and we can add as the project kind of grows and matures we can add more implementations based on that particular interface and then the factory class that instantiates the right
26:46
implementation for that particular use case for that particular situation all right so that is all for the theoretical introduction let's move to our uh code and I'm I'm going to create a new file here new python file and I'm going to call this Factory uncore Method All right so in the factory method we're going to add first of all we're going to add some objects to kind of serve as um the things that we kind of move around and pass around in our code so here I'm going to have
27:18
a class country and what I would like to do I'm GNA just pass here what I would like to do with this code is I would like to have a kind of quote unquote currency Factory that will give us the currency for that particular country okay and we will have multiple instances of those currencies we will see how uh that works so I have my class country and I will have another class here let's say USA that is a subtype of country I'm going to pass here I will have class let's say Spain
27:56
country class and let's add one more class Japan which is also a country all right now um let's create a kind of abstract class however uh since python doesn't natively support that we need to import from ABC import um ABC and Abstract method so that we can create abstract methods and down here we will say class currency Factory okay this is going to inherit from ABC and we're going to have an abstract method let's Define currency underscore Factory okay we have ourself but we
28:46
need to pass the country here and we will return a string and for now we're going to pass okay so this is the abstract method um which will allow us to implement our um our Factory okay so we have our currency Factory now let's define uh this is our interface let's define our let's say Fiat which is the usual kind of money that you have in a country the usual currency um current fiat currency Factory and this is going to be a currency Factor okay so we're going to
29:26
Define here our currency Factory that returns a string and we're going to say if country is USA then return uh USD United States dollar L if country is Spain then return Euros else I forgot there we go um else return JPY Japanese Yen okay so um very simple very standard now let's create another one that's going to be virtual currency Factory and this is also going to be a currency Factory let me just copy
30:22
and paste this because it's going to be exactly the same however here instead of USD let's return let's say Bitcoin for Spain let's return ethereum and for Japan obviously we're going to return Dogecoin okay so we have our Factory so now we need to we can simply have a uh client code so let's Implement our main we're going to have fub1 equals the currency Factory and F2 is going to be virtual currency Factory okay so now if we use F1 we're going to say print f1.
31:12
currency factory and we're going to do that for USA and that's going to create the um USD and let me just duplicate that two times and instead of USA I'm going to have Spain and I'm going to have Japan here okay so this is all with F1 and I'm going to copy and paste and I'm going to replace this with F2 okay so this should generate all the coins all the currency that we have in our application there we go so USD Euros Japan and then Japanese Yen and then we have Bitcoin
31:51
ethereum and Dogecoin all right so that is the implementation for the factory method so you can see the use is uh where we use in our code in our main code is separated from the creation right um we don't know how to instantiate these currencies but the factory itself does so that is uh really useful in a lot of situations in your project abstract Factory is you can think of it as kind of one level of abstraction above the factory meth and it is simply put it is a
32:30
factory that creates factories okay so just one level up so let's see how this works in practice so let's imagine we have a data source here um data source type this is an interface and then we have database we have user input Network and whatever other data sources we might want we might have all of these have underneath them they have certain uh types so for example the database might have SQL database mySQL database or whatever type of database so you can think of this as kind
33:04
of a factory for uh databases Factory for user inputs and so on now which one we use doesn't really matter from the point of view of the user which is in our case is going to be the display so the display doesn't really care where it gets the data as long as data is retrieved in the way that it wants so in this case we can have a data source Factory which is kind of higher level abstraction which gives us the data source whatever that data source may be okay so the data source
33:38
Factory instantiates the actual Factory that we're going to use which in turn will allow us to access the resource okay so this is how it works we have a data source Factory Here We instantiate the particular database Factory Network Factory or whatever we will need and then that in turn will be uh will implement the data source okay and I haven't drawn here but obviously the database and network will um will retrieve the actual information so abstract Factory
34:10
provides a way to access functionality without caring about implementation this is kind of true for the factory method as well but as I mentioned this is a one level up and in this case we don't even care about which Factory we use okay so we want to abstract that information away as well one level of abstraction higher and again it is a uh it follows the principle of separation of concerns okay so we don't care about how we use sorry we don't care about how we
34:39
create the the object we only care about using that implementation and again here it allows for testability so we separate the concerns we separate the components in our application and therefore we can more easily uh test components in isolation okay so that is how um that is the theoretical introduction let's move to code and here we are in our editor I am in py charm but you can use whatever you like obviously I'm going to create a abstract Factory file first of all like
35:19
in the factory method I'm going to say from ABC import ABC and Abstract method okay so that we can create abstract classes now what I would like to do here is I would like to have kind of a uh a practical example and we're going to implement a quote unquote restaurant Factory okay so we want to have a particular type of food as a client as a user I want to decide what kind of food I want
35:49
and then I would like to go to a restaurant so I don't really care which restaurant it is obviously in a real world situation you might care for other reasons but in our situation here we don't care what restaurant as long as we get the type of food that we want so the abstract Factory is going to be kind of the restaurant Creator the uh Factory implementation we're going to have a uh let's say
36:13
American and French restaurants and the client the user is going to just care about the food that it receives okay so let's Implement that we're going to have a class food type and we're going to have French equals 1 and then American equals 2 okay so we have our food type now let's create an abstract Factory let me put here an abstract Factory here and I'm going to have class restaurant and this is
36:48
going to be ABC and the restaurant is going to be uh is going to have an abstract method let's say Define make food and this is just going to have self um we're going to pass for now and let's define another me method abstract method Define make drink okay and we're going to pass for now all right so let's define a class restaurant that inherits from ABC we're going to have an abstract
37:26
method um let's say Define make food okay we're going to pass for now and another abstract method Define make drink okay and we're going to pass all right so we have our restaurant kind of interface and here we're going to create a first type of restaurant that's going to be French restaurant
37:59
which obviously will inherit from restaurant so here we need to Define make food and we're going to just print some food that we make let's say cordon blue okay and then we're going to Define make drink and we're going to print something like Mero okay so that is our French rest restaurant um we're going to also Define a American restaurant okay so these are all factories right so restaurant and we're going to have our make food here we're going to print
38:47
hamburger and Define make drink and we're going to print something like Coca-Cola
38:59
okay typical American food right so um now we want to have our uh abstract Factory so let's define um a restaurant Factory okay so the client the user doesn't really care which type of restaurant they care which type but doesn't They Don't Really Care which instance of the restaurant right so that's what the restaurant Factory will create so um here we will have a de let's say suggest restaurant
39:34
based on um here we will have a type which is going to be food type I think type overrides some uh built-in type so let's say R type restaurant type okay so we have our R type and now we can say if R type equals food type. French then return French restaurant else return American restaurant okay so we have our restaurant Factory now we just need a client method def dine at and here we have
40:24
our restaurant of type restaurant right so this is going to Simply print line um let's say for dinner we are having I forgot to put the quotes the right so for dinner we are having then we will simply call the method in the restaurant so make food and restaurant. make drink okay so that is the client method and we of course need a main here we have let's say suggestion suggestion one is going to
41:02
be restaurant Factory do suggest restaurant and let's say first we want some french food so food type dot French and then for suggestion two is going to be restaurant factory. suggest restaurant food type. americ okay so the first suggestion is going to print our French food and drink the second one American food and drink so we're going to dine at the first suggestion and then dine at suggestion two okay so pretty uh self-explanatory I think the abstract Factory the restaurant
41:45
Factory will basically create the factories that we have French and American and these in turn will um share a common interface which we have defined here as restaurant so we can actually use them in the client Without Really caring which type there are which instance they are we know the food that we want and based on that the factory method the abstract Factory uh defines our restaurant but we
42:12
don't really care about the instance okay so here is where we use it and we just call that in dine at so let's run this the only thing I forgot here is to make this method St IC it's also a suggestion there so we could just create a um we could create a instance here but this is uh this should be easier right so this is static for some reason the types have disappeared so food type. French and food type. americ right so since this is a static method we can now access it so let's
42:48
go ahead and run this there we go so it is working as expected for dinner we are having french food and then American food all right that is all for the um abstract Factory U design pattern all right in this video we're going to talk about the Builder pattern now the Builder pattern is really useful in many situations because it allows us to have an object that is initialized in different ways so you can think about an object that has multiple parameters that we need to initialize
43:23
and in some situations we might want to use some parameters in others we might want to use another set of parameters right so the Builder allows us to be flexible when initializing an object for example if we have many parameters it's quite impractical to build all Constructors to build the object that has the uh multitude of options when it comes to building it so for example if we have
43:50
five parameter combinations that will give us 120 Constructors so we don't want to list kind of all possible permutations and combinations of these parameters we have some optional parameters in our objects which means that some parameters are uh you know mandatory others are optional so we might exclude them if we want to when building our object and in addition it should be quite easy to read okay so we should we should have a way to construct the object that is easy to
44:25
understand and easy to use by whoever wants to instantiate that particular object all right so that is all for the quick introduction now we're going to move to our code um and we're actually going to see two ways of putting in place this Builder pattern in Python one is the classical way and one is kind of a simplified way to achieve the same thing in an easier way all right so let's start with the first one I'm going to create a new file and I'm going to call this Builder
44:57
so what I'm going to do here is I'm going to have a class called network service where I can instantiate it with uh different parameters we're just going to have three parameters in our example but we can add uh many more if you like so you can kind of visualize how that would be so let's have a class network service and we're going to Define our init method here and we're just going to say self. components is going to be an empty dictionary all right so we're going to add a key
45:36
of type string and a value of also type string and we're just going to say self. components of key is going to be value okay easy enough and we also want to just print out our components to see what we have there so here we have do show okay and I'm going to say print self. components okay so that is our network service it's quite simple but as you can see we can add as many parameters as we like here now for the purposes of this design pattern we're going to
46:19
have a class network service Builder which we're actually going to use to inst instantiate our network service so here I'm going to Define an init method self. umore service is going to be a network service all right now let's add a few things so first of all we might want to add a Target URL to our network service right so def add Target URL and here we have our URL
46:58
of type string so what this does is basically it says Self doore Service do add and we have a key URL and our value URL okay easy enough that's the target URL let's define a couple more let's say add O Okay so that's authorization and here we have self and O of type string
47:28
okay um we're going to say selfservice do add we're going to have the key authorization and we're going to have the value off okay and finally we're going to add um something like Cash add cash or rather caching because this is just the time interval for the cache okay so caching and and here we're going to have our cachee of type int okay and we're going to say self doore service. add here we're going to have cache control which is how you would um
48:11
spell that in the um URL headers okay and here we have our cache okay that's going to be converted to an to a string automatically now we're going to Define also a method build that will return us a network service okay so we're going to Simply say here uh sorry service equals Self doore Service I want to reset this underscore service so underscore service equals network service so reinstantiate that and then I'm going to just return our service
48:57
okay that is fine so here we have our Builder and now we're going to use it in our main so let me Define the main and we're going to just say here Builder equals network service Builder and now we can use it to create our network service so first of all we will create a very simple network service that doesn't have authorization and doesn't have caching so we're going to say build
49:27
do add Target URL that's the only thing we're going to need and let's say google.com Okay so we have our first network service we're going to say service one equals Builder dot build and then service one. show okay so we're just going to print it out now this has been reset okay so we have it here we reset it so going to create a new one we're going to say builder. addtarget URL let's say youtube.com we're going to say Builder do um add o okay so here we have let's say abc1 23
50:14
this is the key for the authorization and finally we're going to add caching and let's say this is 60,000 milliseconds that's 1 minute and then if we do service 2 equals builder. build and then service 2. show okay so that should basically build our two services so let's go ahead and run this and we will get the first one we have only the parameter that we have instantiated and the second one we have all three parameters okay so very very uh clear I would say how we use
50:55
this Builder to to instantiate the network service now as I mentioned earlier we have an actually a simpler way to do kind of the same thing so this is the classic way you would build a builder okay if you require some more complex functionality but let's keep it simple and let's create a new file Builder 2 and in this file we're going to build the same thing except we're going to take advantage of the named parameters that we have in Python okay so we're going to have
51:27
class um network service okay and here we're going to have our init function but here the difference is we're going to pass all parameters here in the init function and we're going to give them default values so URL of type string and empty by default empty string we're going to have off of type string also empty by default and we're going to have cache of type int and that is going to be Zero by default so here we're going to build our dictionary so self. components is
52:08
going to be an empty dictionary now we're going to say if URL okay so if this is not empty then we're going to just add it to our components so self do components of URL is going to be our URL and we're going to do the same thing for the other so if o self. components of O this is authorization is going to be equals to O and then if cache so if this is not zero because this is an integer we're going to say self. components of cach control is going to be
52:56
our cache okay very very simple so this is kind of equivalent to what we had previously and now let's define our show function and here we're just going to like before we're just going to print self. components all right so we have that and now that is basically all we need okay so this is equivalent to these two um to these two classes so now we can use our main and we can say here service one is going to be equal to network service and here we have the option to provide the
53:36
names of the parameters so we're going to have URL google.com and we're going to leave it at that and say service one. show okay and service two is going to be network service and we're going to provide more parameters this time so we're going to say URL equals uh youtube.com we're going to say o equals abc123 and cash equals uh what did we say here 60,000 okay and service 2. show so that
54:15
is basically all we need this is equivalent to all of this right so let's go ahead and run this code and then we get the correct output what we were EXP in so that is the Builder pattern it is quite useful um especially the second version that I've shown you here it is quite useful and quite often used to uh provide optional parameters in your initialization okay so you will find it quite often um in code all right the last of the creational patterns that we're going to talk
54:50
about is the Prototype now here is the problem that this is trying to solve so let's say in our code we have some existing object it can be of pretty much any complexity and for some reason we need a copy of that object okay so we don't want to kind of create a new object from scratch um and then go through the whole process because this particular object might already have some state in it or has gone through uh user interaction or some other reason right so there is some complex
55:26
it in this existing object and we just need a simple copy of that object how do we do that well the problem here is that we cannot simply copy uh the existing object because some Fields uh for example might not be visible in that object right so we cannot go and kind of get each field individually and initialize a field with the same name in the new object we cannot go one by one also we might only have an interface to that object we might not have the uh reference
56:01
the object itself right so we might not know exactly what we're dealing with also um a kind of a third problem in copying uh line by line or field by field is it requires tight coupling in the sense that we would have to know exactly the structure of that class uh of the initial object that we're trying to copy okay so that's obviously uh going to lead to tight coupling which is not ideal so what does this prototype um need to do first of all it lets us copy existing objects
56:34
okay quite simple um we will not be depending on their class that is um quite necessary we can have situations where we only rely on an interface the copied object must provide the copy functionality so we don't want to externalize this functionality outside the existing object we want the object itself to provide the the copy functionality because the object itself will have access to everything that it needs um and it avoids tight coupling on the outside okay so it will have
57:05
access to its private Fields it will have access to all the required functionality to its state and so on and then finally this is quite useful in testing and pre-production so for example you might have a live uh system where you have certain Long Live objects like a database connectivity or some user state management object something like that if you want to test that object or you want
57:31
to kind of U provide some pre-production staging environment then you want to be able to copy uh those objects into your second system okay so quite useful all right so that is the kind of theoretical overview let's go to our code now the good news is that python out of the box provides us with this copy functional we just need to kind of import it in our project and know how to use it so let's go ahead and create a new file here and I'm going to call this prototype okay now the
58:09
copy functionality is going to be in um a copy uh package so we're going to import copy okay and I'm also going to import um sorry from ABC import ABC and Abstract method so that we can Define kind of abstract functionality so what we're going to do here is we're going to have kind of a uh theoretical example we're going to have a shape structure um these are the objects that we're going to have in our code and we're going to using these shapes we're going to build
58:46
a quote unquote uh abstract art functionality okay so an object that combines these shapes together to provide some output and we want to copy this uh art okay so we're going to have here class shape that inherits from ABC and we're going to have an abstract method def um draw and we're going to pass for now okay so with this shape we're going to have a class square that is a shape and we're going to define the init function here with a size parameter
59:30
so we're going to say self. size equals size and obviously we need to define the draw method that simply prints out a F string drawing a square of size and the size that we have self. size okay okay so that is our Square let's define one more we're going to have a circle here which is also a shape very similar in it we're going to add a parameter radius and we're going to say self. um radius equals radius and Def draw a print a F string draw drawing a circle of radius
1:00:31
self radius all right so very good so far now uh we're going to have an object that we will eventually copy and that's going to be class um abstract art okay um here we're going to have an init we're going to overwrite the init and we're going to say let's say we have a background color and we're going to have our shapes so self dot BG color equals BG color and self dot shapes equals shapes so shapes is going to be a list so we're going to say def
1:01:17
draw okay and we're going to just print um a F string background color is self. BG color and we're going to print all the shapes so I have x. draw for X in shapes um X do draw a function call here all right so we have our abstract art and we need to just have a main where we can create some objects so here let's create some shapes that is going to be a list let's say we have a square of five we have another square of three and we have a circle of
1:02:15
eight for example and then we can create an art one object that is going to be an abstract art where we provide the background color as red and provide these shapes okay so so far so good now this is quite basic quite simple functionality um the Prototype part comes just now with this copy functionality so as I mentioned we get this uh by default out of the box so we can say art 2 equals copy do copy we have two options here we have copy and deep copy the only difference is that
1:02:57
um with copy the objects that you have inside your copied object will be copied by reference whereas the Deep copy will be copied will copy the objects themselves as well so copy just gives you references uh deep copy gives you copies okay so I'm just going to use copy um in our example the output is the same the result is the same whether we use copy or deep copy so here we have copy of art one and we're going to have art 1. draw and art 2. draw okay so we're just going to print
1:03:38
them out just to see that they are exactly the same so running that we get the art one and art two okay so they are exactly the same they have uh the same parameters and the kind of um prototype pattern was implemented automatically for us was given to us uh out of the box with the copy um package all right so the first of the structural design patterns that we're going to talk about is
1:04:10
the adapter and the adapter is kind of the maybe the easiest to understand of the patterns um what it does is simply it converts one interface of one class into another interface that the client expects what this means is is that we might have in our code we might use a uh third-party library or we might have some part of our code that is used in multiple places um so that means we cannot actually modify that particular class or that particular interface so what we want to
1:04:42
do is we need to convert the interface that we do have control over um and we want to be able to access the thirdparty library or third party interface in an easy way so we cannot contact it we cannot access it directly because these two interfaces uh are more or less incompatible so what we do need is some sort of adapter between our interface and the thirdparty um interface or we can think of it as converting data from one format into another whether we're talking about
1:05:18
interfaces or data we still need need this kind of adapter between the two types so here is a small diagram to kind of show you um in graphic form what we mean we have a client that has a Target API or Target interface and we need to call this adap T interface that we cannot modify have no control over so what we do is we implement this adapter class that converts the call that we have in our code to this specific call in the third party Library we don't want to put this
1:05:53
functionality this adapter functionality in our Target because we want this kind of separation of concerns and also we might not want to modify the target if it's used in multiple places in our code so the best solution is to have this separate adapter class that converts from one type to another or converts the data from one type to another all right so let's move to our code and let's go ahead and create a new file here and I'm going to call this adapter so what we going to do
1:06:26
here is we're going to have um a type of data in the thirdparty library we're going to implement that and kind of think of it as immutable we don't have access to it and we're going to then implement this adapter class that converts from one data type to another so first things first let's go ahead and import from data classes import data class and let's define here a data class and I'm going to call this class display data type okay and this will just have an index of type
1:07:07
float and it will have some data of type string okay so I'm going to mark this as thirdparty functionality okay we don't have access to it we cannot modify this um functionality also in the third party functionality we have a display data class okay and this has an init where we have some display data of type display data type and we just want to uh store it for now so we're going to say self. display data equals display data and we have some functionality
1:07:55
so let's say def show data okay and we're going to just print line an F string third party functionality um okay we're going to have here self. display data do index we're going to put some dash line and then self. display data. data okay so we just want to show the data in a certain way and we're going to assume that this is kind of third party functionality it's a bit more complex but we're going to simplify it here
1:08:35
just for the purposes of this code all right so what do we do with this uh data type we cannot simply use it let's say we want to store it in some database this is display data so we cannot just simply um access it as it is and here we're going to implement some uh of our own function functionality so our code okay this is part of our functionality let's say we Define a data class um class database data type okay and this has a position of type int
1:09:16
and it also has a let's say amount of type int all right so we have our code now here we also have a class store data store database data okay we're going to Define an init function okay here we have database data of type database data type self. database data equals database data and we're going to have some local functionality some of our our own code functionality let's say store uncore data okay we pass some data here and we'll just print it out so print an F string
1:10:11
database data stored and let's just display the data so self do database data do um position some Dash line and then self. database data do amount all right so this is our functionality so what we want to do is we want to access this show data but we don't want to do it directly for the reasons that we mentioned previously so what we're going to do here we're going to create a class uh display data adapter okay and in this display data adapter we're going to inherit from store
1:10:55
database data and display data so we're going to use both we're going to have an init function that retrieves some data self. data equals data okay and we're going to Define our store data okay let's print a message to know that we are in this uh code so call our code but use
1:11:26
third party code okay so we're adapting our code to the third party and then we're going to say for item in um self. data we're going to convert the data type so we're going to say DDT equals display data type okay we're going to say we're going to convert to float um item. position and then we're going to convert to a string the item. amount okay so this is um very specific to your code to your
1:12:04
functionality in our case we're converting the data that is passed to us which is position and amount we're converting that to our display data type so that we can call our show data okay so we with that let's go ahead and say self. display data equals DDT and we're going to call self. show data okay so we're storing the data in the right way which is required by the third party library and then we're just calling the functionality you can see here that the specific call to the
1:12:43
third party functionality is encapsulated in our adapter so the actual store data database data is not actually changed okay so we're just adapting this call to the third party functionality all right so with that let's have a function here def generate underscore data okay and this will generate our uh list so let's say data is going to be a list and data do append um database data type
1:13:19
we're just going to generate some fun some data here let's say two and two let me just duplicate this twice and let's just change the values here 3 and 7even and 9 and one okay so this is the data that we're trying to adapt and we're going to return data okay so now we have our main implementation we're going to uh instantiate the adapter equals display data adapter and here we're going to pass the generate data and then finally adapter. store data okay so we're calling our
1:14:04
functionality but in reality we're using the third party functionality okay let's just remove a few lines here so that the compiler is happy and then I'm going to run the adapter I think I just made a mistake here we don't actually need this call here since we have the self do data okay so just remove that try it again and run okay another small mistake instead of display data type we're not
1:14:38
actually creating we need to create our own data type so instead of display data type we're going to have have database data type my mistake there okay so basically we are using our own code we're not using the third party uh code the third party is going to be restricted to the adapter so this has to be a database data type all right let's run it and then we get our correct functionality
1:15:07
so we are calling our code but using the third party code and third party functionality has been converted right so our data converted to this data type all right that is it for the adapter pattern thank you for watching I will see you in the next video all right so let's now talk about the bridge pattern now the bridge pattern is very useful when we have uh multiple inheritance in our code so
1:15:33
you can imagine a part of our code a class that needs to inherit from multiple um other classes multiple components um that class is uh will be composed of a certain number of other parts and other components um the issue here is that the more of these components we have in our class the more this class needs to handle different functionality in different cases so separation of concerns is kind of broken here since one class will handle multiple aspects so let's have a quick
1:16:07
theoretical diagram um to kind of illustrate this point so here I have a class that inherits kind of two components one is going to be shape and one of is going to be color so this will need to handle basically all these kind of permutations right red circle blue circle Circle red square and blue square a better solution um in this case would be to kind of separate these two components shape and color into their own classes and you can imagine here if we add another dimension
1:16:39
one two three other dimensions then these this inheritance will kind of grow exponentially right so the better solution is to separate these two components one is going to be shape and that's going to handle Circle and square and another is going to be color red and blue and here we have the relationship between the two which is what we call the bridge okay so having classes with multiple orthogonal traits multiples of these components exponentially increases the size
1:17:12
of the tree right so the more we have Dimensions here the more we have permutations down below at its most basic the bridge pattern converts from inherit to composition so we no longer inherit from multiple components we compose them in our class and let each component kind of manage its own state and its own uh functionality we split into multiple interfaces and multiple classes as we've seen here instead of having one that handles everything we have two that are composed within
1:17:48
our code and then finally we associate them using a bridge reference okay now this is kind of of a theoretical introduction let's move to code and see how that works in practice all right so here I'm going to create a new file new python file called Bridge all right now the first thing I need to do is from ABC import ABC and Abstract method I want to be able to set a few abstract methods
1:18:20
um in my upper class in my uh main class so here I will defined class device all right so to give you a description of what we're going to do here is we're going to have um a few different types of devices and a remote control to control those devices and we're going to connect them through a bridge okay so the first thing we need to Define is the device that's going to inherit from ABC we're going to set a volume variable equal to zero and we're going to Define an abstract method
1:18:55
def get name okay and here we will simply return a string now since this is an abstract method we're going to pass for now and we're going to use this class in our inheritance so class first of all let's say we have a radio that is a device and here we have get name and we will return an F string something like radio self okay very simple now let's define another one that's going to be TV which is also a device we're going to overwrite the abstract method and
1:19:41
here we will return an F strring TV self okay quite um simple and straightforward so far now let's Define the remote class so class remote ABC and here we will have two abstract methods the first one is going to be def volume up okay we're going to pass since we are in the abstract class and then we're going to Define another abstract method def volume down and again pass okay so now we have the remote Base Class we're going to define a class basic remote which is
1:20:33
going to be a remote and we're going to Define an init method where we pass the device that this remote is for okay so here we have our device um you will notice here that we don't really specify which particular device it is we can pass whatever device we like so here we're just going going to store self. device equals device all right now let's implement the abstract method so volume up and that's going to be self. device. Volume Plus equals
1:21:10
one and then print an F string um we're going to say self. device. getet name okay volume up and then we're going to print out the volume self. device. volume all right so that's volume up we're going to Define volume down in a similar fashion self. device. volum minus equal 1 and then let me just copy this code and then we're going to say volume down to this level so that is basically all we need to do now let's define a main function and here we will have a radio and a
1:22:01
TV okay and of course we need two remotes one for radio and one for TV so radio remote equals basic remote of our radio and TV remote is going to be basic remote of TV all right so we have those now let's go ahead and change up the volume a bit so radio remote. volume up okay we're going to say radio remote. volume up and finally volume down okay and we're going to do something similar for TV remote do volume up TV remote. volume down let's say
1:22:56
volume down and then let's say TV remote. volume up and duplicate that so um just kind of random randomly changing the uh the volume for those two so you can kind of see how we work here we have our basic remote and we have a bridge to our device okay so we connect those um in this way and we can manipulate them uh regardless of which particular type of device we pass here okay so if I run the bridge then I would get the radio volume one two and then back down to one the
1:23:35
TV one then back to zero and then increase it twice okay so hopefully this gives you an idea of how the bridge pattern works we have only two dimensions in our case we have device and remote but you can imagine a code where we have multiple of these uh dimensions and in this case the bridge pattern will be even more useful than it is here all right so the composite design pattern is often used when we need to apply a certain functionality to a structure in our code and
1:24:11
most often that structure is going to be a tree of components within our code right so um let's have a look at a quick highlevel example here so here we have a computer um that is composed among other things of memory hard drive and a processor and the memory itself is composed of a random access memory and a readon memory and you can imagine this kind of tree structure going much deeper um each of these components can be subdivided into other components and so on and
1:24:45
so forth right but I've simplified it here to just a tree uh that looks kind of like this now each of these components has a price okay a price assigned to it and what we would like to do is we would like to calculate the total price for that um element for that computer or individually we want to calculate the price for the subtree for example the one we have here is the memory right so we want to calculate the memory price and then we want to calculate the computer
1:25:17
price and this is where the composite design pattern comes in because we can apply one uh particular functionality one um calculation to the entire tree or to a sub tree within that larger tree so the composite design pattern allows us to structure objects into trees um and then it works really well when the functionality uh can be represented as a tree so whenever we have this kind of structure it can be a tree it can be a graph um as long as we have a way of navigating
1:25:54
that structure then we can apply the composite pattern it allows us to manipulate multiple objects as a single one so for example here it allows us to either manipulate the entire uh structure as one as the computer or if we want a sub uh tree from that uh larger one we can manipulate the memory for example as a single element all right so that is the composite design pattern I know this is kind of ract but let's go to our editor and start to implement an example of
1:26:29
how this might work so I'm going to create a new python file here and I'm going to say composite all right now the first thing I need to do is I need to define a class equipment and by the way we will implement the structure that we uh saw in the in the theoretical example right so we'll have a computer memory hard drive processor and so on all of these are equipments okay so I'm going to Define here an init that has a name of type string and a price of type int okay
1:27:06
we're going to limit our prices to integers and let's just store self. name and self. price and that's going to be price okay so that is my com my equipment now I'm going to create a class composite okay let's define the init here now here I'm just going to provide a name for my composite self. name equals name I'm also going to Define a variable self. items is going to be an empty list okay so we're just going to store the items that this composite is made of
1:27:54
of and the composite can be uh the computer that we talked about or can be the memory basically the tree nodes okay in our example so here um I'm going to Define an add function with a equipment of type equipment okay and I'm going to say self. items do append my equip equipment so we're going to store it in our um in our equipment and then I'm going to return self return self okay so we can kind of chain the operations um together now I'm going to Define here a add property okay
1:28:43
and the property is going to be def price okay we have self and then we can simply return a sum I want to return the sum of the um equipment elements okay so the sum of elements in this array and I'm going to have x. price okay for X in self. items okay so a list comprehension here and we're going to sum everything together okay now we have the price and I'm going to have a at price. seter
1:29:25
and I'm going to Define price of self and value okay self do price equals value okay so we're just going to store that uh value here that price value okay so that's pretty much all we need to do and we're going to Simply Define Composites and add elements to it add equipments to it okay so let's create a main function and here we have a computer is going to be a composite we need to provide a name so I'm going to say PC and we're going to
1:30:02
have a processor that's going to be an equipment here I have my name processor and the price let's say 1,000 okay now um I have my processor let's define uh hard drive is going to be another equipment of a pro uh sorry a uh hard drive and this is going to cost 250 let's say um we're going to need to define the memory now the memory is going to be another composite okay this is a node in a tree it's not a leaf yet in our structure so here we have memory and we have ROM ROM memory
1:30:52
which is going to be an equ equipment of read only memory and let's say this is 100 and finally we have our am which is going to be an equipment random access memory and that's going to cost 75 all right so we have all the components now let's add them to um to create kind of the tree structure we're going to say mem equals memory. add we're going to add the ROM and do add RAM and then finally the PC is going to be computer. add we're going to add the processor do add hard
1:31:44
drive do add the memory okay so that's pretty much all we need to do and now since we have all the structure in place we have the composite design pattern in place we can simply say print PC do price okay so the pc. price will basically calculate everything for us and just print out the price of this whole system okay so let's go ahead and run this and that is 1,425 okay so what you can see here is that we have um created our structure and we automatically app apply
1:32:24
one uh functionality to the entire structure we can apply the price to memory or to the PC itself okay so that is the composite design pattern all right The Decorator pattern also sometimes called the wrapper pattern is another pattern that is quite easy to understand and really really useful in practice what it does is allows us to um update or modify the behavior of a class that we do not have control over so you can imagine a class that is part of a third-party library that
1:33:04
we cannot modify however we do want to alter Its Behavior slightly or perhaps add a bit more functionality than is currently available in that case we can use the decorator pattern to add that kind of personalization to that third party class so in simple terms it allows us to attach new Behavior to an object that we do not control also um it allows us to do that without altering existing code so of course if the class is part of a library we cannot modify it so we want to be
1:33:40
able to update it uh without altering the code of the class um when I say attach new Behavior I also mean uh so on the one hand attach new Behavior but also modify existing behavior that doesn't really suit the current requirements overriding Behavior so changing existing behavior that um is present in the class but we want to update it for our current purposes so to kind of give you a quick um theoretical example we can imagine here we have a network communication system where
1:34:18
we have a lower layer tcpip communication protocol and let's say uh as an example that tcpip protocol has a send packet functionality okay so that send packet functionality will not be enough for the purposes of our application okay we want to communicate easily uh seamlessly with a uh library with a uh an API let's say so we want to add some more functionality to this layer so we can imagine
1:34:49
that we can create a network communication layer where we have a send Network message that uses the underlying send packet functionality okay we have put a wrapper on top of this tcpip and we can actually keep going we can create another layer that is let's say an API service that we can actually use in our code in our application where we can say something like retrieve API that will
1:35:17
kind of hide the details of the implementation but just give us the required functionality for our uh code okay so you can see how we can um wrap or decorate lower layers layers and lower functionality into um more um abstract and uh more higher let's say higher level functionality okay in kind of these layers all right so um that is all for the theoretical introduction let's take a quick code example to kind of solidify um how this works in practice okay so let's go ahead
1:35:54
and create a new python file here and I'm going to call this decorator now what I would like to do here is I would like to build a kind of an abstract example where we have a coffee machine that produces some coffee and since we are let's say opening a coffee shop we would like to have a bit more kind of fancy functionality where we create better coffees than just simple basic stuff okay so let's go ahead and Implement that and see how it works first of all I would like to import
1:36:26
from ABC import ABC and Abstract method okay we want to create um abstract functionality here so first of all class coffee machine that inherits from ABC Now this will have two abstract methods so first one abstract method def make small coffee okay I'm going to have pass for now and another abstract method def make large coffee okay again we will pass all right so this is my abstract functionality now this we're going to say is a third-party Library we don't have
1:37:18
access to it okay what we want to do is we want to wrap this coffee machine um in another layer that uses the functionality that we've defined here but also adds its own functionality or maybe um overrides some Behavior okay so let's create a class basic coffee machine okay um here we will have a um inherit from coffee machine here all right now this one will simply implement the the uh two abstract methods so I'm going to have def make small coffee let me change that
1:38:01
real quick small coffee now here I'm just going to print U basic coffee machine making small coffee and then I would like to define a make large coffee print basic coffee machine making large coffee all right so this is the uh implementation of my uh abstract class now uh we would like to override this functionality so let's say enhanced coffee machine that inherits from coffee machine all right so importantly here if we're going to wrap this functionality then we need to provide
1:38:55
um this object to my enhanced coffee machine so I'm going to have an init where I can pass basic machine of type basic coffee machine okay and I'm going to store this self. basic uh machine equals basic machine all right so we have the object and this is going to be the wrapper on top of the object or The Decorator of this basic coffee machine so what I would like to do here since we inherit from coffee machine we need to implement the basic functionality So Def
1:39:34
make small coffee now here we can simply use the functionality that the uh initial object the underlying object gives us okay so for this example we're not going to do anything special we're just going to use self. basic machine. Mak small coffee this is to kind of show you that we can have some functionality that is there and we're going to use it as it is in the lower layer now if we want to override some functionality we're going to override make large coffee self.
1:40:11
basic machine um so for the large coffee let's override the functionality right so instead of printing the basic coffee machine message we're going to print the enhanced coffee machine making large coffee okay so that is the um alteration that's the change in functionality okay we can see that it's different we print a different message now we can also have um added functionality okay
1:40:43
so we have same functionality change functionality and added functionality let's say make milk coffee
1:40:55
okay so for milk coffee we can use some of the underlying lower level functionality but add our own so here I'm going to have print enhanced coffee machine making milk coffee okay we can use self. basic coffee machine. Mak small coffee so we're using the functionality but we're adding a bit more so print line or print um enhanced coffee machine let's say adding milk okay so we're uh kind of wrapping this um lower level functionality with our own
1:41:40
custom one okay so that is the example now let's move on to the main and call these functions okay so here I'm going to have a basic machine is going to be equals basic coffee machine and then the enhanced coffee enhanced machine is going to be enhanced coffee machine and here of course we need to provide the basic machine okay so we have the two now I'm going to call enhanced machine. makes small coffee um let's just print line uh print one line here enhanc machine
1:42:23
make large coffee print an empty line again okay and then enhan machine. make milk coffee okay so that should call all the functionality that we have defined so let me just run The Decorator now now the first one simple uh functionality that's defined in the lower level um updated functionality or overridden functionality and then the final one is uh kind of decorated so we have some functionality before we call the lower level um class and then we add some of our
1:43:01
own functionality afterwards okay so we kind of wrapped the existing functionality into our own code all right so hopefully that's clear that is how uh The Decorator pattern Works in this video we will talk about the facade pattern now this is one of the most useful design patterns um because what it does is it allows us to hide away some complexity and some uh difficult to understand functionality behind a very simple interface okay so let's have a look at what it is basically it
1:43:37
allows us to provide a simple interface to complex functionality you might have a third-party library that has some complex uh working inner workings or you might yourself develop a library that you want your clients to use there is no reason for the client to understand all the complexity that goes around um the functionality that you're trying to implement what you're trying to do is simply
1:44:03
provide a way to use that complexity okay so you want to provide an easy interface an easy facade to allow a client to use a particular um system or a particular piece of code and it removes the need for complex object or memory management it the facade itself is not a simple interface it is a let's say a class that provides some functionality however it manages all the complex uh object or or
1:44:36
whatever memory or uh data types or whatever it is it manages that within your class and provides an easy way to access the underlying system and it simplifies client implementation of course since the client doesn't need to understand how uh the third-party Library works or how the system uh that you're hiding behind the facade works then the client is much simpler and of course this provides us with separation of concerns since the client doesn't need to know how you
1:45:10
implement something it just needs to know how to call that particular something all right so that is the quick introduction let's have a look at some code to see how this works in practice so here um let me Define a new or create a new python file that's going to be called facade all right so what I would like to do here is I would like to create a kind of quote unquote simulated storage system um and store some data okay some uh in our case it's going to be some user data
1:45:46
and we're going to provide a facade for that user storage system that hides away the complexities of that implementation all right now here I'm going to create a class complex system storage or store complex system store and we're going to Define an init that takes a file path of type string now a few variables here self. file path is going to be file path okay this is where we're going to store
1:46:22
the data in our system self. cach is going to be a dictionary so we're going to provide a caching functionality with this complex system store and we're going to just print a message here let me just make that an F string reading data from file and here I have my file path all right now um we're going to define a store functionality with a key of type string and a value of type string and we're simply going to say self. cach of key is going to be value okay easy enough so
1:47:08
far now let's define a read with a key of type string and this will return um self. cach of key okay we're going to be able to read from that cache and finally def commit we're going to have simply print line an FST string storing cached data to file self. file uh file path okay so this is the complex system store and let's define the type of data that we will uh be storing I want to just add here at the top uh from data classes import data class okay so that we can Define data
1:48:07
classes in our code so here I'm going to define a data class class user and I'm just going to have a login of typ string this is of course very um kind of um just an example for us so I'm not going to put too much effort into um adding complexity we can simply keep in mind that this is u a complex quote unquote complex system store even though we haven't implemented much complexity here this
1:48:35
is a third-party library or it can be defined in our own application but we just want to um kind of Shield the user from using such complexity and understanding the inner structure okay so for that purpose I'm going to have a facade here for the facade I'm going to define a class user repository okay so the user repository will allow us to store users with our caching system on a file path okay
1:49:09
so class user repository here I'm going to Define my init and let's say self. system uncore for preferences is going to be a complex system store and let's give a file path so something like data for/ default. prefs all right so that is my init now let's define a save um function where I have a user of type user so I would like to be able to save this user on the complex system store so self. system preferences. store I'm going to define a user uncore key and user. login okay so
1:50:05
I have stored this and I want to commit so self. system preferences. commit okay and then finally we have uh find first okay and here um we want to retrieve a a user so here we will return user of self do system preferences do um read and I want to read my user key okay so I'm just going to retrieve the first we can imagine a system that stores multiple users I want to just get the first one that is available and I read it with my key that I have defined of course again here we
1:50:53
are simulating some um real system in reality you would have a lot more complexity complexity here obviously but we're just going to limit it to this so um main here we want to just use our facade to store and retrieve our user so user uncore repo is going to be a user repository okay we're using the facade and we will have a user let's say John okay so we're just going to store and retrieve
1:51:29
this user in our repository so user repo Dove my current user so I have saved it in my repository and I now I want to retrieve it so retrieved user is going to be user repo doind first that just retrieve my user and I can just print my retrieved user. login it should be the same as the one we defined here all right so that is my functionality that is my facade you can see in my main system in
1:52:06
my uh client uh code I am only using the facade I am not actually using the complex system store that is hidden behind my user repository facade so let's go ahead and run this code and we can see first of all reading data from file default preferences this is the um init function then storing cache data to file okay that is the commit functionality and then we are retrieving the user and just to confirm that we have retrieved the right user we are printing the Lo the login and we
1:52:40
have John that we have defined here so that is how um on a very high level you implement the facade all right so let's talk about the FL fly weight design pattern the flyweight design pattern as its name suggests is all about reducing the um load or the requirements on a system so for example on a memory system it reduces the amount of stuff that you store in memory in a processing system
1:53:13
it reduces the amount of processing that you might want to do that is where the name flyweight comes from so let's have a look at a quick kind of abstract example to give you an idea of how this works so let's say we have a system that has some limited memory capacity right um let's assume we are in some sort of game where we want to produce elements on a screen and we want to display them so one kind of naive implementation would be to uh create as many objects as we require
1:53:47
and store them in memory as they come up okay and remove them as they they get destroyed so you can think about a kind of a war game where you have soldiers that come up on the um on the screen and you want to store every single soldier you want to store them in memory when they are visible okay so that is how this might look but of course this is kind of an inefficient way to go about it because
1:54:14
that will fill up the screen quite quickly okay a better implementation would be to have some sort of shape Factor or kind of character Factory or element or whatever um that simply produces elements that are required that are unique and then you use those elements in memory um as um a more efficient solution right we don't want to create the same functionality over and over again we just want to display it whenever we have whenever we require and just store it in one place
1:54:49
as a single entity now of course this doesn't work for everything so if we take the example of our war game um you could store these Sprites uh that are required to display the elements you could store them in a factory however you might want to have elements for every single uh quote unquote Soldier or character on the screen you have unique information related to that character so for example the direction where they're moving or whether they are firing or
1:55:21
hiding or whatever it it is the state of that element you still have to uniquely store it for that particular character however the stuff that you can store um in the stuff in common like the Sprites you can and should store them in a central location so that is where the flyweight um design pattern comes in it works when you have a lot of similar objects that can share some functionality
1:55:50
the functionality that they share can be unique okay the functionality that you cannot share has to be individual for each individual element it allows us to reduce memory footprint in our example but it also allows us to remove um to reduce processing uh footprint right you don't have to kind of retrieve from memory and process those Sprites over and over again you just have them already uh ready to go and just grab them whenever you need to and of course it um allows us
1:56:23
to improve efficiency we don't want to if we have a large um amount of elements in memory retrieving them and displaying them and um you know updating them uh does take a lot of time so efficiency is uh quite important as well okay so flyweight helps with all of that all right so that is all for the theoretical introduction let's have a look at some code and I would like to implement here a system
1:56:53
where we have some Sprites and some characters um that have different ranks okay we have we saw the example of three types of elements we're going to implement three types of characters in our code so let's go and create a new python file and I'm going to call this flyweight all right and first of all I'm going to import random we're going to need this uh to create some random characters and I'm going to import from ABC import ABC and Abstract method okay just to create abstract
1:57:31
classes so first of all we're going to have a class Sprite with ABC and here we will have an abstract method that we can Define as draw okay pass for now and then we're going to have an another abstract method Define move and we're going to provide the coordinates X of type int and Y of type int and here we're going to pass again for now right so let's define a class fighter rank okay so we're going to have three types of Fighters um let me just spell that correctly uh
1:58:16
three types of fighters in our code we're going to have a private let's give it the value zero we're going to have a Sergent value one and we're going to have let's say major value to okay so these are the types of Fighters and let's create a class Fighter um with a that inherits from Sprite all right so um here we're going to Define first of all our init or override our init we're going to pass the rank okay and we're going to store that self. rank equals rank all right so uh
1:58:59
let's implement the fighter this is a Sprite so we need to override these two methods so first of all draw for the draw we're simply going to print a message um an FST string drawing fighter and I'm going to have the self here and for the move method we're also going to print an F string okay and here moving Fighter um self to position X comma y okay so very uh kind of abstract implementation but it gives us the uh kind of picture of how this might work
1:59:49
now let's define a class fighter Factory um that will allow us to create Fighters so first of all let's override the init and here we're going to just have a self. Fighters is an empty array and we're going to have a function get fighter with um where we provide the rank of type fighter rank all right so um here we're only going to store three types of three actual Fighters one for each type okay so first of all we're going to say f equals self. fighters of rank okay
2:00:40
so we're going to try to retrieve the fighter with that particular rank now we're gonna say if not F so if is if uh we don't have one then we're going to create it f equals fighter of our Rank and we're going to store it for later use so self. fighters of rank equals F okay and then finally we will return F so again here we have our Fighters the fighters will eventually have only three elements one for each rank that we have have all right so that is pretty much
2:01:24
it now let's create a class Army okay with a self. army is an array uh sorry just Define the variable here we don't need self now um I'm going to Define spawn fighter with a rank of type fighter Rank and here we're going to say self. Army um append now keep in mind that this will not contain unique Fighters they will just contain the ones that we have here okay so uh here we have fighter of rank okay so we're adding fighters to our army and then finally def draw
2:02:17
Army okay and for or fighter in self. Army we're going to say if fighter. rank equals fighter rank dot let's say major then we're going to say print um I'm going to print an m and I'm going to define the end equals empty string so that everything gets printed on a single line L if fighter. rank equals fighter rank. Sergent in that case I'm going to print s space with an end empty string and then else print a p space with an end empty string okay so our army will be printed on a
2:03:18
single line all right now um we have our main okay and army underscore size is going to be let's say 1,000 to start with then Army equals Army okay so Define or instantiate the object and here for I in range of uh Army size okay R equals random dot um Rand what is it Rand rage uh Rand ra range hard to pronounce that um here we have three so since we have 0 one and two as a as fighter ranks 0 one and two we can get a random uh number from here and then we can say army. spawn fighter
2:04:21
with our R and then finally we just need to say army. draw Army okay so hopefully that will work if I haven't made any mistake typing let's go ahead and run this and then we get our army so we have private sergeant and we have major okay so we can see here that um this is quite efficient obviously even if we created individual objects for each one um you know have 1,000 elements it's not that many but um in a real system where each element does have to store
2:04:59
some local state um this is much more efficient so everything that we can share between the elements we want to share it as much as possible and we do that in our case in the uh Factory okay where we have only three types of Fighters but we just create as many as we uh require for our army okay this is the shared function functionality and we can actually increase this to um let me just
2:05:26
comment this out and if I put Army size equals 1 million we can actually run this as well it just takes a bit of time and then it just displays everything okay so again for a modern system this is a very uh quick program but in reality where we have um many many elements on a screen um we can think of a game but there are other situations where we might have that this is uh a way to reduce the memory and processing footprint in this video we will talk about the proxy design
2:06:04
pattern now the proxy design pattern will allow us to have an interface um or um kind of an object in front of an existing object in our code the point of the proxy is to um kind of manage the life cycle and the access to the underlying object now why we might we want to do that let's take a quick example let's say we have a disk management system that let's say might take quite a bit
2:06:36
of time um to access the functionality underneath to access files to retrieve them to store them uh update them and so on and let's say we have a bunch of clients that are trying to access the disk uh more or less at the same time you know creating files writing U reading and so on so immediately you might see a few problems with the system first of all uh since the disk takes a bit of time to retrieve the information required all the clients accessing the dis at the same
2:07:09
time might overload the system and lead to a lot of delays furthermore accessing the same file at the same time from multiple clients might lead to inconsistencies in that file if we're trying to write at the on the same file at the same time then that might cause a problem so one of these Solutions might be to implement a disk proxy in front of our disk okay so this dis proxy might provide some let's say caching functionality to try to speed up the um the system right so
2:07:45
if multiple clients are trying to read the same file at the same time or sub sub sequently then we might provide the caching ver the cached version of that file rather than accessing the disk every single time also we might provide some sequencing operations where if we're trying to write the same file at the same time the disk proxy might make sure that the written file is consistent okay so
2:08:15
the sequencing is quite important and we want to remove that from that dis since the disk might already be doing a lot of processing um to access to retrieve the information all right so the proxy in brief just provide some functionality before or after calling an object so we might want to provide functionality before after or instead of the uh underlying object um importantly for the proxy it provides the same interface as the original object so here the accessing
2:08:51
system so in our case the clients do not actually know they are talking to a proxy rather than the dis and they actually don't care okay as long as the information is consistent the proxy might just um you know they might access the proxy instead of the real dis without actually realizing it now there's a couple of similarities uh with other design pattern so the proxy pattern is quite similar to a few other patterns but there are some there are some important differences first
2:09:23
of all the proxy is quite similar to the facade design pattern the important difference here is that while um in the facade we provide a simple interface to a complex system the proxy has the exact same interface as the object that it is um kind of hiding right so the clients in the proxy case do not actually know they are not talking to the object they think since there is the there's they are accessing the same interface then they think they are talking to the real object even
2:10:00
though they are not in the facade they know or they're actually accessing an interface that is a simplification of the functionality underneath in addition to that we have a similarity with The Decorator pattern so as we've seen The Decorator pattern can also override can also um replace the functionality underneath the difference here is that the proxy manages the life cycle of the object that it is hiding okay whereas the decorator simply replaces some functionality or
2:10:35
adds its own before or after the proxy actually um uses the underlying object every single time and manages the life cycle of its object so quite a subtle difference but an important one all right so with that said let's move to our code and create a or write an example uh for the proxy pattern so let's create a new file called proxy and what I would like to do here is I would like to have um a image object that might um take some time to display and I would like to have
2:11:14
a proxy image that adds some uh functionality on top of it maybe something similar to caching the image all right so um I'm going to first of all import from ABC import ABC and Abstract method uh sorry AB C and Abstract method okay and I'm going to define a class image with ABC and Abstract method def
2:11:52
display okay and I'm going to pass for now so the uh image the object underneath is going to be an image and we're going to call that real image which is a type of image okay and here let's define or override the init first where we provide a file name of type string self. file name equals file name and let's print a message real image loading and we have our file name here okay in addition to that let's define a display um or rather overwrite the display
2:12:39
uh from the uh abstract class and here we're going to print again an FST string real image displaying displaying self. file name okay um I'm going to just add an end with two lines here just to kind of have a bit of Separation there when we actually display the image all right so um let's define here a proxy image that is also an image okay proxy image will have an init that has the same signature as the other one so we have a file name name of type string we will
2:13:23
store self. file name equals file name and we also want to store self. real image is none for now okay so we have our proxy image of course we need to implement the display and again here we have the same signature but the functionality will be quite different so first of all let's print a fing proxy image displaying and here we have self. file name okay if not um self. real image so if
2:14:08
we don't have anything stored there then we can simply print from disk okay we are retrieving the actual information for the image from the disk and we say self. real image equals real image of self without the quotes self. file name okay however else we will print from Cache okay so if we have accessed this before we just display it from the cache and what we want to do here we say self. real image. display Okay so so a very basic caching functionality all right so
2:14:55
that is all we need to do we have implemented our proxy we just need to call everything we need so first of all image is going to be proxy image okay let's say test. jpeg okay now here first of all uh let me put a comment load image from disk okay and we say image. display then the second time we call that we will say load image from Cache okay and we will get the confirmation when we actually run the code since uh we will print out the correct information in the code so
2:15:37
let's go ahead and run our proxy the first time we run this we get proxy image displaying we get it from the disk uh real image loading and real image displaying however the second time we run we get proxy image test that is standard but we will get it from Cache this time we are no longer loading because we have it loaded already and we are just displaying it so you can see it's faster the functionality is more efficient of course we are just simulating here we're just
2:16:09
displaying a message but you can imagine loading image an image from um the disk or let's say from a uh an API might take quite a bit of time so so this um this print out here in reality in a real world system might take quite a bit of time and we just remove it uh remove the necessity for it when we are displaying the image for a second time so that is the uh use of the proxy design pattern all right in this video we are talking about the chain of responsibility design pattern
2:16:46
so we're going to start with a quick diagram to show you what uh kind of conceptually high level what this uh design pattern is about so here we have a chain of handlers which are classes or objects or components that can handle a specific request so what we do here is we pass some sort of request to one of the handlers the Handler will process that request in some way and then it has
2:17:16
two options first of all it has a link to the next Handler in the chain so it could pass the request down the chain or it has the option to kind of quote unquote consume that request and return a result thus skipping the rest of the chain and if the request moves on to the next Handler the next Handler does the same thing either processes it and consumes it or simply hands it over to the next Handler depending on the logic that is implemented in that particular Handler Okay
2:17:50
so you will find this uh pattern quite often in various systems where you have multiple components in your system that can handle a specific request so to kind of sum up we Define a chain of handlers to process a request each Handler contains a reference to the next Handler so we can pass that request to the next one and that is how we actually create the chain okay each Handler moves
2:18:17
the request down to the next Handler the Handler decides whether or not to process the request okay it have it has the option to skip it entirely it has the option to consume the request and return a result or it has the option to do some processing and pass it on to the next Handler in the chain and then we can have different types of requests so we can have some requests that start from the beginning of the chain and move down all the way to the end we can have
2:18:50
Quest that start somewhere in the middle of the chain and then go forward with the next um elements in that chain okay various types of requests various types of handlers all right so that is the introduction to the pattern let's go ahead and Implement a uh an example here in code so let's create a file chain of responsibility so what I'm going to do here is I'm going to implement A system that can add uh we're going to assume that we pass some Network request to
2:19:29
our chain and our chain consists of handlers that can add headers to that Network request so we're going to have for example an authentication header we're going to have U some body payload header and so on right so each header can have the option to add something to that request all right so first of all let's import some uh elements from underscore uncore future import annotations okay
2:20:03
this is because I would like to use some classes in uh random order maybe before they are defined and then from ABC import ABC and Abstract method right so first of all let's define our Handler chain class okay this inherits from ABC and here we're going to overwrite the init method to provide an input header as a parameter and this is going to be a Handler chain okay um and here self. um in sorry next header is going to be the input header let me just type that spell
2:20:53
that correctly input header right so let's define an abstract method def add header with an input header of type string we're going to pass and then we're going to define a doore next um method with an input header of typ string okay so the input header is basically where we add our functionality our uh data for this particular header so um here we can say if self. next header if we have a next
2:21:39
header then we will return uh we will pass it on so we will return self. next header. add header with input header and then return input header so if we don't have an X header we just return the result if we have one then we invoke the ad header with the information that we have here all right so um let's define a class and here we're going to implement some um handlers okay so um we're
2:22:13
going to have authentication header with uh that inherits from from Handler chain that's going to be part of the Handler chains and we're going to Define an init method with a token of type string and we're going to provide the next header of type Handler chain we're going to have none by default okay so we're going to first invoke the super method dot in it with Handler chain with next header that's the name okay and then we're going to say self. token is going to be equal to
2:23:00
token all right so that is the um instantiation so we're going to implement the add header method and here we can simply say h equals an F string input on in curly brackets input header so what we have already received sln and here we're going to have authorization colon self. token okay so we're just adding our information to the existing header and we're going to return self. do next with h okay so
2:23:42
that is the authentication header let's create another one we're going to have content type header okay this is also part of our Handler chain um again we're going to have our init method and here we're going to pass some parameters content type of type string and of course next header of type Handler chain that's none by default okay let me just make some space here right so for the init it's very similar we're going to add uh the super call and then self. content type equals
2:24:28
content type right so now we have to implement the add header method and here we just add our content type information to the header so H equals an F string with our input header okay sln content type one word colon space self. content type and then we're going to say self uh sorry return self. do next with h okay and just for fun so that we can have three handlers let's add another one class
2:25:10
and here we have body payload header right with which is a Handler chain as well very similar um I'm going to override the init method and here we're going to pass the body of type string and next header of type Handler chain is none by default the super method call and then self do body equals body okay um now the ad header that's going to be slightly different from the others here we're going to have H is an F string with input header and we're going to have forward sln
2:25:59
and here we just put the self. body okay this is not a um a header anymore this is the body of the request and then we can return self. do next with h all right so hopefully that um is enough so now we have three elements we need to um instantiate them and arrange them in a chain and then call some requests on them so let's implement the main authorization header is going to be authentication
2:26:40
or we should call this authentication even though the header itself is um authorization the class is called Authentication okay authentication header and here we can pass some information some token 1 2 3 4 5 6 this is our authentication token then content type header is going to be is going to be content type header with um let's say Json okay body header is going to be body payload header and here we have some Json so let's say body colon and curly bracket Let's
2:27:30
Escape the quote let's say username escape a quote colon uh not not colon equals escape a quote and let's say John and final Escape quote all right so we have our three headers we need to arrange them in a chain so Authentication header. next header is going to be the content type header and content typ header. next header is going to be the body header right so we have our chain now we need um a couple of requests so we're going to have one request that goes through
2:28:09
the whole chain from authentication to content type to body and then a second request which skips the authentication and just goes through content type and B body okay so we're going to say message with authentication is going to be equal to authentication header dot um add header for the message header with authentication okay and we're going to have message without authentication equals so we're not to call authentication header but we're going to
2:28:52
call content type header. add header to header without authentication all right so that's all for now and we just need to print the message with authentication I'm going to print an empty line here and then we're going to go print message without authentication so let's see if I haven't made any mistake typing that I'm going to run this and then the first one is going to be header with authentication so we'll get authorization content type and body I forgot a curly bracket
2:29:34
for the body here okay right so I have the curly bracket and then without authentication we get Json and the body so you can see here that all these messages go through the chain um all the headers that we implemented will process the request and then pass it on to the next header until we reach the end and we know that we reach the end here because we don't have a next header so we just return the result also here we have one um request
2:30:12
that goes through the entire chain and we have another request that goes through only part of the chain because this one for whatever reason doesn't require authentication so that is the chain of responsibility design pattern right so the next design pattern we're going to talk about is the command design pattern so let's have a look at a diagram a high level overview to see what um kind of overall problem the command design pattern is trying to help us solve so
2:30:43
here we have a system that contains two parts we have a guy or graphical us user interface and we have some business logic right the GUI will be made up of some components a few buttons some text Fields sliders and so on and so forth right now each of these um components on the guey side will want to interact with the business logic there is a separation of concerns there the GUI takes input from the user and invokes some uh request some functionality on the business logic
2:31:20
so that means this particular button will invoke some functionality here the other button that we have will invoke some other functionality the text field let's say when it's updated will let the business logic know and so on so all of these will call their respective functions on the business logic side so immediately you can see a problem with this and that is the tight coupling between the GUI and the business logic right so all the components here need to know exactly
2:31:52
which function to invoke on the business logic side this leads to tight coupling which leads to low usability and a host of other issues right like testing and so on so of course we uh want to solve this problem and we can uh solve it by introducing another layer between the two and that is the command so the command will simply take all the information from the graph iCal side in our case will package that in some standard form and it will pass it to the business logic
2:32:27
so immediately you can see here that there's no more there's no longer a connection between the UI and the business logic everything goes through the command and the command kind of package will go to the business logic and will get processed right so the request is wrapped in an object that contains all the required information and based on that information information it can be later
2:32:52
processed on the business Logic the command object is passed to the correct Handler so in our case in this small example we have a business logic layer that can handle commands but obviously in a real world situation we can have multiple components on the backend side that can all handle different commands so the commands will be passed to the relevant business logic site or we can also have
2:33:19
multiple servers of business logic and we can have um some sort of load balancer that sends commands to various servers to kind of um make the system more efficient right um now benefits obviously include decoupling as we've seen But there are many other benefits such as efficiency um we can introduce some ordering if we require okay so um in our case we have UI commands but in a real world situation we can CL any type we can package any type of information into an object
2:33:54
that can then be processed into a command that can then be process so we can um Implement some sort of ordering or some priority for the commands Okay all this is uh possible once we encapsulate this information otherwise um you know calling the methods directly simply won't allow us to do that all right so that is the uh theoretical introdu uction let's have a look at some code um to see how we implement this in practice so here we can create a new python file Command right so
2:34:32
what I would like to do here is I would like to implement um a um let's say restaurant or some sort of ordering system that can um package the information in a the ordering information um in a command and pass it on to some sort of processor for processing right so it's going to be obviously very abstract we're just going to print some information but the logic behind it is going to be sound so first of all from ABC import ABC and Abstract method okay and let's add
2:35:10
the class command now the command will be will inherit from ABC and we're going to override the init method where we pass a command ID of type int and we're going to store that so self do command ID equals command ID and we're going to have an abstract method where we Define an execute and here we will simply pass okay so we have the command ID and we have a way to execute it that is our um command object now let's go ahead and Implement a class order add command
2:35:59
which is going to be a command and we're going to override the execute method here we simply print a message let's add an FST string adding order with ID self. command ID okay we're going to assume that this actually executes the command that we have here and then we're going to have an a class order pay command Okay command this can be um an online shopping system or a restaurant or whatever it is and here we will override the execute so importantly all the commands will
2:36:45
execute in the similar in a similar way right so um that will allow us to kind of implement this um let's say ordering or priority system okay so here I'm going to print an F string um paying for order with ID self. command ID right so um we have our orders we're going to limit ourselves to two order types two kinds of commands so I'm going to have a class and let's call this command processor okay and we're going to just define a class variable let me make some
2:37:29
space here a class variable called Q okay an empty array and we're going to have an add to Q of self and command of type command Okay self doq append our Command so we're going to add the command and then we're going to process them so def process commands Okay and we're going to have just a list comprehension here item. execute for item in self. Q okay and then we're going to importantly set the
2:38:16
que um to an empty array so reset those commands because they have been processed so that's pretty much all we need to do um now let's go ahead in main let's go ahead and create some commands and execute them so first of all I'm going to have my processor equals command processor and let's add some commands so processor. add to Q here we're going to have a order add command with order one duplicate that and order two and so I have added two orders now let's pay for two orders
2:38:55
so processor. add toq um what's the name order pay command of one and duplicate that and two all right so that's it and then we can simply call processor dot process process commands all right let me just remove all the white spaces here right so um that is all there is to it so we have our q and we have the execution so let's go ahead and run this right so we have adding order with ID 1 adding order with ID 2 then paying for order one and paying for order two so you can see
2:39:37
here how we encapsulate that um information that requirement into a command class and we pass that command class to processor okay process processor handles commands it doesn't really care which kinds of commands it just calls execute so you can kind of Imagine here we can Implement um a lot more complexity if our system requires it where we order some commands we execute them in a certain order we balance them across multiple servers if you require and so on and so forth The
2:40:13
Interpreter design pattern is a pattern that's very useful when we're trying to have a system that can quote unquote understand or process a language now languages don't have to be you know human languages they have they can be mathematical languages or some sort of programming system um however The Interpreter allows us to move through uh a quote unquote sentence in that language and understand it and do some processing on it now this sounds a bit abstract so let's start with
2:40:47
an example let's say we have um a mathematical um you know equation right so 5 + 3 whatever that is we have um four main symbols here plus multiply divide and we have minus as well although it's not included here and we want a system that can kind of understand this uh expression okay so it can allow for the flexibility of algebraic equations using only these four symbols so how do we do that
2:41:21
um we have we can build a kind of a tree uh from these Expressions so first one is going to be for example here we're going to take the last symbol that is a division and add the two components of it on the left side we have another Tree on the right side we have the number five now on the left side we can take the symbol multiplication and have a binary tree there as well on the left side we have some expression on the right side we have the number two okay so if we take the left
2:41:56
side again we have a plus symbol on the left side we no longer have an expression we have a um this should be five right five here that's my mistake but on the right side we have the number three so we build this tree this binary tree with this expression that we were provided and then we can have the system go through that tree in a binary way and calculate the result okay so um this is
2:42:25
kind of an example but this works very well with any other uh kind of quote unquote language right so we recursively evaluate grammar and expressions grammars in this case is going to be the symbols and expressions are going to be the numbers um this works for parsing systems for processing engines so uh if you're trying to understand um the best example is a mathematical equation a
2:42:54
processing system that can cope with that will use an interpreter pattern we have two main components in our pattern one is going to be a terminal expression which in our case are the numbers okay these are terminal and we also have non-terminal which in our case will be these four symbols or three in the case of this particular equation so terminal and non-terminal Expressions all right so
2:43:21
that is the introduction let's have a look at a CO a piece of code to see how this works in practice so I am here in um in our editor and I'm going to create an interpreter file and first of all I'm going to Define an abstract expression that's going to be a class it will have a static method okay and it will define an interpret that will simply pass for now okay so we have Expressions right abstract expression now we're going to Define two types of Expressions one are
2:44:04
going to be terminal and the other are going to be non-terminal so first of all terminal expressions or expression cuz that's going to have a class now in our case the terminal expression is going to be the number which is going to inherit from abstract expression so let's define let's overwrite the init method and pass the value here for our number self. value is going to be now let's convert this to float since our expression uh will be able to give us
2:44:42
floats right if we have division then we can probably assume that at some point we'll have float values right so so this is the number now let's define an interpret or override the interpret and of course I need self here and I'm going to return self. value right so um that is the terminal expression now let's define non-terminal expression and that is going to be class algebra or algebraic that's call it algebra for short expression algebra
2:45:23
expression is going to be an abstract expression we're going to override the init method okay um here we have two parameters left and right okay so let's just store that self. left is going to be left and self. right is going to be right now we're not going to add the interpret here because we will um overwrite that since it comes from abstract expression we'll overwrite that when we um Implement classes that extend from algebra expression which we're
2:46:00
going to do now so we're going to have class add um which is an algebra expression okay and we're going to Define interpret with self and here um we will have return self. left. interpret plus self. write. interpret okay so um that is the ad very very simple so we allow the left expression to interpret and the right expression to interpret as well often we will get the right expression is going to be just a number which interpreted will
2:46:46
just return the actual value however the left expression will require um you know um recursive calls for this interpret all right so that is ADD now let's add subtract with algebra expression and we're going to Define interpret here and we will return self. left. interpret minus self. right do interpret okay very very simple um we're going to have class um multiply algebra expression and actually I'm just going to copy this and paste it here
2:47:38
and I'm going to have here multiplication and then finally class um divide algebra expression and I will paste that and make sure we change the symbol division okay so these are my four algebra expression or algebraic expressions um so that's pretty much all we need to do uh now let's just go ahead and implement the main the main will kind of contain a bit more information okay so let's
2:48:10
have um a Target expression here that's going to be let's say 3 + 5 make sure you put the spaces in um I've set this requirement so that it's easier for us to kind of break down this expression into symbols right so um space after before and after every symbol whether it's a number or a actual symbol so uh 3 + 5 - 2 multiplied by 7 div divided by um five let's say plus 11 okay whatever you want the expression to be it doesn't really matter now we have our tokens which is going to
2:48:53
be target. spit split by a space okay so this will give us a list of tokens and we have Expressions is an empty array now for I in range of length of tokens okay now here if I equals z if we reach the end we can say Expressions dot append our um this is going to be a number so we know that always at the beginning we have a number so we can just add it to our Expressions tokens of I we can put tokens do0 here it doesn't really matter any case L if um tokens of I
2:49:49
equals plus okay so if it's a plus then we say expressions. append here we have an add expression okay now we have expressions. pop and the right side is going to be a number of tokens of I + 1 okay so we know that after after each let me put a colon here after each um actual algebra token we have a number so we can just add it there okay then L if tokens of I is going to be minus okay we can do the exact same thing so let me just copy and paste that except this is a subtract
2:50:47
okay let me make sure we haven't made any mistake I think it's fine then L if tokens of I equals multiplication then we have instead of add we have multiply and then we have L if tokens of I equals division um we need some quotes there okay then we have divide right so we have all the information we have constructed our Expressions so we can just call result is going to be equals expressions. pop
2:51:34
dot interpret okay and then we can hopefully print the result here if we haven't made any mistakes so I think that is all there is to it um let's run this code and see if it works fine yes 19.4 so that works fine um now there might be some um edge cases which I haven't verified so for example if we just provide a target of a single number um you can check that but the main idea here is U
2:52:08
that the uh if we provide this kind of expression in a certain format an algebraic expression this interpreter pattern will go through it recursively build the Expressions that we've defined and apply the interpret function to each one recursively and give us the final result okay so hopefully you see how um The Interpreter pattern works all right next on our list is the iterator pattern now to
2:52:39
be fair if you've been a developer for more than 5 minutes you've probably already used iterator in your code okay so you probably already know what the iterator is I have included it here for completeness but also because I want to show you an example of how you might implement it yourself if you want to if you have a requirement for it right so an iterator is basically a way to Trav to
2:53:04
Traverse a collection to go element by element and apply some functionality to it or simply return it for whatever purpose it has two methods first is going to be the has next which will return a Boolean on whether or not there is anything more in that collection and then next we'll actually move to the next element and retrieve and return that element okay so very very simple very intuitive we already know how this works but let's go ahead and Implement an example
2:53:37
and see it in practice so I'm going to create a new python file here and I'm going to call this iterator right now what I would like to do here is I would like to have a um a functionality a couple of classes a couple of iterators that will scroll will uh sorry will Traverse a collection of words and will return them either in an alphabetical order or in a reverse alphabetical order now of
2:54:09
course you could do this very very easily uh with um functions on a collection uh the point here is not to reinvent the wheel is to show you how you might Implement that if you choose to do that for whatever reason right so class um alphabetical order iterator okay now let's define or rather override the init method and provide the words here as a collection and then reverse of type Boolean and by default that's going to be false okay so um we're going to have self. collection
2:54:54
is going to be equal to sorted of words so we're going to sort the words um in our collection and then self. reverse is going to be reverse okay and we're going to also store self. position is going to be length of words minus one if reverse else zero so we're either going to start at zero or at the last element okay so let's implement the two methods first we're going to have next and
2:55:33
here we'll have value equals self. collection of self. position and then self. position plus equals minus one if self. reverse else1 so depending on uh which way we go we either go back or we go forward okay and then we'll simply return value very simple now let's define has next okay and here we can say if self. reverse and self do position greater than minus1 return true L if not self. reverse and self. position smaller than length of self.
2:56:37
collection okay and in this case return true else return false okay so um we're just trying to see if there is any element um that is after the current one right so um very simple very intuitive now let's define a class words collection and we're going to overwrite the init method here with a collection and we're going to say self do collection equals collection and then we have def get iterator and here we will return alphabetical order iterator with self and um self. collection
2:57:41
okay um so this is the alphabetical order now let's define get reverse iterator and we will of course return the reverse one so return alphabetical order iterator with self self. collection um sorry we don't need to pass the self I don't know why I put that here right and same for here right so self. collection and then here we have true okay so the reverse iterator is true right so we have main um and here let's create our collection so collection
2:58:25
is going to be words collection of let's have some words John Alice um we have Michael and maybe Carol okay so U random order now iterator is going to be collection do get iterator and reverse iterator is going to be collection. get reverse iterator right so let's um print out some elements while iterator dot has next okay in this case we're just going to print iterator do nextt and let's print out a empty line here and then we're going to say while iterate sorry reverse
2:59:27
iterator do has next we're going to print the iterator sorry the reverse iterator do next okay so now if I have if I haven't made any mistakes we should be able to see the result now here I need to provide a collection that's my mistake now let's go ahead and run this again and then we have our results so the first one is going to be um alphabetical order and then for the second iterator is going to be reverse alphabetical order so that is how you might Implement an
3:00:05
iterator in your own code all right in this video we're going to talk about the mediator pattern so the media pattern is very useful when we have multiple components in our system that kind of individually do their own thing do their own processing and then need to communicate with each other okay so you can think about a distributed system where various nodes communicate with each other directly right so we want to have this kind of communication now of course in a system
3:00:38
when uh the nodes or the uh distributed components communicate directly with each other it means that they have to know about each other right they have to know how to contact every other component that they need access to so this is obviously a problem especially when the system grows and when we have um dozens hundreds thousands or even millions of nodes right you can think about a chat application
3:01:08
where we have millions of users that need to be able to communicate with each other they need to know how to contact each other in a chat application a uh for example your phone doesn't talk directly to another phone what it does is it talks to a central server and then that server relays the message to the required destination so this is the mediator pattern you want to have a central location where each client communicates or sends their message to only one destination
3:01:40
and then that destination based on that message will forward it l um to the um you know to the appropriate node okay so that is the mediator mediator pattern it provides a central object that is used for communicating between objects we've talked about um you know chat applications but also this can be used in larger applications with various components or let's say microservices or things like that components that need to communicate with each other and share
3:02:12
information should uh have a central location where those messages are being sent the objects don't talk to each other they talk to the mediator and this obviously reduces dependencies between objects the objects no longer need to know how to reach every other individual node in the system they just know how to reach the mediator and then the mediator will forward the message to the uh Final Destination right so very uh simple very intuitive now let's Implement a um a system a
3:02:47
program to kind of see how this works in practice so I'm going to go ahead and create a new python file here called mediator and here we're going to have a very basic chat system or rather group chat system where each user can send a message to all other users right so we'll see how that works now first of all I need to import from future import um annotations okay I want to be able to call uh or reference classes before I Define them so here I have a chat user class okay I'm
3:03:28
going to have a mediator variable that's none by default and let's initialize the element or overwrite the init method and provide a name as a string right so self. name equals name all right so let's implement the meth method setor mediator and here we have M uh Ed mediator okay which which we haven't defined yet but we will no worries now um self. mediator is going to be mediator
3:04:09
or rather Med as I've Define it here all right now um we need a send method which will have a message of type string and we're going to just print a F string that says self. name um colon Sending message MSG okay so that is the send message and we're going to say self. mediator. sendor message which is a function that we will Define at some point MSG and self okay okay and finally we need
3:04:50
a def receive with self and MSG of type string so this is where we receive the message and we just want to print line an F string with self. Name colon receiving message MSG all right so um that is our chat user now let's go ahead and define a class mediator and here we have a list of users an empty array by default and we're going to have def ad user with um user of type chat user okay
3:05:33
so here we just want to add self. users. append with uh user and we want to set the mediator for this user as well so user. set mediator creator with self okay so that is our ad user but we want to have a send message method def send message and here we want to have a MSG of type string and we want the user that sent that message Chat user okay for you in self. self. users here we can say if you is different from user we don't want to send the same message to the actual to
3:06:27
our own user to the user that sent it initially we say. receive. receive and here we have MSG okay so we're just calling the receive method on each user that we have in our list right so that's all we need now let's go ahead and create our structure so mediator is going to be a mediator instance and let's create maybe three users Alice equals chat user with the name Alice then we have Bob chat
3:07:07
user with the name Bob and then finally let's add um Carol is a chat user with the name Carol
3:07:18
right so we have our users now let's say mediator. adduser Alis and duplicate that twice I'm going to add Bob and I'm going to add Carol all right so we have our structure now any one of these users can send a message so I'm going to say carol. send um hi everyone right so let's see how that works let me just run this and we get Carol is sending the message hi everyone and then Alice is receiving that message and Bob is also receiving that message so we can see
3:08:00
that we have this mediator pattern where we have the central structure which is used to facilitate transmitting messages between our components in our case uh multiple chat users in this video we're going to talk about the momento design pattern and to kind of quickly give you an idea of what this is about think of a undo or redo system where you have let's say an editor and you want to um store a trace of uh states that you went through and operations that you did so
3:08:36
that you can undo any operation that the user has done whether it's writing something or deleting or um you know whatever keyboard shortcut they might might um use you want to encapsulate that so that you can uh have this chain of events that you can kind of go back and forth into so this is a momento we have some operations that the user does for example here we have multiple text edits and we encapsulate those with their own State into some quote unquote momentos
3:09:12
right and then we can kind of move back and forth between this chain of operations this will allow us to do an undo or redo and go back and forth um now so the momento add its core is simply a system to save and restore previous state without revealing the implementation details so we don't care what the user actually did or what the uh details are in each momento we simply want to be able to move back and forth between the chain uh between elements of the this momento chain we
3:09:49
have three main components first of all we have the momento which stores the state which is kind of the implementation or the details of what has been done we have the originator which creates the state and we have the caretaker which decides to save or restore the state so the caretaker will allow us to move um forwards or backwards in the chain now usually in these kind of systems you
3:10:17
have have um undo which goes back one element in the chain and redo which goes forward but there's really no reason to uh limit ourselves to one step we can actually jump through the chain as much as we like as long as we have the uh ID of the element of the momento that we want to get to all right so um let's go ahead and implement this in code to see how this might work so I'm going to go ahead and create a new file here called momento now here we're going to Define first of
3:10:53
all we want to use data classes so we're going to say from data class from data classes import data class and we're going to create an at data class and we're going to call this momento and we're going to just store one attribute which is State and we're going to make that a string
3:11:18
so let's define an originator this is remember this is the one that creates the state for us creates the momentos and let's override the init method where we pass a state and we're going to store it self. State equals State okay so we have that now let's go ahead and create a function called create momento and here we will simply return momento of state self. state okay and then we have restore momento and here we have momento of type momento and here we just say self. State
3:12:10
equals momento do state okay so here is where we keep the Cur current state of the system and then we're going to define a class caretaker okay here we will have a momento list okay or you could call this MOS with an S at the end it doesn't really matter let's define a save underscore State function where the state is of type momento okay self. MTO list. append we're going to add the state right so we have our state or rather our momento in our momento list and we're going
3:13:02
to Define restore self and index of type int and here we just return Moto list sorry self. mol list of Index right so that is all that we need you can see the actual implementation is quite simple let's go to our main and here what I would like to do is I would like to create a few States we will add them to our list and then we will move back and forth between the states so first of all we're going to have an originator originator that's going to be originator of um
3:13:45
we're going to call this in initial State okay and a caretaker that's going to be an instance of caretaker all right so um we're going to say caretaker. saave State and here we're going to have originator do create MTO okay so we have uh saved our first state in our caretaker let's print a message and here I'm going to have an F string current state is originator do state right so this is my original state so now let's update this so originator do state is going to be State
3:14:36
one let's call this and then we're going to say caretaker. saave State originator do create MTO
3:14:48
and then I would like to print the current state again so let me just copy this and paste it here so we are now at state two um let's create another one so we have another state copy and paste that state two origin. create momento that's fine and then current state is that's fine right so we now have three states so we can actually move between them as much as we like so we can say origin Ator do restore Moto so here we can provide the required momento and we're going to
3:15:25
say caretaker. restore and we need to provide the index let's go up one so from state two to State one right so that is going to be one and we're going to print the current state okay so copy and paste that so we have moved to the first state or rather we have state zero State one and state two okay we have moved to State one but let's move to the state zero quote unquote so we can say originator do restore and caretaker. restore Z okay and we're going to print the current state
3:16:10
and then we can actually jump to the last state right we don't need to go step by step we can just jump between them so we can say originator do restor MTO caretaker. restore to two and we can print the current state right so that is all there is to it we kind of jump around between the states that we have let's go ahead and run the code so what do we get we get the current state is initial
3:16:37
State then we move to State one then we move to state two we go back one right so we go to state one we go back to zero so we have initial State and then we kind of jump straight to state two bypassing State one so current state is two okay so you can imagine um a system where these states are dynamically generated based on user actions and the system will kind of go back and forth of course um a real world system like an editor would go back one or forwards one but as you
3:17:13
can see we can jump around if we need to so that is the implementation of the momento pattern in this video we will talk about the Observer design pattern and this design pattern is one of the most useful ones and one of the most common ones so you will see it quite often in various projects and um in a lot of code in a lot of components in the code so what it does at its most U basic is
3:17:42
let's imagine we have two components one is kind of a time intensive processing unit that does some processing in the background and at some point will return a result and we have on the right side we have a user of that result now this is not an actual user it's not an actual person uh more of a component in the application right so here um the user requires this result so it's waiting for it
3:18:11
and it has basically two options one option kind of the naive um imp M mentation would be to uh periodically poll this time intensive Processing Unit just to ask for the result and as it's shown here you ask it once it says no is it ready yet no not yet is it ready no is it ready no so you can ask many many times um you will get the result no until eventually you will get the yes so of course
3:18:39
this would lead to a lot of communication back and forth and a lot of wasted kind of processing right and this problem would get exponentially worse when we have multiple of these users so if everybody starts to pull at the same time if we have dozens or hundreds of these users then that could cause a major issue where um we cannot deal with that amount of communication right so we need
3:19:05
to find a better solution obviously the solution would be to implement some sort of middle layer here that groups together this communication so that we don't have to pull it um many many times so we Implement some sort of registry here that is simply kind of waiting for the time and intensive processing unit to finish uh and return the result so as soon as the result is returned we can start to notify the intended users so how this the way this works is the users basically
3:19:38
register um their interest to this result um and as soon as that becomes available then they will get no modified we can also have in this uh kind of architecture we can also have multiple types of results um that can become available so the users uh or the com interested components would register their interest for specific uh results or for specific types of results and then they get they only get notified when those results come in so to kind of sum up um The Observer
3:20:14
pattern it descri it defines a subscription mechanism similarly to how you might subscribe to a newsletter online or um you might purchase a subscription to your favorite newspaper in real life and get it delivered to your home this is a very similar mechanism where users subscribe for events that they're interested in we can notify multiple objects simultaneously so whenever some result gets um is available then multiple uh interested parties get notified and also
3:20:50
we can have multiple types of events notifying multiple objects so there's kind of a on to many relationship where we have multiple interested parties to one um one object right one type of response of course we can have multiple types of responses but each individual type of response can have multiple interested parties so to kind of sum up in a bit more pseudo code kind of architecture we have here an event generator and we have the event manager the event manager gets notified
3:21:24
when the events um are available and it retrieves a it has or stores a list of subscribers right the list of subscribers um can be updated whenever a new subscriber comes in or a subscriber leaves and then when the event gets uh becomes available then we notify the subscribers we have a standard subscriber interface that is implemented by whoever is interested in that event and then they
3:21:53
have an implementation a method that gets called when the event becomes available right so that is the theoretical introduction let's go to code and have a look at how we can implement this so here I will create a new file and I'm going to call this Observer right so first thing I'm going to import from ABC import ABC and Abstract method okay so that we can uh create some abstract classes so
3:22:24
the first one is going to be event listener that inherits from ABC and we'll Define an abstract method called def update and here we have an event type of type string and let's say a file okay okay we want to notify when that file becomes available and for now we have pass now we have a class event manager okay and here um we will I don't need these parentheses we can overwrite the init method
3:23:07
and we can add a um operations list operations um and here we can store itself. operations equals operations all right now we can also have a self. listeners that is a dictionary by default and we're going to say for op in operations we instantiate those listeners so self. listeners of op of operation equals a list so for each operation we have a list of listeners okay it's empty for now right so let's define a subscribe method
3:23:56
where we have an event type of type string and we have a listener which is going to be an event listener so this is very simple we just say users equals self. listeners of event type okay and we just add our user to these users or our listener to these users so users. pend and here we have our listener right so this is the Subscribe now we need the opposite def um unsubscribe and here we just need the uh actually we need both we need the event
3:24:42
type type of type string and we need a listener of event listener okay so very similar to what we've done here I'm just going to copy this paste it so we retrieve the uh users of the event type and we instead of append we will use remove uh remove the listener all right so that is the uh users or subscribers list and then we need a notified my method of event type um and a file okay so here we
3:25:25
simply retrieve the event type so users equals self. listeners of event type and then we want to notify or update those users so we're going to say. update of event type and file um and and here we have for you in users all right so we just notify all these um interested parties now um we're going to have an editor class that generates our events so editor events equals um event manager of and we have let's add two types of operations we have have an open and we have a
3:26:18
save we can of course have as many as we want but we're just going to limit to these two and then file equals none um by default so let's define an open file method which has a file and here we can say self. file equals file and then uh let's print a message an FST string saying something like edit
3:26:47
um opening file and we can print out the file here all right so we open the file so of course we need to notify everyone of this event so self. events do notify okay and here we can notify of the Event open and the file all right so that is the open file functionality um we can also have a save file So Def save file of self and the file that we want to save and this is going to be uh quite similar we're going to say print we don't need
3:27:32
to save the file because it first we assume it's open before we save it so we already have the file stored and we can say print um editor um instead of opening file we'll say save or saving file and let's print the file here all right and then we can notify the event so self. events. notify and here we have save and file right so that is our editor it simply allows us to generate events and call the manager to update everyone interested now let's define some
3:28:11
listeners so of course um we need to implement the event listener uh abstract class right so here we will have two listeners first one is going to be an email listener that will um kind of send emails whenever an event has occurred and the second one will be a logger that will basically log events so the first one class email notification listener this will Implement event listener and we need to
3:28:46
uh first of all let's define or override our init and pass here the email um that we will quote unquote update or send emails to so self. email equals email okay and then we need to implement the update method that we have defined here so def update okay we have our file so here we can simply print a message to kind of simulate sending that email and we can say email to self. email and we can say some let me just uh there we go um we can say someone has performed um
3:29:40
event type operation on the file file and that's pretty much it we can just simulate sending that email we don't need to actually Implement that functionality so that is one listener let's define another one let's say log um open listener that's going to be an event listener here again we need to Def uh override our init method and we have log file self. log file equals log file okay and we
3:30:23
need to override our update or rather Implement our update method and here we can say something similar so print an FST string um save to log self. log file colon um someone has performed event type operation on the file file all right so that is our listener those are our two listeners so all that's left to do is put this together in our main function so main we have our editor equals editor okay instance of that editor let's instantiate our listener so email listener equals
3:31:16
email notification listener and here we need to pass an email so let's say test at gmail.com and then we have our log listener that's going to be a log open listener and here we have let's say path to file pass to log file dot text okay so we have our two listeners so let's go ahead and start to subscribe to events so we can say editor. events. subscribe and we want to subscribe to open events with the log listener but we also want to subscribe to save events so editor.
3:32:08
events. subscribe to save events log listener so we want to log everything however for the email we only want to email when we have a save let's say so editor. events. subscribe save and um email listener all right now editor let's generate some events. openen file and here test. text and editor. save file okay um why do we need a file here we don't really let me update this I don't uh we don't actually need to save the file to pass the file here because we already have it
3:32:55
so self. file and notify self. file all right so that is looking good I think that's pretty much it let's go ahead and run the code there we go so no problems editor has opened the file and then we can see that immediately the uh log um uh log listener has uh printed out a message someone has performed an open operation and then as soon as we save the file both listeners will um you know print out a message or rather get notified right so someone has performed the save someone has
3:33:34
performed the save this the first one is for the logger and second one is for the emailer so this is how it works um whenever we generate an event the U event manager will notify every subscriber right so we can subscribe or unsubscribe to various events all right in this video we're going to talk about the state design pattern so the state is basically um thinking of your code or your program as a state machine or in our case a state algorithm that simply means that um your
3:34:12
algorithm can only be in a certain number of predefined States and cannot kind of deviate from those right so we have this example here of a um let's say a article on a blog post or on a news site and it can go through let's say three um steps or three states the first one is going to be draft where the author just creates the uh the article then it moves into moderation where the um let's say someone else would come in and check the article to make sure it's fit for production
3:34:48
and then it will get published right now from moderation it can go back to draft so we can move back in the kind of stack of States um but it can also go forward so here we have the option however there are some states that cannot move uh between them so for example it cannot go from published to moderation it has to be uh it has to go from published to draft and then to Moder ation
3:35:16
right so there are some limitations in how you can move through this um tree or through this chain of States you can go from some to others but not from um you know certain ones to other ones importantly also the state itself is encapsulated in an object that we can pass around in our uh code as we need to so the object uh an object changes its Behavior based on an internal state so depending on which
3:35:44
state we are on we can update our code um when we receive that state object so this is encapsulated in an object it gets pass to some component and that component updates its internal uh logic based on the state it receives at any moment there's a finite number of states a program can be in so as we've seen here we have only three in our example of course you will Define um as many as you require in your code and then State can be encapsulated in object as we discussed all right
3:36:18
so that's it for the quick introduction let's move to code and see how we implement this in practice right so let me just create a new file here called State now the implementation for this design pattern is um logically quite simple but we do have quite a bit of code to get through uh to implement this uh to implement the state in our code so what I would like us to think of is a game
3:36:47
that has let's say four states that it can be in so first of all we have the kind of welcome screen where we open up the game and are ready to start playing we have the uh playing state where the user or the uh the player is actually uh engaged with the game play then we have the kind of pause or break state where we take a pause and then we find uh finally we have the end game state where
3:37:15
the the game has finished and we can kind of go back and restart the game so we have the option to go back and forth through these states so let's implement this in our code all right so the first thing we need is from future import annotations okay we will need to use some classes before we Define them we will also need to import random and then from ABC import ABC and abstract method okay so we can Define some abstract classes now the first thing we need to do is we need to
3:37:50
define a game class and let's override the init method and here we need to um we don't need to provide any arguments but we need to store self. State and by default at the beginning of the game we will have what we call an welcome screen State we haven't defined this yet but we will shortly right um for the welcome screen State we will need to define the uh abstract class so we will need to
3:38:20
take a bit of time before we actually Implement that right so we also want a uh function called change state where we pass the state object here and we update our internal one okay self. State equals state so that's all for our game now let's go ahead and Define the abstract class for the actual State object so class class state of ABC here we have um def in it we're going to overwrite the init method and we need to pass a game to this method and we're going to store it
3:38:59
here self. game equals game and let's print out a message to see which state we are on so we have an F string currently in self state right so now we need to Define our four methods that relate to our four states we have in our code so first of all abstract method um we have def on welcome screen okay and we don't need any parameters and we're just going to pass for now we have a second
3:39:38
one def on playing self and we're going to pass we have an abstract method def on um this is going to be on break okay when we actually take a break from the game and then the final one abstract method here we have def on end game okay and pass right so we have our state so now we need to implement the the four um classes so the first one is going to be welcome screen State this is going
3:40:24
to be a state okay and I'm going to we need to implement all these four abstract methods okay so I'm going to um Implement abstract methods select all of them and Implement them here now before we actually add functionality to these methods uh we will need to have all the other States as well so it might be easier to just put up this um this structure for all the states so let's add class um playing state that inherits from State and this will also this will also Implement all the
3:41:05
abstract methods okay so we have welcome screen we have playing State we will need the uh class break State okay um let's implement the abstract methods and then after that we will go through each one and Implement them correctly and then class the final one is going to be end game state which also is a state and we need to implement all the abstract methods right so we have all this so we
3:41:41
have to go through each one and Implement them um step by by step so not here the first one is going to be welcome screen state so welcome screen State um we cannot move from welcome screen state to another welcome screen State however we can just simply print out a message saying currently on welcome screen okay now we can move from welcome screen state to playing so here we can say self. game. change State and we need to pass a playing state with self. game okay so we
3:42:18
move to that now from welcome to break we cannot move right so here we're just going to print a message from welcome to break not allowed okay and then from welcome to endgame again not allowed from welcome to end game not allowed all right so that is our welcome screen State now let's move to playing so from playing to welcome screen again it's we cannot move so not allowed from playing to
3:42:53
welcome not allowed here on playing we are already here so we're going to print a message currently playing for the break from playing to break yes we are allowed to do that so self. game. change State we move to break state with self. game and then from playing to game to end game not allowed so print um actually from playing to end game we are allowed to do that so self. game. change State um and game State self. game we can finish the game um at some point all right break from
3:43:39
break to welcome we are not allowed so print from break to welcome not allowed from break to playing yes of course we can do that so self. game. change State playing State now obviously in a real world situation you would not create a new playing State you would retrieve the existing one but we're going to simplify our code here just to kind of um you know work on the design pattern now from break
3:44:13
we are already on break so let's print that here currently on break and then from break to end game we are not allowed from break we can only go back to playing so here we can say print from break two um playing to end game end game not allowed all right and the final one is the end game state so uh from end game to welcome yes we can go there so game do change State Welcome State with self. game
3:44:51
from endgame to playing no we cannot do that so print from end game to playing not allowed okay from end game to break we cannot do that so from end game to break not allowed and then finally print from or currently on end game all right wow that's quite a lot of print messages but we got through it so now all that's left for us to do is implement the main okay so here we will instantiate a game and I want to introduce some Randomness uh some random moves from one state
3:45:37
to another okay so I'm going to say 4 I in range of let's say we're going to do 20 moves um and and we're going to say State equals random. Rand range of four okay we have four states we're going to introduce four um possibilities so if state is zero then we can say print move to welcome and here we can say game. state do on welcome screen okay so we're trying to move to welcome we might be able to we might not depending on which state we are currently on L if State
3:46:22
equals 1 then in this case we move to uh playing okay and we have game do state DOT um on playing L if State equals 2 then we want to move to break okay and we're going to say game. state.on break okay and else we are at the final step so we can say print move to end game okay end game. state do on endgame all right so that's pretty much it if I haven't made any mistake I did type more than
3:47:11
a 100 lines so let's go ahead and try it out all right so it's working fine um so let's have a look at what it's doing so we start off in welcome screen State then move to end game well we are not allowed we're try to move to break we're not allowed endgame endgame welcome we're currently on welcome um break not allowed playing that's fine we can move to playing from playing we try to move to playing about three times not allowed but then we try to move to break right so we can do
3:47:43
that so it kind of goes on like this for a while it sometimes manages to move to a state that's allowed most of the times it would probably not do that right but we can see how we can move from one state to another in our game this is how we um Implement a state um design pattern of course it will be a bit more complex in a real world situation a real world system where you need to
3:48:09
store a lot more information in your state so that you can uh pass it around and your components will update based on that but the concept of a state machine um is uh very similar to what we did here
Here to help you learn
Sign in to keep a private conversation with your course tutor.
Ask me anything about the material…
Sign in to chat