本章要点:
1.所有的工厂都是用来封装对象的创建。
2.简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类解耦。
3.工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。
4.抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中。
5.所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合。
6.工厂方法允许类将实例化延迟到子类进行。
7.抽象工厂创建相关的对象家族,而不需要依赖它们的具体类。
8.依赖倒置原则,指导我们避免依赖具体类型,而要尽量依赖抽象。
9.工厂是很有威力的技巧,帮助我们针对抽象编程,而不要针对具体类编程。
----------------------------------------分割线 ----------------------------------------
简单工厂,简单工厂其实不是一个设计模式,反而比较像是一种编程习惯。
现在有一个披萨店超类PizzaStore,其中有一个订披萨的方法orderPizza(),如果你选择用NEW的方式制造不同种类的披萨,按以前的经验,你往往会这么做
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();
} else if(type.equals("clam")){
pizza = new ClamPizza();
} else if(type.equals("veggie")){
pizza = new VeggiePizza();
}
//这是我们不想改变的地方,因为制作披萨的流程是不变的
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
很明显,如果实例化某些具体类,会有很多问题。如果要加入新的披萨类型,或删除掉卖的不好的披萨类型,那么你就必须修改超类PizzaStore,这与我们第一个原则不符,我们必须对修改关闭。现在我们已经知道哪些会改变,哪些不会改变,该是使用封装的时候了。我们把改变的部分搬到另一个对象中,这个新对象只管如何创建披萨,我们称这个新对象为“工厂”。
建立一个简单披萨工厂
//这个简单工厂只做一件事情:帮客户创建披萨
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;
}
}
重做PizzaStore
public class PizzaStore {
//为PizzaStore加上一个对SimplePizzaFactory的引用
SimplePizzaFactory factory;
//PizzaStore的构造器,需要一个工厂作为参数
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type){
Pizza pizza;
//使用工厂创建披萨,把new替换成工厂对象的创建方法,不再使用具体实例化
pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
定义工厂方法模式
工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
声明一个工厂方法,原本是由一个对象负责所有具体类的实例化,现在通过PizzaStore做一些小转变,变成由一群子类来负责实例化
public abstract class PizzaStore {
//PizzaStore的子类在createPizza()中处理对象的实例化
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);
}
披萨加盟店
public class NYPizzaStore extends PizzaStore {
//返回一个Pizza对象,由子类负责实例化哪一个具体Pizza
protected Pizza createPizza(String type) {
if(type.equals("cheese")){
return new NYStylePizza();
} else if(type.equals("pepperoni")){
return null;
} else if(type.equals("clam")){
return null;
} else if(type.equals("veggie")){
return null;
}
return null;
}
}
实现披萨本身
//从一个抽象披萨开始,所有的具体披萨都必须派生自这个类
public abstract class Pizza {
//每个披萨都具有名称等属性
String name;
String dough;
String sauce;
List<String> toppings = new ArrayList<String>();
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 NYStylePizza extends Pizza {
public NYStylePizza() {
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
}
public class ChicagoStylePizza extends Pizza {
public ChicagoStylePizza() {
name = "Chicago Style Deep Dish and 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 App {
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");
}
}
定义抽象工厂模式
抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道实际产出的具体产品是什么。这样一来,
客户就从具体的产品中被解耦。
继续扩展,现在我们要建造一个工厂来生产原料,这个工厂将负责创建原料家族中的每一种原料。
开始先为工厂定义一个接口,这个接口负责创建所有的原料
public interface PizzaIngredientFactory {
//每个原料都一个对应的方法创建该原料
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClams();
}
创建各地原料工厂
//具体原料工厂必须实现这个接口
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 = {};
return veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClams() {
return new FreshClams();
}
}
重做披萨
//从一个抽象披萨开始,所有的具体披萨都必须派生自这个类
public abstract class Pizza {
//每个披萨都持有一组在准备时会用到的原料
String name;
Dough dough;
Sauce sauce;
Veggies[] veggies;
Cheese cheese;
Pepperoni pepperoni;
Clams clams;
//现在把prepare()声明成抽象。在这些方法中,我们需要收集披萨所需的原料,而这些原料现在来自原料工厂
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 String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
继续重做披萨
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 class ClamPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public ClamPizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
clams = ingredientFactory.createClams();
}
}
回到披萨店
public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String type) {
Pizza pizza = null;
//纽约店会用到纽约披萨原料工厂
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if(type.equals("cheese")){
//把工厂传递给每一个披萨,以便披萨能从工厂中取得原料
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese 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");
} else if(type.equals("veggie")){
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("New York Style Veggie Pizza");
}
return pizza;
}
}
比较工厂方法和抽象工厂
-------------------------------------------------------------------------------------------------------
总结新的设计原则(依赖倒置原则):
依赖抽象,不要依赖具体类。