Skip to main content
Loading Explore guides…
Design Patterns · Acedra
Home
Explore
Computer Science
Design Patterns
Sign in to save
Chat
If YouTube asks you to sign in, open the video on YouTube.
Watch on YouTube
Reload player
Transcript
603 segments
Copy
Hide video
0:01
welcome to the design patterns course in this course we're going to cover the fundamental design patterns that all software Engineers need to know so what is so special about using design patterns what are they first of all design patterns are well- tested reusable solutions they are like a solution template for how to solve common software engineering problems they are flexible and adap
0:30
able you can use them in a variety of scenarios and they save time because they have been used so many times that they have been optimized they are not only well tested and reusable solutions they are also Optimal Solutions so why would we use them well you know when you use a design pattern that you're using something that has been utilized successfully in countless projects before and they
0:52
are like a common meta language for developers if you and I both know what a Singleton pattern is then we already have a common understanding in of everything about how it would be used or how it should be implemented you can think of a pattern as a common vocabulary for architects in other words we are on the same page and this is very powerful because it facilitates good communication and that's true even if you code in different languages if you and I work with
1:22
different languages you work with c and I work for example with Java or Dart we still will understand what a Singleton pattern is in in other words design patterns in General carry over to different programming languages if you understand a pattern in a general manner then you know that regardless of the language implementation it will effectively offer the same useful reusable solution so in
1:46
summary design patterns are associated with good well organized software design as developers we are obviously prone to mistakes and sometimes inexperienced decisions think of design patterns as a bag of reusable experience from many generations of coders and software Engineers that we get to use and learn from next we're going to learn what are the design patterns covered in this course in this course we'll cover the seven most crucial design patterns each pattern will be
2:19
covered as follows we will explain and explore the design pattern architecture we will then implement this pattern with objectoriented python we will look into how this pattern helps good architecture and in a practical manner we're going to use all these patterns to architect and write a simulation of John Conway's famous Game of Life which will run on your mobile device here are the
2:45
patterns that we'll cover in this course in the creational family of patterns we're going to cover the Singleton pattern Factory method pattern and the Builder pattern the creational design patterns are design patterns that manage object creation mechanisms they are designed to create objects in flexible and highly adaptable ways these patterns offer the most reusable and flexible ways
3:11
of creating object instances next we're going to move into the structural family of design patterns specifically we're going to look at the adapter pattern structural design patterns are design patterns that improve and simplify your designs by identifying the most efficient and re usable way of composing relationships among entities this allows for the best and most efficient ways of creating complex hierarchies of objects lastly we're going to move to behavioral family
3:44
of patterns specifically we're going to look at the strategy pattern The Observer pattern and the state design pattern behavioral design patterns are design patterns that improve interaction and communication between objects these patterns for Loosely coupled ways of allowing objects to talk to each other and exchange messages once you master these patterns you will immediately see the world of coding differently you will start seeing those patterns everywhere just remember these
4:16
pattern families we have creational structural and behavioral they Encompass a whole world of design patterns creational patterns deal with ways of creating objects or families of objects structural patterns deal with ways of managing complex object hierarchies and behavioral design patterns deal with ways of identifying and improving object messaging next we're going to learn as to why
4:43
do you actually need software architecture we'll see you in the next lecture great to see you again let's talk about why is it that we need software architecture as you know complex software systems are plagued with many issues timelines are stretched as requirements change multiple developers have a hard time coordinating their efforts and often there is code redundancy and poor documentation this in turn creates issues with maintenance and overall flexibility for
5:17
adding new features in general this means poorly designed systems that are hard to maintain and are not adaptable one answer to all decided problems is having a proper design and architecture think of a skyscraper being built like the Empire State Building there's always a highlevel blueprint this blueprint is used to show everybody involved from Architects to supply chain to construction workers
5:46
to Machinery scheduling what is being worked on and we want that kind of predictability and coherence in our software projects in other words we want to have a well-organized and cohesive construction site so how do we achieve that let's look at the development cycle you start with requirements you gather and document all the requirements for UI the ux and general goals of the project once you have all that what you do next is you do a design you
6:17
architect the system based on the requirements you've gathered finally you implement and this is where you code and test the solution to ensure that it follows the design and fulfills the requirements now mind you it doesn't have to be exactly this way where first you gather all the requirements and then you do all the design and you do all the implementation you could do it more in a manner where you get some requirements you design it and you code it as a
6:42
proof of concept then you go back to get more requirements you add them to your design and you code it so you start building it from a core system no matter which way you do this no matter how you follow this development cycle you need to have a good design in this course we will concentrate on the design part and we'll explore how design patterns facilitate and help achieve good software engineering let's look at all the different documents and artifacts that
7:10
you will normally encounter in a project you will have requirement documents you will have design documents you're going to have UI and ux documents test Suites and documentation for that you're going to have deployment documents so that you can deploy Your solution to a particular system once your system is running you're going to have an audit and logs which are artifacts and they're also documents and you will also possibly have analytics but of course all of
7:37
this revolves around the source code that is the final outcome of all your requirements Gathering and your architecting but you'll also have architecture documents and possibly you might have diagrams for your database as well but what we want to talk here about is how design patterns facilitate good architecture documentation to document and teach you those design patterns we're going to use uml so as you can see projects will have many moving Parts but in the realm of
8:07
software the final product is always the source code and good architecture is crucial design patterns help greatly in that aspect and this is what we're going to explore in this course next we're going to look at why are we going to use uml in this course welcome back so whyl why are we going to use use uml in this course uml is a good tool to document something as well defined as design patterns design patterns are well understood and uml communicates their
8:39
structure very well and that is because it is a highly visual representation and while it is not easy to maintain uml diagrams when requirements change for our illustrative purposes in this course they are great we'll only use two types of diagrams class diagrams and sequence diagrams there are many other types of uml diagrams but they are not needed for this course so let's talk about the two types of diagrams class diagrams are used to convey the structure of a design
9:11
pattern here's an example you see a director a builder a concrete Builder and a product there is a relationship between them you can see that a director Aggregates one or more Builders you can see that a builder is an abstract class from which you could create specific concrete Builders and you can see that what the Builder does is it creates a product okay that's great so we kind of understand the structure how does information flow how does it actually work sequence diagrams are
9:41
used to illustrate the interaction of the main objects and the classes here's an example of a sequence diagram we have a client and we have two objects we have a director and a concrete Builder just as you see in a class diagram the flow goes as follows a client creates a specific new concrete Builder it then creates a director initialized with that concrete Builder it then calls the director's construct method and then behind the scenes what the director does is it
10:10
communicates with the concrete Builder to build the different pieces of the product so it can build part A Part B part C and then it delivers the product to the client so what the director does just like directors usually do it coordinates possibly multiple Tas tasks so that you get a well organized object and in fact what the Builder design pattern does it is used to build complex hierarchies of objects so because design patterns form their own blocks of interaction we can study
10:44
them independently and uml is a great tool at that level of abstraction in other words we can concentrate on one design pattern fully Master it and understand it and then move on to another they're isolated you can study them on their own that's what makes this great and it's basically following that divide and conquer approach next we're going to have a look at the difference between organized and unorganized code what you see here is a grid of simple rectangles you can
11:15
see that there's a very simple grid pattern there is some color differences but it's very simple in its organization on the other hand when you look at this this looks more like a bunch of overlapping blobs with some larger some smaller it's not as well organized if you think of these two as different ways of implementing code you can imagine the one on the left as being nicely broken
11:38
up kind of like the divide and conquer approach and well encapsulated whereas the one on the right has a lot of large pieces of codes sitting in one place and you can see a number of overlaps in other words the one on the left is more structured the one on the right seems a little more chaotic and then if you facilitate the communication between the two you can see that it's easier to see who communicates with what on the left hand side whereas on the right hand side it's a little
12:06
little harder to discern a pattern this is a visualization of what organized versus unorganized approach is in this course we'll strive to get you to the left side so that your architectures and your code are going to follow very nicely and well defined patterns as opposed to what you have with the more chaotic and unorganized approach on the right hand side next we're going to have an optional lecture which is a uml refresher for those of you who want to refresh your knowledge of
12:38
uml welcome back in all the coding examples for this course we will be using visual studio code so I will quickly describe and show you how to set up vs code environment for python first a few assumptions you have python interpreter already installed and running I recommend the version 3.7 plus so anything above that is good you also have Visual Studio installed and running on your system Next Step will be to install a visual studio python extension from the marketplace and then
13:14
after that we'll go over a few python environment commands which are run from the terminal let's go over them ven or virtual environment creates a virtual environment for our project for example in the terminal command line you could write something like python DMV then you put the project path the project in which you're working but you also have to include the ven subdirectory because that's where the actual environment is going to be created we will also be using pip pip which
13:46
is a package manager for python libraries whenever we need a specific library to be imported we will need to use that for example you could just say pip install and then you give the library name we're going to go through these examples in this video so you will be able to see all of this in Action a few additional notes we will mostly work with objectoriented paradigms this means that we'll mostly work with objectoriented python and we'll try to keep it as purely objectoriented
14:16
as possible this means using the following Constructors and initializers instances and classes and we'll also use some inheritance we will also touch upon threat safety when needed so let's have a look at the actual process of making your Visual Studio code ready for python to check the python version all I need to do is simply just run the python command with the version so this way you can check that your python is installed and what version you have you could also just do
14:48
this this is pretty much equivalent the next thing that we're going to do is we're going to now start Visual Studio get rid of that so now we're going to install the python extension so you go into extensions we're going to look for python we're going to grab this first one by Microsoft intellisense just click on it and install it will install a number of other sub extensions takes a little bit of time to install all right this is done so now that we have the python extension
15:24
what we're going to do is we're going to create a project folder and I'm just going to do it right inside my little practice I'm just going to create a folder I'm going to call it project one get rid of this and now what I'm going to do is I'm just going to open the folder my C drive impr practice and project you could do it anywhere and you can call it anything you want so now that we have this what I'm going to do is I'm just going to create a file and I'm just going to
16:04
call it main. UI and as you can see because of the extension Visual Studio understands what we have here so now what I'm going to do is I'm going to create a virtual environment let me just actually put some code in here first so as you can see we have SIMPLE fully functional visual UI python code but as you can see here that squiggly line under py game tells us that it doesn't know it cannot resolve it in other words it cannot import it so we will need to
16:34
do two things first what we're going to do is we're going to create a virtual environment so the way we do that is we go into terminal and pay attention to where the terminal opens it happens to open inside our project so what I'm going to do right now I'm going to create the virtual environment this just shows me the current path and I'm going to create the new environment inside the VNV folder so this is it so as you can see here it has created a number of configuration
17:08
files also this is where the libraries are going to go so we have our virtual environment so what I'm going to do right now so I'm going to get rid of this and start a new terminal and as you can see at this point because of this VNV in green that we have the environment understands that this is now a virtual environment so now what we're going to do is we're going to use pip to install this Library above so we're going to go pip install and the library name is simply py G
17:41
I'm just going to copy that paste it here so now I'm going to run this and as you can see we have our little pame window and our environment is running fine all right so this is it this is just me showing you how I set up my environment but of course if you're only using The Interpreter and you're familiar with the way you're doing things that's all good as long as you're able to run the code that's all that we really need to worry about but because I will be
18:14
showing you everything within the visual studio environment it might be easier for you to just follow exactly what I'm doing and therefore I wanted to just make sure that you have an environment set up like I do all right thank you so much and I'm going to see you in the next video and as always keep on coding welcome back we will use uml to visualize two aspects of our learning we're going to look at structural relationships between objects in our designs this is where
18:42
you see how all of the objects fit together what are the relationships between them and how are they built we're also going to look at behavioral relationships between objects in our designs this is where you see how the objects behave how do they communicate with each other the end goal is to understand what is being done and how it is being accomplished to illustrate the structure of our design patterns we will use a class diagram a class diagram is used to construct
19:12
and visualize object-oriented relationships we use the diagram to capture the following information class information such as attributes and operations or basically methods here is a very simple abstract class it's just your generic shape but you can see that it has two two attributes color and location and then it has three operations get area get color and get location you can also control the visibility the plus sign here means that something is public if we put a
19:40
minus it would for example be private but we don't have to really worry about that in this course the next thing that you have in a uml diagram is class relationships between the different classes in the diagram the first type of relationship that we're looking at are generalizations for example we have an abstract class here shape and then we have three descendent classes triangle Circle and
20:05
rectangle all of these three classes they inherit from shape what we have here is inheritance which is a generalization the next type of relationship would be an association here's a uml diagram that shows a cylinder a cylinder could be created from two circles and a rectangle first of all notice that cylinder is a generalization of shape so we inherit from shape and then also notice that
20:31
we have Elements which is a list of shapes and we stipulate that there are going to be three shapes in there we also put a comment so the idea here is that a cylinder is made up of three separate shapes so this is an aggregation in other words the cylinder Aggregates some elements that happen to be shapes now let's have a look at dependencies here is an example it's exactly the same diagram
20:56
but here we have two things that I would like you to pay attention to one is that we have put everything into a package this means that all of these things belong together and also notice that the calculator is not really associated with any class but there is a dependency between shape and calculator and what this shows you is that shape uses a calculator so the dependency is much looser here in a sense that a shape in order to do something needs a calculator but
21:23
the calculator is not part of shape all right so let's kind of put all of this together and have a look at another uml diagram that represents a house so notice first that we have an interface called iro notice then that we have an abstract class room so what you have there is that room implements the interface IO then you have three generalizations of room we have a bathroom living room and a bedroom when you look at the house you can see that the house has a lot of associations
21:52
you have an association where a house Aggregates three bathrooms there's one living room and it has five bedrooms the house also has two doors 10 windows and a garage the house also has a pool now I would like you to look at the difference between the two diamonds there's a black diamond and a white diamond when you have a black diamond that means that whatever contains the other elements so for example house contains a bathroom what a black diamond means is that you
22:22
cannot have an instance of the bathroom unless you have a house or to look at it in a different way if I remove or destroy the house the bathroom is going to go with it this is the same for garage window door because these are all inherent parts of a house notice though that a pool also belongs to a house but it has a white diamond and what that basically means is that we still consider a pool to be part of the house but if the house goes away the pool actually is able to stand on
22:52
its own it's not completely composited into the house so that's just a simp simple example and you can also see that the house it depends on the electrical grid but the electrical grid is not part of the house it's a completely independent entity but the house uses it therefore it's a dependency so this is a very quick refresher of class diagrams next let's have a look at sequence diagrams a sequence diagram is used to visualize objectoriented Behavior we use it to
23:22
show message exchange over time and we capture the following we capture interaction information such as actors of operations so basically what is part of the sequence and Method calls with data for example here's a very simple sequence diagram of how the cylinder would calculate an area a user wants to get an area of the cylinder so it calls the method get area inside the cylinder it creates
23:48
a variable called area sum and initializes that to zero then it goes into a 4 each Loop it goes through all the elements which are shapes inside the cylinder and for each one of them it calls the get area for that particular shape and when that gets returned it adds that to the area sum and it does that for all three once it goes through the loop it puts the area sum into the result and returns it to the user this gives you an idea of not only what is happening but also to some
24:17
extent how it is being done that was just a very quick uml refresher next we're going to look at what makes great architecture we'll see you in the next lecture welcome back let's do a quick refresher of the Python language with reference to objectoriented programming and we're dealing here mostly with the version of python which is 3.5 or more we'll discuss the following main Concepts classes and objects encapsulation inheritance and within inheritance we're going to talk about
24:52
interface contracts as well as abstract classes and we'll also look at uml diagrams for python object rented programming so that you have a good introduction to what to expect in terms of how code translates into uml diagrams and vice versa when you're looking at the rest of the course all right so let's talk about classes and objects classes are simple or maybe more complex recipes that are used to create two main things we want to have a container for our data so they provide
25:24
a data container which would mean variables or constants and it also also provides operations on data and this would be functions or methods we then can use the classes to create instances of objects which hold the specific data that we can operate on and because we use classes to create instances that's why we call them recipes here's an example of a very simple class in Python we have a greeting class we have a Constructor which accepts a name we can then initialize the name
25:56
variable with the name that was passed in and then we can define a method for example a say hello method which simply prints a personalized greeting message using the name attribute which simply does this so what you see here is a class this is a recipe for creating objects so let's see how we would create a greeting object and it's pretty straightforward we simply specify that a greeing variable is equal to a greeting object constructed with the value JN and then once we
26:27
have the particular instance we can then call a method on it which will specifically work on that particular instance and since that instance was initialized with the value John that will print hello John here is what this would look like in a uml diagram we have a class name greeting it has a name variable which is shown here as being private in Python you don't really have that kind of privacy like you do in other languages but we want to show the intent of our design in the uml
27:00
diagram so if I want the variable to be treated as private I'll show it with that little minus as being private we then have a init method which is basically a Constructor and notice that we're also showing self as being of the type greeting and name is a string and then we have this say hello method now this is a slightly awkward way of notating that it's it's very very language specific we don't really want to have a super language specific uml so I'm going to simplify
27:31
it a little bit and this is what we're normally going to be working with we have a greeting class it has a variable n name and then we have a simple Constructor which is simply greeting that accepts a name the reason why we don't show self here is because that's how the Python language works you don't have to really add that into the uml diagram notice that when we actually create an object of the greeting type we don't for example provide anything other than the name itself
27:58
so this is how you would do it in the actual code you would basically say greeting is equal to greeting with John being initialized sometimes we will do a bit of a Java like syntax and we're going to show it like this and clearly this is not python syntax per se but what we want to draw your attention to is that we expect the greeting variable to hold the greeting type that's why we put it on the left of course this is not how we're going to show it in code but this is how we're
28:30
going to show you in the examples to draw your attention that we would like the greeting variable to be what you see on the left of the greeting type now you might think this is a little strange to show that kind of notation but as you will see a little bit later you could have something that could for example follow an interface which means that the actual type expected is different from the type being initialized which is a little bit strange for python but it's very important
28:57
when we deal with certain aspects of the design pattern so I would like you to basically think of this as interchangeable obviously the first one is the correct python syntax but the second one is just a little bit more explanatory and it's going to be used more as an example rather than actual code so this is basically how we would create a class in Python you create the recipe by using the class syntax create an init method which is basically Constructor and then
29:24
you provide a number of methods that you want to operate maybe on some inner data perfect okay so let's look at the next topic so what about encapsulation what exactly is encapsulation in general a well-designed class already achieves encapsulation in the sense that it gathers all the relevant data and functionality in one place it helps us with controlling complexity as well in other words when you encapsulate Behavior you can actually put a lot of very complicated code behind
29:53
a very easyto use method that's one of the main aspects of what encapsulation is all about so do have the following points in mind make sure that you create classes for all the objects you need in your code and also create collections of related objects so that they can be treated as units this means that you start with very simple objects and then you can put them together into more complex
30:17
objects kind of like the way a house is made of different rooms if a room was an object we would Define it first and then house would be an object that is made up of rooms which are other objects or classes so here's an example let's say we have a class author we have a very simple Constructor basically the Constructor takes a name and a birth year for the author we initialize it and then we
30:40
have a method which is going to be get author info which simply returns a string that encapsulates or shows the information about this author so in this case we simply return a string that is going to specify the name of the author and what year they were born so this is one class we just created a very small a simple recipe for what an author is next we're going to create a class called book
31:05
now the book itself is a more complex class it has variable title it has a publication year but it also Aggregates or contains within itself the author object so in other words a book is a class that Aggregates or contains another class and this is how you can build hierarchies of more complex classes or objects with relatively simple ones so let's have a look at our book so the book is going to initialize the title it's going to initialize the publication year and it's going
31:37
to initialize the author next we will have a simple method which is going to be get book info all of that does is it returns a string that basically Aggregates all this information such as title which was by a particular author and you get the author info from the author object and you specify that it was published in a particular year here and this is how you would create an author object and this is how you would create a book object which Aggregates the author object so
32:07
this is pretty straightforward and this is how you would use it you could for example say let's print the book info so what you have here is a slightly more complex hierarchy of objects now let's have a look at the uml for that so the uml is pretty straightforward we have a book which Aggregates a single author so what you have here is hierarchy a book can contain an author and an author is a
32:32
smaller encapsulation and then book is a larger encapsulation that encapsulates another object all right so now let's have a look at inheritance very important object oriented programming idea inheritance allows us to generalize we can think of it as a tree which grows more complex as we keep on extending its branches so it's almost like you start building more and more by dividing your hierarchy more and more basically the idea is that we start with some property or behavior
33:03
that is present in different instances or types of entities and we then create new and more specialized versions of the parent by inheriting either data and or behavior in the children you will see what that means in just a second so let's say that we create a simple animal Base Class so what this is is basically just a generalized idea rest recipe that creates an animal that has a name and the animal can speak it can say it can basically vocalize something and all that this
33:37
class does is basically says let's initialize the name and then let's write in the speak method where it prints basically that the name makes such a sound so this is very very specific so let's assume that all animals make sounds and let's assume that all animals have names so rather than creating now a dog object that will have its own name and it will have its own speak method we can actually inherit some of that already from the animal class so let's have a look
34:08
so we can now create a dog class so notice that dog inherits everything from animal and then we are going to override so we're going to change the inherited speak method we're going to make it more specific to the dog and we'll say for example that the dog barks but no notice that we did not have to reinvent the name the name has been inherited already from the animal class and notice another
34:36
interesting thing we do not have in the dog class we don't actually have a Constructor well we do but that Constructor is inherited from animal in other words the same way you create an animal object you will do exactly the same thing with dog now let's look at a different animal cat again simple idea we just override the specific behavior of a cat but a cat still inherits everything from
35:01
animal so now let's have a look at the objects of the derived classes so we can say dog is equal to the dog class with Buddy and then cat could be whiskers and then when we call the speak method on the dog it will say that buddy barks but if we say for the cat it will say whiskers meows this is very important to understand that what inherit allows you to do is twofold it allows you to inherit behavior and certain data from the parent class but more importantly it is
35:37
still considered in the same category so a dog is still an animal a cat is still an animal in other words if the animal class which is the parent class has a method speak we expect for both the dog and the cat or any descendant of the animal class to also have the speak method and that's very important you you're not only inheriting you know some methods and some data you're actually inheriting an expectation of behavior so let's have a look at what it looks like inl and inl
36:09
it looks pretty straightforward we have our animal class the animal class has a name it also defines a Constructor and it has a speak method now our dog class we're notating here that a dog has a dog Constructor but that actually Constructor is inherited from animal and then we over write the speak method and we do exactly the same thing for the cat so this is pretty nice because what this
36:33
shows us is that a dog is an animal a cat is an animal so whatever the animal class does in terms of behavior you can expect that from both dog and a cat so how does this extend to the idea of an interface well that's actually very powerful the concepts of an interface contract and an abstract class are crucial in software engineering we'll be using them heavily in our design pattern so I would like to make sure that you have both of these Concepts well covered in Python so we
37:03
will devote a whole lecture to those topics I'll see you in the next video and as always keep uncoding welcome back in this lecture we'll go over interfaces and Abstract classes let's talk about contracts a contract is like a promise that you will provide some specific behavior in classes this means that you promise to provide some functionality as you've seen before with the animal if I know that a cat is an animal then I know that it can speak because all animals can
37:34
speak so in other words I expect the cat class to fulfill the promise of the animal contract one way to create a pure contract is through a concept of an interface in Python we do not really have strict interface syntax what we can do is we can use an abstract class with no implementation to the methods so let's have a look at that so these are the Imports that you would need for abstract classes you would have to import ABC and you would have to import abstract method so here
38:07
is an interface contract and children will have to implement all of the abstract methods and in an interface method what we do is we have no implementation so we simply use pass instead of the function body so for example here is a simple class it's called My interface notice that it inherits from a ABC all abstract classes have to do that and we Define an abstract method called let's say My Method and notice that we put pass in it because we're not providing any
38:36
behavior and that is it so now what we do is we're going to derive or create a class based on my interface contract and we know that since my class inherits from my interface it has to have the method My Method so what do we do we Define the My Method and then we put this specific implementation that we would like for that particular class implementation here is an example of another class that also inherits from my interface and it also has to it has to fulfill
39:09
the contract and it has to define the abstract method My Method and here we do it with that print and then basically all that we do is we can just create an object like my object is equal to my class and then you can just call the My Method and you can have the other object and you do the same thing call My Method so this is pretty powerful because we can now use this idea of my interface as being a contract which has we know for sure a method called My Method so I know that if I
39:41
call that method on any object derived from my interface I am guaranteed that I'm going to have an implementation of My Method now this is very very important for design patterns so let's have a look at the uml for this so usually the way we're show it in the uml is we're going to just show it as an interface notice that there's no such thing as an interface in Python but given what we have
40:04
explained you will understand the uml and what you see on the right the code on the right is basically reflected in the uml diagram we have a my interface and then we have a my class and another class which implement the methods of my interface okay so how do we enforce contracts we can use something known as type henting in Python to enforce a contract so this is our interface just as before we have our my interface it's based on the ABC which is abstract base class and then
40:36
we have one abstract method we could have more and this abstract method is not implemented so here's what we can do we can now create a class that is derived from my interface so let's call it my class and as you've seen before we just implement the Mind method that's great but now let's say that we have another class but it has nothing to do with my interface and it has its own method called Su method notice that we're going to create a method that expects specifically my
41:08
interface contract to be passed as a parameter to this method that's very important and in this one we'll call the My Method and why is this important because we are guaranteed that any descendant any child of my interface is guaranteed to have an implementation of My Method so now we could pass into process my interface my class object and it will work perfectly but if we pass the not implementing interface we're going to get an error so let's have a look we create
41:39
my object which is my class wonderful and we pass it into process my interface and that will work properly but if I create an object of the type not implementing interface and I pass it into process my interface we're going to get an error all right so now let's talk about abstract classes abstract classes are kind of in between normal classes and interfaces so let's talk about partially
42:03
implemented contracts which is all about reuse in many cases you have actual functionality not just a signature but actual functionality behind the signature that you would like to have all child classes inherit so as we've seen in case of like an interface there is no actual functionality these are just contract signatures every method is just signature the name and the parameters are just a signature of what is expected but sometimes we already know that there should be
42:33
some sort of a base implementation so this is where you would use an abstract class you can add specific complemented methods and you can also add specific constants or variables that everybody is going to inherit like you've seen with the case of the animal where every single animal like a cat or a dog or anything that has been descended from the animal class automatically inherited the name
42:57
so let's have a look at this okay so just as before we have our ABC and Abstract method inut and we will create an abstract class shape we create a very simple Constructor which accepts color and we simply specify that the color is going to be whatever the input color was great but now we create an abstract method we call it area we put it a pass because different shapes will have different ways of calculating the area we're also going to create a parameter method same
43:27
same thing it's a pass because we don't know what the actual parameter calculation will be because that depends on the specific shape and we'll have one more abstract method which is going to be the description and we will actually create an implementation of this method so in other words this particular method doesn't have to be implemented the other ones have to be but this one doesn't have to be so let's have a look at the uml this uml is a little more complex so
43:53
we'll use the kind of like deeper green to specify abstract classes and as you see we have our ABC the abstract Base Class which comes from the ABC import package and our shape descends from that our shape has color it has a Constructor which accepts the color to initialize it and then we have two abstract methods area and parameter that have to be implemented and we have a description method which does not have to be implemented and here you have an example of two derived
44:24
classes these are children one is a re angle which defines its own width and height and the other one is a circle which defines a radius notice that neither of them has to overwrite description because they've inherited it and they can actually use it as is the only thing that they really need to do is they need to overwrite the area and the perimeter because obviously for a rectangle the
44:45
area is going to be width times height but for a circle the area is going to be different it's going to be Pi radius squared and obviously the parameter is also going to be different okay great so this is pretty straightforward so as you can see a shape is kind of like in between a contract interface and an implementation kind of like a half done implementation and the whole idea here is of reuse why create a rectangle that defines its own description if you can already defined
45:13
the description in the parent class and in most cases the children can use it you basically are writing less code so let's look at the actual code implementations for this so the idea basically is that abstract classes can re Implement methods or keep the existing methods so again we're seeing this shape class here on the left and then we have for example a rectangle class and as you can see
45:37
the rectangle overrides The Constructor so this is important since rectangle inherits from shape the simple in it color The Constructor color notice that what it needs to do is in its own defined Constructor which accepts width and height and call it has to delegate to the super class which is in this case the shape to make sure that that Constructor is invoked with the color because the
46:05
shape class knows what to do with the color notice that our rectangle class doesn't know what to do with the color so it just delegates that and this is the idea of reuse and then the area and the perimeter methods are properly defined this is just an example of how you would use it rectangle is equal to rectangle 45 red and then we have the rest of the code so this is a very important objectoriented parad the idea of inheritance using abstract classes all right so
46:32
this is a very quick refresher now we're going to have a look at very quick code examples and we'll do that in the next video all right welcome back let's look at some examples of code that are going to solidify the two refresher lectures that we did about objectoriented programming with python so let's look at this first simple class this is very much a classic example we have a simple class called greeting we have a Constructor which accepts a name the name itself is a public
47:10
variable then we have an instance method say hello which basically prints the name to the console then the way we create an instance of our class is as the example shows we say greeting is equal to the greeting class initialized with the string John that creates a specific instance in memory of the greeting class recipe as an object and then of course we can manipulate the object and the way that we do it here is we call the say hello method and when they say hello method
47:44
is called it will print the name so if I run it right now it basically says hello John exactly as what we would expect and if I create another variable for example let's say greeting to
48:00
and I'm going to say for example Jane and then if I print that it's pretty obvious what we're going to get when we say greeting to and we run this we're going to see hello John and hello Jane of course now what can we do with this class this is a fully formed class this is not an abstract class one thing though that we can do I'm just going to remove this for now one of the things that we easily can do is we can actually
48:29
use this class as some sort of a base class so I can create another class called better greeting now this better greeting class is going to inherit everything from greeting so we're going to derive it from the greeting class and then for now we're just going to define the body as pass what that means is that the better greeting class inherited everything from greeting but it's not changing anything so in other words greeting and better greeting to some extent are equivalent so
49:07
for example if I change this right now to better greeting the output should be exactly the same as it was before and as you see we have hello Jan now basically what is happening here is that the better greeting class has a Constructor exactly just like greeting and it has the say hello method so one thing that I can do is I can override this say hello method the way I would do it is just as follows I'm basically using the name and what I can do here is I'm going to print something
49:38
else I'm just going to say hello and I'm going to just say better that's it this is actually a different implementation now and if I run it now you can see that we have hello better John so what is happening is we have overridden the method that we have inherited from greeting but that method from inside greeting it is still sitting within the hierarchy that we have inherited and the way we could do it is I can actually refer to it using super super refers to the base class
50:09
so if I go super and I invoke the inherited say hello method what do you think is going to happen now I'm going to give you a few seconds to think about it what do you think we're going to see right now well if I run this right now we're actually seeing both so as you see when you overwrite something and you inherit it it becomes part of you when you overwrite it what you're doing is you're basically kind of putting that one aside and defining almost like a
50:38
new Logic for that particular method so this is pretty straightforward pretty interesting I would say but very very basic all right so now let's go to another example all right so here what we're looking at is an example of basically object aggreg gation we have one class that Aggregates another so we have a class author which is pretty straightforward we have a Constructor that initializes the inner name and birth year variables and then we have a get
51:08
author info method that simply returns a string representing the instance data for the author pretty straightforward simple class next we have a book class now the book class is just a little bit more complicated just like the author class it basically has a Constructor that initializes its inner data and then we have a getbook in method which returns some information about the book The Thing of note here is that the book Aggregates the author in other words the book contains a
51:34
reference to the author instance so this is to a great extent an aggregation the showcases being able to build a more complex object out of simple objects so this is a very very simple example then what we do is we create an author object we initialize it with George Orwell and 1903 as the birth year and then we create a book object with a reference to the author object so at the end our book object not only contains its own data but it also via reference contains the data for the
52:05
author and then of course we print it and when we print it we get the following basically as we expected we get 1984 by George Orwell who was born in 1903 and it was published in 1949 and if you look at the get book info you can see that the return basically gets the inner value of the title for the book but then it goes into the author instance and then asks the author instance to get the author info so we're following the API of the inner object so that it tells us something
52:33
about itself and this is basically what this example is all about so let's go to the next code example all right let's have a look at another example here we're looking at an abstract class class animal which as you can see is derived from ABC which is the abstract Base Class this is how make abstract classes in Python and we create two abstract methods one is a sound method and the other one is a description method now what is interesting here is that this is almost
53:10
like what you would consider an interface to be the only difference is that our abstract method description actually has an implementation so let's have a look at this this is like a little bit strange maybe but it's pretty interesting okay so now that we have defined an animal Base Class we're going to derive some classes from it so the first thing that we're going to do is we're going to create a class dog and you can see that it extends animal and then what we're going
53:36
to do because we have to we're going to implement the sound method and we're simply going to say in it that we return the Woof so like a bark now notice that because the description is an abstract method we have to override it but it doesn't mean that we have to implement a new one so this is a bit of a trick so what we're doing here is that in the dog description method all we're doing is
54:02
we're simply delegating back to the description implementation in animal class this is why you see super. description and we do the same thing for cats so when you have cat which is derived from animal the sound is going to be meow and the description is exactly the same as for the dog so if we run this right now you will see that the dog says wolf and a cat says meow so let's look at the
54:28
description method so if you go back up to the animal description method you can see that what it does is it prints first we have a self and then class and then name that double underscore name double uncore is actually a private class name so for example animal has a class name of animal dog has a class name of dog and cat has a class name of cat this is why when we print it you can see that it says dog says for example woof and that that is like an automatic variable
54:58
that you get whenever you create a class so what is interesting here is that the animal description method has proper access to the child classes sound implementation so basically what happens is that for example when you take the dog instance and you call the description class on it what it will do is it will call the super description but the super description is actually still Within
55:26
for example the dog class so when it says self it will work on the current child classes instance and that's why self. sound returns woof for a dog because of the fact that we have an implementation there now one interesting variation that we can do is why do we have to make it abstract well we don't have to if I remove this abstract method from here what I can do is I can actually completely then remove the overrides and if I do that let's see now what this is going to
55:56
run us and as you can see it does exactly the same thing the main difference basically being that if you create an abstract method you have to implement it if it's not abstract you don't have to implement it but let's see if we can overwrite it what I'm going to do right now is in the dog class I'm going to overwrite the description method so what I'm going to do is I'm going to create my own description instead of class name I just going to basically say my little dog says so
56:26
so if we run this again you can see that I can actually override the method that I have gotten before but because the description method is part of the animal contract anything that expects for example an animal will work properly okay so this is it for this particular example next we're going to have a look at an encapsulation example welcome back now let's have a look at a slightly different example of using classes in Python this is not an example that we have seen
56:58
in lectures but it does deal with something we have discussed which is basically encapsulation what you see here is a bank account class this is a normal non-abstract class we have two instance variables in there which is an account number and a balance we initialize them in the init method which is our Constructor and then we have two methods that deal with the balance variable which is get balance and set balance we'll get back to that in a second and we also
57:26
have two complimentary methods for deposit which is putting money in and withdraw which is removing money so let's quickly look first at the two class variables so what we have is we have an account number and we have a balance notice that we have a protected attribute which is accomplished when you put an underscore before the name and we also have a private attribute which is achieved with a
57:51
double underscore which is basically name mangling in Python now the private aspect that we achieve here is actually very good you will not be able to access that variable directly through for example the account instance you have to use get balance and set balance now let's have a look specifically at the writing method which is set balance what is important here is that we can put a precondition
58:20
for example if the input balance is not greater than or equal to zero then we consider that to be invalid in other words it doesn't make sense to set a balance of an account in this case to be negative and notice also that we have a similar thing with a deposit it's a modification function that basically changes the amount of money in your account it does the same thing it works with the balance variable and we basically check if amount is greater than zero then
58:49
we change the balance but if the amount is zero or less then we basically consider that to be an valid deposit the reason why this is important especially in encapsulation is because what we're doing is we're setting certain rules as to what is considered valid or invalid when it comes to the operations in our account so for example the fact that we can set the balance not to any value that we wanted but specifically only to values that are greater than or equal to zero it's a
59:21
way of controlling outside changes so this is very important for encapsulation and in general when you're creating a class you want to make sure that you have some sort of control over how the data is for example set especially how the data is written what you see here if balance is greater than zero or if amount is greater than zero these are preconditions similarly we have a withdrawal which is like if you want to withdraw an amount that is greater than the amount
59:50
you actually have in your account you will not be able to withdraw it either you need those kind of checks you need those kind of verifications to be done by the class itself rather than having it external to the class because what that means then is that the actual logic of how we work with an account is encapsulated within the account or within the bank account class that's very important so testing this code is very simple we create a bank account with an account number and
1:00:24
balance of 1 ,000 we can then print the account number notice that we can actually access the protected attribute it is not really recommended usually any kind of attributes inside a class it's probably best to have some sort of a Setter and getter so that you can control the precondition which is checking when something is being changed and also post condition to make sure that the changes are valid so next we see the accessing and modifying the private attribute through a
1:00:53
getter so we can for example find out what the initial balances we then set the balance to 500 and then we have the updated balance and finally we have using public methods that internally use the private attribute in this case it would be deposit we can deposit $100 then we can see what the balance was after deposit we then withdraw 50 and we can see that the value is correct after that and if I run this right now you can see that it works perfectly so this is again just a very
1:01:22
simple example of encapsulation that specific L works with being able to control how data is modified externally to the class so this is very important aspect of encapsulation so this is just a quick kind of refresh for you in terms of how encapsulation work or should work and next we're going to look at how interface contracts work and as stated before understanding interface contracts
1:01:49
is very important especially for design patterns because we're talking about software engineering patterns where contracts and a promise of what a class will or will not do is very important all right so let's have a look at what an interface contract is in code so we start with something pretty fundamentally similar to what we have done before we create a class my interface which extends from ABC an abstract Base Class and we create a method
1:02:21
we could create more than one but here this is just one method and the me method is an abstract method which means that it doesn't have to be implemented specifically when the methods are not implemented it becomes an interface contract and the idea basically is that you cannot really create an object from that class you can only derive another class that will Implement all those methods so you can't really for example take my interface
1:02:49
and create an object with my interface you can for example say something like this
1:03:01
this will fail so if I for example run it right now as you can see this failed and there's a reason for that and the simple reason is that basically the point of creating an abstract class with all its methods being abstract is that it's not supposed to be used to create objects it's supposed to create a contract for derivative class classes and why do we need contracts well the idea behind contracts is that if I create an expectation that some
1:03:33
sort of functionality will always be available if I go through a certain interface then you can create very extensive pieces of software that are very flexible I'll give you a very quick example if I create an interface that specifies shape and the shape has a method called area if I want to create a piece of software right now that will add up for example all of the areas of different shapes I can write that piece of software based on the shape interface not on the
1:04:04
specific instances like whether it's a triangle or a square or for example a circle I don't need to know what the specific classes are all I need to know is that all of them are derived from shape and because shape would have an abstract method called area then I know that any shape will have that that's a pretty straightforward idea okay okay so let's have a look so once we have created the interface what do we do with it well we extend that interface so we're creating a
1:04:33
derived Class A Child class and in that class we have to implement whatever the abstract methods are in the interface so in this example here we have one method and notice that my class has to implement My Method and then if we create another class which also is derived from my interface it also has to implement My Method that's the idea of a contract by contract you have to do it what that allows me to then predict is that if I create for example here my object which
1:05:05
is my class and I create another object which is another class I know that both of my object and another object they have to have my method and that's really the idea that if I know what the parent class is and I know what the apis are then I can basically work with any kind of object as long as that object is derived from that interface so if I for example run this code right now you will see that the output even though we're calling exactly the same method my method
1:05:40
for each of the objects each one of them is going to have a different implementation and therefore different output and that's wonderful because from the perspective of the code it doesn't know which one it's calling it simply calls the interface contract method which in our case happens to be my method that's all it knows okay so next we're going to look at a slightly more complex version of this code that kind of shows us exactly how we can now use the idea of an interface
1:06:11
to ensure that we follow a contract all right so right now let's have a look at an interface contract being used in an actual method so we have our interface contract just like before my interface is derived from ABC it has one abstract method and then we create two classes my class and another class that both are derived from my interface so we know for sure that they're going to have all the abstract methods implemented and then we have another class which is just it has
1:06:48
nothing to do with our interface so we just call it not implementing interface it just implements its own particular method so then what we do is we create a test method and this test method which we called here process my interface is smart enough to know everything about my interface contract and in this case it knows that my interface has a method called My Method so what basically happens
1:07:16
here is that this interface if you pass it an object that was created or instantiated from a class that was derived from from my interface then this method knows that it will definitely have a mycore method function or method it knows that on the other hand if I pass to it the wrong interface it will also know and will throw an error so let's have a look at that I'm going to run it and if I
1:07:44
run it here you can see something very interesting when you look at this particular error here it basically says that not implementing interface has no attribute My Method well exactly it does so the important thing about this process my interface method is that it is aware of the my interface contract and this will be important later on especially when we are dealing with design patterns where we specifically engineer contracts via interfaces to have very specific
1:08:16
API which can then be properly used by a client and the important thing here is that this piece of code here my Pro interface it doesn't know anything about my class or another class it doesn't have to know all it knows is that anything that is my interface dve so my class or another class or any other class from for that matter that is derived from my interface will have a My Method
1:08:42
so it can call the My Method signature because it knows it will definitely be there so this is quite important we're going to see that used in our design patterns okay so next we'll see an abstract class that is somewhere in between an interface and a fully implemented class we're going to be talking about an abstract class with some built-in functionality all right so in this particular
1:09:11
example we're going to look at a combination of an abstract class used as a contract so we have a class here which is shape we're going to use that as a parent class for all of our shapes and our shape class is not a pure contract because first of all it has an instance variable which is color and it also has a description method which is fully implemented so the actual contract part spans all three methods but the two abstract methods is what we would like other shapes for
1:09:50
example rectangles or triangles Etc to implement so that we can then man at different shapes only knowing that they're shapes we don't care what shapes they are so in our case we have an abstract method area and we have an abstract method parameter and we have also a defined method description okay so let's have a look now at how we would use the shape contract so what we do is we have a rectangle class and the rectangle class it implements the shape abstract class now what we
1:10:21
need to do is because this is a specific rectangle shape we need to Define some extra data for it so in this case we're defining extra data as being width and height so we create a Constructor which accepts width and height but we also need to accept color because we have inherited color from the shape class so in this case how we initialize our instance data is we set width and height to be
1:10:49
the input width and height but the color itself is passed on to the Super Con Constructor which is basically the shape Constructor that we have inherited and then we Define the two abstract method implementations for area and perimeter specific to rectangle and as you can see the actual algorithm is pretty straightforward for area we're just saying width time height and for perimeter we just basically add the width plus height and we multiply it by two if you look at
1:11:17
the Circle class the circle class is really no different this specific data for Circle class is radius we initialize the radius to the input data we also initialize color through our super or through the shape Constructor and then we Define the area and we Define the perimeter notice that the implementation of area specifically knows how to determine the area for a circle using radius and also for the perimeter the same idea we have a formula that uses the radius okay so
1:11:47
what's the big deal about this well look at this little interface contract method which is process my color notice that process my color accepts shape and all it does is it simply prints the description that method doesn't have to know what shape is being past because it's guaranteed that no matter what the shape is whether it's rectangle or triangle that method will be there so that code
1:12:10
can accept any shape even shape that we have not created yet we could create a new shape in a month which will be triangle and the process my color method will not have to change that's the beauty of it we can keep on extending the set of children for the shape class but we don't have to change at all process myol method this is actually great and makes for very flexible and reusable code
1:12:32
and now if we just look at how we would create the two shapes we have a rectangle which specifically creates a rectangle with a four five and red so width and height and red and we are doing that because we know what a rectangle needs the same thing for a circle a circle is defined with a radius but notice when I call the process my color method it does doesn't have to know if it's Circle
1:12:56
or rectangle so past the initialization step we can write code using contracts that don't have to know anything about what kind of child classes are implemented or will be implemented in the future so to some extent process my color is a future proof method that's the point of creating contracts and if I run this right now you can see that right here at the very bottom it says says rectangle has the color red circle has the color blue the beauty is that process my color doesn't
1:13:29
know if it's a rectangle or a circle it doesn't matter all right this is great so we are at the end of doing a refresher for objectoriented programming in Python next we're going to go into actual design patterns I'm going to see you in the next lecture and as always keep on coding welcome back in this lecture we're going to look at what makes a good architecture what are the Hallmarks of a good architecture there are a number of principles we have loose coupling which
1:14:09
is weak knowledge association between components we have separation of concerns which is breaking your architecture into tears we have something called law of the meter LOD and we have the very popular solid principles of OB object oriented programming let's have a look at all of that in a little bit more detail so what is loose coupling as we stated it's weak knowledge association between components but what does that mean imagine that you have a number of classes these
1:14:41
classes will communicate here is an example of the different intercommunication channels that these classes use as you see it's a lot here's a very similar diagram but it shows the classes with a much cleaner route of communication the one on the left is an example of tight coupling the one on the right is an example of loose coupling changes to one component least affect the existence or performance of another component so when you look at the diagram on the left
1:15:12
and you look at the diagram on the right you can definitely see that any changes to class B when it's Loosely coupled affect less classes than it does when it's tight coupling and of course that means changes to testing changes to documentation so in general loose coupling is one Hallmark of good architecture next let's look at separation of concerns breaking your architecture into tear what does that mean well that basically means that you structure your architecture around main
1:15:42
tiers of functionality you can have for example a presentation layer it could be your web UI it could be your mobile UI you would also have a business layer that is where you have a lot of communication happening deciding what is it that the user through the presentation layer what is it that they want and then you would have a resource access layer which would be data access layer so as you see we have split our architecture into three separate tiers presentation the business
1:16:12
and reading and writing the data the separation of concerns is achieved using modularization and capsulation and arrangement in software layers the next thing that we're looking at is the law of the miror LOD first each unit should have only limited knowledge about other units it should only know about units closely related to itself each unit should only talk to its friends it should not talk to strangers and that unit should only talk to their immediate friends so let's have a
1:16:44
look at three classes we have a b and c and let's say that a can talk to B and B can talk to C we can basically say that b is a friend of a but C is a stranger to a so a can communicate with B that makes sense but a should not communicate with C that's basically what law of the meter States a given object should assume as little as possible about the structure or properties of anything else other than its closest friends and this is also known as principle of least knowledge
1:17:19
this leads us into the solid principles very important principles in software engineering and architecture and this is what we're going to cover in the next lecture let's have a look at the solid principles of object-oriented programming we have a number of them we have the single responsibility principle which basically states that there should never be more than one reason for a class to change each class should only have one Central responsibility then we have the open close
1:17:49
principle software entities should be open for extension but closed for modification then we have the Lis of substitution principle which states that functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it the interface segregation principle states that clients should not be forced to depend upon interfaces that they do not use and finally we have the dependency inversion principle which says that you should
1:18:18
depend on abstraction not concretions let's have a look at this in detail first let's look at the single responsibility principle there should never be more than one reason for a class to change each class should have only one Central responsibility for example it should only deal with persistence or it should only deal with pre and post conditions validation in other words or it should only deal with notification or only with logging or only with formatting or only
1:18:45
with parsing or only with error handling the idea being that a class should not deal with any collection of these it should specializ in one thing now what about the open close principle that software entities should be open for extension but closed for modification let's have a look at an example let's say we have a simple calculator class and this calculator class has four methods
1:19:09
or four functionalities it can add it can subtract it can multiply and it can divide but imagine that somebody comes to you right now and says oh but you know what I would like to have a business calculator or a scientific calculator what should you do according to the open close principle you should not change the calculator class you possibly already have code depending on it what you should do is you should extend it you should create new classes that extend the calculator
1:19:38
class so your loan calculator which could maybe calculate some monthly payments or some future value of your mortgage you just create a new loan calculator same thing with a scientific calculator if you want to add s cosine and tangent don't put it into the original calculator class extend
1:19:55
what about the Lisk of substitution principle functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it for example if Class A is a subtype of Class B then we should be able to replace B with a without interrupting the behavior of the program at all here's an example if we create an abstract class shape and then we create specific concrete classes like triangle Circle and rectangle any code that uses shape to get
1:20:25
doesn't have to know anything about the actual specific shape that it's using whether it's a triangle or Circle or rectangle if I want to find out the area of something all I need to do is ask for its area I don't need to know specifically that it's any particular shape that would be the Lisk of substitution principle and you can see that the idea is that if I have code that for example calculates all of the area of all the shapes on the screen and let's say
1:20:51
that I add a new shape some sort of an oval or maybe a cylinder my code that calculates the area shouldn't have to change at all if it changes that means there's something wrong with your architecture it shouldn't change because it is still a shape the next one is the interface segregation principle clients should not be forced to depend upon interfaces that they do not use declaring methods in an interface that the caller doesn't need pollutes the interface
1:21:19
and leads to a bulky or fat interface let's have a look at an example let's say that I would like to to create a loging functionality so I create an interface called iog notice that the interface has only one method which is log so now what I could do is I could simply just create a DB logger automatically and I could create a file logger automatically but then I need certain functionalities like open connection close connection I could add for example open connection
1:21:46
to the iog interface but then the file logger which would extend it wouldn't need it that's the problem if I put the open connection the close connection inside ilog the file logger would be using a fat interface it just doesn't need it what we're doing is we're creating a generic iog and then we're creating sub interfaces idbl log and I file log which allows us to specifically create two classes DBL logger and file logger that use exactly what they need to use they don't have to
1:22:20
use some extra methods or anything like that that is what the inter interface segregation principle is all about the last one that we have is the D in solid is the dependency inversion principle which basically says depend upon obstructions not concretions imagine that we have a book service that creates a book so we have two classes we have a book service class and we have a book class the book has a number and this number needs to be generated but because book service is the one
1:22:50
that creates it it would need to generate the number now how would you to do that the best way to do it is to have the book service use an interface which is an abstraction that has a pretty generic method called generate number then whatever number needs to be generated the book service doesn't need to know what it is the book service simply wants to generate a number so whether the actual number generator happens to be ISBN generator or an issn generator is irrelevant
1:23:19
to the book service that is the idea the book service depends on the number generator it does not depend on the concrete elements we will see quite a bit of this in our design patterns to summarize we'll do the following four things in this course we'll learn about the most fundamental design patterns in the industry we'll study them in a proper architectural context we will see how they cover the solid and good architecture principles and we'll use them
1:23:48
practically in a project thank you for watching and listening and I'll see you in in the next lecture welcome back let's talk about our first design pattern the Singleton pattern Singleton is a creational design pattern that ensures that a class has only one instance and provides an easy Global access to that instance the pattern does not stipulate what to do with a Singleton this is where you can be creative the main tenets of this pattern are you have to ensure that the class
1:24:23
has only a single instance you have to provide easy Global access to this instance you have to control how it is instantiated and any critical region must be entered serially what this last Point means if there's any kind of thread safety issues then you must ensure that whatever clients are calling the Singleton they are calling it one at a time you have to serialize that access let's
1:24:48
have a look at what the Singleton pattern actually does so here you have an instance of a Singleton and you have a number of clients all of these clients are going to access just a single instance all you have to basically make sure at this point is that the class has only a single instance in other words if one client goes to create the Singleton and another client in a different piece of code also tries to create the Singleton both of these instances
1:25:17
have to be the same one that's what defines a Singleton what does a Singleton do that's completely completely up to you when you see those little red rectangles and squares this is just a way of me showing you visually that the Singleton itself doesn't stipulate what you do inside it only stipulates how it is created what you do inside is really up to you so what can we do inside a Singleton like what is it actually good for you can use it for a variety
1:25:48
of functionalities where you want to control that there's only a single point of access for example loggers or caching or thread pools or database connections or configuration access all of those access the same elements so for example when you're logging no matter where in your code you're actually doing the logging it should all be going to the same destination so the best way of doing that would be to use a Singleton here you have an example we have three clients and they all
1:26:19
go into the Singleton that little red rectangle just stands for a logger so what this Singleton does is it logs to a singular log file so in other words any one of these clients when they call the class they don't even have to be aware that is just one instance but what is happening here is that you're actually ensuring that because it is a Singleton they are all logging to the same log file which is exactly what you want here's another example consider caching it's a
1:26:48
similar idea you have a Singleton and all of the clients are going to hit the Singleton checking whether there's something in the cache that little red square stands for caching mechanism but that caching mechanism is wrapped inside the Singleton so all that the Singleton does here is it ensures that all of these clients just access a single instance how about database connections same idea we have our DB connectivity and we have our clients and the clients are hitting
1:27:17
the Singleton and that red rectangle would be some sort of a database Connection Manager okay so who came up with this pattern well this pattern is one of the famous gang of four patterns and its motivation is stated as follows in other words why would we want to have something like a Singleton it is important for some classes to have exactly one instance although there can be many printers in a system there should only be one printer spooler there should be only one file
1:27:46
system and one window manager whenever you need to ensure that all the colors are going through the same instance that's when you're going to be using a Singleton and this pattern the Singleton is often times used with some of the other gang of form patterns it's used with abstract Factory it's used with a builder which we're going to see because we're going to cover this in this course
1:28:11
also in Prototype facad as well as state so we're going to see the Singleton being used a little bit more in the rest of the course when do you use a Singleton pattern when you want to control access to a shared resource when not to use you should use the Singleton pattern with restraint and do not let it degenerate into just Global access for everything Global access hides dependencies and might make it harder to read such code so make sure that you have a good reason to use this
1:28:45
pattern the main question you should ask yourself as to whether you're going to use or not use the Singleton pattern is do you violate the SRP the single responsibility principle if yes then you should reconsider using it in other words if your Singleton is doing too many things you're actually violating the single responsibility principle so if you're going to create a Singleton make it just deal with logging or just with file caching Etc not with logging and file caching at the
1:29:14
same time so what is the design consideration when designing a Singleton consider lazy construction which means that the class in should only be created when it is first needed in some cases we might consider eager loading if for example we need the Singleton to be always ready and loaded fast the main difference here is very much with memory for example if you're creating a game if it's some sort of a shooting game you want the sounds of the bullets to be happening
1:29:45
every time a bullet is fired at that point you would eagerly load the sound so that it would be ready the moment the first bullet is fired lazy construction or lazy loading would be when you're not 100% sure if a particular instance is going to be used so if you're not sure about that and you don't want to waste resources then you can consider to load it only when it's actually accessed for the first times now the other thing you have to consider is thread safety you need
1:30:14
to consider that specifically in languages that allow multi-threaded access because you need to ensure that access is properly controlled and locked so that the of the singl ton if it has one is always deterministic because python does support multithreaded programming we will need to take special care when creating multi-threaded Singleton we will need to lock the critical section that is something that we're going to look into when we look at the coding all right
1:30:44
well I hope that you found this informative and helpful and I'll see you in the next lecture and please do not hesitate to ask any questions you might have I would love to answer your questions see you in the next lecture welcome back let's have a look at a generic implementation of the single term pattern first before we go into the specifics of coding it in Python there are a few ways in which we could implement the single term pattern in Python so first I would like for us to
1:31:13
look at the generic architectural implementation which we're going to use as a starting point so I would like you to note a few things about this uml diagram first of all we have have a private Constructor this prevents instantiation of the Class by an external caller secondly we have a private instance of the Singleton class itself note that this instance is static this means that it is defined at the class level and not at the object level so there can only be a
1:31:42
single instance of the Singleton class globally we obtain the instance by calling the static get instance method which does a very simple test if the instance variable is null we create a new Singleton object and assign it to the instance variable and since we're inside the class body we can invoke the private Constructor if it is not null we return the existing instance variable which is always the same note also that this is an example of lazy instantiation we only create the
1:32:15
instance when we are asked for it through the get instance method so in essence the Singleton class creates its own instance this is why you see the creates dependency so this is a generic example of how you could achieve the Singleton pattern but how would we accomplish this in python python is a very rich language and there are multiple ways of implementing the Singleton pattern but there are some limitations first of all python does not really have conr structors but it does
1:32:49
have a creation and initialization routines for classes it does not strictly have a static keyword for variables but it does support class level variables and methods which are for all purposes static the Singleton pattern can be implemented in a number of different ways in Python some possible methods include Base Class decorator or a meta class from those The Meta class is the best suited
1:33:16
for this purpose since with it we can manipulate and control the task of the class creation itself before we look at the specific implementations of the Singleton pattern let's do a very quick review of some of General python function overrides so we have new which is a static method that is responsible for creating and returning a new instance of a class and it is the first step in the object instantiation process it is typically overwritten when you need to control the object
1:33:48
creation process that is definitely something that we'll need in a Singleton the other one that we're looking at is the init this is an instance method that is responsible for initializing an object's attributes after it has been created by new the init method does not return a value and it's called automatically after the object is created this method is typically overwritten to Define custom attribute initialization for the class and we also have the call method this is also an
1:34:18
instance method that allows a class's instances to be called as as if they were functions we'll see why that is relevant so let's look at those in the context of meta class which we're going to use to create the best version of the Singleton so new in this context since a meta class is a class that creates other classes new in a meta class is responsible for creating and returning a new
1:34:46
class object in it this method is responsible for initializing the new class object created by The Meta class such as setting class attributes or other class level properties and call in a meta class this method allows instances of the meta class which are class objects themselves to be called as if they were functions now this is executed when creating instances of the classes created by The Meta class so by customizing this method you can control how instances of those
1:35:16
classes are created or returned so we'll need to remember these definitions when we're actually looking at the Singleton implementation so this was just a quick overview or maybe a refresher what we're going to do right now we're going to build three different versions of the Singleton progressively from a good one to a better one to the best one so what we're going to do is we're going to create a classic gang of four Singleton and python this will give us the most simple and
1:35:43
generic version and in this one we will need to control Constructor access and instantiation of our Singleton will be done through a static method next we're going to have a simple or maybe like a bit of a naive python Singleton that will be our second version and in this one we're not going to control the Constructor so instantiation will be done through Constructor we will use the new
1:36:07
method override to achieve this and then the best version will be done using the meta class so we're going to have a metac class Singleton and here we will overwrite the Call method and we will also use the new and init methods but we'll see that in later examples when we talk about eager instantiation all right so let's have a look right now at the actual implementations of these three versions of the Singleton let's look first at the classic gang of four approach to Singleton
1:36:37
implementation in Python have a look at this particular uml we have a static instance variable that holds the only instance of the class and we initialize It To None So initially basically it is not set we then have a Constructor that cannot be called we show it here as private I'll explain that in a bit but the main idea is that we remove the ability for the caller to create more objects
1:37:03
through a Constructor so this is the way in which we control instantiation we then have a static get instance method which is how we will control the creation process so let's have a look at the actual code itself so notice that we have the class level variable to store the single class instance the trick here is that we over over write the init method to control external initialization and we simply throw an error if anybody uses a Constructor so basically we remove
1:37:31
the Constructor at runtime so this is not really a private Constructor per se but we do not allow its usage externally we raise an error then we create a class level get instance method which allows us to fetch the single instance copy of the Singleton so basically if the instance has not being initialized then we initialize it to an instance of our classic Singleton otherwise we simply just
1:38:00
return the instance back to the caller how do we use this particular version of our Singleton we simply call the class level get instance method to get the instance of our Singleton also please notice that this is an example of lazy instantiation so what we get here is a legitimate Singleton but this is not necessarily preferred way of doing this in Python let's look now at a more of a python kind of way of implementing our Singleton so this is a simple way in which we
1:38:38
could create a Singleton in Python and it would be done as follows so as you can see this time we have a Singleton where we do not control the Constructor just as before we have the in static variable and we have a Constructor so let's see how it is that we actually control now the creation of this instance so that there's only one instance of the Singleton globally so let's have a look as usual we have the class level variable to store the single instance of our class we set it
1:39:13
to none but this time what we do is we implement the singl ton by overwriting the new method which gives us control over how objects are created so what we do here is we check if the instance of the class has been created before and if it has not been created we create it we then assign it to our class level instance variable and then we simply return either the one that we have just created
1:39:40
or one that has been previously created but it is always the same instance and this is how we would use it so this way we're actually going through the Constructor and also notice that this is another example of lazy instantiation so this implementation will work but there is a syntactically and logically a much better and a more sophisticated way to implement a Singleton in Python and that is using metac class and metac class will not only allow us to create a
1:40:12
better version of the Singleton but it will also allow us to control both the lazy versus eager instantiation of the instance so we'll do that in the next lecture I'll see you in the next video video and as always keep uncoding welcome back using the meta class is probably the best way to create a Singleton in Python a meta class in Python is just a class that defines the behavior and rules for creating other classes in other words meta classes are like the classes of classes
1:40:44
or like factories of classes by default all python classes implicitly inherit from the type built-in class which is its self a meta class meta classes allow us to customize the class creation process and modify class attributes methods or other properties before the class is actually created they provide a way to apply certain behaviors consistently across multiple classes so let's have a look at one way of implementing the Singleton using a meta class what you see
1:41:17
here is a Singleton meta class and a Singleton class what is interesting here is that the actual Logic for what makes this a Singleton is inside the meta class so just a quick description of what we see in the uml the instances is a dictionary that stores in our case the single instance of the class for each subass we have only one subass here which is the Singleton so we will store basically the single instance of the Singleton now whenever we create instances of
1:41:50
the Singleton class they will actually be handled by the call method override of the Singleton meta class ensuring that only one instance of the class exists so to some extent what you're seeing here is that the Singleton class is actually created within the Singleton meta class so let's have a look at the actual source code so we Define our Singleton meta class the parent of this class is
1:42:19
going to be type then we Define a dictionary that stores the single instance of the class for each subass of the Singleton meta class in our case we have only created one subclass which is the Singleton so this is our addictionary then we have the call override and what happens is in the call we basically check hey if we don't have our class inside the class instances dictionary we create
1:42:44
the instance of the class that is calling this which would be the Singleton class and what we do then is we take the instance of that class and put it inside our dictionary this ensures that we have only a single copy and then we have a return We simply return whatever we have there and now our actual Singleton class can do whatever it wants so our class Singleton depends on the Singleton meta
1:43:10
class so the Singleton here itself does not define the logic for the Singleton that is defined in the Singleton meta class which is which is actually pretty wonderful because if you think about it we could create one Singleton meta class and then we could have many other subtypes that are also Singletons but we don't have to keep on redefining it it's almost like a bit of an abstract class
1:43:32
in that sense in terms of behavior and how would you use this just like any other class you would simply say S1 is equal to Singleton we call our normal Constructor and that would be basically it one thing to notice of course is that this is also lazy instantiation okay so now let's have a look at how we would do e loing so let's look at what eager loading is good for in the context of a Singleton because every Singleton so far that we've seen uses lazy instantiation okay so let's
1:44:04
first discuss why would eager loading be good so first of all eager loading allows for data preload and caching it allows for connectivity pre-caching it's important when access Is frequent and needs to be fast so we initialize a load the instance before we need it that would be the idea of eager loading you load it before anybody asks for it so that when they actually ask for it it's
1:44:28
already there we don't have to create it we don't have to build it we don't have to get the memory for it everything has already been done now python does not support eager instantiation out of the box or you know something in an elegant manner but using meta class we can achieve eager instantiation when the meta class is executed so let's have a look at that okay so let's look at eager python implementation so using the meta class we can create an eager loing Singleton this
1:44:57
is very similar to how it was before we have a meta class and we have our Singleton class which is derived from The Meta class the difference here is that what we do is we overwrite the in it method before we deal with the call method now the init method is called before and what is important here is that the Singleton meta class is going to be loaded into memory before anything else is executed because it is meta class so therefore if we want to generate eager loading
1:45:27
all we need to do is overwrite the init method and then in the init method create the instance of the Singleton so let's have a look at what that would look like so here is our class we have the same dictionary as before that has not changed but now what we do is we overwrite the init method and in the init method what we're doing is we do an eager loading of the class and then once we have eager loaded it then we overwrite the call method and in the call method we simply
1:45:59
return already loaded instance of the class and here is our actual Singleton class you can do the definition in it because that's where you can for example create whatever initialization you would like to do for your Singleton now how does this actually work whenever you run your code even before your code actually executes any lines of your source code it will load all of the class definitions inside the memory so when the singl ton meta is loaded it will load all the classes
1:46:32
that depend on it in the init method this is how you would actually call it exactly the same as before and this is eager loading as you will see later on when we actually run the code here is a few things to keep in mind when implementing the Singleton pattern in python as you've seen in some cases we're using get instance versus as a Constructor Now using get instance might sometimes
1:46:56
be syntactically and architecturally a little bit better because it allows for consistency of usage it provides flexibility of easy modification of the initialization logic it allows for ease of expanding the setup and initialization code and it creates a better flow than a Constructor since the name could be uniform across different class names and implementations and that's quite important because if normally looking at a class you don't know if it's a Singleton but the moment
1:47:22
you see that you have a get instance method that might right away give you a hint that this is actually a Singleton all right so next we're going to look at how to deal with threat safety issues with our Singleton implementation and we'll look at how to use the Singleton in some practical examples all right so I'm going to see you in the next lecture and as always keep on coding welcome back one of the tricky things with writing a Singleton implementation in Python is that we need
1:47:52
to worry about thread safety all of the previous Singleton examples are not inherently thread safe if multiple threads try to create or access our Singleton object at the same time it can lead to race conditions and other issues which could create subtle and hard to debug erinus Behavior you do not always have to create a threat save Singleton but in some cases it is very important so we will explore how to create a threat save version of our Singleton so let's talk quick
1:48:23
a little bit about threat safety as a refresher so basically the idea is when you write your code and you have multiple threads in it for example threat T1 and T2 what happens is you might have in your code something known as critical section now the main task when making something threat safe is to make sure that only a single threat at a time can access the critical section what is a critical
1:48:48
section A critical section is the area of the code where allowing multiple threads to write data can create unpredictable Behavior establishing the part of the code that is a critical section can be tricky but usually involves areas of your code where data is changed so how do we ensure that only a single thread at a time can access the code in this example what you have is you have T1 and T2 both enter the critical section where they can both execute the code that works on the
1:49:19
data and then they both could literally leave the critical section the problem with that is that we simply don't know what the state of the data is going to be because depending on the timing of the running of the two or more threads the outcome could be different that's why this is called a race condition because what you have is you have a number of threads racing to change something
1:49:41
about the data and because we don't control which thread goes there first it becomes unpredictable and actually can create very strange results okay so how do we ensure that only a single thread at a time can access the code we use a lock mechanism so here is a simple visualization of how threat safety operates in a python environment so again we have code we have our critical section and
1:50:04
then what we have is we have a lock so the lock is basically used to protect our code from race condition so we have two threads come in before anyone of those threads can go into the critical section they have to go through a lock and then what happens is that one of them is going to acquire the lock that one could be T1 for example in our example it will hold on to the lock which means that it is the only one allowed inside the critical section but what happens to T2 T2
1:50:36
is put into a special Q which is a waiting que so basically as long as T1 holds on to the lock T2 is going to wait to enter the critical section let's assume for example that at some point another thread shows up T3 and it also wants to execute the code in the critical section but because it has to go through the lock it will also be put inside the waiting queue so now what happens is that eventually T1 is going to be done and it's going to go outside of the critical section and
1:51:08
it's going to relinquish or give back the lock so then what happens is the queuing mechanism is going to take T2 out of the Q and it's going to give it the lock and it's going to put it inside the critical section so as long as we know how to create such a lock we can protect the critical section from any kind of race condition issues okay so let's have a look at an example of very simple thread safe code not a singl ton per se but just a threat save code let's say we are creating
1:51:40
a class called counter and this counter class is going to do a very simple thing it's going to take a piece of its data and it's just going to add one to it we initialize the count itself to zero we also initialize lock to a lock class variable and then what happens is we have a method called increment this method will be our critical section what we do now is before we change our count data
1:52:05
we acquire a lock that's the part that you've seen where the two threads came in and only one of them is guaranteed to acquire the lock we don't necessarily know whether it's going to be T1 or T2 the problem is not basically which thread gets access the problem is just that only one gets AIS that's the important thing so if two threads are going to call this only one of them is going
1:52:27
to acquire the lock and the one that acquires the lock will be able to then execute self. count and add one to it that part is the critical section once it's done with it it will then release the lock when it does that another thread that was waiting could go in so this is simply an example of a thread save code in Python that protects a critical section our a critical section here happens to be just one item of data which is count so now let's have a look at a threat
1:52:58
safe implementation of the Singleton pattern so we'll import threading we'll have a thread save Singleton we'll have a class level variable which stores the instance just like before and also we will have a lock notice that what we're doing here is simply just creating a lock variable we're not locking anything yet now we'll have a new method this is our critical section which is our Constructor and inside the critical section we're going to acquire the lock and once we have
1:53:28
acquired the lock we're then going to check if we have created an instance before and if we haven't then we create the instance and we return it and that's basically it you can at that point either release the lock or simply returning releases the lock and how would you use the thread save Singleton pretty simple just like everything else in this case we're just is using the Constructor
1:53:53
so what is really happening here notice that the lock variable is a class level lock that is used to ensure threat safety the new method is used to create the single instance of the class in a threat safe way because it uses the wi block to acquire the lock before creating the instance and if the instance has already been created the width block is skipped and the existing instance is simply returned if the instance has not been created yet the width block is executed
1:54:21
and the instance is created so this is pretty straightforward simple example of a threat save Singleton all right this is it for this lecture in the next one we're going to look at some practical examples of how to use a Singleton okay then see you in the next lecture and as always keep on coding welcome back let's have a look at examples of how the Singleton can be implemented in Python
1:54:49
in code let's look at this first example which is basically the classic Singleton in the style of gang of four as you can see here we're overriding the init method to control initialization so for example if I run the code as we have right now as you see I am actually invoking the Constructor here if I run this right now you can see that we have an error we have the runtime error which is
1:55:10
basically this error right here so what we have done in this implementation we are overwriting the init method and we basically do not allow instantiation that way so we're controlling external instant entiation of this class by disallowing The Constructor so then what we do instead is we create a get instance method which is a class level method or a static method and what we do there is through the instance variable we control the actual class level instantiation
1:55:39
the underscore instance variable is a class level variable and it's going to store the single class instance in the get instance method what we do is we simply check whether the instance has been initialized and if not we initialize it we create a new instance of the class and we assign it to the instance otherwise if it already has been created before we simply return that particular instance in other words we always create the same instance which is the definition of a Singleton
1:56:08
now let's actually run this as is properly stipulated by the contract here we can call the get instance method class level method to get the actual instances so as you can see here we have S1 and S2 and if this is working correctly then when we print s S1 is S2 we should see true so let's go back here I'm just going to clear that and we're going to run this so what you're seeing here is that the get instance is called twice because that's where we have the print statement
1:56:34
so this is correct and it's true and also it outputs the memory location of the object and you can see that they're exactly the same as it should be so this Singleton implementation works pretty well on the surface but now let's have a look at the other implementation
1:56:54
here we are looking at an example of a simple Singleton implementation we override the new method which allows us to control how the objects are created and the logic is exactly the same as we did in the gang of four get instance method we simply check if we have our instance variable set and if not we set it with a new instance and if it is already set then we just return that instance
1:57:18
so it's really not that much different we are not overwriting the new method to disallow the usage of the Constructor so the way that you would call this is pretty simple you can use the Singleton Constructor so if I run this right now the print out should be true so if I run it right now it tells you that it's creating so basically at each call of the Singleton it goes into the new method which is obviously what is expected but then the most important thing is when we print S1
1:57:44
is S2 it becomes true okay great so this is pretty simple this is probably the simplest singlet tone implementation but let's look at one more thing here before we get into the other implementations let's look at this overwrite all I'm doing here is I'm just overwriting the init method to kind of like show you the specific order I'm going to clear the output here and I'm going to run this one more time and now as you can see that new is called and then in it is called and then
1:58:11
new is called and then in it is called you can see it here in the printout so what basically that tells you is that new creates an instance and then in it initializes it okay great so this is just a quick simp simple little think about the Python language but one thing that would be important and this will become obvious why this is important when we go into the meta class implementation if I remove this code right now right here all we have right now is just the
1:58:35
code for the class Singleton nothing else going to clear this again so now the question would be if I run this code right now is anything going to happen you can pause this and think about it for a second but I'm going to run it and as you can see nothing happens well to a great extent makes sense nothing has happened the only thing that has happened is that the class Singleton has been loaded into memory but nothing has invoked it which is why we don't see any output we don't
1:59:02
see any of the new or the init printouts being called I just want you to remember that for when we go into the meta class let's now have a look at some of the meta class implementations all right so now let's have a look at how we could use a meta class to create a Singleton so what we're doing here is we have two classes we have our Singleton meta which is the base class for our Singleton and notice that what we do here is we use the instances to hold an instance of any
1:59:37
of the single meta children in our case we only have one child which is the Singleton class so inside the instances the actual element the only element that we're going to have in there is going to be a Singleton entry instances is addictionary or like a hashmap where the key is the name of the class in our case it's Singleton and the value if there is one would be the instance we overwrite
2:00:01
only one method inside the Singleton meta and that is the co function so what happens here is that if the Singleton Constructor is invoked the call method is going to be invoked so let's run this and see what we're going to get looking at this what you would expect is that we're going to run Singleton for S1 we're going to run Singleton for S2 and then we're going to print whether S1
2:00:23
is actually S2 because we want to have obviously a Singleton but notice that I put a print statement here which is basically whenever the call function is invoked in our single meta then this print is going to be printed so what we expect here is that when I run this we're going to see two of those so let's run this right now so we do see two of them and we see that the Singleton is working
2:00:46
well that is true this is great because what this allows us to do is it creates a separation between the definition of the Singleton which is inside the Singleton meta and the actual Singleton functionality that we would like to have which would be part of our Singleton class and this is where I've put a very simple little stub for some sort of a business logic which is the definition
2:01:08
that you see there one last thing that I wanted to show you I'm going to clear this is if I remove this right so right now we don't have any actual code running and if I run this nothing happens this is what we expected but in the next example that we're going to see we're going to see how to do eager instantiation all of these are lazy instantiations the only time that you see that the call Meta is being actually invoked is if we actually call The Constructor if we actually ask
2:01:37
for it so this Singleton meta is definitely a lazy instantiation so now let's have a look at an eager instantiation how would we do that all right so let's have a look right now at two different eager Singleton implementations so you see we have two pieces of code right here we have one where we overwrite the init and we overwrite the call in our meta definition and then we have another one
2:02:06
where instead of overwriting the init we overwrite the new and the call and in both cases the actual singlet ton class after that basically just does the same thing it just implements some sort of an init of its own to for example initialize its own attributes so what I would like you to do first is observe the following very interesting things so what I'm going to do is I'm going to go into
2:02:30
the first eager implementation this one right here and what I'm going to do as you can see I have some print statements here I have a print statement right here in the init method of the Singleton meta and I have an init right here in the Singleton so what I'm going to do right now is I'm going to remove this code which is actually the code that drives this and I'm going to run it and based on everything that we've done before which is previously whenever we just run the
2:02:55
class definitions nothing happened Watch What Happens right now as you can see not only did we get the Singleton being initialized but we also get the meta class initialized what is interesting is that we didn't call anything so what happens with meta classes is that if you overwrite the init method it will be invoked when the class is loaded into memory notice that we haven't run any
2:03:19
code here this is the code that we would have run but we haven't run this code now let's go quickly into the other eager implementation which doesn't use in it it actually uses new let's remove this and check whether the same thing happens here and I'm just going to clear this let's run this right now and see whether this version which instead of using in it it uses new whether it will do
2:03:42
the same and again it's exactly the same behavior the only slight difference if you notice is that in this version super is is initialized first and then you see the child being initialized but if I go back into this eager implementation just remove this for a second you can see that this is reversed so this is very interesting the most important thing about this let me just clear this the most important thing about this is that both of these implementations just use
2:04:16
very similar logic to what we've seen in all the other Singleton what is different here is just the tiny cling of the call so notice that for example in this one the if you look at this one for example the first eager implementation what we do is we have our instances just like before and we overwrite the init method and in the init method we set the instance and we put it into the instances dictionary and then in the call we simply return it so just to prove that this
2:04:44
is working I'm going to run it and as you can see I am Singleton I am Singleton it's true and if I go into the second implementation let me just get rid of this and if I go into this implementation the idea is very much the same instead of in it we are overriding new basically getting exactly the same thing which is we initialize this instance before the class is ever actually constructed in other words eager loading and if I run this we get exactly the same as we had before the
2:05:14
only difference is just the order of the actual eager loading initialization okay so this is it for eager implementation next we're going to look at thread safety with our singl ton all right so let's have a look at how we would Implement threat safety in our Singleton what we have here is a very very simple implementation this is not a meta class-based implementation all that we're doing
2:05:45
here is we're taking the simple implementation of our Singleton and we're making a threat safe and the way you make a threat save is you have to identify the critical section and in our case the critical section is when we're actually creating the instance which would be here in the new method we have an instance that has nothing we create a lock which is a Threading lock and then what we
2:06:06
do is we acquire the lock with the with block and once we have acquired the lock we basically check okay if we don't have an instance we created it but otherwise if we do then we simply return it and that's basically it and now if we run it of course it's going to be exactly the same as before this is still a Singleton the only difference is that this is a threat save Singleton okay so what
2:06:30
about doing it in a meta class well let's have a look at a meta class implementation again the idea is very much the same we have our instances we have our lock and then in this case we're going to get the actual instance for the Singleton in our call method wherever we are actually instantiating the Singleton inside the class that our critical section so what we're going to do here is we're
2:06:55
acquiring the lock and then we do basically the same thing if we don't have an instance we create it and we add it into the instances dictionary but if we have the instance we simply return it and that's basically it so in this example we have a little helper function which is get Singleton instance it constructs a Singleton returns that Singleton and also prints the Singleton's memory
2:07:18
location next we have an array of threads and we create 10 thread objects and we add them into that array we append them into the array at this point the threads are not running they're simply ready they have been created next we're going to run them we walk through the array and we start all the threads and then in this particular piece of code what we're doing is we're creating a barrier which is basically a piece of code that's stops and waits for all the threads to finish in
2:07:49
other words it will not go past l 39 if we had more lines here like line 40 50 60 it would stop here and wait for all the threads to finish and if I and if I run this right now what you would expect to see is the print statement being invoked 10 times and here it is now what you could do is you could go into the Singleton class where right now we just have a pass which basically means that
2:08:13
this is an empty class and you could add something like for example printing time or something like that I'll leave that as a little exercise for you to do okay so I'm going to see you in the next lecture when we're going to look at some practical examples of a Singleton pattern in action because we're going to create a logger okay so I'll see you in the next lecture and as always keep on coding all right welcome back in this lecture we will create a utility logger and we'll make
2:08:44
it into a thread save Singleton we'll go through a few iterations so that you can see the process of refining and refactoring the codee so in this first iteration we'll just have a very simple logger note the following points we are creating a named logger which we simply name my logger we do this so that we can tell the source of the logging for example in a different session we could name the logger something else to differentiate the output from a different
2:09:14
session we also set the logger level to debug next we set up the lock data handlers of our log think of these as destinations so we create two destinations or two handlers first we create a file Handler which will have as its destination my log file this will be logged in the current code directory we will also set the level of this destination to debug next we also create a console Handler which will write log data to the console which is right here in our Visual Studio terminal
2:09:51
output and we'll set the level of this Handler to info we then Define the format of our long entries we set the timestamp and the name which is the name of our logger which is going to give us the source then we set the level with which the entry is logged and finally the actual log message so the format of our full log entry is going to be time stamp name level and the message we then add
2:10:19
this formatter to both Des destination so that both the file and the console handlers use this formatter so basically both of them will output the same looking log data and finally we add these handlers to the logger itself okay great so this way we have finished our logger as you can see the code for running the logger is pretty straightforward now let's run it and see what we get and here you see the out output so first you see the output here in the console it's pretty
2:10:55
straightforward we have our messages and we have the levels you can see the format and here you can see the same output in our file well actually if you look at the output carefully you can see that in the file we have an extra message which is the debug message and the reason why we have a slightly different output in the file versus the console is because that that's exactly how we have configured our logger so if you go back and you look at the code you will see that when
2:11:28
we are creating the file Handler we're setting the logging to be debug which basically means that this will log everything that is debug or above but for the console Handler we're specifying that the level is info which means that it's only going to log things that are info and above and since debug is below info that's why you don't see the debug level showing up up in the console so this is pretty great but there are a few issues with this code and in general with
2:11:57
this approach first of all this is not very object oriented in other words this is not encapsulated and thus it is not really reusable so to fix this issue will do a couple things first we'll create a Singleton version of this loger and we'll also encapsulate the initialization of the logger into a function will also make it threat safe to be absolutely sure that the single is reusable in all circumstances even those involving multiple threads okay so let's have a look at our new
2:12:28
version of the logger so as you can see we have a Singleton here now this is not a meta class we're starting with a very simple version so what we have here is we have our instance and in the get instance method is where we ensure that only a single instance exists we also make this Singleton thread safe by using the lock and then ensuring that our critical section is properly protected
2:12:54
we also have an initialized logger routine where we encapsulate the logger creation logic and this is the exact same logic as in our previous class what we have done here is simply refactored it into this reusable method note that our logger is simply stored as an instance variable which we named logger and this is it now to get our logger instance we go to the Singleton logger class and invoke the Singleton get instance method which returns our instance and then we just access
2:13:24
the logger instance variable and we use it so as you can see this is definitely better than before but this is still not very elegant and as you see when we run it it runs exactly the same as before it writes to the console and it added some more entries into our file as you can see from the different timestamp but there are a few issues here first of all I do not like this Singleton logger doget instance. logger syntax one simple solution would be to Simply have the Singleton
2:13:55
logger get instance return just the logger we'll fix that in the next iteration of our code so next what we'll do is we'll refactor this code into a meta class based Singleton logger all right so let's have a look at a third iteration of our logger so this is like the third version now the main difference here is that we're now using a meta class but otherwise the code is pretty much the same as in the previous class that we have just seen the main differences are as follows
2:14:28
we now have a Singleton Meta Meta class in which we Define the Singleton logic as we have seen in the previous lecture of course we also make it threat save once we do that we Define the logger Singleton class which is in our case just the logger class and we set its meta Class Type to be the Singleton meta which was defined just above this is exactly what we did in the previous lecture so what we do now here is we simply add the initialization code for the wrapped logger
2:15:00
class in the initialized logger method but note that we have made one simple Improvement in the syntax rather than providing a logger variable to get the logger instance like we did in the previous example here we return the actual logger instance through the get logger method so basically we have our initialization just like we did before we have our initialized logger method and then rather than providing a logger variable to get the logger instance like we did in the
2:15:30
previous example here we return the actual logger instance through the get logger method so now when you look at how we actually use the code the code is just a little bit more elegant now what we do is we simply say logger is equal to we get the logger instance and then using the get logger method we actually get the proper logger instance and then we use it exactly the same as before and
2:15:55
if we run it right now as you see it works exactly the same way as before and if we go into our log file you can see that a new set of messages has been added but I am still not happy with this implementation for two reasons I still do not like the fact that we need to get the logger through a getter I don't like this logger Dot and then get logger method and also I would like to make it
2:16:22
a little bit more reusable by using some sort of a logger abstraction in other words I would like to use an abstract class to Define some sort of a base logger okay so let's have a look at that in the next and final iteration of our logger let me just get rid of this okay so let's have a look now at the last one so here is the final version of our logger Adventure this is our our final version
2:16:52
of the logger class so we have made the following improvements we create a new version of The Meta class which is based on the ABC meta class which is basically an abstract Base Class we do this so that we can create a nice clean abstract class based on this meta class so apart from that the Singleton meta is exactly the same as it was before it is just a straightforward meta class which defines the Singleton logic just like before but what we do here that is new is we
2:17:27
now create a base logger class which as its meta type has this Singleton meta class this is a bit of a trick but basically we have just made the base logger an abstract class and you can see that we Define five abstract methods which will be used for our logger so let's have a look at the logger class so now what we do is we Define find the actual implementation or the proper my logger class which does the following first of all it wraps around the logger instance which in our
2:18:00
case is the underscore logger variable and what we do is we expose the inner underscore logger functionality only through the logging methods that are defined in the base logger class and as you can see right here we have the five logger methods implemented so basically what we do is we wrap around the underscore logger instance and then we delegate through our own methods which is the debug info warning error and critical methods we then delegate to the inner logger
2:18:37
so this is a pretty nice simple implementation now when you actually look at the syntax that we use you can see that the syntax is pretty nice all we do is we just say logger is equal to my logger and and then we just use it like any other logger before we don't have to call any extra methods so basically what we have now is a Singleton that is based on an abstract class and the final class which is my logger follows the Base Class contract so basically we're following some good
2:19:08
software engineering paradigms here the nice thing about this contract for our base logger is that if I remove any of the implementations for example let's say I remove this if I try to run it right now watch what happens you can see that it tells us that the abstract my logger class is missing a method so if I put that method Back The Interpreter is smart enough to know that I have now follow the contract so if I run this right now let me just clear this if I run
2:19:43
this right now everything is working exactly as it's supposed to be basically mission accomplished what we have here is a nice threat safe Singleton which is based on an abstract contract and if you go into the log file you can see that now there are extra lines in our file all right so this is everything for this lecture please go through it one more time just to make sure that you understand everything this is a pretty powerful design pattern next what we'll be looking at is
2:20:14
the factory method pattern all right I'll see you in the next lecture and as always keep on coding
2:20:22
all right welcome back let's talk about some exercises that you can do that will solidify your understanding of the Singleton pattern all right so exercise number one I would like you to create a Singleton implementation which will generate a sequence of numbers to the colors the idea here is that there is only a single number or sequence generator so a single generator and all the numbers follow a perfect sequence so when we do a call to the
2:20:52
generator using something like maybe a get next number method we should get the next number in the sequence no matter how we obtain the generator so basically this way it is kind of like a global generator then the next point would be to make that implementation both with eager instantiation and with lazy instantiation and you know ask yourself this question which one would you prefer to use and why which one of them is just a little more efficient or better
2:21:23
exercise number two I would like you to make the Singleton that you created in exercise number one thread Save Make It thread save consider also the following issues how would you test it and how do you know it is properly thread save okay let's look at exercise number three create a singl ton implementation which will write to a common file common meaning share imagine that you wanted to
2:21:49
audit all the actions in your application so it's kind of like a log it would make sense to log that information into a file with a guarantee that all callers all clients will write to the same file and we also want to make the file configurable as to where is the file for each call to the file audit manager your code will generate a timestamp and will write the message entry into the file with a new line character at the end of each message so it becomes to some extent human
2:22:20
readable make that implementation both with eager instantiation and with lazy instantiation and then again which one would you prefer to use and why also make your implementation thread safe all righty then good luck with these exercises and if you have any questions please do not hesitate to ask in the forums as always keep on coding and I'm going to see you in the next lecture welcome back let's look into our second design pattern the factory method pattern normally we create
2:22:55
objects using Constructors this is how you would create a rectangle by simply calling the rectangle Constructor this is what normal object-oriented programming languages do but the question is is this a good thing the answer is no in fact this approach has a major architectural disadvantage you have to know the exact Class Type you want to instantiate why is this bad when you learn programming languages nobody tells you hey don't use Constructors you use Constructors what if we
2:23:24
wanted to create an app that use different types of shapes what if the app was going to use shapes that we have not even thought of yet how is that relevant let's look at a more specific scenario assume that you're creating a space shooter game think of maybe like an Asteroids game or maybe Space Invaders or something like that in this game you will have different types of bullets that the user could use but you do not not know which bullets the user will want to use so how do you
2:23:54
create them Additionally you might in the future have new bullet types so you might have types that you haven't even thought of yet typically this is how you would instantiate your bullets let's say you have a fast bullet so you create a fast bullet using a fast bullet Constructor the same thing goes maybe for a slow bullet maybe you have some sort of a splash bullet as you can see nothing
2:24:14
wrong here we're just using different Constructors and of course we might have some sort of a bullet type in the future we don't even know what it is yet maybe our users will say hey it would be nice to have this type of a bullet and you'll say you know what I will add it to the game great so what's the problem the main problem with this approach is that your code is unnecessarily aware
2:24:36
of the type of objects that it creates and thus it is strongly coupled with the type so now let's look at the factory method the factory method is a creational design pattern that lets us abstract out the cre cretion logic of specific class instances so it provides a mechanism for creation of objects without exposing the instantiation logic to the client in our case the instantiation
2:25:02
logic is using the specific Constructor there are two main and crucial points in understanding this pattern objects are created by calling a factory method instead of calling a Constructor also objects are created through an abstraction not a concretion so in the factory method design pattern we we create objects without exposing the creation logic to the Coler and the Coler refers to the
2:25:26
newly created object through a common interface going back to our bullet if we make the bullet an abstract class then we can make it to be the parent class of all bullets here's our bullet class here is our fast bullet here's our slow bullet and here's our Splash bullet each one of those is a bullet so how does this help us I mean okay sounds great we're using some polymorphism here well we can now create bullet instances based only on the bullet interface contract instead of
2:25:56
the specific bullet definition we then use the factory method design pattern as follows here is our bullet Factory and it creates a bullet it's pretty much that simple at least the essence of the pattern is that you always return the contract interface so the idea is pretty straightforward in code the caller will ask for a bullet instance based on some abstract name for example I could say my bullet 001 is equal to Bullet factory. create some bullet based on some sort of an ID
2:26:29
so as you can see we have two different bullets and one creates a fast bullet the other one creates a splash bullet the nice thing is that at this point we don't have to worry about how the bullet is created that's hidden in the bullet Factory the main advantage is that the caller does not need to know how the factory creates the bullet the caller only needs to know how to call the factory and with what initialization data we could even make this more generic by using some
2:26:54
type of context data to initialize the instance for example we could create some sort of a bullet build context which has some initialization data and then we simply pass that context to the bullet Factory one example advantage of using a factory method pattern is that we could cache objects in the factory for added efficiency here is the factory used without a cache and here is the same Factory but with a cache that is hidden behind the factory call so the caller
2:27:23
does not even know about this implementation we could simply cash all the bullets so that your code could be much more efficient the beauty of this is that the caller that uses bullet Factory has no idea that there's bullet cache so where does the factory method pattern come from this is yet another famous gang of four pattern and its motivation is stated as follows Define an interface for creating an object but let subclasses decide which class to instantiate
2:27:52
factory method lets a class defer instantiation to the sub classes this pattern is often used with the following gang of four patterns strategy and iterator patterns are often used with Factory method pattern to let collection subclasses return different types of iterators that are compatible with the collections in this lecture we're going to cover the strategy pattern but you definitely can look up the iterator pattern Factory method pattern can also be used in the object pool
2:28:22
pattern when creating cached objects of different subtypes as you seen with the previous slide where we've looked at a possibility of having a cache sitting behind the factory so when do you use it when a caller can't anticipate the types of object it must create let's say you're coding a mobile game the user can choose any weapon or a spaceship but you do not know which one they will use another example is new types of spaceships or weapons can be added to the game in the future
2:28:50
without any changes to the client code also when you have many objects of a common type like for example a shape with subclasses such as rectangle circle triangle it's a perfect opening for a factory method pattern when not to use there is no specific restrictions as to when not to use it what are the pros of using Factory method pattern it allows subclasses to choose the type of objects
2:29:14
to create it's simple to implement it is simpler than Builder pattern which we're going to cover in the next design pattern we're going to go over it also promotes loose coupling by eliminating the need to bind application specific classes into the code that means that the code interacts only with the resultant interface or abstract class it definitely adheres to the single responsibility
2:29:38
principle you can move the creation code into one place making the code much easier to support also it follows the open close principle you can introduce new subtypes into the program without breaking existing client code this results in architecturally very clean code what are the cons one disadvantage of the factory method pattern is that it can expand the total number of classes in a system every concrete class also requires a concrete Creator class but please do note
2:30:09
that the parameterized factory method avoids this downside let's look at the design considerations when you have many objects of a common type like for example a shape make all such object types like for example I shape follow the same interface or extend the same abstract class this interface should then declare methods that make sense in every shape but in a generalized way for example
2:30:35
for a shape we might have a method called Draw or calculate area then create a factory with a static creation method which always returns the same common interface reference for example like an ey shape or like what we had with the bullet also let the factory know which instance you want through some sort of a constant ID for the specific object type this can be accomplished through maybe an enum or a list of constants which could be integers or strings there are two main variants
2:31:05
of this pattern the very popular simple Factory method variant which is also called parameterized Factory method pattern we're going to mostly cover the simplified variant here as it is more useful for the majority of projects but we we will briefly mention the classic variant as well the classic or the original gang of four Factory method variant we will have a look at that one as
2:31:27
well but we're not going to cover that extensively all right do not hesitate to ask any questions and in the next lecture we're going to go through the uml diagrams and the implementation details for how does one go about using the factory method pattern in real code see you then welcome back let's have a look at the architectural diagram of the factory method pattern as you remember from a previous lecture there are two main variants of the pattern we have the classic gang of
2:32:00
four Factory method variant and we have the very popular simple Factory method variant also called parameterized Factory method pattern let's look first at the classic gang of four Factory method variant we start with an interface that stands for some sort of an instance of a class that we would like to abstract this could for example be shape this could be product and it has an operation
2:32:28
here we have two concrete implementations of the instance interface which we call concrete instance a and concrete instance B notice that both of them have defined the operation which was defined in the instance interface what we then have is we create a contract for a factory which is going to create instances of our instance interface for example we would create a concrete Factory a implementation of the factory which would specifically create the concrete instance a
2:33:04
objects and we also create a concrete Factory B implementation of our Factory which would specifically create objects of the type concrete instance B for every concrete implementation of an interface we have a concrete implementation of a factory method that will create that so for example instead of instance if we had shape and our concrete implementations were triangle and rectangle then our shape Factory would have two implementations of the factory interface
2:33:40
which would be triangle Factory and rectangle Factory that's the idea of the original gang of four Factory method variant for every concrete instance of the implementation of our interface we would have a concrete Factory implementation this is how you would use it in code we would like to get a concrete instance a so what we would do instance a is we would use the concrete Factory a we would instantiate it and then we would call the method create instance which would
2:34:12
return to us the concrete instance a through the instance interface if we wanted to get concrete instance B then we would have have to call the concrete Factory B to create that instance let's say that our client would like to get an object of the type concrete instance a what the client would do is it would call the concrete Factory a create instance method and that concrete Factory a would then hide the implementation of how it actually creates the concrete instance a and the
2:34:44
same thing would work for any other instance how is this different from the simple Factory method variant which we also call parameterized Factory method pattern there's actually a huge difference we start with the same idea that we have some object that we would like to create we abstract this object through an interface in our case we have instance with one operation and we have two
2:35:07
concrete implementations concrete instance a and concrete instance B the main difference though is that in the simple Factory method variant we do not have an interface for our Factory we have an instance Factory we have One factory for all the different instances that we would like to create notice also that we have a private Constructor so you cannot instantiate this Factory and we
2:35:29
have a static create instance method which takes a parameter that parameter could be anything really but in this example it's going to be the name of the class that we would like to instantiate here's a uml diagram our client would like to create a concrete instance a implementation so what it does is it calls the instance Factory static method create instance it passes a parameter which in this case is a name instance a the instance Factory has a switch statement behind
2:36:02
the scenes where it basically matches the name against how it should create the instance for you in this simple example we just have a simple switch and if the case happens to be instance a then it creates specifically a concrete instance a object if you pass instance B B then you would get a concrete instance B object the idea is basically that as a client I don't care how the object is created I would just like to get the specific object I'm interested in and the way you would
2:36:31
do it in code is pretty straightforward if you wanted to get instance a you call instance Factory create instance with the parameter the instance a if you wanted to create instance B implementation you would call instance Factory with the parameter instance B so the nice thing about this as compared to the original one is that we only need one Factory and we call the factories method with some sort of parameters which allows us to even expand this as you will see later on
2:37:01
in code so that we can actually provide some sort of a context and provide more information to the specific class in this course we're going to use the simplified variant which is this one as it is more useful for the majority of projects all right so this is everything for this lecture in the next lecture we're going to be looking at some code for the simple Factory method see you then welcome back in this lecture we will be looking at some code of factory method pattern in
2:37:33
action so let's run this code first as you can see this is a very simple game likee implementation of displaying different shapes when the user clicks on the screen the shapes are randomly generated as follows we gener at the random shape type we currently have rectangles and circles and then we also generate a random color for the shape as well as the signs okay so let's look at the uml for the first iteration of our code first we have an abstract shape class which has two
2:38:09
instance variables X and Y which are the location of the shape in the coordinate system we will use this when we display the shape this will be the location of the user's Mouse click okay so what makes this class abstract it will be the draw method which will be implemented by each shape child with a specific shape rendering so that for example the rectangle will draw itself as a rectangle and a circle for example as a circle we also have these two specific implementations we
2:38:37
have a rectangle and we have a circle we also have a shape Factory which will isolate any client code from the specific knowledge of how to create a given shape the whole point of having a factory is to abstract the object creation process note that the factory returns just a shape contract okay so now let's have a look at the code itself so we have our shape abstract class with the abstract
2:39:04
draw method we also have two derived classes we have a circle and we have a rectangle note how each class implements the draw method that is specific to its own shape some of the important things to know are the following each shape is responsible for its own random data generation we have random color generation and we have random size generation and this is of course according to each shape so a circle will generate the radius and rectangle will generate the width and height
2:39:35
so we're delegating the random generation of data that is specific to the shape to the shape itself next we have the shape Factory this is a parameterized factory in other words this is the simple version and what it does is pretty simple it accepts a shape type which is a string name such as Circle or rectangle and it also accepts the location of the shape it then Returns the specific shape back to the color this is very flexible since it allows us to not only create
2:40:04
the shapes easily it also allows us to place any pre- and post conditions on the creation process for example we could not allow any shapes that have been created of the screen that's just one example and notice that we also have an exception if there there was an invalid input to our Factory such as for example an illegal shape so one can say that this fully creates the factory method pattern now let's have a look at what the code actually does so in the main what we're doing
2:40:36
is first we initialize the P game environment we set the display size to be 800x 600 pixels which defines our output window then we create a clock variable which will be used in our game Loop to generate the update ticks with a specific frequency you will see that in a second next we create our shape Factory as an instance for E of use even though we could directly use it because it's a static method that we're using we also have an array or list if you will of all our
2:41:06
shapes that were so far generated in this session we need this so that we can render all the shapes at each game Loop iteration we also have a simple flag which controls if the game Loop is wrong or if we're done the main engine of this particular little code is a game Loop a game Loop is just an infinite Loop that quickly iterates over specific code this is done for two reasons first of all we
2:41:34
can quickly render any changes to our data since at each iteration of the loop we reender all the screen data and also we can listen for any data changes in real time with each Loop iteration and in our case we listen for the user Mouse click so first we process any events that are happening in our environment and we really only care about a mouse click or the quit event our main interest of
2:41:59
course is in the mouse click event we specifically wait for a mouse down event when that happens we get the position of the click we then randomly generate the shape type and we ask the factory to create the instance of that specific shape with that data once the shape has been generated we add it to the list of shapes we have so far generated we then clear the screen and this is a preparation
2:42:24
for a redraw or a render of the entire P game window screen what you see here is basically a white collar that we will repaint the screen with then we go through each shape in our shape list and we call the draw method for each shape while passing to the shape the screen variable which represents the drawing surface notice that at this point the draw method hasn't really drawn anything
2:42:49
it has actually just created that in a buffer we then Force the screen update with the flip method finally we set the refresh rate to be at most 60 frames per second and we do it for a number of reasons first of all we do this to control how smooth the refresh rate is by basically controlling the loops iteration frequency and basically by calling the clock. tick 60 at the end of each iteration of the game Loop we ensure that the loop runs at a consistent 60 frames per
2:43:20
second let's run the code again this time with the explanation in mind so the loop is running right now rendering the surface at 60 frames per second so what you see right now even though it doesn't look like anything is happening you have the game Loop redrawing the whole window with white colar at 60 frames per second it also checks if there is any shapes already drawn which would be saved in
2:43:48
our list and if yes it draws each shape into the buffer if I click on it right now this shape has just been added to the buffer and it was rendered and it's being rendered right now 60 times per second in the same Loop the mouse down event is being monitored and if the user clicks on the screen then the shape is randomly generated and added to the list of shapes at the next iteration of the loop it will draw this screen white and then redraw all sh shes including the one just
2:44:20
generated so as you see I'm clicking a few times it gets saved it is rendered at the same time the loop is checking whether the user is clicking and this is just constantly happening okay now back to the factory method pattern how good is the code that we have if you examine this code and the uml you will notice that the shape factor is not very flexible have a look at the create shape method it is literally baked in when it comes to its parameters if at any point we wanted to
2:44:54
change something about the parameters we would have to change every single client call in our code so now there is a better way what we can do is we can use a context class which will simply aggregate all the data necessary to create the given shape okay so let's look at the improved uml diagram the change is relatively simple what we do is we add a new class shape context and we will use this class as the input to the create shape method so in other words we're encapsulating
2:45:24
the input into one parameter in general this is a better way of passing data to a factory method as it allows also for more flexibility with required versus optional variables for example this is really a small change but it adds flexibility to our architecture and the rest of the code stays the same so let's have a look at the change and as you can see here is our shape context class and here is the shape Factory which now uses the context information and if we run it right now
2:45:55
you can see that the code runs exactly the same as it should what we have done is we have refactored the code to make it more architecturally flexible okay so this is really it for the factory method pattern but before we go into the next design pattern I would like you to think about the following aspects of our code first of all can we move the color from each of the shapes implementations which is the circle and the rectangle and move it into the base shape
2:46:24
class number two could we improve the way each class generates the random data and number three notice that we use just a normal string when we're comparing the shape type in our Factory you can see that we're basically just comparing it to a circle string and a rectangle string that's pretty bad actually that's not a good way of coding first of all not only could you have a lot of typos
2:46:48
but you also have to keep on chasing where your constants are so what I'll do is I'll leave those for you to think about and experiment with but I will make one final code change we're going to use an enumeration in other words we're going to fix that third problem which is just using a normal string so let's have a look at the updated piece of code so here's the code and first of all notice
2:47:12
that we' have added an enumeration and if you want to use enumerations in Python you will need to import the inm an auto from the enum library now what we're doing basically is we Define two types that we need right now we can easily add new ones later on and this is very safe to use as you eliminate any kind of typos or spelling mistakes note also how the shape Factory uses the enum it's
2:47:37
much nicer and cleaner code but the main advantage is how we generate the random shape note that we now have future proofed Our Generation code so before we basically used this so let's have a look at the code before so before you can see that to generate the shape we did this shape type is equal to random Choice and then we just create an array or a list of currently known types but if you look at the code in number three you can see that in the improved code we're basically just
2:48:12
creating a list of shape types and this is very nice because this code will not change no matter how many new shapes you create you can see that now our shape type is just a random Choice from a list that already pulls the data and it's also pretty elegant here is the updated uml where you can see the enumeration being used and please do the exercises provided next and then I'll see you in the Builder design pattern introduction see you then and as always keep on coding welcome
2:48:43
back here are some exercises for you and these are for the factory method pattern these are coding exercises so exercise number one is create an abstract class called spaceship which will simulate a spaceship for a video game you can assume such simple properties as position size display name and speed position would obviously be the location of the spaceship on the screen so this would be an XY value size can also be expressed in terms of XY or width and height
2:49:18
display plane name would be simply the name of the spaceship and speed you could give it as a scaler like for example 10 but usually this is given as a vector which would again be as an X and Y next thing you will do is you will create a number of concrete classes which are going to extend spaceship we'll have a Millennium Falcon we're going to have a UNC Infinity you will have USS Enterprise and serenity the Millennium Falcon is Star Wars USC Infinity is from the video
2:49:48
game Halo USS Enterprise is Star Trek and serenity would be from Firefly using the simple Factory method create a factory implementation that will create each one of those instances and simple Factory method pattern simply means you don't have to do a lot of abstractions now for exercise number two you can try to implement the exact Factory from exercise number one so exactly the same functionality but this time using the classic Factory method pattern this basically
2:50:18
entails that you need to create a specific Factory for each of the concrete spaceship class types so for example when you need to create USS Enterprise you will in fact delegate to a USS Enterprise Factory this means that the creation will need to be done in some sort of a switch to sort out which Factory to use okay so this is basically it for these exercises give them a try and if you have any questions do not hesitate to ask and as always keep on coding welcome
2:50:47
back let's talk talk about the Builder design pattern it's a very important design pattern to put Into Your Arsenal of architectural design patterns Builder is a creational design pattern that is used to encapsulate reusable logic of building complex objects what do we mean by complex objects those are objects that are made up of many distinct Parts those are objects with a well-defined relationship between those distinct parts so it's not just a bag of loose
2:51:19
related objects normally we have some sort of a good design that creates a complex object the main tenants of this pattern are you want to separate the construction of a complex object from its representation and you want to design it in a manner so that the same construction process can create different representations this is very important as we're going to see in the next slide consider the following scenario imagine that you're coding the process of building a house
2:51:51
for a video game with the following attributes we have walls we have Windows we have floors we have a garage we have a swimming pool we have doors backyard a garden basement and maybe other things as well this is obviously a complex object that is made up of many other objects so one can think of this as having an object hierarchy one way to design this object is to create a house class
2:52:21
with a very long and complex Constructor something like house and you see all those parameters so for example we could look at that as we would like to build a house with 10 walls 10 Windows two floors one garage no swimming pool Five Doors no backyard no garden and no basement the problem with this is that a code like this would be ugly and quickly would become unreadable it is very flexible and
2:52:49
most importantly it's difficult to maintain the Crux of the problem the reason why we have this problem here is that the object the house creates itself we have the object representation which is the house itself and then we have the object creation the problem here is that house also has the responsibility of creating itself through the Constructor so what should we do we want to extract the object construction away from the class representation in other words we do not
2:53:24
let the object create itself to the user we want to abstract the creation process so how do we do it we use the Builder pattern it hides the process of building complex objects a builder is an object that is able to construct other objects it constructs and assembles different object parts to build the result objects but the Builder pattern is much more than that the Builder pattern is not just about encapsulation and hiding of the creation process it is also
2:53:57
designed for reusability and flexibility a builder organizes object construction into a set of processing Steps step one for example could be building the walls step two could be building a pool step three or any other step would be something else and finally we might have the end step which would be build a ro Pro to create an object the Builder is instructed to execute a series of these steps this is what is at the heart of the Builder pattern but do
2:54:28
we have to always call all those steps the answer is no the strength of the pattern lies in that we don't need to call all of the steps we can design the Builder to only call those steps that are necessary to produce a particular configuration of the object this is great flexibility
2:54:49
consider another simple scenario imagine that you have a document for example a rich text format document on your hard drive to view it it has to be loaded and then it has to be constructed we can imagine the following workflow you have the RTF document the document then is read by a reader which creates a number of tokens these tokens then are passed onto a parser a parser then parses the
2:55:15
tokens into elements the elements then go into a builder and a builder can create a Google Document it can create a Word document it can create an HTML document or it can create a PDF that's the beauty of this workflow here is a bit of a pseudo code for it for each element that we parse we can switch on the element type for example if the type is a title then we have the Builder create
2:55:42
the title if the type is a page we have to Builder create a page if the type is author we have the Builder create the author the idea again here is that the Builder executes a number of distinct steps in order to create the larger object which is the document in this case but the Builder gets even better we can create variants of the builders to produce slightly different version of those
2:56:08
steps in fact the previous example needs to be expanded this is what we had before but what we really have is this we start with the RTF document we go into reader we then generate the tokens then the parser creates the elements and then we have a special step or a special controller called the director what the director then does depending on the type of document that we would like to view
2:56:32
it as let's say if it's PDF or HTML the director then is going to delegate it to a specific Builder so we might have a Google doc Builder which is going to create a Google doc we might have a Word document Builder which is going to create a Word document we might have an HTML Builder which is going to create an HTML document and we might have a PDF Builder which is going to generate a PDF document so you can see that the idea of the Builder pattern is that we have some sort of a
2:57:00
director that then delegates to the specific type of Builder that we would like to use notice the Synergy all of these Builders will follow the same steps because they are all creating documents but obviously the construction of the documents is a little different for each document this is an important gang of four pattern and its motivation is stated as follows separate the construction of a complex object from its representation so that the same construction process can create
2:57:29
different representations that is exactly what we have seen in the previous example the pattern is often used with the following gang of four patterns it's used with the composite pattern when you're creating tree likee aggregations of objects and often times you will Implement your Builder pattern as a Singleton when do you use it when you have a complex class with many Constructors because the Builder pattern lets you build your objects in a step-by-step manner you
2:57:56
can skip some steps if they are not needed also when you have to build complex composite tree like objects and also when you want to build different representation of your complex object but still use the same general steps that's exactly what you have seen in the example with the document when not to use there is no need to use this pattern for simple objects at that point you can just use the factory method for those what are the pros first of all it provides clear separation
2:58:26
between the construction and representation of an object it provides finer control over the steps of the construction process and it supports changing the internal representation of objects what are the cons a distinct concrete Builder must be created for each type of object so it increases the number of classes that you have Builder classes must also be mutable and it may complicate dependency injection if you happen to dynamically generate certain elements of your
2:58:57
code what are the design considerations you should encapsulate creating and assembling the parts of a complex object in a separate Builder object create a specific class a director which will delegate object creation to a builder object instead of creating the objects directly look for Synergy in the family of the objects that your model you should be able to use the same construction
2:59:20
process by delegating to different Builder objects to create different representations of a complex object think of different types of houses or documents or pizza orders or for example cars all of these can slightly be different but the construction will still follow the same steps next we're going to look at the architecture of the Builder pattern see you then welcome back now that we understand the need and motivation for the Builder pattern let's go over its architecture
2:59:50
in detail imagine that you have a product you would like to use the Builder pattern to create the product so you have an abstract product class and let's assume that you have two implementations of the product product 001 and product 002 the next thing that you're going to do is you're going to create a builder abstraction which is going to build a product it will have a method called for example get result which is going to to be the final build of what the product
3:00:21
is and you're going to have the necessary steps to build any given product in this case we have built part A Part B part C it might vary from product to product so some of the products might actually not include one of the methods for example build part A could be empty but the important thing is that you abstract the Builder interface the next thing that we do is we create concrete Builders so in my
3:00:48
have a concrete Builder 001 and we will have a concrete Builder 002 notice that each Builder creates the specific product but it returns in the get result an actual product abstraction this is important so now that we have this what is the next step we need a director notice that the director Aggregates the Builder and there are two ways in which we can initialize the director with the builder we can either initialize the director specifically when we're creating the director with
3:01:17
with a given Builder or once we have created the director we can use the change Builder method to move from one Builder to another and notice that we have a method for the director called construct which given whatever the current Builder is Will construct a product based on that Builder so basically the way internally director would do it is the director would simply go through all the different parts so it will called built part A Part B part C and then that would return
3:01:47
the result this is a pretty straightforward way of looking at the Builder pattern now let's have a look at the sequence diagram the sequence diagram would look as follows we have a client we have a director and we have a concrete Builder what the client does is it will first create the concrete Builder it will then assign that Builder to the director by using the change Builder method and
3:02:10
then it will simply call the director's construct method to create the specific product the director simply calls build part a build Part B build part C and then it Returns the full result which is the product back to the client note a few things the client is aware of what type of product they want so they initialize the director with a specific Builder product which is completely fine the director calls all the specific methods of the Builder in a predefined sequence but it does
3:02:45
not know anything about the product it's simply just calls the methods one after the other if you wanted special logic per product then you would need to code this knowledge into the director or somehow code it into the Builder notice that all the builders have exactly the same method build part A Part B part C if for some reason a builder doesn't need one of the parts then all you need to do is just Implement that as an empty Method All right so now let's look how we would model
3:03:16
the following using the Builder design pattern you remember that from the previous lecture let's see how we would model the director and the Builder part using uml so as before we have a document abstraction next we Define the document builder in our case the document Builder is going to have a create title create page create author Etc and then we also have a get result method which Returns the full document next what we do is we Define the different builders that we want for the
3:03:50
types of document so if we have a document that is a Google doc we'll create a Google doc Builder if we need an HTML document builder then we'll create that as well and ultimately in the end the document director is pretty straightforward just like what we've seen in the previous diagram the document directory is going to aggregate the document Builder you can change which Builder you're using depending on the type of document you're trying to create and then all you do in
3:04:16
this case is called the get document method which invokes all the necessary methods to create a particular document and returns that document to you so hopefully this is a little clearer right now I know that the previous lecture was pretty fast and pretty compact so hopefully this architectural overview has given you a better idea of what to do here all right then next what we're going to do is we're going to actually look at how to code the Builder pattern in Python
3:04:48
see you then welcome back let's have a look at a relatively simple Builder design pattern code so let's have a look at the uml diagram first a developer's favorite which is pizza so what we're doing basically is just have a very simple builder for different types of pizzas so let's have a look at first of all our pizza object or our pizza class and as you see that the pizza basically just holds things like toppings price name size as well as crust and sauce and you can see
3:05:25
we have enumerations for those like for example we have pizza sauce as tomato garlic hot or mild pizza crust could be classic deep dish and you also have pizza sizes in small medium large so that's pretty straightforward so what we have is we have an object which is a pizza then what we have is we have an abstraction of the pizza Builder and what a pizza Builder does is it creates a pizza notice that we have a number of concrete methods like create Pizza get pizza
3:05:52
set Pizza price and set size and add notes but we also have three abstract methods build sauce build topics and build crust that's really what builds a different type of pizza we have two concrete Builders we have a New York Pizza Builder and we have a Hawaiian pizza Builder and notice that each one of those implements the three methods the build sauce build toppings and build crust okay so that's pretty straightforward you can see that the pizza Builder creates a pizza now
3:06:23
let's have a look at the pizza director what the pizza director does is it basically Aggregates a pizza Builder you can then set a pizza Builder and then you would call make pizza and then get pizza and of course internally what the pizza Builder does is it calls the build saws build topics and built crust methods for whatever Pizza Builder has been put inside so that was uml let's have a look
3:06:53
quickly at the actual code so what I'm going to do is I'm going to run it first so that you can see it's just a very very simple UI if I click here it's going to build our pizza so what I have done is just I'm kind of alternating between New York and Hawaiian so if I click plus right now you can see that a delicious New York pizza with New York crust covered in mozzarella cheese and pepperoni
3:07:17
has been created we also output that to the debug console if I click it again it's going to now create a Hawaiian pizza a delicious Hawaiian style pizza with classic crust covered in ham and pineapple so if you now go back into our let's have a look at an outline if you go back right now into the code you can see that in our pizza. Dart we have our enams which is the pizza size pizza
3:07:44
sauce and pizza crust we have our our pizza class with some business functions and some Getters and we also have a simple stringifying method so that the topics which are just strings can be nicely concatenated for for better formatting and we also have overridden the two string method for our pizza that's merely just convenience so that we can output it for example to the screen so you
3:08:13
can actually read it now let's have a look at the pizza Builder the pizza Builder is an abstract class it Aggregates a pizza we made it protected and it also has a string name in the create Pizza what we do is we create a new pizza object and then we set the name of that pizza you can also get the pizza you can set the pizza price you can set the size and you can add notes these are all specific things that have nothing to do with the pizza building it has to do more with
3:08:41
initialization of the Builder now at the heart of the Builder we have these three abstract methods build sauce build topics and build crust so let's have a look quickly at what the Hawaiian pizza Builder does it extends Pizza Builder so it gets all the implementations of the concrete methods but we also override the three abstract methods from the pizza Builder which is build crust build
3:09:05
sauce and build toppings and as you can see all that we're doing here is first of all the Hawaiian pizza Builder the name is Hawaiian style the crust is classic the sauce is mild and the toppings are simp ham and pineapple so as you see what the Builder does is it takes each step of the building process and adds this specific implementation details for that particular in this case pizza
3:09:30
if you look at the New York Pizza it's exactly the same idea we put the crust as New York sauce as tomato and then mozzarella cheese and pepperoni and then we have the pizza director and all the pizza director does is it Aggregates the pizza Builder and it has a method called make pizza and notice that the make pizza simply delegates to the pizza Builder by creating the pizza building the
3:09:52
crust building the sauce and building the toppings and that's really it this is a very very simple example it can get much more complicated but I would like you to understand here the relationship between the director and how the pizza Builder abstraction is created notice also that of course Pizza could have been an abstract class as well but it was unnecessary here in more complex scenarios imagine you have a document creator and you need to create different types of documents
3:10:22
then not only will you have a different builder for each document you will actually be creating different documents so in that case your document which is what your Builder would be creating would actually be an abstract class which you then extend how is this actually driven if you go into main. Dart you can see that all that we're doing is we're creating a pizza director we then create two concrete Builders and then what we do is we build the Hawaiian pizza for example
3:10:50
by setting the pizza builder for the director as being Hawaiian pizza builder then we just make the pizza and then we get the pizza and the same thing for the New York Pizza Builder all right next we're going to go into the adapter design pattern I'll see you there welcome back this is going to be a set of coding exercises for Builder pattern all right so exercise number one is is going to
3:11:17
be create a simple user class which holds the following data first name which is required last name which is required age which is optional phone number which is optional and address which is also optional you can keep all of these as simple values so first name last name phone number and address Can Be Strings and age can simply be an integer you'll also have an email address which is also required make all these elements immutable in your user class provide only Getters in other
3:11:50
words once the user class has been initialized and instantiated you should not be able to change any of that data next create a user Builder class that can build a user with the above elements and which then can be used to initialize the user class good luck if you have any questions do not hesitate to ask and otherwise I'm going to see you in the next lecture and as always keep on coding
3:12:18
welcome back today we will tackle the adapter pattern let's start this lecture with two simple scenarios imagine that you have some Investments and you have a provider class which allows you to track your investment by sourcing the investment data in XML format but since it is always better to visualize things you would like to see some Investments charts so you also have a charting class which creates a visual representation of your investment data but unfortunately this
3:12:49
charting Library class only accepts data in Json format let's see what the problem is here we have our provider which generates XML data output and here we have a Json data sync which is the data format for our charting Library class the problem is that the two data formats are not compatible so this is an example of data incompatibility one simple solution would be to rewrite the provider to generate Json data but you could already have a lot of code
3:13:23
that depends on the XML output so rewriting this class is not a good idea and it also violates the solid principles but there is a better and more maintainable way of doing this before we do that let's look at the second scenario imagine that you have some shape libraries and in there you have a legacy rectangle code which uses the following Constructor basically it's a rectangle XY wh which
3:13:51
defines a rectangle based on its XY position and its width and height now let's say that your code needs to start using a new version of rectangle which has the following Constructor rectangle X1 y1 X2 Y2 which defines a rectangle based on its X1 y1 upper left corner position and X2 Y2 which would be its lower right corner position these two representations are both valid for a rectangle but
3:14:21
the definitions are not compatible this is an example of interface incompatibility we could rewrite all of our Legacy code to work with the new rectangle but this could mean lots of code changes there is a better and more maintainable way okay so how do we deal with these issues you guessed it we have a design pattern for it to solve these incompatibilities we can use the adap design pattern the uml for the diagram is not very complex adap T is the service we want to
3:14:53
use for example the charting library or the new rectangle interface it Target is the interface that our own code uses we abstract it out into an interface finally the adapter is the wrapper which will allow seamless conversion between the it Target and the adapt here is the definition an adapter is a structural design pattern which is used to convert the interface contract of one class to be compatible with another structural in this context means that this design pattern
3:15:23
deals with issues of structure of interfaces in our objects the conversion that this pattern deals with can take two different elements into account first an adaptor can convert Source data into formats that the client can understand in the case of the investment provider we're converting from XML which was our source into Json which was the destination for the client here we see the provider which generates XML data and here we have the client the charting
3:15:50
Library which only accepts Json data the adapter would reconcile the differences by accepting the XML as input and providing the corresponding translated Json as output so this example deals with adapting data formats the other scenario is that an adapter can help objects with different or incompatible interfaces collaborate here we see the Legacy rectangle mentioned in our second scenario and it has this specific initialization interface which accepts XY position and then the
3:16:23
width and the height of the rectangle and we would like to adapt the Legacy code with a new rectangle definition which uses positions X1 y1 and X2 Y2 to define the new rectangle the adapter in this case would accept as input the Legacy interface and would transform that into the new rectangle input which would then be fed into the new rectangle definition so where did adapter come from this is yet again a very useful and classic gang of four pattern
3:16:54
and its motivation is stated as follows convert the interface of a class into another interface that the client expects adapter lets classes work together that couldn't otherwise because of incompatible interfaces so how does adapter pattern relate to other gang of four patterns we have Bridge State and Strat patterns which are based on composition which means that they delegate work to other objects adapter pattern also relies on composition so sometimes it might
3:17:26
be hard to decide which pattern to use but each of these patterns attempts to solve different problems in other words they have different motivations note that in essence the adapter pattern provides a different interface to the wrapped object some other gang of form patterns that are sometimes similar to the adapter would be a proxy which provides the exact same interface and decorator which enhances existing interface all we have to remember really is that the adapter
3:17:55
adapts one interface to be used by another so when should we use the adapter pattern use it when you have an existing class or contract that you would like to reuse but its interface is not compatible with the rest of your code when should we not use it in general if you can use the adapter pattern but if your code is very time sensitive and adding an extra layer of code would slow you down because of the overhead that the extra layer creates then you might reconsider and rewrite
3:18:27
your original code so what are the benefits of using the adapter pattern you can separate data conversion from the main business logic of your application this follows the single responsibility principle in other words you can use adapters in your architecture as a starting point for data exchange and if a new data change protocol is needed you just add a new adapter you can also introduce adapters into your code without breaking any existing client code this follows the open
3:18:57
closed principle what about the cons the adapter pattern can increase the overall complexity of your code since you are introducing more interfaces and classes but in my opinion this is a small price to pay for the convenience of good code abstraction that adapter pattern gives us okay next we're going to look at the architecture of the adapter pattern so how do we go about designing an adapter we can break down into easy to follow steps first identify the service the
3:19:32
adapt that you want to create an adapter for next you declare the client interface and Define how the client will communicate with the service you then Define the adapter class by implementing the client interface but for now you can leave all the methods simply stubbed not implemented yet next you will add a private field to the adapter class to store the reference to the adapt object
3:19:58
next you're going to implement each of the methods of the client interface in the adapter class the adapter should internally delegate most of the actual work to the service which is the adapt object handling only the interface or data format conversion finally you need to make sure in your code that all your clients always use the adapter through the client interface that was declared in step to this will let you maintain the adapter code without affecting the client
3:20:28
code let's have a look at an example how would we create an adapter for this our first step would be let's identify the service here the service would be the charting Library let's assume that the class is called charting library and has a single method called display data that takes in adjacent string that will be our adaptive The Next Step would be since our provider gives us data in XML
3:20:55
we will Define a simple iart renderer interface that has a single method called display investment data and it accepts an XML string this is our ey Target next we'll Define the adapter class by implementing the client interface that is our charting Library adapter note that we have also added inside this adapter a private field to the adapter class to store the reference to the adapt object which in our case is the charting Library class reference next we would implement the data
3:21:27
conversion algorithm inside the display investment data method in our adapter we simply convert the incoming XML data string into the outgoing Json data string and we delegate the rest to the adapt by calling the displayed data with the Json data that we got from the conversion of course in our code we will then have to make sure that all our clients go through the I chart renderer interface whenever they want to talk to the charting library but the important thing here is
3:21:58
that the Coler of the iart renderer interface has no idea that they're calling the charting Library that's the beauty of this pattern all right so this is it for this lecture hope you've enjoyed it next we will look at some python code that showcases implementation of the adapter pattern see you then and as always always keep on coding welcome back let's have a look at some sample code for the adapter design pattern in flutter let me run the application first it's a very simple
3:22:28
application it deals with personal contacts and we have a menu here which shows XML contacts Json contacts and CSV contacts the CSV part the comma separated values will be for you to do as an exercise when you're writing your own adapter into this framework if I choose X L contacts what it does is it brings a list of all the contacts that we have the little green thing shows that they're
3:22:53
a friend the Amber shows that they're not a friend this is just really a very simple UI the point of this demonstration is just to show how the adapter pattern works so we have John Smith I just put XML here so that you know what the source was if I get out of this right now and go back and load for example Json contacts it's basically the same thing same idea we load the name of the person the email and the phone number and this shows you that this is a friend and this shows you
3:23:23
that it's not a friend okay so what exactly are we doing here let's look first at the actual uml for this so what you're seeing here first of all is we have our data our contact data which has a full name email phone number and information about whether this contact is a friend or not what we then have is we have two two readers we have an XML contacts reader and we have a Json contacts
3:23:51
reader these two readers deal with different data formats we don't want to be dealing with different data formats so we would like to create adapters that are going to go around them that will always give us the same format of data so what we're going to do is we're going to create an ey contact adapter and that has one abstract method which is get contacts which returns a list of the
3:24:17
actual contact data so in order for that to happen we're going to create two adapters one for the XML contacts the other one for the Json contacts and we're going to use the readers as the adap tees within the adapters so let's have a look right now at how this works in code so we have our class contact here we then have the definition of the eye contact adapter which is an abstract class we
3:24:46
then have the XML contact adapter but first let's have a look at the actual reader so here is an XML contacts reader it's basically just mock data so we have a number of simple XML records inside it and we have a method which is get contacts XML which Returns the XML as a string we have exactly the same thing for the Json which basically again mocks the Json data and it returns adjacent version of our contacts so we have completely two different formats here and again the contacts
3:25:22
Json it also just returns string ultimately these two readers don't know what it's going to be used for so they're just returning a string version of their data so these are our adap tees so now let's have a look at the XML contact adapter so the XML contacts adapter what it does is it implements our ey contact adapter interface or abstract interface and then it has the reader which is the adapt and
3:25:51
then what it does is it implements the get contacts method which is our contract method gets the contacts from the XML and then we have a special parse method which is right here which parses the data the way the eye contacts adapter stipulates so it creates a list of contacts if you look at the Json it's exactly the same idea we have the ad T which is our Json reader and then inside the get contacts method we simply get the data from our adapt from the reader and
3:26:25
then we parse it and we create the exact format which is the list of contacts that the abstract interface for ey contacts adapter stipulates this is basically it and in here we just have two simple UI list representations you can have a look at that and then in our main Dart all we do is we just Define the menu so really this here this particular file contact not Dart has all the necessary information all right so this is it for this lecture please go over this code
3:27:02
and have a look at the exercises and I'm going to see you in the next lecture and as always keep on coding welcome back let's have a look at exercises for you to code the adapter pattern so exercise number one is add an additional adapter to the lecture code which will provide a conversion from CSV format to the format that we render the CSV format is simply comma separated value which is
3:27:31
a file that has strings separated by new line and each string which forms a record has its values separated with commas to do that come up with your own test SCV format that your code will be able to read parsing the CSV should be relatively easy with dart string functionality what you really need to do is just split the file data against new lines and then for each record you need to split
3:27:58
it with commas test your adapter with the existing code by making sure that your adapter can properly read and convert the CSV data into the format that our code requires exercise number two consider the Legacy rectangle provided in the adapter pattern lecture create an adapter which will take take the Legacy rectangle and convert it into the new rectangle signature Legacy rectangle class uses the following Constructor as you see it's a rectangle with X and Y and wh the X and Y is
3:28:29
let's say the lower left corner of the rectangle and then width and height are simply the width and height of the rectangle the new version of the rectangle class uses the following Constructor XY y1 X2 Y2 which is basically the lower left corner and then the opposite upper right corner so you should be able to convert these two so remember the key Point here is that you have a rectangle
3:28:54
already which is the Legacy rectangle which needs to be adapted to the new signature so that your new code can use that all right give it a try good luck and if you need any help or if you have any questions do not hesitate to ask and as always keep on coding see you in the next lecture welcome back today we're going to be looking at a favorite design pattern of mine which is the strategy
3:29:20
pattern as usual let's have a look at a scenario let's assume that you have a Web Bot which is like a web crawler and you wanted to scrape some data from the internet you might be interested in data contents such as XML files SQL files comma separated value files perhaps Json files or some other type of data here is what it would look like you have a website you have a scraper and the scraper then is going to scrape the XML data it's going to scrape the SQL data perhaps comma
3:29:52
separated values data and maybe something else here's a simple pseudo code for our scraper if the scraper encounters a document whose protocol is XML then you can just extract the XML data if the protocol is SQL you extract the SQL data and of course if the protocol is CSV then you extract the CSV data this will create code blow this is not a good idea okay so how do we deal with this what
3:30:18
is the problem with allowing the scraper to handle all the logic of scraping there are a few reasons why you do not want to have the scraper handle all those different protocols the scraper class will grow uncontrollably with each new data protocol the extraction code that you put inside the scraper is not reusable it's only in the scraper and most importantly the design violates the solid principles specifically the open close princi principle which states that we should extend
3:30:48
the class not modify it one aspect of the solution would be to abstract each protocol so the CSV and the XML Etc into its own class and then have the scraper call that specific class when needed now that is a good start so let's have a look at it so here is our original scraper architecture which is pretty bad let's have a look at the slightly modified architecture right now so we start the
3:31:16
same way we have a website then we have a scraper but now this scraper delegates to a Handler that understands a specific protocol so we'll have a Handler for XML we'll have a Handler for SQL and we'll have a Handler for comma separated values so this is a much nicer way of doing this we defer the decision about which algorithm to use until runtime obviously the scraper will only know which algorithm to use when it Encounters this specific data when it's scrap in the website and
3:31:47
that can only happen at runtime next we expose the clients which in this case is the scraper only to the interface of the algorithm and never to the specific implementation in other words all the handlers will expose exactly the same interface they will just do different things in terms of the protocol that they will use to extract the data and why do we do that well because this will allow for seamless substitution at any given point if we want to change the Handler we simply add
3:32:16
a new implementation of the interface the scraper at the time wouldn't be affected and also we use composition to Loosely couple the Handler with the parent decision making class in other words we embed the Handler inside the scraper so we basically use composition for that so what we have really done in the previous slide is we have defined the tenets of the strategy pattern so let's have a look at the uml so you can see that we have a strategy G interface it has one method
3:32:47
called execute which accepts some data and then we have specific implementations such as concrete strategy a concrete strategy B Concrete strategy C these are the handlers notice also that we have a context context would be like our scraper and notice that the context has an aggregation of one strategy interface reference there's a composition here noticed also that in the context we have
3:33:12
a set strategy method that way we can tell the context which strategy to use so you can see the client there you can see that the client uses the context but also the client will tell the context which strategy to use so for example you see in the comment there that we have Str strr is equal to new concrete strategy a so that's created and then we simply set that strategy to the context
3:33:38
and then we tell the context to process that and then if we want to use a different strategy then we can supply a different strategy and use the setter to set that as well so this in an Essence is what the strategy pattern is all about the main idea of the strategy design pattern is to extract related algorithms or any piece of code such as concrete strategy a b and c into separate classes and Define a common interface for them in other words this is a very Dynamic design pattern
3:34:07
that allows your code to be flexible and depending on the runtime Dynamic that's very important the strategy pattern I would say is the crowning Jewel of all the gang of four design patterns next we're going to look at the architecture of this strategy pattern see you then welcome back the strategy is considered to be one of the most practical design patterns you will find yourself using it again
3:34:31
and again here is its motivation as coming from the gang of four Define a family of algorithms encapsulate each one and make them interchangeable strategy lets the algorithm vary independently from Cent s that use it how is the strategy pattern utilized with other gang of four patterns well State design pattern can be considered as an extension of strategy both patterns utilize composition at their core which means that they allow the modification of their process
3:35:01
by delegating work to helper objects strategy makes these objects completely independent and unaware of each other however the state design pattern allows the objects to alter their behavior when the internal State changes we will cover the state design pattern in this course command and strategy seem similar but they have slightly different motivations command pattern allows for conversion of any operation into an object strategy allows for objects to achieve the same
3:35:32
thing but in different ways when do you use it you use this pattern to obstruct the business logic of class from its implementation details so that it can be plugged in you should use it when your class has a potential conditional state statement that switches between different variants of the same algorithm use the strategy pattern when you want to use different variants or version of an
3:35:53
algorithm within an object and need to have the ability to switch from one algorithm to another during runtime when not to use if you only have a few algorithms and they rarely change it might be unnecessary to over complicate your code with new classes and interfaces you'll just have to make a judgment call what are the pros in the strategy pattern the behav behavior of a class should not
3:36:17
be inherited but instead the behavior should be abstracted and encapsulated using interfaces this is compatible with the open close principle which proposes that classes should be open for extension but closed for modification you can introduce new strategies into the context without breaking any client code this follows the open close principle what are the cons clients must be aware of the differences between strategies to be able to select a proper one for the task at hand also
3:36:47
in Dart you can use anonymous functions just like you could as strategy but it will not be as clean a code we'll see an example of that in our coding so what are the design considerations first you identify the algorithm that can vary depending on the circumstances of the context then you declare the client interface which will be the strategy contract for all the variants of the processing
3:37:09
algorithm next you implement each interface for the specific algorithms you have identified then you add a private field to the context class to store a reference to the strategy object also provide a way to initialize the specific strategy in the context we usually do that through a Setter note if this strategy needs access to some context data then provide a way for the strategy to access
3:37:34
such data finally clients of the context must be able to properly initialize the context with the correct strategy for a given circumstance let's have a look at an example of a strategy pattern so we have seen this before we have a scraper and then we have a couple handlers we start with an interface which is our data extractor and we basically say extract some data from a source and return the data itself we also have a scraper which is our context and notice that the scraper
3:38:04
Aggregates the strategy and notice also that we have a way of setting the strategy through a Setter and then all that we have to do is just create new versions of the extractors that is all there is to it it's a beautiful design pattern very simple very powerful all right so next we're going to see the strategy pattern in action we're going to code some of it in Python see you then welcome back let's have a look at some code for the strategy design pattern within the context of
3:38:37
flutter and dart what you see here on the screen we have a simple choose your renderer dialogue with three radio buttons when I click on render image it renders an image if I click on render button it replaces that and renders a button and if I click on render widget it renders some kind of a widget so this is a pretty straightforward workflow the question is how did we implement this
3:39:03
let me just put it back into render image so let's have a look at the uml diagram we have an abstract renderer class which has one method render which returns a widget and then what we do is is we extend that with three separate classes we have an image renderer we have a button renderer and we have a widget renderer so what you see here these four classes that basically comprises of a strategy design pattern now if you look at the main page state which is the one right here in
3:39:37
code main page state if you look at that what you see is that we have a renderer aggregated inside the main page State and whenever you click on any one of the radio buttons what that does is it grabs the corresponding strategy and uses it to do whatever the strategy does and in this case what it does is it simply renders or displays that particular Widget the nice thing about it is that when you look at the underscore main page State class it is not aware of what the renderer
3:40:12
is going to do it simply renders it by calling the abstract render method and this is where the Simplicity and the power of the strategy design pattern shines so let's quickly have a look at it so here we have our main page state so basically if you go into main. Dart we can see that it's a very very straightforward simple application in the build we have our title we have the theme and
3:40:39
we have the home now the home is our main page if I go now into the main page you can see that all that the main page does is it keeps track of their renderer just as we've seen in the uml diagram and before we look at how this main page State class uses it let's have a look at the renderer so the renderer is pretty straightforward we have an abstract class renderer it has a method render
3:41:04
which returns a widget and then we have three subclasses we have the image renderer which simply just returns an image asset by the way this asset is right here you can see on the left hand side inside our images next we have a button renderer which basically has an elevated button code in it and then finally we have a widget renderer which basically has a gesture detector widget inside of
3:41:32
it we have a row widget which inside of it has a few children the children are an icon a text and an icon which is exactly what you see when I click on render widget here this is the class that gets called and it simply renders an icon a text and an icon notice also that the nice thing about this widget renderer is that it actually comes with a gesture detector so if I show you here
3:42:00
let me just get rid of that if I click on it right now you can see that it actually tells you that I have tapped the row widget and you can see that right here this is pretty powerful so let's go now into our main page and you can see that in our main page we keep track of the renderer itself so whenever I click on any one of these buttons here what that does is it assigns a different renderer
3:42:25
based on the user action so here is how we track the renderer if you go into the list of the radio buttons you can see that we have one that says render image we have one that says render button and we have one that says render widget and notice that whenever you click on that it simply assigns a different renderer this is pretty cool because based on the action you assign a different renderer but here is the the power if you will and the Simplicity of the strategy design pattern
3:42:55
because if we go now into this area right here this container right here you can see that all it does is it's container for a child widget and it gets the child widget by simply calling the render method of the current renderer in other words this class the main page state is not aware of which render it uses and that's very powerful and a very important architectural strategy if you will okay
3:43:24
so this is basically it I hope that you really like this pattern I absolutely love strategy pattern it's my favorite pattern please note also that in one of the exercises that you are given for the strategy pattern there's an extra class in here see this one shipping. dark when you're going to refactor it so that it actually uses a strategy pattern as part of your exercise the reason why we put this shipping do Dart in here is so that you can maybe play around with the UI
3:43:53
that we've already created so that you have a a bit of a visualization but that's completely up to you all right I hope you enjoyed this and I'm going to see you in the next video and as always keep on coding welcome back let's have a look at a few exercises that you can do to understand and practice the strategy pattern a little better so let's have a look at the exercises
3:44:16
exercise number one is we have provided you with a source code shipping. Dart which needs to be refactored to use the strategy pattern your task is to identify the main issue correct it by refactoring the bad code into a number of strategies write some sort of a main with simple UI to test your code you will need to test the following when the user switches the shipping method you should be able to dynamically switch which algorithm is being used to calculate the
3:44:43
shipping cost either use a drop down like a menu or a few radio buttons for your UI that's your choice after that add a new Handler strategy for another shipping carrier which is Amazon delivery with the cost of delivery being $3 and A4 here is the code that we would like you to look at this is shipping. Dart and there is a simple address there are shipping options UPS FedEx and puralator we have an order which is basically who is going to ship it where and where is this
3:45:16
coming from and we also have the shipping cost calculator service now this is what you will need to refactor the shipping cost calculator service that's a really bad code all right so have a look at that for exercise number two what we would like you to do is go back to the adapter pattern code so basically the lecture before this and look if the code can be refactored to use this strategy
3:45:43
pattern to make it more abstracted maintainable in other words I would like you to have a look with a critical eye at that code and see whether it can be improved specifically using the strategy pattern the adapter itself is fine so you're going to be looking at something else in there the hint is look specifically at how the code handles displaying the contact records all right so that's basically everything I hope that you're going to have fun with it and good luck and I'm
3:46:13
going to see you in the next video and as always keep on coding welcome back today we're going to be talking about a very exciting design pattern which is the Observer pattern The Observer pattern is a very interesting useful and dynamic pattern the main intent of the pattern is to create an event notification mechanism between objects think of the relationship between a publisher and a
3:46:40
subscriber a subscriber subscribes to a publisher of event EV when an event happens the publisher notifies all the subscribers in its list that an event has happened let's look at the subscription process you have a publisher and the publisher has some sort of State some sort of data that you are interested in you have a number of subscribers and these subscribers can subscribe themselves onto the Publishers list of subscribers any subscriber can add themselves to the
3:47:10
subscribers list or when they're not interested they can remove themselves from the subscribers list this is how the subscription would work each subscriber can say to the publisher hey I would like to be on the list of your notifications please add me or please remove me how does the notification work you have the publisher the publisher has a state which is basically data that you're interested in and there is a list of subscribers whenever there's a change in the data
3:47:38
that represents the publisher State the publisher sends notifications to each subscriber about the change in the state of the data The Observer pattern is a behavioral design pattern in which an object usually referred to as the subject which is the publisher in our examples the subject maintains a list of its dependence called observers which were subscribers in our examples and it notifies them automatically of any state changes this pattern perfectly suits any process
3:48:11
where data arrives from some input that is not a available at a predefined moment but instead it arrives at random for example HTTP requests user input from peripherals like a mouse or a keyboard distributed databases and blockchains Etc it's an incredibly useful design pattern The Observer pattern is also a key part in the well-known and utilized model view controller MVC architectural pattern next we're going to look at the architecture of the Observer pattern see you then
3:48:46
welcome back here is the actual uml diagram let's look at this uml in a little bit more detail imagine that you have some sort of a subject and this subject has a state and some process can set the state meaning can change it and then of course we can pull or get the state from the subject so we have this encapsulation of some data that we might be interested in we don't know though when the state is going to change so what we do is we create an observer interface and this
3:49:20
interface at least has one method which is update the idea behind the update method is that we would like the subject to tell us whenever its state data changes how do we do that well we modify the subject to keep track of all the observers that are interested in a notification we create two methods attach and detach to be able to add and remove observ and we of course have the set State and get state from before so this is clearly a composition the subject has an aggregation of
3:49:54
zero or more observers who would be interested in knowing when there is a change in its state whenever the state is changed when somebody changes the state what the subject does is it goes through all the observers and its observers list and it simply calls The Observers update method notice how we're using here the strategy pattern because the Observer is just a pure interface so now we will create an abstract Observer which all that it does is it keeps track of the subject
3:50:28
that it is interested in so you can see that the subject has The Observers but each Observer also has access to the subject when the subject's data changes the subject goes through all the observers notifies them and then each Observer already has access to the subject so it simply goes and checks oh wow the data has changed let me get the new data that's the idea here notice though that in
3:50:54
the abstract Observer the update method is still abstract it has not been implemented what remains is we create the different concrete observers and it is in those observers that we can now do whatever we need to do in the update method so when the subject calls each one of The Observers by calling the update method notice that the update method basically just states that we grab for example The Observer state to be whatever it is that we want to extract from the subject's
3:51:26
state object The Observer pattern is one of the most useful design patterns here is the motivation of the pattern as stated by gang of four Define a one too many dependency between objects so that when one object changes state all its dependents are notified and updated automatically some of the other similar design patterns coming from gang of 4 would be chain of responsibility command and mediator all of these chain of responsibility command mediator and observer they
3:51:57
all address different ways of connecting senders and receivers of requests chain of responsibility is used to pass a request sequentially along a chain of receivers until one of them handles it in other words chain of responsibility sends a notification but it expects only one of of the dependents to actually respond to it command is used to establish a unidirectional responsibility between a sender and a receiver in other words we know exactly who we want to do some sort of
3:52:26
a task we send that object a command saying do it mediator which is also known as a controller removes direct connections between senders and receivers forcing them to communicate indirectly via a mediator or controller object it basically makes a more complex but also more Loosely coupled version of The Observer pattern when do we use it use the Observer pattern when changes to the state of one object need to be known by other objects especially when the actual set of the
3:52:56
other objects is either unknown initially or changes dynamically throughout the life cycle of the application use it also when an object or a set of objects need to observe changes in other objects and need to be notified in real time about those changes when not to use because the order of notifications is potentially random be careful about your requirements if order of notifications is important what are the pros you can introduce new subscriber implementation without having to
3:53:28
change the Publisher's code this is compatible with the open close principle it also supports the principle of loose coupling between objects that interact with each other you can establish relationships between objects dynamically at runtime and if you think about it the The Observer pattern is the key to reactive Behavior meaning something happens and then your application reacts what are the cons the order in which Observer subscribers are notified is potentially random
3:53:58
debugging notifications can be difficult given the random nature of those notifications what are the design considerations look for elements in your business logic that need to be aware of State data changes break it down into two parts the controller slre receiver of the events this will be your publisher the components that need to be aware of those changes these will be the subscribers once you have done that declare the subscriber interface which should at least have
3:54:27
a single update or notify method then declare the publisher interface and Define the contract for adding and removing subscribers then you create an abstract publisher implementation with a mechanism for subscriber management and notification then you identify the state data that will need to be observed and create a concrete publisher implementation for that state Define a way for subscribers to get access to that state and then create the specific implementation of subscribers
3:55:00
that you need in other words the objects that will need to be notified when data in the publisher changes we're going to look in more detail at this when we actually code an example of the Observer pattern in Python and that's what we're we're going to do in our next lecture see you then welcome back now let's have a look at how we can use streams to deal with notifying the UI that the data has changed unfortunately with set state it will refresh everything so it does a little
3:55:32
bit extra work here if you use on the other hand stream and stream controller and stream Builder that will allow the notification to happen at the level of an individual widget so you have your data you have your change notification but now the refresh command is going to Target one specific widget within the widget tree so especially when you have a complex widget tree that is such a better and more efficient way of doing it so let's have a look at the stream so we have a stream
3:56:05
Builder and a stream Builder is basically a widget that builds itself based on the latest snapshot of interaction with the data stream this is based around a stream controller what is a stream controller a stream controller works with a stream and something known as a stream sync we use the stream sync to add data value and we use stream to read it so let's think of it this way a producer
3:56:30
is somebody who creates data the producer puts the data into the stream sync that then goes into the stream controller the stream controller then produces a stream and a consumer can be listening on the stream to get the data this is what a stream controller does with a sync and a stream you can think of it this way producer pushes events with data and the consumer consumes those events with data so basically what you have here is an observer pattern producer alerts the
3:57:02
consumer that something has changed consumer is the subscriber there are a number of ways that you can use streams here is one if you use the stream Builder that's specific specifically works with a widget and what the consumer gets it gets an async snapshot with the data of the event you will see that in code in a little bit there's another way though in which the consumer can consume the
3:57:27
data from the producer you can use a listener a listener works with a stream subscription and the difference here is that a stream subscription is not a widget so what you're seeing here is that on the producer side the producer doesn't really care who the consumer is it's simply just pushes data into the sync the stream controller then moves that data and makes that data available through
3:57:51
the stream to the consumer so what the consumer does is it subscribes to the stream controller stream and it can then consume or basically get those notifications and we have two different ways we can use a stream Builder which automatically works with widgets or we can use a listener which simply just works with data it might sound a little bit confusing it's a lot of information but we will have a look a little bit later at the code now one very important thing is that
3:58:21
we cannot subscribe to the stream subscription with multiple listeners in other words if you go back here you can see that when the producer talks to the consumer there can only be one consumer but in the Observer pattern we saw that we could have many observers so let's go back here so what this really is is like a one to one Observer pattern but what we want is a one to n Observer pattern so if you want to use this mechanism where many listeners many subscribers
3:58:49
wait for the data updates we would have to use the following mechanism instead of this code where you basically create a stream controller and it's initialized through a normal Constructor you can do this the moment you instantiate your stream controller through a broadcast method you will actually get a stream controller that will allow multiple listeners to get the notifications
3:59:16
all right what we have here is a very simple demo of the way the stream works we have three widgets in our application UI we have an increment button we have a decrement button and we have counter text which shows us basically the current state of our counter we start with a zero now the way this works is that if I press on this right now to increment it you can see see that we get a one here and if I click again it will just keep on adding if I click here and then again it
3:59:52
decrements that seems pretty straightforward that's pretty easy to do well the difference though is we do not use set State here we actually use a stream controller so let's have a look at that so what we have here is we have created a counter controller the counter controller is a class that contains the counter a counter is just the value that you see right here we then create two streams we create a counter controller stream and we create an
4:00:19
event controller stream one stream will tell the controller that a button has been pressed and the other stream is going to tell the widget that it should refresh itself if you look up here you can see that we have two events we have an increment event and a decrement event notice that the stream controller works with an INT because it will actually work with the counter itself on the other hand this other stream controller here the event controller doesn't work with ins
4:00:49
it works with our enumeration in other words with events what we really have here is a communication that kind of hops from one part of data to another in other words if I press on the button here if I click increment this button is going to send a notification to the counter controller through the event controller stream when whenever I press on any one of these buttons what happens well notice that we have a stream subscription listener here and when we create
4:01:21
the counter controller which is the actual full controller class notice that right away what we do is we set up a listener on the event stream in other words the counter controller itself becomes a listener for the event of any one of those two buttons being pressed what happens in here is that the listener listens for an event when the event happens on the increment it increments the counter
4:01:47
and on the decrement decrements the counter that's pretty straightforward so in other words whenever I click any one of those two buttons this counter controller gets a notification saying hey there's an event either increment or decrement and I want you to know about it so the listener will be notified but then look at what happens inside the listener notice that the controller takes the other stream which is the counter controller stream goes in into the sync and it adds the
4:02:16
current value of the counter to that sync in other words it sends a notification to whoever is listening to the counter stream that there is an event okay so how does this work our UI creates the counter controller which automatically starts listening for the events so now let's go to those two buttons so if we go into those two floating buttons notice that on pressed it goes into the
4:02:41
counter controller's event sync and it it adds the event the moment it does that look what happens in the counter controller The Listener will then because it's listening on the stream it's the consumer it's going to get that event and it's going to say oh wow if the event was an increment for example you can see here if you press the plus button here you can see that the event being added is increment and if you press on the minus button here you will see that it's going to be a
4:03:10
decrement so what happens inside here is that the event stream listener gets the event and then it automatically updates the counter once it's done updating the counter what it does is it then adds the current value of the counter to another stream now that stream is what takes care of the actual text so let's have a look here so what happens here is look at this child right here in the child
4:03:35
we have a stream Builder the stream Builder is the widget that is able to listen for stream events so what we do is we initialize the stream Builder with a stream and the stream is simply going to be the counter stream which is this one right here this is the counter stream right here so this is initialized with the counter stream and it puts initial data as zero because initially it still
4:03:59
needs to show something so before it gets any data from the stream it will initialize the widget with a zero let's look at what the Builder is so the builder takes of course the build context but the more important thing for for us here is the async snapshot int the snapshot that is actually the listener that will be listening constantly to check whether there's an event and what this
4:04:25
code does is basically it says if the snapshot has data then all that we do here is we basically go to the counter controller and we get the counter or we could have we could have easily just taken instead of the counter we could have simply just put snapshot data if I if I run this right now you can see that it works perfectly okay let's go one more time over flutter streams to make sure that
4:04:55
this process is clear to you so let's recap if I'm going to press the plus button right now what's going to happen is this button right here is going to call the counter controller's event sync and it will add to it an event so if I click on it right now we should see in the debug console some information as to what is happening so I'm going to press it right now so notice what happened my
4:05:21
homepage State just sent an event trigger this one right here and this is what it tells us is happening then this line gets executed which is the event sync so then what happens is inside the counter controller the counter controller receives the event notification so let's have a look here and if you see right here this is it it just received the event notification then what happens is once the controller has processed the data in this case it just adds one or subtracts one it
4:05:54
then is going to tell this widget right here that there's new data that it should refresh itself so what this does is it puts the data into that stram and we output this you can see it right here send event trigger and then what happens is this will actually send an event to this widget right here if we go back up here into the stream Builder you can see that this is what is printed here in
4:06:22
other words it's a much cleaner it's a little more complex but it's a much cleaner way of controlling State Management at the level of individual widget pieces within the widget tree all right so this is it for the code please go through it it's very well documented have a look look at the code itself I've documented everything here and experiment with a little bit and try to understand what is happening and how it relates to the Observer pattern now as we've seen in the lecture
4:06:56
right now there's only one sync if I press here I can send it to multiple widgets so what I'm going to do right now is I'm just going to modify the code a little bit I'm going to go into Main and I'm going to put another widget right above this I'm just going to basically just grab this hole
4:07:15
copy that and I'm going to add it right now behind this one and if I run it right now look what happens we got some sort of an error like we have the zero but nothing else is showing so I'm just going to take you to the very top and we're going to look at this error and you can see that it says bad State stream already has been listened to the problem here is I have two listeners but right now we have only set it up for one listener so how do we fix this the way
4:07:42
you fix that is pretty straightforward when you create the stream like this this can only work with one to one subscriber to publisher just one publisher one subscriber if we want to be able to send the same notification to multiple subscribers which is like the classic example of the Observer pattern all we need to do here is we need to use a broadcast Factory so it will create controller
4:08:09
for broadcasting so I'm just going to go broadcast going to remove that perfect let's run it right now and as you can see right now we have two widgets and if I click just once you notice that they're both changing at the same time and that is because each one of them right now is receiving exactly the same data I could be publishing to a number of different widgets so this is now a more
4:08:36
of a classic example of the Observer pattern all right so this is it for this particular lecture I know it's been a little bit different we haven't really built our own observer in this particular code but I would like you to go back to all of this code and think in terms of the Observer pattern in terms of what you see is happening in flutter now I will give you an opportunity to exercise the creation of the Observer pattern all right in the next lecture we're going to look
4:09:07
at a very important design pattern which is the state design pattern all right I'll see you then and as always keep on coding welcome back let's have a look at some exercises for you to do for the Observer pattern exercise number one I would like you to create a uml diagram for the part two code that we looked at in the coding section for the Observer pattern specifically that was the one where we had the increment button and the decrement button and then we had a text widget
4:09:38
that was showing the current value look at how the streams are us utilized in passing State data between the buttons and the text widget and create a uml sequence diagram that shows that interaction include the following objects in your sequence diagram The Floating Action button the stream sync for the event the stream subscription the stream sync for the int which is for the counter value and the stream builder for the int which is the widget that actually builds and shows the
4:10:11
text that shows the increment or decrement value and also the text itself now you don't have to use any specific tool all you need to do is just for practice try to do it even by hand on a piece of paper I'll provide you with a solution so you will be able to compare exercise number two is a little more ambitious using the grid code from part one code that we looked at in the coding section for the Observer pattern create the following modifications using a timer refresh each tile
4:10:45
of the Grid at every second add a number inside each tile that is randomly generated at every tick make sure that each tile in the grid becomes an individual subscriber to the timer tick event when the time ticks notify each tile widget that a new timer event has happened when that happens simply generate a random number between 1 and 10 and place it in the middle of that tile use what
4:11:12
you have learned about flutter streams to refresh each tile widget with the new data this is more ambitious because it's going to be a little bit more difficult as you are going to have to deal with literally 64 subscribers I suggest that you maybe start the grid with just like eight but that's up to you but the idea basically is that I would like you to have the grid refresh each tile as a separate Stream So that when the timer and this is just the timer that we're using
4:11:47
is just the flutter timer the timer is going to be the publisher and the subscriber are going to be all the tiles good luck with it I hope you're going to find this challenging it definitely is and this is about halfway to creating the game of life simulation so give it a try ask any questions that you would like to in the forums and I'll help you and otherwise I'll see you in the next lecture and as always keep on coding welcome back today we're going to talk about a
4:12:20
very exciting and interesting design pattern the state design pattern before we look at the details of the state pattern let's talk a bit about what states actions and transitions are in context of a logical workflow consider a simple scenario of the workflow of making toast in the morning we start with the toaster being idle basically it's doing nothing thing we then insert a slice of bread we then pull the lever to start toasting we pull it down the bread is then toasting at any
4:12:53
point we can eject the bread once the bread has been toasted it is automatically ejected by the toaster we can then remove the ejected bread which brings the toaster to an idle State I know that it's a little strange to kind of like break down such a simple process into these steps but what we have done here is we have created a very simple algorithm how do you translate an algorithm like
4:13:16
that into code and what does this have to do with the state pattern let's look at the algorithm and how we can break it down here is a little table where we have state action and transition so the states are the states of the toaster we can have the toaster being in an idle state it could be in a state where the bread has been loaded it can be in a state where it's toasting the bread and it can be also in a state where the bread is ejected notice though that bread ejected and
4:13:46
bread loaded are very similar States let's have a look at the action for example when you are in an idle State what you can do is you can load the bread if you're in the bread loaded State you can start toasting or you can just take the bread back if the state is in the toasting the toaster is actively toasting the bread you could either eject the bread you know that happens many times
4:14:11
you see oh wait a minute I'm hoping that it's not burned so you just stop the toasting process when the toaster is done toasting the action could be that it simply has finished toasting and a similar thing for bread ejected transition is basically where you move from one state to another so for example if the state is idle and the action is low bread we then move into a new state which is
4:14:36
toaster has the bread loaded if we are for example in the bread loaded State and the action is is remove the bread we go or we transition into the idle state or if we're in the bread loaded State and we pull the lever down which is the start toasting action then the transition is such that the toaster goes into the toasting State I know that this can sound a little bit complicated but the important thing is to just remember the following three things a state defines one of the
4:15:09
states in which a particular device or action can be action is something that we can do to change the state or we can affect the state transition is how the action has changed the state let's represent this workflow as a finite State machine using a simple State diagram so here is our table and we're going to translate this table into a state diagram so the first thing we're going to do is we're going to put the four states into our state diagram so we have the idle bread loaded
4:15:45
bread ejected and toasting States next we're going to specify which one of those States is the first state so that is our initialization and then what we do is we have the actions if I'm in the idle State I can then insert bread which transitions me to the bread loaded state if I'm in the bread loaded State and I pull the lever down then the bread gets pulled down and we move into the toasting State when the toaster is done then it goes into the bread ejected State
4:16:19
and then finally if I remove the bread then it goes back into the idle state so this is like a full loop but that's not the only thing that can happen for example if I'm in the idle State and I go to insert the bread and the bread is loaded I don't necessarily have to pull the lever I could simply at that point remove the bread so you can see that we have all these other actions that show us the things that we can do when we're in a given State what you see here is a very
4:16:49
simple State machine and this is a state diagram that shows us first of all all the different states the actions that we can perform on those States and also how we transition from one state to another based on the action so what does all this have to do with our state pattern the state pattern is a behavioral software design pattern that allows an object to alter Its Behavior when its internal State changes as you have seen State pattern is similar to the concept of finite State
4:17:22
machines in other words there's a finite number of possible states with discrete transitions between those States this pattern can also be interpreted to some extent as a strategy pattern which is able to switch a strategy through invocations of methods defined in the patterns interface we'll see what that means in just a bit here's a bit of an abstract uml diagram for a state pattern let's have a look at this in more detail we start with something which is the context this is the
4:17:54
actual thing that can change state for example in our toaster example context would be the toaster next what we do is we have a state interface this interface will be used to define all the different states that we have in our state machine notice that context Aggregates State this is how you could do a transition you could set the state in a context to be a specific State and then you can
4:18:24
initialize that state with the context itself and then what you can do is you can invoke an action on that state by basically saying toast or eject or action a here what you see is an abstract State class which gives us the the ability to look back at what the context is notice that the state defines a few actions like action a action B action C these actions are the actions for our state machine as you can see our abstract State Aggregates context so what we have here is that
4:19:01
context keeps track of a state but our state can also have an aggregation of the context well let's say we have a concrete State like concrete State 001 the concrete state is going to have a number of actions and the transition that you could do from one state to another for example inside concrete State 001 we could create a concrete State 002 we can then initialize it with a context and then we can tell the context please change your state to state 002 and here we have
4:19:35
other implementations of the state and how would the client use this the client can do two things first of all the client can create an initial state for example concrete State 001 and it can then create the context initialized with that state and it can then do an action on the actual context here's a sequence diagram that shows a client that is requesting for the context to do some sort of an action a context then delegates action a to whatever state it is in
4:20:10
at that time let's say that the state happens to be concrete State 001 so what context will do is it will ask the state to execute action a what the concrete State 001 will do based on action a it will decide at that point what should happen what kind of transition should happen when action a is called on that particular State and in our case you can see that we have a simple delegation where it says move to next state and what it does it creates concrete State 002 initializes
4:20:45
that with the context and then it sets the state of the context to be the concrete State 002 so the context has transitioned from concrete State 001 to conrete State 002 so when the client calls the context with another action like action B inside the context the actual action B is going to be executed on the current state which happens to be concrete State 002 which will possibly do the
4:21:16
same thing it will also move it to the next state what you need to get from this uml diagram is that the states are aware of the transitions they're aware of what should happen for a given action on a given state so the way that these actions move from one state to another is that the states themselves know how to do it this is an important important distinction all right we'll learn more
4:21:45
about State design pattern in the next lecture we'll look at a classic example of a very simple State machine and itsl design welcome back let's continue with the state design pattern the state pattern is absolutely an awesome design pattern here's the motivation of the pattern as stated by gang of four allow an object to alter Its Behavior when its internal State changes the the object will appear to change its class what are some of the design patterns that are either similar or
4:22:21
dependent on the state pattern State strategy bridge and to some extent the adapter pattern have very similar structures since they are all based on composition this allows them to wrap around other objects and delegate work to them State pattern to a great extent is an extension of the strategy pattern both both patterns allow the context to change its Behavior by delegating work to composited helper objects the strategy makes all the objects independent and unaware
4:22:53
of each other but State pattern allows for the change to Its Behavior when its internal State changes please note also that state objects are often Singletons when do we use the state pattern you should use the state pattern when you have an object that changes its Behavior depending on its internal State especially when the number of possible States is non-trivial so look for many IFL statements that change behavior of the object you also use it when you have a
4:23:23
number of rules that act on an object based on the object State especially when modeling real world workflows when not to use if the state transitions are very simple and infrequent it might not be advantageous to use the state pattern as it would add unnecessary complexity what are the pros of using the state pattern State pattern reduces conditional complexity by removing bulky and hard to maintain e than else or switch case statement logic you're
4:23:54
able to introduce new states without changing the existing state classes or the context this follows the open close principle all the code related to a specific state is in its own separate class this follows the single responsibility principle what are the cons the state pattern can require a lot of code to be written which grows in complexity as more states are modeled here are some of the
4:24:19
design considerations so what you do is you look for a class or logic if it's distributed across a few classes that has some rule dependent or state dependent code this will be our context declare the state interface and design State specific method behavior for each actual State create a concrete State implementation in the context class add a reference to the state interface with a public Setter for each state conditional implement the corresponding method in the context
4:24:52
class switching the context state will be done by setting the correct State instance now note that this can be done within the context itself in the state instances or even by the client let's look at another quick scenario and how we would model it the scenario is a classic a simple onoff light switch we have two states we have the light being on and the light being off and we have one very
4:25:21
simple action you simply flip the switch if the light is off and you flip the switch it will be on if the light is on and you flip the switch it's going to be off very simple so we have two states light off and light on we start with the light off and we have very simple transition which is just switch and switch this is a very very simple State machine so as you can see that when the light is
4:25:46
off and I click the Switch it goes into light on if the light is on and I flick the switch it goes into light off so let's see now how we would model that using the state pattern our context would simply be the light the light has a state we have two methods for manipulating the state we can get the current state of the light so let's say if we wanted to find out is the light on or not
4:26:09
and we also have the ability to set the state and we have a business method which is switch this is our state we'll just have a simple do action which accepts context and we'll also have a simple name method that would just tell us what the particular state is as stated in the design considerations our context which is light will aggregate the state we have two states we have light on and we
4:26:35
have light off pretty straightforward the client will start the light with light off and then the way way you would use the light is you simply take that light and you just hit the switch and you can do it repeatedly light on in its due action will do something very simple will take the context and it will set the state of the context to light off and for light off we do the opposite the important
4:27:01
thing to get here is that each one of the states is aware of the other state light on knows that it should set the state to be light off and light off knows that it should set the state to be light on all right so this is everything for this particular lecture what we're going to do next is we're going to code an example of the state pattern in Python we'll see you there welcome back let's have a look at how we would code a simple practical example of the state design pattern so
4:27:34
let's talk about coding traffic lights let's code the behavior of traffic lights using the state design pattern this pattern is a good candidate to model such Behavior because we would like to have the ability to Simply tell the traffic lights to just go to the next state while the actual State transition is hidden behind context so the idea is pretty simple we have a traffic light we start with a green then we go into a yellow then we go into a red and then from Red we go back to
4:28:04
green and this repeats so basically we're looking at something like this pretty straightforward we all have seen traffic lights before so let's see how we would actually design and then code such a simple transition let's look at the steps that we need to consider first we will need to declare the state interface and design the state specific method Behavior how will the state have its transition move from one state to another for each actual State we will create a concrete State
4:28:37
implementation so in our case we will have a state for each color of the traffic light so we'll have a state for green for yellow and for red now in the context class we're going to add a reference to the state interface with a public Setter the idea being that our traffic lights should let us know what state they are in actually for each state conditional we implement the corresponding
4:29:02
method in the context class so the idea basically is that the transition from one state to another is going to be coded within the states themselves now switching the context state will be done by setting the correct State instance and finally this can be done within the context itself or in the state instances or by the client so what I'm going to do right now is I'm going to go into the uml diagram but it would be a good idea for you to just pause the video and think about how
4:29:31
would you create using the state design pattern a traffic light simulation okay so I'll show it to you in the next slide but for you it would be a good idea to pause this video right now and just try to design it yourself all right so let's have a look so we will have a traffic light state which is going to be our state abstraction it's going to have two methods a get color and the next which is
4:29:59
going to be the transition itself and we're going to have three specific concrete instances we'll have a green State a yellow State and a red State and they will Implement those two methods the get color and the next now the actual implementation is going to be relatively straightforward you're in the next method you're going to accept a traffic light we'll see what that is in a second and we're going to Simply change that to the next state which is going to be from Green
4:30:27
we're going to go into yellow from yellow we're going to go into red and from Red we're going to go back into green this is our context this is our traffic light notice that the traffic light has a current state which Aggregates the actual traffic light state which we initialize to start with a green light which is the Green State notice that we have a next method and we have a get color
4:30:51
the next method will simply move the transitions behind the scenes as you can see being implemented in the green yellow and the red States and of course the color itself is just going to return the color of the state and as you can see the traffic light itself Aggregates the traffic light state so the traffic light state uses the context and the context Aggregates the state so next what
4:31:16
we're going to do is we're going to look at the actual implementation of this the actual code you can pause the video again look at this diagram and kind of think about how you would implement this of course the code itself has been attached with the lecture so now what we're going to do is we're going to go into the code itself again if you want to pause the video right now do so otherwise let's
4:31:39
go in there so let's go into theod code itself I'm going to run it first I'm going to run it on Chrome all right here it is so what you're seeing here is we have our green yellow and red cycle and as you can see this is periodically changing we have a green now we have a yellow and now it's going to go into red and then it will just keep on repeating we also output which cycle we're in basically which step in the transition okay so let's have a look at the code itself so as you
4:32:17
can see here we have the traffic light state which is our abstract State class we have the next method and the get color which are abstract now let's have a look at the actual implementation so we have three implementations we have the Green State we have the yellow State and the red State notice that the methods are relatively straightforward what the green state does is it accepts the context and inside the context it sets the next state to be yellow State and it Returns
4:32:47
the color of this which is the Green State to be green yellow state does the same thing it simply moves or transitions the state from yellow into red and returns of course the color itself is yellow and then the red State moves the transition into the Green State so basically what we have is we go from green to Yellow from yellow to red and from red to green and here is our context class our traffic light notice that the traffic light Aggregates the current state we initialize
4:33:21
it first to the Green State and then the next method on the traffic light all it does is it basically calls the current state and asks for it to transit to the next state and then the color simply Returns the color of the current state notice that when we call the current state next it actually provides the context as input okay so now let's have a look at the actual class itself the class itself is pretty straightforward what we have obviously is a timer so first we have our
4:33:54
traffic light right here which is our light we initialize the cycle to zero so we're basically starting with a cycle zero in our init State what we're doing is we're starting a timer the timer is just a simple periodic timer that basically creates a notification every 3 seconds the actual notification what it does is it calls the set State on the widget which basically refreshes the whole widget tree and what it does is it adds one to our cycle and calls the next method of our
4:34:25
traffic light and inside the build actually it's pretty straightforward we create a nice little container 150 by 300 so it's basically a rectangle we use the Box decoration we create a border and then the light inside the border is is the current traffic light and then we output the cycle this is pretty much it it's a very straightforward usage of the design pattern the beauty of this is that if you look here all that the timer has to do is simply keep on calling the
4:34:58
next method and the actual transition the actual State changes are hidden behind the context so this is what makes the state design pattern so powerful all right so so please have a look at this code look at the uml diagram and otherwise I'm going to see you in the next lecture and as always keep on coding welcome to the course assignment we're going to design architect and develop a version of John Conway's famous game of life simulation why this particular project there
4:35:33
are a few reasons first of all we think it will be fun it's going to utilize all the design patterns we have learned in this course it is complex enough that a good architecture will make a huge difference but it is simple enough that you can develop it within the busy schedules of your lives and also it will involve some interesting and dynamic UI design what is a game of Life the game of life is a cellular automaton invented by the British mathematician John Conway in 1970 it
4:36:02
is a zero player game its evolution is determined by its initial State requiring no further input you interact with the Game of Life by creating an initial conf configuration and then observing how it evolves this is why it's a zero player game you start with an initial State then at a certain time interval which is what we call a tick here you go into the next state so you basically take the
4:36:24
data from the initial State and then at the tick of time you go to the next state and then when the time ticks again you take the current state and you go into the next state so basically your current state becomes the next state and this goes to Infinity the idea is that the game of life is an evolving game you set a certain initial State and then the game just progresses you don't even know exactly how it's going to turn out because it depends on what kind of input you gave
4:36:52
it the universe of the game of life is an infinite two-dimensional orthogonal grid of square cells Each of which is in one of two possible States it's alive or dead or basically you can think of it as populated or unpopulated orthogonal in this case simply means a grid where the lines that intersect are at 90° angle so for example this is a very simple orthogonal grid which is simply a 4x4 it's not infinite we can specify that a cell is dead unoccupied with the lighter color and
4:37:24
we can show that the cell is alive with the dark color so for example this shows that you have one organism alive in Row 2 column 3 Let's Define what neighbors are when you look at this picture you can see that we have one alive cell in the middle of this grid the neighbors are all the cells around it that touch it if your alive cell happens to be on the edge of the grid then of course it
4:37:50
will have less than eight neighbors it's going to have five neighbors and if you happen to be on a corner then you will only have three neighbors every cell interacts with its eight Neighbors which are the cells that are horizontally vertically and diagonally adjacent at each step in time the following transitions will occur this is where you have the workflow of the game any cell with two or three live neighbors survives any dead cell with exactly three live neighbors
4:38:19
becomes a live cell so it's like a birth and all other live cells die in the Next Generation similarly all other dead cells stay dead let's have a look at this in a little bit more detail these are the three main paradigms of the game one any life cell with two or three live neighbors survives so let's look at that one first in these examples right now we we will be looking always at the middle cell as this cell we're talking about so this one has two live neighbors we're
4:38:48
just showing the slightly darker color as being live so that you can discern which cell we're talking about versus which are the neighbors so for example this one has two live neighbors this one for example has three live neighbors and this one also has three live neighbors any one of these three configurations means that the middle cell survives you do this Logic for every cell in your Grid at the same time okay all of these are computed at the same time so we're just looking
4:39:19
at one cell right now but this same logic would be happening with all the cells at the same time before you transit to the next state okay let's look at number two any dead cell with three live neighbors becomes a Life Cell so basically that becomes a birth so for example here in the middle we see that we have a dead cell there's nothing there here we also have a dead cell in the middle and here we also have a dead cell in the middle but notice that in each case that middle cell
4:39:45
has exactly three live Neighbors which means that the cell is born now let's look at number three all other live cells die in the Next Generation similarly all other dead cells stay dead so for example here we have a scenario where the cell has four neighbors here we have a scenario where it only has one neighbor and here we have a scenario when it has no neighbors so in the first scenario here you could basically look at it do the cell is going to die because of overpopulation in
4:40:15
the second and third diagrams the cell will die because it is lonely here is another example of overpopulation this will result in the middle cell being dead now in all these examples you could ask why is it that we don't show the other cells like in the outcome because we're just showing you a single cell computation you would then apply the same logic to every other cell in the grid now it
4:40:39
doesn't have to actually happen at the same time but it has to happen within the same transition here is an actual demo from YouTube of What The Game of Life could look like when you just let it run and as you can see some very interesting patterns emerge just because of these simple rule interactions so depending on what your initial state is you might have a lot of different things
4:41:05
happening here this is pretty exciting and pretty interesting sometimes what will happen as you see for example at the top top we have Behavior known as oscillators you can see that it's basically moving back and forth it's the same pattern that is moving back and forth so this is very very interesting so your task will be basically to design which is write down the requirements and constraints on the application design the user interaction with the game what input and
4:41:30
output will be needed create a high level diagram for all the main pieces which is the objects that you will need and how they will interact then you will architect expand your design into specific classes and objects and look what design patterns could be used in this application hint you could use all of them make sure to abstract the UI layer so that it can be easily swapped In and Out Create
4:41:53
any necessary uml diagrams to make it easier to follow your own design once you have all that you will develop which is write the code for the application based on your architecture make sure to document your code well and then test it here's some considerations or some hints if you will if you are unsure about some of the Python apis you will need then develop a simple POC which is a proof of concept first this allows you to explore and test the apis before you architect
4:42:20
the solution if your architecture is well hashed out then you should be able to create a quick POC with minimal UI initially at least you could for example output the transitions into just debug console you don't have to necessarily start drawing anything on the screen just so that you know that it every tick you're Computing things properly and you can also start with a small grid maybe like a 10 by 10 or 16x 16 concentrate initially on object communication
4:42:45
and process workflow for example generating The Tick at regular intervals use a timer or create your own timer before you code anything consider going through a few high level iterations of your architecture to ensure that you have captured the following all the data objects all the data constraints for validation and also the process algorithm for the transitions how do you actually find the neighbors and how do you make sure that a cell is Dead or Alive we have to assume
4:43:12
a certain finite grid the grid for example could be your whole screen where each cell is a pixel or four pixels or 10 pixels make sure that the UI is flexible so it would be easy to reskin for example stylize the look and feel of the game separate the UI like the classes of the UI away from the rest of the code think about what design pattern you would use to create pluggable renderers for UI
4:43:37
think perhaps strategy this would be both the grid which is the UI as well as the stylization of each cell in the grid you should be able to swap out for example how you present the animation consider also what UI elements for initial stage should be are you going to initialize your grid by using like certain buttons like start stop are you going to have sliders maybe you will uh use a slider to
4:44:03
expand the Grid or Shrink the Grid or perhaps you will have text box input which will basically say hey I would like a grid which is 20 x 20 and then you have to consider how do you input the initial state which is where the organisms are this could be just simply tapping on the screen for example here are some support materials for you to consider please have a look at the game of Life Wikipedia page for more research there's also sample YouTube visualization and you can find
4:44:29
a lot of visualizations on YouTube If you simply plug in Game of Life or John Conway's Game of Life if you want to use some UL modeling tools here is a couple that are free at least in a basic form but you could actually just do it by hand and take a picture of it so if you for example want any feedback from me you could very easily just draw it by hand take a picture and then post the
4:44:53
picture in the forums and you know that's that's good enough you don't have to necessarily create a super duper looking uml diagram I have provided you with some starter code and I would like you to analyze the code that I have given to you break it down into organizational units such as for example the generation timer the display the Next Generation processing Etc the code that I'm giving
4:45:18
you does not actually have a timer the generation is calculated whenever I click on the button so what I would like you to do is make that into a timer and as a hint you could then use an observer pattern for what happens when the timer generates a tick I would like you to come up with what design pattern could be utilized to make this code first of all architecturally flexible written with
4:45:47
interchangeable logic so I could for example just replace the goey for it relatively easily think for example of being able to pause the game as well so like think ahead of what could I do with the UI for example so that I could pause the game or being able to persist and then load back the game for example let's say you've been running it for 20 hours and you would like to save it so that
4:46:12
later on when you restart your system and when you restart your mobile device it continues where it was that's basically the main idea here so let me show you now the actual code quickly here I just have a very very simple single class that basically creates a game of life and it starts like this and now if I click repeatedly kind of pretending to be the timer you can see that the generations are being calculated so I have already given you all the logic in here and what
4:46:43
I would like you to do is first of all break it down into separate classes and then use specific design patterns as you see fit to recreate this code to be much more flexible and to be much more interchangeable the code is provided as an attachment with the resources in the course okay so this is basically it please have a look at this and I'm going to see you in the next lecture and
4:47:08
as part of the assignment at some point I'm going to post an architectural document for you to kind of like check you know whether you're on the right path or not so look for a quick architectural Exposition in some later lectures SL documents all right see you soon and as always keep on coding welcome back let's have a look at how you would start an actual architecture for the game of
4:47:34
life simulation you can go through the following design steps first write down the general workflow of the application based on what you have read about it based on the demos that you've seen Etc Define the inputs and the outputs in general terms perhaps explore a validation as well that's not really necessary but you can start thinking about that as well next you extrapolate and outline the main agents in the application these are the objects that will do the work for example
4:48:07
we could have a grid renderer or a tick Damon which is the timer at this point you're looking for work-based relationships and not really methods or variables Define how these objects will communicate in other words some objects are going to require for something to happen like for example compute the Next Generation you might have an object that deals strictly with that start thinking about what design patterns you will want to use and build your
4:48:33
first proof of concept and then refine keep on going through this as if it was a development cycle so let's look at one of the first steps for this so here is a highlevel workflow create and initialize the simulation grid it could be n * m for example 20 by 80 or 100 by 100 initialize the starting organisms which are the occupied cells on the grid that will be your initial State start the simulation with a tick interval of X seconds it could be 1 second it could be 3
4:49:04
seconds it could be 0.1 seconds depending on how quickly you want the refresh of your generation to be happening how quickly you want your simulation to refresh next at each tick you're going to compute the new generation of organisms one way that you could do this is you could Loop over each element on the grid or on the board and you compute if the organism at that location should live or die then you render the new generation once you have computed all of
4:49:33
the cells in the grid and then at some point if the user wants the simulation stopped then you stop it so you should give your user the ability to stop the simulation this is a very high level workflow but it's a good starting point we can then begin to refine it and add more details such as input and output as well as any validation needed so the next thing that you would do here is you would start to think of what are the main objects that can do all
4:50:00
this congratulations you have finished the course so where do you go from here we have covered what are in my opinion the most fundamental set of design patterns in the software engineering Universe this is a good start actually it is a great start what is needed now is practice and more practice the best thing to do is to try to utilize these patterns in your projects break your projects down across object lines and remember the solid principles as a good
4:50:34
guide to beautiful code there are more patterns to learn so I suggest that you explore those as well we will be creating other design pattern courses in the future so hopefully you're going to look into those as well a few other topics to really SN your teeth into would involve the following when it comes to software architecture you can look up architectural patterns especially anything to do with peer-to-peer communication anything involving microservices and anything to do with
4:51:04
event bus architectural approach these are pretty powerful and important threat safety is another topic that is definitely worth exploring and I would look into the producer consumer problem as a great exploration to understanding threat safety issues State Management which is especially important if you're interested in Python gaming it's very important for you to understand how applications manage their state data this is especially very clear in mobile
4:51:35
applications where you're moving between different screens and you need to remember what was for example created in a previous screen all right that's it for this video see you later I would like to thank you for being a part of this course I really hope that you have learned some useful things and I hope that our paths will cross again and I'll be able to teach you something useful in the future take care and as always keep on coding
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