工厂方法模式,定义了了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
抽象工厂模式,提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
书中给出的是一个Pizza店的例子。假设你有一个pizza店,你可能会这样定义你的预定菜单:
Pizza orderPizza() {
Pizza pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
你可能需要更多的pizza类型,于是会这样定义
Pizza orderPizza(String type) {
Pizza pizza;
if(type.equals("cheese")) {
pizza = new CheesePizza();
} else if(type.equals("greek")) {
pizza = new GreekPizza();
} else if(type.equals("pepperoni")) {
pizza = new PepperoniPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
但是你以后可能又会增加或减少Pizza的类型,这时你就会发现哪些代码是会变的,哪些是不会变的。
封装创建对象的代码,我们将创建对象移到orderPizza()之外。如下所示:
我们称这个新对象为"工厂",orderPizza()就变成此对象的客户。接下来我们建立一个简单Pizza工厂
public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("cheese")) {
pizza = new CheesePizza();
} else if(type.equals("greek")) {
pizza = new GreekPizza();
} else if(type.equals("pepperoni")) {
pizza = new PepperoniPizza();
} else if(type.equals("clam")) {
pizza = new ClamPizza();
} else if(type.equals("viggie")) {
pizza = new ViggiePizza();
}
return pizza;
}
}
然后我们重做PizzaStore类
public class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
Pizza orderPizza(String type) {
Pizza pizza;
pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
接下来我们可能会有不同地方的加盟店,不同的加盟店口味又会有所不同,例如纽约加盟店跟芝加哥加盟店,这个时候你可能会想到为每个地方建议一个工厂分别为NYPizzaFactory、ChicagoFactory。如果你想要预定一个Pizza,你就要像如下调用:
NYPizzaFactory nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
nyStore.orderPizza("Veggie");
ChicagoPizzaFactory chicagoFactory = new ChicagoPizzaFactory();
PizzaStore chicagoStore = new PizzaStore(chicagoFactory);
chicagoStore.orderPizza("Veggie");
但是在推广SimpleFactory时,加盟店采用你的工厂创建Pizza,但是其他部分,却开始采用他们自创的流程:烘烤的做法有些差异、不要切片、使用其他厂商的盒子。这时候,我们可以用工厂方法设计模式来解决问题,如下:
public abstract class PizzaStore {
Pizza orderPizza(String type) {
Pizza pizza;
//把createPizza()方法从工厂对象移回PizzaStore。
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
//把工厂对象移到这个方法中,作为一个抽象方法,允许子类自己实现这个方法,并保证了其他流程不被改变。
protected abstract Pizza createPizza(String type);
}
开一家纽约加盟店
public class NYPizzaStore extends PizzaStore {
Pizza createPizza(String item) {
if(type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
} else if(type.equals("greek")) {
pizza = new NYStyleGreekPizza();
} else if(type.equals("pepperoni")) {
pizza = new NYStylePepperoniPizza();
} else if(type.equals("clam")) {
pizza = new NYStyleClamPizza();
}
}
}
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList<String> toppings = new ArrayList<String>();
public 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));
}
};
public void bake() {
System.out.println("Bake for 25 minutes at 350");
};
public void cut() {
System.out.println("Cutting the pizza into diagonal slices");
};
public void box() {
System.out.println("Place pizza in official PizzaStore box");
};
}
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza() {
name = "NY Style Sauce and Cheese Pizza";
doiugh = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
}
PizzaStore nyPizzaStore = new NYPizzaStore();
Pizza pizza = nyPizzaStore.orderPizza("cheese");
所有工厂模式都是用来封装对象的创建。工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。组成元素有:创造者类(PizzaStore)、产品类(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 {
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() {
return new ReggianoCheese();
}
public Veggies[] createVeggies() {
Veggies veggies[] {new Garlic(), new Onion(), new Mushroom(), new RedPepper()};
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClam() {
return new FreshClams();
}
}
重做Pizza
class Pizza {
String name;
Dough dough;
Sauce sauce;
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni;
Clams clam;
abstract void prepare() {};
public void bake() {
System.out.println("Bake for 25 minutes at 350");
};
public void cut() {
System.out.println("Cutting the pizza into diagonal slices");
};
public void box() {
System.out.println("Place pizza in official PizzaStore box");
};
void setName(String name) {
this.name = name;
}
}
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory factory) {
this.ingredientFactory = factory;
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new PizzaIngredientFactory();
if(type.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
} else if(type.equals("greek")) {
pizza = new GreekPizza(ingredientFactory);
pizza.setName("New York Style Greek Pizza");
} else if(type.equals("pepperoni")) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("New York Style Pepperoni Pizza");
} else if(type.equals("clam")) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("New York Style Clam Pizza");
}
}
}
PizzaStore nyPizzaStore = new NYPizzaStore();
Pizza pizza = nyPizzaStore.orderPizza("cheese");//这里可能会传入错误的值导致错误,因此我们可以采用枚举类型作为参数。
设计原则:
1、要依赖抽象,不要依赖具体类。
2、倒置原则:变量不可以持有具体类的应用;不要让类派生自具体类;不啊哟覆盖基类中已实现的方法。