原文出处:http://www.cnblogs.com/gnuhpc/archive/2012/12/17/2822403.html
1.我们在前边提到一个原则就是“面向接口而不要面向实现编程”,但是我们一直在使用Duck duck = new MallardDuck()这样的模式,后边的new语句却违反了这个原则。工厂方法就可以解决这个问题,它用来封装对象的创建。
2.这一节我们考虑如下场景:一个披萨店要制作各种各样的披萨,甚至还要开分店。其中涉及到订购披萨的步骤。我们可以把orderPizza中的关于制作pizza的部分单独提取到一个类中。
public class PizzaStore {
SimplePizzaFactory factory;//为pizza工厂设立一个引用,在后边制作pizza时就会用到。
public PizzaStore(SimplePizzaFactory factory) { //需要传入一个工厂作为参数。
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = factory.createPizza(type);//这里不再使用new,而是通过工厂方法创建,从而到达了抽象这部分代码的作用
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
而制作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;
}
}
这样,一个pizza商店在制作pizza时等于有了一个供应商一样的东西专门去制作,而商店不必要去关心这些的细节。这本来是可以再orderPizza中实现的,现在单独形成类时为了可以服务更多的商店(比如同时经营的连锁店)或者不同的吃法(比如送货上门)。这样的实现称为简单工厂模式,也可以使用静态方法来定义者这个工厂方法,当然这样的实现有个缺点就是无法通过继承改变创建方法的行为。注意,简单工厂模式并不等价于工厂模式。
3.工厂方法:现在我们考虑更多加盟店的需求:他们需要他们自己风格的口味,但是加盟店的一些业务流程又必须严格按照总店进行处理。
针对这样的需求,我们重新设计PizzaStore类,将createPizza方法从SimplePizzaFactory 中移出来,设置为抽象的——加盟店必须自己完成这个方法自定义他们的Pizza,而orderPizza就保证了流程都是一样的,要是担心新的加盟店在继承后通过覆盖而改动这个方法则可以在前边加上final:
public abstract class PizzaStore {//定义这个抽象类的意思是这个类必须通过继承才能创建对象,这个类被称作抽象工厂类,提供了一个抽象的工厂方法供子类实现这个方法。注意面向接口编程。
abstract Pizza createPizza(String item);//工厂方法,这里也可以不是抽象而定义一些基本操作,具体工厂类就可以不需要任何操作就可以创建一个默认的“披萨”。
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);//注意在这里的createPizza方法只是一个抽象方法,具体实现由子类完成——这就是工厂方法所谓的一大特点的表现:“让类把实例化推迟到子类”——因为orderPizza方法并不知道哪个子类将实际上制作Pizza(这个方法只知道一个流程)——这是由具体的XXXPizzaStore来决定的。
System.out.println("--- Making a " + pizza.getName() + " ---");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
此时,我们开一家芝加哥的分店:
public class ChicagoPizzaStore extends PizzaStore { //继承自总店的模式,有点像简单工厂方法中的工厂类,但是区别在于工厂方法创建了一个框架,让子类决定要如何实现。
Pizza createPizza(String item) {//实现了createPizza这个方法,并且面向接口编程返回Pizza。当然,这个函数也可以不传参
if (item.equals("cheese")) {
return new ChicagoStyleCheesePizza
} else if (item.equals("veggie")) {
return new ChicagoStyleVeggiePizza();
} else if (item.equals("clam")) {
return new ChicagoStyleClamPizza();
} else if (item.equals("pepperoni")) {
return new ChicagoStylePepperoniPizza();
} else return null;
}
}
我们然后实现pizza本身的一些事情:
public abstract class Pizza {//这个类被称为抽象产品类,也就是说工厂方法产生的产品都是这个类的子类。
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();//这个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 String toString() {
StringBuffer display = new StringBuffer();
display.append("---- " + name + " ----/n");
display.append(dough + "/n");
display.append(sauce + "/n");
for (int i = 0; i < toppings.size(); i++) {
display.append((String )toppings.get(i) + "/n");
}
return display.toString();
}
}
根据总店的规定,我们来自定义一个芝加哥风味的pizza:
public class ChicagoStyleClamPizza extends Pizza {
public ChicagoStyleClamPizza() {
name = "Chicago Style Clam Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
toppings.add("Frozen Clams from Chesapeake Bay");
}
void cut() {
System.out.println("Cutting the pizza into square slices");
}
}
OK,All done,我们可以去吃一顿披萨大餐了:
public class PizzaTestDrive {
public static void main(String[] args) {
PizzaStore chicagoStore = new ChicagoPizzaStore();
Pizza pizza = chicagoStore.orderPizza("clam");//依赖于接口
}
}
4.工厂方法模式的定义:定义了一个创建对象的接口(abstract createPizza),但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。优点在于将创建对象的代码集中在一个类或者方法中,可以避免代码中的重复,并且方便以后的管理,并且客户在实例化对象时只会依赖于接口而不是具体的类。在代码中使用new创建其他的类就是对其他类的依赖,减少对于具体类的依赖是件好事情,这就引出我们第六个设计原则:要依赖抽象,不要依赖具体类——这有点类似于我们第一个原则,但是这里更强调不能让高层组件依赖低层组件,而且,两者都应该依赖于抽象。在这个例子中,XXXPizza --> Pizza <- PizzaStore ,而Pizza则是一个抽象类,这就是well designed.思维的方式是逆向思维,在设计系统时,先从底部向上抽象(抽象出Pizza类),然后撇开具体的类,在抽象层面拓展(设计PizzaStore),有几个原则可以帮助我们规范使用这个原则,这些并不是都要做到,只要尽力即可:
- 变量不可以持有具体类的引用(如果使用new就会持有具体类的引用,可以使用工厂模式来避免)。
- 不要让类派生自具体类(都是从抽象类或接口派生出来的)。
- 不要覆盖基类中已经实现的方法。(这样会导致继承的复杂度上升,同时说明基类不合适被继承——基类中已实现的方法该由子类共享)
5.抽象工厂模式:
我们现在的场景是要在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,使用原料工厂提供的原料,这是一个抽象产品类:
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");
}
void setName(String name) {
this.name = name;
}
String getName() {
return name;
}
public String toString() {
}
}
针对这一个基本的方式,我们构建具体的Pizza(这是具体产品类):
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
接着,我们要在店面上进行抽象(抽象工厂类):
public abstract class PizzaStore {
protected abstract Pizza createPizza(String item);
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
System.out.println("--- Making a " + pizza.getName() + " ---");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
在这个抽象的基础上我们开一家分店(具体工厂类):
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 PepperoniPizza(ingredientFactory);
pizza.setName("New York Style Pepperoni Pizza");
}
return pizza;
}
}
现在我们试着订购一个Pizza:
public class PizzaTestDrive {
public static void main(String[] args) {
PizzaStore nyStore = new NYPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
System.out.println("Ethan ordered a " + pizza + "/n");
}
}
抽象工厂方法提供了一个接口(此例中为PizzaIngredientFactory),用于创建相关或者依赖的对象家族,而不需要明确指定具体类,每个家族成员(此例为NYPizzaIngredientFactory)都负责创建一个具体的产品。与工厂方法的区别在于:工厂方法使用继承,针对的是类,利用工厂方法创建对象要扩展一个类,并覆盖它的工厂方法(例如这里的createPizza)——用来创建对象,工厂方法的实质在于通过子类创建对象;抽象工厂方法使用的是组合(ingredientFactory),针对的是一族对象,要使用这个工厂要首先将其实例化,然后将它传入一些针对抽象类型所写的代码中,优点在于可以把一群相关的产品集合起来,具体的工厂都是由工厂方法(prepare)创建的(这就是工厂方法和抽象工厂方法的联系)。在这个例子中商店的实现是采用的工厂方法,而制作Pizza的原料相关的类是采用的抽象工厂方法。
总结起来,这三者如下:
==========================
简单工厂方法中,
首先包括一个“抽象产品类”(该类可以是接口Interface,也可以是实际的类Class,本例中是Pizza),所有需要的产品类都是该“抽象产品类”的子类(如果是接口的话,那么就是说所有产品类都继承了该接口),本例中为各种XXPizza。
另外还包含一个具体的工厂类(本例为SimplePizzaFactory),所有需要的产品类都是该类生成的产品类对象。生成产品类的方法,其内部一般是类似于switch的结构,根据输入的标志,选择创建不同类型的对象。由于不知道创建的对象到底是哪个类的,所以方法的返回值的类型是“抽象产品类”。譬如,Pizza createPizza(String type),type就是一个标志,返回的是Pizza这个抽象产品类。
==========================
工厂方法中,
首先包括一个抽象产品类(本例中是Pizza),可以派生出多个具体产品类(本例为XXXPizza),这个和简单工厂方法没有区别。
另外还包含一个抽象工厂类(本例为PizzaStore),可以派生出具体工厂类(本例为XXXPizzaStore,这个与简单工厂方法中的具体工厂类没有区别), 每个具体工厂类(比如本例的ChicagoPizzaStore )可以根据输入标志创建一个具体产品类的实例。
较之简单工厂方法,工厂方法对于工厂类进行了抽象产生了一个抽象工厂类,通过这个抽象工厂类规定了一系列流程框架(orderPizza方法),另外,还将具体产品类的创建交给了具体的工厂类(createPizza在XXXPizza类中的实现)——也就是说,有多个具体工厂类相对应多个具体产品类,工厂类和产品类的耦合度下降。在本例中,如果不使用工厂方法,那么createPizza除了传入Pizza的类型还要传入商店的地点信息,譬如Chicago,这样的耦合度就会很大。
==========================
抽象工厂方法中,
首先,包括一个抽个产品类(本例中为Pizza),该抽象产品类可以派生出多个具体产品类(本例为XXXPizza),在每个具体产品类中以组合的形式将另一个抽象工厂类(本例为PizzaIngredientFactory )的引用包含进来,完成一群具体产品类的构建(XXXPizza的Prepare方法)。
另外还包含两个抽象工厂类,本例中PizzaStore,可以派生出多个具体工厂类XXXPizzaStore,这个与简单工厂方法中的具体工厂类没有区别, 每个具体工厂类可以创建一个具体产品类的实例(XXXPizza),但是这个具体产品类的实例比较特殊,是利用另一个抽象工厂类PizzaIngredientFactory 的具体工厂类创建的一群具体产品类而构建(XXXPizza的Prepare方法),请注意这个类才是抽象工厂方法的实质。