Factory Pattern: Motivation • Consider the following scenario: – You

Factory Pattern: Motivation
• Consider the following scenario:
– You have a superclass (P roduct) and a set of subclasses that inherit from
it (P roductSubclass1, P roductSubclass2, ...)
– As usual, there are a number of methods that the subclasses share in common, that they inherit from the superclass
– You have client code that needs to be able to create objects of the various
subclasses
∗ We’ll call this the orderProduct method
∗ We’ll refer to the created objects as products
∗ To specify to which subclass a product should belong, orderProduct will
have a parameter which specifies the type
1
Factory Pattern: Motivation (2)
– For example:
public class Product {
//constructor
...
... step1(...) { //Methods that apply to all subclasses in common
...
}
... step2(...) {
...
}
...
}
public class ProductSubclass1 extends Product {
...
}
public class ProductSubclass2 extends Product {
...
}
public class ProductClient {
...
Product orderProduct (String type) {
Product product;
if (type.equals("subclass1"))
product = new ProductSubclass1();
else if (type.equals("subclass2"))
product = new ProductSubclass2();
...
product.step1();
product.step2();
...
return product;
}
...
}
• The problem with this approach - as usual - is limited flexibility
– If we want to add new subclasses - types of products - (or remove existing
ones), the client’s code must be altered
– The above design violates the Open/Closed Principle, among others
2
Factory Pattern: The Simple Factory Model
• We want to encapsulate what varies
– The part that varies is the product creation - We want to encapsulate the
code that creates new products
– To accomplish this, we’ll create a new class that we’ll call a factory
– It’s purpose is to create new products of a desired type
– The client will have an instance variable for the factory
∗ The factory will be used from within the orderP roduct method of the
client to create products
3
Factory Pattern: The Simple Factory Model (2)
• Sample code:
//Superclass and subclasses as before
public class ProductSubclass1 extends Product {
...
}
public class ProductSubclass2 extends Product {
...
}
public class SimpleFactory {
public Product createSubclassProduct (String type) {
Product product null;
if (type.equals("subclass1"))
product = new ProductSubclass1();
else if (type.equals("subclass2"))
product = new ProductSubclass2();
...
return product;
}
public class Client {
SimpleFactory factory;
public Client (SimpleFactory factory) {
this.factory = factory;)
}
Product orderProduct (String type) {
Product product;
product = factory.createSubclassProduct(type);
product.step1();
product.step2();
...
return product;
}
...
}
4
Factory Pattern: The Simple Factory Model (3)
• Summary of the changes:
1. Create a new factory class
– The factory implements the method for creating new products
– This code had originally been in the client
2. Remove the product creation method from the client
– Replace it with a call to the product creation method in the factory
3. Add an instance variable for the factory in the client
– Its value is passed in via the constructor
• Class diagram:
• Simple Factory is not a true pattern
5
Factory Pattern: Further Considerations
• Suppose you now want to have different types of clients
– Each client type produces the same set of products, but
– Each product of a given subclass varies in some way from client type to
client type
• We can think of each client type as representing a category for the subclasses
– For example , there is a
1. A category1 for ProductSubclass1, ProductSubclass2, ...;
2. A category2 for ProductSubclass1, ProductSubclass2, ..., etc.
∗ Each category makes products for the same set of subclasses
∗ But each category produces them with slightly different characteristics
– For the subclasses, some inherited methods may be implemented exactly
the same no matter what category a product belongs to
– Other methods may differ based on the category of the product****
• One approach would be to have a factory for each category
– A factory for category1
– A factory for category2
– ...
6
Factory Pattern: Further Considerations (2)
• Class diagram:
• Sample driver code:
Category1Factory factory1 = new Category1Factory();
Client cat1Client = new Client(factory1);
cat1Client.orderProduct("class1");
Category2Factory factory2 = new Category2Factory();
Client cat2Client = new Client(factory2);
cat2Client.orderProduct("class1");
7
Factory Pattern: Further Considerations (3)
– In the above example
∗ Two factories are created:
1. One for creating category1 products
2. One for creating category2 products
∗ Two objects are created:
1. cat1Client, which ”orders” class1-type products from a category1 factory
2. cat2Client, which ”orders” class1-type products from a category2 factory
∗ Both clients end up with class1 products, but they differ as per the
category to which they belong
• A better approach would be to link the product creation back to the client,
but with more flexibility than our original approach had
– To achieve the flexibilty of having product creation back in the client:
1. Make an abstract class for clients (Client)
2. Move the createP roduct method back into the Client class
∗ This method becomes abstract here
∗ With the createP roduct back in the Client class, Client becomes a
superclass for factories
3. Make a client subclass for each category (Category1F actory, Category2F actory,
...)
∗ These subclasses inherit from Client
∗ Since createP roduct is abstract in Client, each of these subclasses
must implement it
∗ These implementations will reflect the variations peculiar to the category
∗ The subclasses are the actual factories
4. We still have a generic P roduct class
5. Create a subclass for each combination of product subclass and category
∗ The subclasses all inherit from the P roduct class
8
Factory Pattern: Further Considerations (4)
– Class diagram:
9
Factory Pattern: Further Considerations (5)
– Code:
public abstract class Client {
public Product orderProduct(String type) {
Product product;
product = createProduct(type);
product.step1(...);
product.step2(...);
...
return product;
}
abstract Product createProduct(String type);
}
public abstract class Product {
... step1(...) { //Methods that apply to all subclasses in common
...
}
... step2(...) {
...
}
...
}
public class Category1Factory extends Client {
...
Product createProduct (String type) {
if (type.equals("subclass1"))
return new Category1Subclass1Product();
else if (type.equals("subclass2"))
return new Category1Subclass2Product();
...
else
return null;
}
...
}
10
Factory Pattern: Further Considerations (6)
public class Category1Subclass1Product extends Product {
//constructor
//Override any steps that are specific to Catgory1Subclass1 products
...
}
public class Driver {
public static void main (String[] arge) {
Client cat1Factory = new Category1Factory();
Client cat2Factory = new category2Factory();
Product product = cat1Factory.orderProduct("type2");
product = cat2Factory.orderProduct("type2");
...
}
11
Factory Pattern: The Factory Method Pattern Defined
• The Factory Pattern (p 134):
Defines an interface for creating an object, but lets subclasses decide
which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
• Class diagram:
• The pattern encapsulates the instantiation of concrete types
1. The Creator class provides an interface for creating products
– It defines the factory structure
2. The concrete subclasses of Creator actually generate the products
3. Methods in Creator other than f actoryM ethod are used only to operate
on the products
• ”Deciding by subclasses” is a bit misleading
– It’s the user/programer who actually decides what subclass will be instantiated
– The use of ”decides” is meant to imply loose coupling between the client
and Creator
– Creator doesn’t know what products are being created
12
Factory Pattern: The Factory Method Pattern Defined (2)
• Even when only a single concrete creator is needed, the Factory Method is
warranted
– It decouples the implementation of a product from its use
– It facilitates addition of additional concrete creators
– It facilitates modification of implementation
• While similar to the Simple Factory, Factory is different
– In Simple Factory, the factory is composed with the client
– In Factory, each client must implement the factory method
• Creator and f actoryM ethod can be concrete
– This allows creation of products even when there are no subclasses
• There can be a single type of product rather than variations (types)
– In such cases, no parameter needs to be passed to the factory method
13
Factory Pattern: Dependency Inversion Principle
• The Dependency Inversion Principle (p 139) (Principle 6)
Depend upon abstractions. Do not depend upon concrete classes.
• It imposes a stricter condition than the ”Program to an interface, not an implementation” Principle
– High-level components should not depend on low-level ones
– Both should depend on abstractions
– This is an ”inversion” of the usual type of thinking in OO design
• In our original approach (Factory Pattern Motivation), if we had taken into
account categories of products, we would have ended up with code similar to
public class Client {
public Product createProduct (String category, String type) {
Product product = null;
if (category.equals("category1"))
if (type.equals("type1"))
product = new Category1Subclass1();
else if (type.equals("type2"))
product = new Category1Subclass2();
...
if (category.equals("category2"))
if (type.equals("type1"))
product = new Category2Subclass1();
else if (type.equals("type2"))
product = new Category2Subclass2();
...
...
}
step1();
step2();
...
}
14
Factory Pattern: Dependency Inversion Principle (2)
• Class diagram:
• This violates the Inversion Principle
15
Factory Pattern: Dependency Inversion Principle (3)
• A reworking (as in our refinement) that conforms to the principle would look
like this
• Guidelines for designing to this principle:
1. No variable should hold a reference to a concrete class
2. No class should derive from a concrete class
3. No method should override an implemented method of any of its base classes
16
Factory Pattern: Abstract Factory Pattern Motivation
• Consider an extension to our original problem
– Suppose that in addition to having different categories for our products,
some categories build a given subclass with different ingredients
– I.e., we now have f amilies of ingredients
• To handle this, we can use ingredient factories
– We’ll use an interface for the ingredient factory
∗ An ingredient factory creates all ingredients needed for the product
– We’ll need a class for each of the ingredients
– We’ll use concrete factories for each category of ingredients
– In the constructor for a (product) category class, pass in the type of product
to be instantiated (as usual)
– In the constructor for a product class, pass in an ingredient factory to be
used for the ingredients for the product
– In the product interface, the prepare method becomes abstract
– The product subclasses implement the prepare method using the ingredient
factory passed to them
– Product factories instantiate an ingredient factory of the appropriate type
to be passed to the product’s constructor
17
Factory Pattern: Abstract Factory Pattern Description (2)
• Code:
public interface ProductIngredientFactory {
public Ingredient1 createIngredient1();
public Ingredient2 createIngredient2();
...
}
public Category1IngredientFactory implements ProductIngredientFactory {
public Ingredient1 createIngredient1() {
create new someKindOfIngredient1();
}
public Ingredient2 createIngredient2() {
create new someKindOfIngredient2();
}
...
}
public abstract class Product {
Ingredient1 ingredient1;
Ingredient2 ingredient2;
...
abstract void prepare();
//implemented by subclasses, which use appropriate ingredients
... step1(...) { //Methods that apply to all subclasses in common
...
}
... step2(...) {
...
}
...
}
public class ProductSubclass1 extends Product {
ProductIngredientFactory productIngredientFactory;
public ProductSubclass1 (ProductIngredientFactory productIngredientFactory) {
this.productIngredientFactory = ProductIngredientFactory;
...
}
void prepare () {
ingredient1 = productIngredientFactory.createIngredient1();
ingredient2 = productIngredientFactory.createIngredient2();
...
}
}
18
Factory Pattern: Abstract Factory Pattern Description (3)
public class ProductSubclass2 extends Product {
ProductIngredientFactory productIngredientFactory;
public ProductSubclass2 (ProductIngredientFactory productIngredientFactory) {
this.productIngredientFactory = ProductIngredientFactory;
...
}
void prepare () {
ingredient1 = productIngredientFactory.createIngredient1();
ingredient2 = productIngredientFactory.createIngredient2();
...
}
}
public abstract class Client {
public Product orderProduct(String type) {
Product product;
product = createProduct(type);
product.step1(...);
product.step2(...);
...
return product;
}
abstract Product createProduct(String type);
}
19
Factory Pattern: Abstract Factory Pattern Description (4)
public class Category1Client extends Client {
...
protected Product createProduct (String type) {
//New composition of ingredient factory
ProductIngredientFactory productIngredientFactory = new Category1IngredientFactory();
if (type.equals("subclass1"))
return new Category1Subclass1Product(ingredientFactory); //New ingredient factory parameter
else if (type.equals("subclass2"))
return new Category1Subclass2Product(ingredientFactory);
...
else
return null;
}
...
}
public class Category1Subclass1Product extends Product {
//constructor
//Override any steps that are specific to Catgory1Subclass1 products
...
}
public class Driver {
public static void main (String[] arge) {
Client cat1Client = new Category1Client();
Client cat2Client = new category2Client();
Product product = cat1Client.orderProduct("type2");
product = cat2Client.orderProduct("type2");
...
}
20
Factory Pattern: Abstract Factory Defined
• Abstract Factory (p 156):
Provides an interface for creating families of related or dependent objects
without specifying their concrete classes.
• Class diagram:
21
22
Factory Pattern: Abstract Factory Defined (2)
• The factory decouples the client from the concrete product factories
• This allows substitution of factories to get different behaviors
• The concrete factories make the same products - they’re just implemented
differently
23