Factory Method Design Pattern

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone wes here welcome to the first installment of my design pattern series in which we'll be exploring all the design patterns that are covered in the famous gang of four design patterns book i'll be making a single video for each pattern we'll be doing the implementations in c sharp and i'll be using food related businesses as a motivation for using each pattern to provide some context i'm also going to be introducing common dependencies to each of the programs that we build so you'll see things like database connections and loggers payment processors and amqpqs some of the really common dependencies that you'll see in real world software so hopefully this lets us kind of contextualize these patterns and put them into more realistic use cases so if you get anything out of this series if you enjoy these videos i'd really appreciate it if you liked and subscribe to find out when i release the next design patterns video in this video we're going to dive into one of the most fundamental creational patterns which is the factory method pattern and with the factory method we'll see how by separating the logic for creating objects from where those objects are used we can really make our software much more extensible we're going to be looking at an example of creating a a service that registers delivery vehicles for delivering food and we'll see how we can register either bicycles to deliver food or cars and we're going to be able to take some data at run time to into our client to actually determine what concrete type gets created our client-side code however will be totally agnostic as to the type of vehicles that gets created when it cues them up in an amqp queue for delivery so that's kind of a lot all at once without looking at the code but first we're going to dive into the definition and the motivation and then we'll look explicitly at this code together so i hope you enjoyed the video let's go ahead and dive in okay so we're going to kick things off here with the factory method and just at a very high level here regarding the series the way this is going to work is each video we're going to look at the definition of a new design pattern and then we're going to look at some diagrams just to kind of get a sense for how things fit together and then we're going to dive right into some real world example code and each of the examples are going to revolve around a food based business where we implement that particular design pattern and in some cases look at other real world dependencies that might integrate with the solution so things like database connections or queues or payment processors and that sort of thing but we're really going to focus here first on some of the most fundamental patterns and so we're kicking things off here with the factory method so let's take a look so the factory method is a creational pattern that uses factories to instantiate objects without having to specify the exact class of the object that will be created okay so we have a little bit to unpack here first of all we're talking about a creational pattern so within the context of the gang of four design patterns we have three main categories of design patterns we have creational patterns of which the factory method is an example we have structural patterns and we have behavioral patterns so a creational pattern is as the name suggests a pattern that is related to the creation of new objects so here we're talking about object instantiation and each of the creational patterns is really going to be sort of an alternative way to use objects than just using a regular constructor so we're not going to be creative we're not going to be using new for instance we're not going to be saying a new instance of some concrete type so we'll get to that momentarily so we know we're dealing with a creational pattern that uses factories to instantiate objects when we talk about factories within the context of object-oriented programming we mean methods or functions or other objects that create new objects and so the return types from factories are going to be new instances of objects in themselves and the factory is going to be something that sort of isolates or encapsulates that that behavior of creation of some new object so these factories are going to instantiate objects without having to specify the exact class of the object that will be created so why might this be important well hopefully this becomes more clear over time it might not be so evident immediately as to why it's useful to be able to create something without knowing exactly what that object is going to be without specifying the exact type but we'll look at that shortly first of all one thing to keep in mind is that this separation between the code that creates products our factories and the code that uses the factories and uses those products is separate and by separating the code that creates products of a type from the code that uses those products we can introduce new types of products without modifying existing client code so here we're kind of talking about or this sort of relates to the open close principle in solid so if you recall from solid the solid design principles open close means that our code should strive to be open for extension and close for modification that means we should be able to add to our code without changing existing code when we want to implement new features we want to extend the behavior of our software so this is about extensibility and it's also about separation of concerns we're going to separate code that's involved with the creation of objects so that we can work on it separately from the code that uses objects that get created so by doing this when we want to say introduce new types that get created as long as we're designing carefully around interfaces then what we can do is we can introduce new types that get created without modifying the code that is involved with working with those classes so that is sort of one of the main motivations here of the factory method the other one that i want to mention here which is of course related but i don't hear this mentioned so often and i really wish people would talk about it a little bit more because this is one of the sort of fundamental benefits of things like the factory method in terms of designing software and it's that it's one technique for allowing us to talk about objects in our system and to allow them to collaborate at different levels of abstraction so to take a real world or a sort of a real world analogy if you will not from code but just from life so here i've got a cup of coffee and if i say to you would you like some coffee and you're a coffee drinker let's say then you might say let's say you say yes you likely don't really care in this example whether or not i give you coffee that comes in a cup like this or if i give it to you in a traditional ceramic coffee cup you're going to accept the coffee and drink it all the same because we're just talking at a high level of abstraction i'm just saying do you want some coffee i don't say would you like some starbucks or whatever in a paper cup etc i don't give you the implementation details we're just talking at a high level of abstraction and then i can fulfill that obligation let's say of giving you a cup of coffee while i take care of the implementation details you just take care of the decision of what you want to do with the coffee which is likely just drink it right so we're talking at a high level of abstraction and then the implementation details as regards our transaction here are just handled by in this case me the factory if you will that's creating the or that's producing the the coffee to give you so we can think of the software that we design in a similar fashion we want to be able to talk about our software often at different levels of abstraction we want to work on the code that works at a high level you know differently than then we want to separate that from the code that takes care of the implementation details and in fact in general we don't want to code against the implementation details we want to code against the abstractions we want to write our business logic as much as possible so that it adheres to things like interfaces and abstractions so that we can switch out the implementation details and implement them in as many ways as we like without changing our core business logic that's a long-winded way of saying that the factory method is one way of achieving this particularly with regards to creational patterns all right so with that let's take a look at a sort of typical uml diagram now if you're anything like me these diagrams aren't really the most useful way of learning at least at first but they do become very useful once you kind of get a grasp for it at least in my case i know some people would prefer to see the diagrams first so i'm going to throw them in here and we're going to cover sort of what they mean and the particular flavor of uml that i'm using here if there is such a thing i'm borrowing heavily from the the format of uml that's used in the design patterns book with regards to like the arrows and things i introduced a couple of my own conventions here that we'll use throughout this series so and those are that yellow boxes here will always refer to interfaces with the dotted lines and then the gray boxes with dotted lines here are going to refer to abstract classes now in the design patterns book a lot of times they talk about like abstract abstractions or abstract things and in some cases those could be implemented as interfaces and in some cases they could be implemented as abstract classes and sometimes both so i've made some design decisions here in some cases for the purposes of the example but know that there are slight tweaks on many of these patterns as we could implement them in c sharp or say java for instance okay so let's dive into what's happening here the other the other sort of conventions i want to mention are that the arrows the solid arrow with the open head here pointing upwards means that the blue class underneath and blue is going to be reserved for concrete classes this class extends the abstract class and then likewise the dotted arrow over here with the open head means that this class implements this interface and then finally the dotted arrow that goes left to right in this case with a solid arrowhead means that this class concrete creator in this click in this case instantiates this class and so we'll use the same convention throughout the entire series many of the diagrams will get much more complex and so this is another good reason to have this the factory method up front because it introduces all of the conventions here with a relatively simple use case so what we have is we have some concrete creator class that has on it a factory method this factory method returns something that is an i product so something that implements our iproduct interface it also has perhaps other methods on it and we know with our abstract classes in c-sharp we can also implement default implementations as well we have our concrete creator and this is a concrete class that extends our creator class and it's going to override this factory method to return a particular implementation of eye product in this case concrete product and so our concrete creators factory method is going to be able to instantiate concrete products and as we can see the concrete products must implement i product as the abstract class of course specifies here for us and so this is the overall relationship for what we have we have an abstract class that says that we have a factory method on it a concrete implementation of that class which returns concrete implementations of the products now this another method can get pretty interesting because our creator here can continue to deal with objects in the abstract and we can have we can have this effectively be a default implementation of something on like our concrete creator for instance and we'll take a look at that shortly now let's look at the example that reflects what we actually have in the code base and again there's a link in the description to all the code and we're going to dive into it here shortly so what we have in this example is a factory that is going to be used for creating different types of delivery vehicles so we're going to have an abstract class delivery creator it's going to have a method on it called register vehicle and this is going to be our factory method this is going to return something that implements i deliver's food and it'll have another method on it called qvehicle for delivery so this is our delivery creator abstract class we can't create new instances of abstract classes but we can and must extend them in order to use them and so we have two implementations we have a bike delivery creator which overrides register vehicle to produce a concrete bike type which implements ideliver's food and then we have an alternative variation car delivery creator which overrides register vehicle to produce a car type and car and bicycle as you can see are both of course implementing eye delivers food and they have their own implementation details as to how they go about executing that delivery so the important thing that i want to talk about here to circle back to that idea of talking about things at different levels of abstraction i've put this dotted line in here to sort of illustrate the fact that above the line we're talking at a higher level of abstraction than we are below the line so below the line we have implementation details we have literal implementations of an abstract class and an interface above the line we're talking about behavior specifically we're talking about it at a high level the delivery creator doesn't know and doesn't care about the particular implementations of things it just says that there must be something when it's extended that delivers food likewise eye delivers food can be implemented by any number of objects that we create as long as those objects adhere to this interface and so again just something to keep in mind here this will help really help us drill in and understand why it's so important that we can do this to sort of see this separation here that we have two levels of abstraction so with that i think we should dive into some code and we can walk through a concrete example okay so here we are in the example for our factory method you'll notice that the code is sort of divided between two places we have our example programs and then i have a namespace for each of the three types of patterns creational patterns behavioral and structural and then each of those directories themselves contains our namespaces contains a console application so we're going to be looking at the food delivery service console application and then likewise i have three separate class libraries for in the solution for each of the three types of patterns and then within creational patterns here we're going to be looking in the factory method namespace so let's start out here by looking at the console app first so we can see some comments here that this is an example that uses the factory method and we talk a little bit about the benefits here but let's just look at the console app and see what's happening and then we'll dive into how the pattern is actually being used so we're going to queue up or create some dependencies here a logger and a queue delivery queue then we're going to use the logger to log something out to our terminal we're going to read in the delivery type that a particular user of our application is going to choose so we're going to get that information in from our user via this client i want us to think of the main method here as our client code then we do a little bit of handling here to make sure that what they type in isn't null or white space and then we enter this try catch block and i'll let you read through the comments if you're interested but i'm just going to kind of talk naturally about what's happening here we have a delivery creator type and we're calling build delivery creator it's taking in what our user types in this case just a string from standard in and we're also passing our delivery queue which is a dependency that we created here in our client then if there's an error we're going to catch the exception and say if there was an error processing the delivery so we really have some very simple code here this build delivery creator is a static method that returns a delivery creator so the return type here is an abstract class delivery creator let's take a look at this really quickly this is our abstract class this is our creator class and it declares that method register vehicle that returns an ideliver's food object so this guy has a constructor and i've got some backing fields for the dependencies if they're needed in this class we have our protected abstract class register vehicle it's an abs or abstract method rather of course this method doesn't have an implementation in the class abstract class itself but we do have a method that does have a default implementation here called q vehicle for delivery and you can see that what this does is it calls register vehicle which we know is going to return something that implements this interface and we're going to create this vehicle payload which is going to serialize that object and then pass it to our queue so this particular implementation detail here is not related to the pattern itself it's just something that you might see in real world code where we're invoking a queue and serializing some object to basically put into that queue so we're adding it to the queue and then we log the info the important thing here is that we have this abstract method register vehicle that returns some type of thing that delivers food and we have the ability to cue it and this method actually calls our abstract method okay so so far so good that's pretty interesting so our sort of factory method here is going to return that delivery creator of some type okay so we new up a logger here we control what the options are so here we say maybe bicycle or a car and if our valid delivery options don't contain that type that the user provides then we're going to tell them that there is an invalid operation here and they can't set up a delivery without a valid type otherwise we enter this switch statement and using a little bit of c 9 syntax here i believe where we have a bicycle in one case and in the case that they enter a bicycle we're going to return a bicycle delivery creator otherwise we're going to return a car delivery creator otherwise we blow up again and throw an invalid operation exception so thanks to the fact that we have polymorphism in our language specifically dynamic polymorphism meaning that at runtime the actual binding of a concrete type happens here this is late binding so a particular type of delivery creator gets returned from this method depending on what user date is provided at runtime so if we go if we think back to the definition of a factory method a factory method should be able to create objects without declaring what the concrete type is that's created and so we're specifically doing that here we build this delivery creator and we make a decision at run time based on what that particular type is going to be to fulfill those implementation details but where this delivery creator is used if we head back up into our client this guy is just calling q vehicle for delivery without our client code here knowing whether or not that's a bicycle or a car and that's huge if you think about it that's sort of like the whole point of this this pattern this means that our client-side code here can work at a high level of abstraction saying i know i'm going to get something here that's a delivery creator i know that the thing that returns it is going to implement something that can queue up a vehicle for delivery at this level of abstraction i don't care about the implementation details i don't care if it's a bike or a car or if there's a new type that comes along as long as that new type that comes along in the future implements the interface i delivers food then we're all good we can continue coding here without coding against those implementation details so this is another sort of fundamental principle to solid design and and object-oriented design in general in that we want to write our code we don't want to depend on implementation details we want to depend on abstractions and here we're depending this method is dependent upon an abstract class delivery creator which itself can cue vehicles for delivery without knowing the particular type of vehicle that it's going to actually create so hopefully that's clear as to why this is so powerful this code if it were extended into more real world application you might have this in a completely separate class this might be part of a different assembly that collaborates with our factories here the point is that we can write against these abstractions and have them create the objects for us without depending on the implementation details like the particular delivery creator in this case and then the types of vehicles that they create and in fact if we want to take a look at it the bicycle delivery crater here registers a vehicle and it's going to register a bicycle this is going to get serialized and pushed to our queue a car could have lots of different data on it and likely would because it's a different implementation detail if i were to give you coffee in a cup like this a paper cup versus coffee in a ceramic cup those two objects the ceramic cup and the paper cup have fundamentally different properties but they still fulfill a fundamentally similar interface in that they deliver liquid so it's the same concept here the objects themselves you can see do have different properties on them and they both implement the one method that's required by our interface which is to deliver food or to deliver in this case and so we just await a task from result and return return that here okay so that is the factory method in a nutshell let's go ahead and spin up the terminal and take this program for a spin okay so i'm in the root of the project directory and i'm just going to change directories into example programs and then you'll see here that we have our three main types i'm going to go into creational patterns recreational examples rather and then food delivery service and we're just going to run dot net run so this is going to fire up that simple console application and we can see that we want to enter a delivery type i'm going to type in bicycle here and you can see that what happens is that we register a bicycle to deliver food and our queue is telling us by means of a different color here just to signal what's happening that a as we can see a bicycle with a color make model and style is getting queued up to our queue for delivery if we run it again and we specify a car in this case providing different data at run time to our application we are now registering a card deliver food and you can see we have a different type that gets serialized here we have a license plate for instance on this particular object that's getting serialized so that's it that's a quick example in the next video we're going to pick up with another design pattern i hope you guys enjoyed this quick overview of the factory method there are several more creational patterns to take a look at and if you're enjoying this series i'd really appreciate if you liked and subscribed thanks for watching i'll see you in the next video
Info
Channel: Wes Doyle
Views: 6,219
Rating: undefined out of 5
Keywords:
Id: ryUB-bU3Se8
Channel Id: undefined
Length: 26min 55sec (1615 seconds)
Published: Fri Jan 22 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.