1. 程式人生 > >設計模式—Factory Pattern

設計模式—Factory Pattern

下面是點披薩系統中的一段程式碼:

Pizza orderPizza(){
	Pizza pizza = new Pizza();
	
	pizza.prepare();
	pizza.bake();
	pizza.cut();
	pizza.box();
	return pizza;
}

因為有許多種型別的披薩,所以我們要根據披薩的型別返回合適的披薩:
Pizza orderPizza(String type){
	Pizza pizza;
	//根據披薩型別返回合適的披薩
	if(type.equals("chess")){
		pizza = new CheesePizza();
	}else if(type.equals("greek")){
		pizza = new GreekPizza();
	}else if(type.equals("pepperoni")){
		pizza = new Pepperoni();
	}
	//披薩的製作過程
	pizza.prepare();
	pizza.bake();
	pizza.cut();
	pizza.box();
	return pizza;
}

因為披薩的種類會不斷地變化,所以我們要不斷地修改以上程式碼違背了(classes should open for extention,closed for modification)原則,根據(encapsulate the things that vary)原則,我們可以將不斷變化的東西從不變的東西中分離出來,可以看出根據披薩型別返回不同披薩的那部分的程式碼是不斷變化的,而披薩的製作過程那部分程式碼是不會變化的。以下是分離之後的結果:
Pizza orderPizza(String type){
	Pizza pizza;
	
	pizza.prepare();
	pizza.bake();
	pizza.cut();
	pizza.box();
	return pizza;
}

public class SimplePizzaFactory {
	public Pizza createPizza(String type){
		Pizza pizza = null;
		
		if(type.equals("cheese")){
			pizza = new CheesePizza();
		}else if(type.equals("pepperoni")){
			pizza = new PepperoniPizza();
		}else if(type.equals("clam")){
			pizza = new ClamPizza();
		}else if(type.equals("veggie")){
			pizza = new VeggiePizza();
		}
		
		return pizza;
	}
}

分離之後,我們可以將orderPizza方法看作客戶端程式碼,利用SimplePizzaFactory類提供的服務,對以上程式碼進一步完善:
public class PizzaStore {
	SimplePizzaFactory factory;
	
	public PizzaStore(SimplePizzaFactory factory){
		this.factory = factory;
	}
	
	public Pizza orderPizza(String type){
		Pizza pizza;
		
		pizza = factory.createPizza(type);
		
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
}

以上為Simple Factory idiom,並不是真正的設計模式,以下是它的類圖:


因為披薩店最近辦得比較火,店主想多開幾家店,每家店的地理位置不一樣,生產的披薩種類也不一樣,可以這樣設計:

public abstract class PizzaStore {
	
	public Pizza orderPizza(String type){
		Pizza pizza;
		
		pizza = createPizza(type);
		
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
	
	protected abstract Pizza createPizza(String type);
}
將以上的類作為父類,可以由子類決定createPizza抽象方法的實現:



下面是NYPizzaStore類的實現:

public class NYPizzaStore extends PizzaStore{
	
	Pizza createPizza(String item){
		if(item.equals("cheese")){
			return new NYStyleCheesePizza();
		}else if(item.equals("veggie")){
			return new NYStyleVeggiePizza();
		}else if(item.equals("pepperoni")){
			return new NYStylePepperoniPizza();
		}else return null;
	}

}

以下是披薩的實現:
public abstract class Pizza {
	String name;
	String dough;
	String sauce;
	ArrayList toppings = new ArrayList();
	
	void prepare(){
		System.out.println("Preparing " + name);
		System.out.println("Tossing dough...");
		System.out.println("Adding sauce...");
		System.out.println("Adding toppings: ");
		for(int i = 0;i < toppings.size();i++){
			System.out.println(" " + toppings.get(i));
		}
	}
	
	void bake(){
		System.out.println("Bake for 25 minutes at 350");
	}
	
	void cut(){
		System.out.println("Cutting the pizza into diagonal slices");
	}
	
	void box(){
		System.out.println("Place pizza in official PizzaStore box");
	}
	
	public String getName(){
		return name;
	}
}

public class NYStyleCheesePizza extends Pizza {
	public NYStyleCheesePizza(){
		name = "NY Style Sauce and Cheese Pizza";
		dough = "Thin Crust Dough";
		sauce = "Marinara Sauce";
		
		toppings.add("Grated Reggiano Cheese");
	}
}

public class ChicagoStyleCheesePizza extends Pizza {
	public ChicagoStyleCheesePizza(){
		name = "Chicago Style Deep Dish Cheese Pizza";
		dough = "Extra Thick Crust Dough";
		sauce = "Plum Tomato Sauce";
		
		toppings.add("Shredded Mozzarella Cheese");
	}
	
	void cut(){
		System.out.println("Cutting the pizza into square slices");
	}
}

測試程式碼:
public class PizzaTestDrive {

	public static void main(String[] args) {
		PizzaStore nyStore = new NYPizzaStore();
		PizzaStore chicagoStore = new ChicagoPizzaStore();
		
		Pizza pizza = nyStore.orderPizza("cheese");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
		
		pizza = chicagoStore.orderPizza("cheese");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");
		
	}

}

以上運用的是Factory Method Pattern,實現類圖如下:



Factory Method Pattern 定義:defines an interface for creating an object,but lets subclasses decide which class to instantiate.Factory Method Pattern lets a class defer instantiation to subclasses.類圖如下:


下面介紹Factory Method Pattern 所利用的一條設計準則:Dependency Inversion Principle。這條準則主要講要依賴抽象,而不要依賴於具體的實現,比如以上的例子如果不用Factory Method Pattern,我們可以這樣實現:

public class DependentPizzaStore {
	public Pizza createPizza(String style,String type){
		Pizza pizza = null;
		if(style.equals("NY")){
			if(type.equals("cheese")){
				pizza = new NYStyleCheesePizza();
			}else if(type.equals("veggie")){
				pizza = new NYStyleVeggiePizza();
			}else if(type.equals("clam")){
				pizza = new NYStyleClamPizza();
			}else if(type.equals("pepperoni")){
				pizza = new NYStylePepperoniPizza();
			}
		}else if(style.equals("Chicago")){
			if(type.equals("cheese")){
				pizza = new ChicagoStyleCheesePizza();
			}else if(type.equals("veggie")){
				pizza = new ChicagoStyleVeggiePizza();
			}else if(type.equals("clam")){
				pizza = new ChicagoStyleClamPizza();
			}else if(type.equals("pepperoni")){
				pizza = new ChicagoStylePepperoniPizza();
			}
		}else{
			System.out.println("Error:invalid type of pizza");
			return null;
		}
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
}

依賴關係可以用如下圖表示:


從上圖我們可以看出PizzaStore依賴於具體的Pizza的實現,Factory Method Pattern改善後的依賴關係可以用如下圖表示:


從上圖可以看出,PizzaStore和具體的Pizza的實現都依賴於Pizza這個抽象類。

由於每個地區人的口味都不一樣,所以做pizza所用的材料也不一樣,老闆決定在每個地區建立工廠專門生產適合本地人口味的材料,下面為材料工廠介面設計:

public interface PizzaIngredientFactory {
	public Dough createDough();
	public Sauce createSauce();
	public Cheese createCheese();
	public Veggies[] createVeggies();
	public Pepperoni createPepperoni();
	public Clams createClam();
}

下面是紐約材料工廠的實現:
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {

	@Override
	public Dough createDough() {
		return new ThinCrustDough;
	}

	@Override
	public Sauce createSauce() {
		return new MarinaraSauce;
	}

	@Override
	public Cheese createCheese() {
		return new ReggianoCheese;
	}

	@Override
	public Veggies[] createVeggies() {
		Veggies veggies[] = {new Garlic(),new Onion(),new Mushroom(),newRedPepper()};
		return veggies;
	}

	@Override
	public Pepperoni createPepperoni() {
		return new SlicedPepperoni();
	}

	@Override
	public Clams createClam() {
		return new FreshClams();
	}

}
下面對Pizza抽象類進行完善:
public abstract class Pizza {
	String name;
	Dough dough;
	Sauce sauce;
	Veggies veggies[];
	Cheese cheese;
	Pepperoni pepperoni;
	Clams clam;
	
	abstract void prepare();
	
	void bake(){
		System.out.println("Bake for 25 minutes at 350");
	}
	
	void cut(){
		System.out.println("Cutting the pizza into diagonal slices");
	}
	
	void box(){
		System.out.println("Place pizza in official PizzaStore box");
	}
	
	public void setName(String name){
		this.name = name;
	}
	
	public String getName(){
		return name;
	}
	
	public String toString(){
		//code to print pizza here
	}
}

Pizza具體類的實現如下:
public class CheesePizza extends Pizza {
	PizzaIngredientFactory ingredientFactory;
	public CheesePizza(PizzaIngredientFactory ingredientFactory){
		this.ingredientFactory = ingredientFactory;
	}
	
	@Override
	void prepare() {
		System.out.println("Preparing " + name);
		dough = ingredientFactory.createDough();
		sauce = ingredientFactory.createSauce();
		cheese = ingredientFactory.createCheese();
	}

}

public class ClamPizza extends Pizza {
	PizzaIngredientFactory ingredientFactory;
	public ClamPizza(PizzaIngredientFactory ingredientFactory){
		this.ingredientFactory = ingredientFactory;
	}
	
	@Override
	void prepare() {
		System.out.println("Preparing " + name);
		dough = ingredientFactory.createDough();
		sauce = ingredientFactory.createSauce();
		cheese = ingredientFactory.createCheese();
		clam = ingredientFactory.createClam();
	}
}

下面重新實現NYPizzaStore類:
public class NYPizzaStore extends PizzaStore{
	
	protected Pizza createPizza(String item){
		Pizza pizza = null;
		PizzaIngredientFactory ingredientFactory =
				new NYPizzaIngredientFactory();
		
		if(item.equals("cheese")){
			pizza = new CheesePizza(ingredientFactory);
			pizza.setName("New York Style Cheese Pizza");
		}else if(item.equals("veggie")){
			pizza = new VeggiePizza(ingredientFactory);
			pizza.setName("New York Style Veggie Pizza");
		}else if(item.equals("clam")){
			pizza = new ClamPizza(ingredientFactory);
			pizza.setName("New York Style Clam Pizza");
		}else if(item.equals("pepperoni")){
			pizza = new Pepperoni(ingredientFactory);
			pizza.setName("New York Style Pepperoni Pizza");
		}
		return pizza;
	}

}

以上介紹的是一種新的模式:Abstract Factory Pattern(provides an interface for creating families of related or dependent objects without specifying their concrete classes).類圖如下所示:

披薩店的材料工廠實現如下圖所示: