Skip to main content

Design Pattern: Factory Pattern

Free2015-03-06#Design_Pattern#工厂模式#工厂方法模式#抽象工厂模式#Factory Pattern

I used to think that defining a XXXFactory class to handle new objects was the Factory Pattern, and for convenience, I usually defined the Create method in the factory class as static. After careful study, I realized the Factory Pattern is far more complex than this. Moreover, strictly speaking, this approach, known as the "Simple Factory Pattern," cannot truly be called a "Pattern".

no_mkd

1. What is the Factory Pattern?

1.“Simple Factory Pattern”, Simple Factory Pattern

It is the common way of defining a static method in a Factory class to handle new objects. As mentioned in the summary, “strictly speaking, this approach, known as the “Simple Factory Pattern,” cannot truly be called a “Pattern””. Although static factory methods are not truly a “Design Pattern”, this approach is widely used and brings some benefits, so we cannot abandon it just because it is not a “Design Pattern”.

2. Factory Method Pattern

It is the core of the Factory Pattern. It defines an abstract “factory method” to handle new objects, and the extension classes of this class implement how to new the process.

This approach successfully separates the object creation process from object behavior (no explanation for now), specifically it “delegates down” object creation to subclasses. (Note the verb here——“delegates down”)

3. Abstract Factory Pattern

It is an application and extension of the Factory Method Pattern. It encapsulates multiple “factory methods” in an abstract factory class to achieve the goal of “newing a group of objects”.

2. General Approach (Without Factory Pattern)

Let's open a small shop selling clothes. First, we need a ClothesStore class to handle receiving orders and returning finished clothes. ClothesStore class needs to depend on Clothes class, after all, the specific object to operate on is Clothes. ClothesStore also needs to have the process of making clothes (production, processing, decoration, etc.). So our ClothesStore will be like this:

package FactoryPattern;

/**

  • @author ayqy
  • Do not use Factory Pattern, directly create concrete objects

*/ public class ClothesStore {

String type;//Define clothes type
Clothes clothes;//Clothes object

/**
 * @author ayqy
 * Define internal class Clothes
 *
 */
class Clothes{
	String type;//Type
	String desc;//Description
	String cloth;//Fabric
	
	public Clothes(String type, String desc, String cloth){
		this.type = type;
		this.desc = desc;
		this.cloth = cloth;
	}
	
	public String toString(){
		return type+ "," + desc + "," + cloth;
	}
}

public ClothesStore(String type){
	this.type = type;
}

/**
 * @return Return undecorated clothes
 */
private Clothes createClothes(String type){
	if(type.equals("外套")){
		return new Clothes("外套", "一件好看的外套", "麻布");
	}else if(type.equals("内衣")){
		return new Clothes("内衣", "一件舒适的内衣", "棉布");
	}else
		return null;
}

/**
 * @return Return finished clothes
 */
public Clothes getClothes(){
	clothes = createClothes(type);
	decorate(clothes);//Decorate clothes
	
	return clothes;
}

private void decorate(Clothes clothes){
	//Add decorations to clothes (patterns, designs, etc.)
	System.out.println("Carefully decorated, clothes become more beautiful");
}

}

P.S. Clothes is defined as an internal class in the code, just to save space and simplify structure, the effect is the same

We find that ClothesStore has a strong dependency on Clothes (over-depending on "concrete" is not a good thing...). Once Clothes changes (e.g., new member variables and member functions), our ClothesStore may have to be modified accordingly, especially the part responsible for newing clothes objects:

/**
* @return Return undecorated clothes
*/
private Clothes createClothes(String type){
if(type.equals("外套")){
		return new Clothes("外套", "一件好看的外套", "麻布");
	}else if(type.equals("内衣")){
		return new Clothes("内衣", "一件舒适的内衣", "棉布");
	}else
		return null;
}

Once there is a new type we need to add an else if to cope with changes. Moreover, since ClothesStore is bound to the specific Clothes class, it leads to ClothesStore being not easy to extend and hard to reuse.

3. “Simple Factory Pattern”

Move the createClothes method to ClothesFactory as a static factory method, like this:

package FactoryPattern.SimpleFactoryPattern;

/**

  • @author ayqy
  • Define static factory method

/ public class ClothesFactory { /* * @return Return undecorated clothes */ public static Clothes createClothes(String type){ if(type.equals("外套")){ return new Clothes("外套", "一件好看的外套", "麻布"); }else if(type.equals("内衣")){ return new Clothes("内衣", "一件舒适的内衣", "棉布"); }else return null; } }

P.S. This time Clothes must be taken out as an independent class (internal class cannot be accessed), besides, the getClothes method in ClothesStore also needs to be changed accordingly... Don't mind these details

After applying the Simple Factory Pattern, we indeed isolated the part of newing objects. The benefit of doing this is:

When Clothes changes, we can directly modify the create method in ClothesFactory, instead of searching for the Create part in the long code of ClothesStore. Moreover, compared with the previous "General Approach", the code has hardly changed, it is very easy to change the previous code to the code after applying the Simple Factory Pattern, so many developers like this way.

“Simple Factory Pattern” is not a real Factory Pattern, its advantages are also extremely limited, but it doesn't matter, we still have Factory Method Pattern, this time it is a genuine Factory Pattern

4. Factory Method Pattern

Define abstract “factory method”, responsible for concrete implementation, the brand new ClothesStore will be like this:

package FactoryPattern.FactoryMethodPattern;

/**

  • @author ayqy
  • Use Factory Method Pattern, define abstract factory method to new products

*/ public abstract class ClothesStore {

String type;//Define clothes type
Clothes clothes;//Clothes object

public ClothesStore(String type){
	this.type = type;
}

//Define factory method, implemented by extension classes
public abstract Clothes createClothes(String type);

/**
 * @return Return finished clothes
 */
public Clothes getClothes(){
	clothes = createClothes(type);
	decorate(clothes);//Decorate clothes
	
	return clothes;
}

private void decorate(Clothes clothes){
	//Add decorations to clothes (patterns, designs, etc.)
	System.out.println("Carefully decorated, clothes become more beautiful");
}

}

We find that ClothesStore becomes Abstract, inside encapsulates the clothes production process and implementation details, but does not implement the Create process

The next step is to extend the ClothesStore class, implement concrete Create details, like this:

package FactoryPattern.FactoryMethodPattern;

/**

  • @author ayqy
  • Extend ClothesStore, implement concrete production details

*/ public class DefaultClothesStore extends ClothesStore{

public DefaultClothesStore(String type) {
	super(type);
}

/* 
 * Implement concrete production details
 */
 @Override
public Clothes createClothes(String type) {
	if(type.equals("外套")){
		return new Clothes("外套", "一件好看的外套", "麻布");
	}else if(type.equals("内衣")){
		return new Clothes("内衣", "一件舒适的内衣", "棉布");
	}else
		return null;
}

}

Well, now we have successfully applied the Factory Method Pattern, let's see compared to the previous Simple Factory Pattern, what features this genuine Factory Pattern has:

  1. Similarly achieved the separation of the concrete process of newing objects and object behavior, but differently we used inheritance (extension) to achieve it, that is the “delegates down” mentioned at the beginning (put concrete implementation at a lower class level)
  2. Each concrete Store must implement its own Create details, but at the same time can utilize the base class Store's production craft (decorate method, etc.)
  3. An abstract factory method easily implements Factory Pattern (even Factory never appeared from start to finish, but we indeed have implemented “Factory”, right?)

5. Abstract Factory Pattern

Abstract Factory Pattern is an extension of the previous way. Above we can only Create one product, but many times we need to Create a group of products, for example, clothes raw materials, including fabric, dye, thread, buttons, etc. At this time, we need to apply Abstract Factory Pattern to implement:

package FactoryPattern.AbstractFactoryPattern;

/**

  • @author ayqy
  • Define raw material factory

*/ public abstract class ResourcesFactory { public abstract Cloth getCloth();//Get fabric public abstract Color getColor();//Get dye public abstract Button getButton();//Get button //Other factory methods needed }

Types in Abstract Factory are all custom interfaces, for example:

package FactoryPattern.AbstractFactoryPattern;

/**

  • @author ayqy
  • Define Cloth interface */ public interface Cloth { //Properties //Behaviors }

With Raw Material Factory, ClothesStore also needs to be changed accordingly to adapt to this new structure:

package FactoryPattern.AbstractFactoryPattern;

/**

  • @author ayqy
  • Extend ClothesStore, implement concrete production details

*/ public class DefaultClothesStore extends ClothesStore{

public DefaultClothesStore(String type, ResourcesFactory res) {
	super(type, res);
}

/* 
 * Implement concrete production details
 */
 @Override
public Clothes createClothes(String type) {
	if(type.equals("外套")){
		//Get required raw materials
		Cloth cloth = res.getCloth();
		Color color = res.getColor();
		Button button = res.getButton();
		//Make clothes
		return new Clothes("外套", color.toString() + button.toString(), cloth.toString());
	}else if(type.equals("内衣")){
		//Get required raw materials
		Cloth cloth = res.getCloth();
		Color color = res.getColor();
		Button button = res.getButton();
		//Make clothes
		return new Clothes("内衣", color.toString() + button.toString(), cloth.toString());
	}else
		return null;
}

}

Now creating Store object needs a concrete ResourcesFactory parameter (concrete raw material factory implements all getXXX factory methods), concrete Store is only responsible for concrete production process, whatever raw materials needed can be obtained from ResourceFactory, after concrete Store makes clothes, Store base class is responsible for craft processing (decorate method, etc.), finally concrete Store returns finely processed clothes. The whole structure is loose, low coupling, easy to extend.

Besides, it is necessary to mention an OO design principle——“Dependency Inversion Principle”. By observing class structure we can find:

High-level component Store depends on Clothes, at the same time low-level component ResourcesFactory also depends on Clothes (this is “Dependency Inversion”, that is low-level components反过来 depend on high-level components, and both high and low-level components depend on abstraction). Such design has the advantage of easy extension (abstraction means extensible...)

6. Summary

Actually the first part is the summary, what needs to be emphasized here is: everything has two sides, a good thing must have disadvantages, design patterns are no exception.

Abstract Factory Pattern looks really good, but also exists fatal disadvantage: Structural complexity problem caused by multi-level abstraction

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment