330 likes | 481 Views
Factory Method. Explained. Intent. Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. a.k.a. Virtual Constructor
E N D
Factory Method Explained
Intent • Define an interface for creating an object, but let subclasses decide which class to instantiate. • Factory Method lets a class defer instantiation to subclasses. • a.k.a. Virtual Constructor • The main intent of the virtual constructor idiom in C++ is to create a copy of an object or a new object without knowing its concrete type and this is exactly what the Factory Method does • Can a constructor be virtual?
Motivation • Frameworks use abstract classes to define and maintain relationships between objects • A framework is often responsible for creating these objects as well • They must instantiate classes but only know about abstract classes - which they cannot instantiate • Factory method encapsulates knowledge of which subclass to create - moves this knowledge out of the framework
Motivation (Cont’d) • Consider a framework for presenting multiple documents to the user • Key abstraction in this framework are Application and Document classes • Both classes are abstract and realized in subclasses • Application class is responsible for managing and creating the Documents as and when required.
Motivation (Cont’d) • The particular Document subclass to instantiate is application specific, and • The Application class can’t predict the subclass of Document to instantiate. As it only knows when a new document should be created. NOT • What kind of a Document to create. • This creates a dilemma…
Motivation (Cont’d) • Factory method solves this problem. It encapsulates the knowledge of which Document to create and moves this knowledge out of the framework. factory method
Motivation (Cont’d) • Application subclasses redefine an abstract CreateDocument operation to return Document subclasses • Once an Application subclass is instantiated, it can then instantiate application specific Documents • We call CreateDocument a factory method as it’s responsible for manufacturing a object.
Applicability • Use the Factory Method pattern when • A class can’t anticipate the class of objects it must create • A class wants it’s subclasses to specify the objects it creates • Classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate.
Participants • Product (Document) • defines the interface of objects the factory method creates • Concrete Product (MyDocument) • Implements the product interface • Creator (Application) • declares the factory method which return a Product type. • May also define a default implementation of the factory method that returns a default ConcreteProduct object • May call the factory method to create a Product object. • Concrete Creator (MyApplication) • Overrides the factory method to return Concrete Product
Collaborations • Creator relies on it’s subclasses to define the factory method so that it returns an instance of the appropriate ConcreteProduct(Subclasses of Product)
Consequences • Factory methods eliminate the need to bind application specific classes into your code • The code only deals with the Product interface; therefore it can work with any user defined ConcreteProduct classes. • A potential disadvantage of using Factory pattern is that client also has to subclass the Creator class in order to create a particular ConcreteProduct object.
Consequences (Cont’d) • Provides hooks for subclasses • Creating objects inside a class with a factory method is always more flexible than creating an object directly • Factory method gives subclasses a hook for providing an extended version of an object • Example: the Document class could define a factory method called CreateFileDialog for opening an existing document.
Consequences (Cont’d) • Connects Parallel Class Hierarchies • Parallel Class hierarchies result when a class delegates some of it’s responsibilities to a separate class • Clients can use factory methods to connect between parallel class hierarchies
Consequences (Cont’d) • The figure class provides a CreateManipulator factory method that lets clients create a Figure’s corresponding Manipulator • Figure subclasses override this method to return an instance of the required manipulator subclass • Alternatively the Figure class may implement CreateManipulator to return a default manipulator. The figure subclasses may inherit the default. • The figure classes that do so need no corresponding Manipulator subclasses. Hence partially parallel class hierarchies • This is how the factory method defines connection between these class hierarchies
Implementation Styles • Two Major Varieties • The case when the Creator class is an abstract class and does not provide an implementation of factory method • The case when the Creator class is a concrete class and has a default implementation of the factory method • The first case requires subclasses to define an implementationas there is no reasonable default • In the second case the subclasses or concrete Creator uses factory method primarily for flexibility.
Implementation Styles (Cont’d) • Parameterized factory methods • create multiple kinds of products • the factory method takes a parameter that identifies the kind of object to create • the object share the Product interface • Naming conventions • The name of the Factory method in code should clearly mark its purpose.
Implementation • So the situation is that you have a set of related concrete classes and there may be some conditions to call any one of them
Implementation (Cont’d) • When we see code like this we know that when it comes to changes and extensions, we will have to re-open this code and examine what needs to be added or deleted. • Often this type of code ends up in several parts of an application making maintenance and updates more difficult and error prone. • By coding to an interface we know we can insulate ourselves with lot of changes occurring down the road • However if you have concrete classes then you might end up in trouble if more concrete classes are added • In other words code would not be closed for modification • So what’s the solution?
Implementation (Cont’d) • Remember the design principle “Identify the aspects that vary and separate them from what stays the same” • We will now see an example implementation of Pizza shop to illustrate the application of this design principle • We will have a function of OrderPizza() to determine one type of Pizza and run different processes for making a pizza.
Implementation (Cont’d) public class Pizza { Pizza pizza; //Declarations public Pizza() { } //Constructor of Pizza //Declarations of Pizza Functions public virtual void Prepare() {} public virtual void Bake() { } public virtual void Cut() { } public virtual void Box() { } //Method of Ordering a Pizza public void OrderPizza() { pizza = new Pizza(); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); } //Return only one type of Pizza
Implementation (Cont’d) • The concrete classes of Pizza like cheese pizza and pepperoni pizza public class CheesePizza : Pizza { public CheesePizza() { Console.WriteLine("Constructing CHEESE PIZZA"); } public override void Prepare() { Console.WriteLine("Preparing CHEESE PIZZA"); } public override void Bake() { Console.WriteLine("Baking CHEESE PIZZA"); } public override void Cut() { Console.WriteLine("Cutting CHEESE PIZZA"); } public override void Box() { Console.WriteLine("Boxing CHEESE PIZZA"); } }
Implementation (Cont’d) public class PepperoniPizza : Pizza { public PepperoniPizza() { Console.WriteLine("Constructing Pepperoni Pizza"); } public override void Prepare() { Console.WriteLine("Preparing Pepperoni Pizza"); } public override void Bake() { Console.WriteLine("Baking Pepperoni Pizza"); } public override void Cut() { Console.WriteLine("Cutting Pepperoni Pizza"); } public override void Box() { Console.WriteLine("Boxing Pepperoni Pizza"); } }
Implementation (Cont’d) • Now we will add code that determines different types of pizza and then goes about making it. The order pizza function will change public void OrderPizza(String type){ if (type.Equals("Cheese")){ pizza = new CheesePizza(); //Calling object of Subclass for Cheese Pizza } else if (type.Equals("Pepperoni")) { pizza = new PepperoniPizza();//Calling object of Subclass for Pepperoni Pizza } pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); }
Implementation (Cont’d) • We have now passed the type of pizza (string type) to OrderPizza function • Based on the type of pizza we instantiate the correct concrete class and assign it to the pizza instance variable. • Note that each pizza here would have to implement the pizza interface. • So each pizza type knows how to prepare itself. • But the pressure in on to add or subtract pizza types
Implementation (Cont’d) public void OrderPizza(String type){ if (type.Equals("Cheese")){ pizza = new CheesePizza(); } else if (type.Equals("Pepperoni")){ pizza = new PepperoniPizza(); } // “here we can add more pizza types” pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); } This part of code is changing This part of code is supposed to remain unchanged
Implementation (Cont’d) • Clearly we can now make out what’s variable in our code and we need to separate it • What we can do is that place the object creation code into another object (class) whose sole purpose is to create different types of pizza • This object can be called a SimplePizzaFactory • Once we have a factory the OrderPizza() becomes a client of that factory. Anytime it needs a pizza it asks the factory to make one • OrderPizza will get a correct pizza and would call prepare(),bake(),cut() and box()
Implementation (Cont’d) • Here is the new class SimplePizzaFactory public class SimplePizzaFactory { public SimplePizzaFactory() { } public Pizza CreatePizza(String type)//This method would be used by all to create new objects { Pizza pizza = null; if (type.Equals("Cheese")) { pizza = new CheesePizza(); //Calling object of Subclass for Cheese Pizza } else if (type.Equals("Pepperoni")) { pizza = new PepperoniPizza();//Calling object of Subclass for Pepperoni Pizza } return pizza; } }
Implementation (Cont’d) • The orderpizza method also be moved out of the Pizza class and would be changed as under public class PizzaStore { SimplePizzaFactory factory = new SimplePizzaFactory(); //Creates the factory public PizzaStore() { } public void OrderPizza(String type) { Pizza pizza; pizza = factory.CreatePizza(type); //Get the correct pizza from Factory pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); }//Method of Ordering a Pizza }
Implementation (Cont’d) • Now the Pizza class would be changed as under public abstract class Pizza //Superclass Pizza { public Pizza() { } //Contructor of Pizza //Declarations of Pizza Funtions public virtual void Prepare() { } public virtual void Bake() { } public virtual void Cut() { } public virtual void Box() { } }
Implementation (Cont’d) • The calling program for our code class Program { static void Main(string[] args) { PizzaStorepizzastore = new PizzaStore();// The client just knows the pizza store pizzastore.OrderPizza("Pepperoni"); Console.ReadLine(); } }
Quiz # 1 • Elements of a Design Pattern with an example • Describe Scope Based Design Patterns Classification?