您的位置:首页 > 其它

《HeadFirst设计模式》读书笔记-第4章-工厂模式

2017-03-18 20:43 701 查看

定义

工厂方法模式(factory method pattern)定义了一个创建对象的接口,由子类决定要实例化的类是哪一个,让类的实例化推迟到子类。



从类图可以看出,工厂方法模式封装了具体类型的实例化,抽象的Creator类提供了一个抽象方法用来创建产品,同时也实现了一些操作产品的方法(如anOperation),这些方法不需要涉及ConcreteProduct,而是通过Product接口。所以,工厂方法模式帮助我们把产品的创建(子类实现的factoryMethod方法)从使用中(Creator中anOperation等方法)解耦。工厂方法的签名如下,

abstract Product factoryMethod(String type)


几点说明:

1. 工厂方法一般是abstract的,但是也可以在定义时提供一个默认的工厂方法,这样可以在不实现子类的情况下,也能创建产品

2. 工厂方法返回的是抽象类型,不是具体的类型

3. 此处通过参数type创建不同的产品,然而工厂经常只产生一种对象,不需要参数化

抽象工厂模式(abstract factory pattern)提供了一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。



从类图可以看出,抽象工厂允许客户(Client)使用抽象的接口(AbstractFactory)来创建一组相关的产品,而不需要关心实际产出的具体产品是什么。这样一来,客户就从具体的产品创建中被解耦。

代码实现

首先来介绍简单工厂,它不是一个设计模式,但是经常被使用。设计思想就是把创建具体对象这种会变化的部分独立出来,放到独立的类中,就是简单工厂,类图如下。



PizzaStore.java

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;
}

}


SimplePizzaFactory.java

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;
}
}


具体产品CheesePizza

public class CheesePizza extends Pizza {
public CheesePizza() {
name = "Cheese Pizza";
dough = "Regular Crust";
sauce = "Marinara Pizza Sauce";
toppings.add("Fresh Mozzarella");
toppings.add("Parmesan");
}
}


具体产品PepperoniPizza

public class PepperoniPizza extends Pizza {
public PepperoniPizza() {
name = "Pepperoni Pizza";
dough = "Crust";
sauce = "Marinara sauce";
toppings.add("Sliced Pepperoni");
toppings.add("Sliced Onion");
toppings.add("Grated parmesan cheese");
}
}


产品基类Pizza

import java.util.ArrayList;

abstract public class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();

public String getName() {
return name;
}

public void prepare() {
System.out.println("Preparing " + name);
}

public void bake() {
System.out.println("Baking " + name);
}

public void cut() {
System.out.println("Cutting " + name);
}

public void box() {
System.out.println("Boxing " + name);
}

public String toString() {
// code to display pizza name and ingredients
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();
}
}


测试驱动代码,

public class PizzaTestDrive {

public static void main(String[] args) {
SimplePizzaFactory factory = new SimplePizzaFactory();
PizzaStore store = new PizzaStore(factory);

Pizza pizza = store.orderPizza("cheese");
System.out.println("We ordered a " + pizza.getName() + "\n");

pizza = store.orderPizza("veggie");
System.out.println("We ordered a " + pizza.getName() + "\n");
}
}


上面介绍的简单工厂,是把创建对象的操作放在了另外一个对象了,如果放在类的方法里,由子类来负责实例化,就是工厂方法模式。下面给出由工厂方法模式实现PizzaStore的类图。



PizzaStore.java

public abstract class PizzaStore {
// 工厂方法是抽象的,返回创建的产品Pizza
// 工厂方法将客户(orderPizza方法)和实际创建具体产品的代码解耦
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;
}
}


Pizza.java

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 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();
}
}


ChicagoPizzaStore.java

public class ChicagoPizzaStore extends PizzaStore {

Pizza createPizza(String item) {
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;
}
}


ChicagoStyleCheesePizza.java

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");
}
}


ChicagoStyleClamPizza.java

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");
}
}


NYPizzaStore.java

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("clam")) {
return new NYStyleClamPizza();
} else if (item.equals("pepperoni")) {
return new NYStylePepperoniPizza();
} else return null;
}
}


NYStyleCheesePizza.java

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");
}
}


NYStyleClamPizza.java

public class NYStyleClamPizza extends Pizza {

public NYStyleClamPizza() {
name = "NY Style Clam Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";

toppings.add("Grated Reggiano Cheese");
toppings.add("Fresh Clams from Long Island Sound");
}
}


测试启动代码

public class PizzaTestDrive {

public static void main(String[] args) {

PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore();

// 客户只要调用orderPizza方法,就创建了对应区域的对应口味的Pizza
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");

pizza = nyStore.orderPizza("clam");
System.out.println("Ethan ordered a " + pizza.getName() + "\n");

pizza = chicagoStore.orderPizza("clam");
System.out.println("Joel ordered a " + pizza.getName() + "\n");

pizza = nyStore.orderPizza("pepperoni");
System.out.println("Ethan ordered a " + pizza.getName() + "\n");

pizza = chicagoStore.orderPizza("pepperoni");
System.out.println("Joel ordered a " + pizza.getName() + "\n");

pizza = nyStore.orderPizza("veggie");
System.out.println("Ethan ordered a " + pizza.getName() + "\n");

pizza = chicagoStore.orderPizza("veggie");
System.out.println("Joel ordered a " + pizza.getName() + "\n");
}
}


下面给出使用抽象工厂模式实现采用不同原料创建不同Pizza的例子,类图如下。



首先给出抽象工厂接口定义:

public interface PizzaIngredientFactory {

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

}


Chicago风味的Pizza原料工厂实现

public class ChicagoPizzaIngredientFactory
implements PizzaIngredientFactory
{

public Dough createDough() {
return new ThickCrustDough();
}

public Sauce createSauce() {
return new PlumTomatoSauce();
}

public Cheese createCheese() {
return new MozzarellaCheese();
}

public Veggies[] createVeggies() {
Veggies veggies[] = { new BlackOlives(),
new Spinach(),
new Eggplant() };
return veggies;
}

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

public Clams createClam() {
return new FrozenClams();
}
}


NewYork风味的Pizza原料工厂实现

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();
}
}


类图中的原料品种太多,这里给出两种的实现,但是不会影响问题的说明。

Dough接口定义

public interface Dough {
public String toString();
}


两种Dough原料的实现

public class ThickCrustDough implements Dough {
public String toString() {
return "ThickCrust style extra thick crust dough";
}
}


public class ThinCrustDough implements Dough {
public String toString() {
return "Thin Crust Dough";
}
}


Sauce接口定义

public interface Sauce {
public String toString();
}


两种Sauce原料的实现

public class MarinaraSauce implements Sauce {
public String toString() {
return "Marinara Sauce";
}
}


public class PlumTomatoSauce implements Sauce {
public String toString() {
return "Tomato sauce with plum tomatoes";
}
}


PizzaStore.java

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;
}
}


NYPizzaStore.java

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;
}
}


ChicagoPizzaStore.java

public class ChicagoPizzaStore extends PizzaStore {

protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory =
new ChicagoPizzaIngredientFactory();

if (item.equals("cheese")) {

pizza = new CheesePizza(ingredientFactory);
pizza.setName("Chicago Style Cheese Pizza");

} else if (item.equals("veggie")) {

pizza = new VeggiePizza(ingredientFactory);
pizza.setName("Chicago Style Veggie Pizza");

} else if (item.equals("clam")) {

pizza = new ClamPizza(ingredientFactory);
pizza.setName("Chicago Style Clam Pizza");

} else if (item.equals("pepperoni")) {

pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("Chicago Style Pepperoni Pizza");

}
return pizza;
}
}


Pizza.java

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() {
StringBuffer result = new StringBuffer();
result.append("---- " + name + " ----\n");
if (dough != null) {
result.append(dough);
result.append("\n");
}
if (sauce != null) {
result.append(sauce);
result.append("\n");
}
if (cheese != null) {
result.append(cheese);
result.append("\n");
}
if (veggies != null) {
for (int i = 0; i < veggies.length; i++) {
result.append(veggies[i]);
if (i < veggies.length-1) {
result.append(", ");
}
}
result.append("\n");
}
if (clam != null) {
result.append(clam);
result.append("\n");
}
if (pepperoni != null) {
result.append(pepperoni);
result.append("\n");
}
return result.toString();
}
}


CheesePizza.java

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();
}
}


ClamPizza.java

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();
clam = ingredientFactory.createClam();
}
}


从上面可以很容易地看出,抽象工厂创建的是一个产品家族,而工厂方法创建的是一个具体产品,这是它们的区别。抽象工厂定义的接口中,每一个方法负责创建一个特定的产品,这个方法一般都是采用工厂方法来的实现的。

包含的OO原则

原则1

从上面Pizza的例子可以看出,创建具体对象可能是经常变化的部分,隔离出来,放到别的类就是简单工厂,放到子类去实现就是工厂方法或者抽象工厂模式

原则2

这个就多了,orderPizza方法里,调用Pizza的prepare, bake,cut,box等方法,这些方法都是接口定义的,不依赖于具体类型。

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;
}


还有抽象方法模式里,具体Pizza对象的prepare方法实现,调用接口创建各种原料。

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();
}
}


原则6

这个具体可以看另外一篇文章《面向对象的设计原则》关于原则六的说明部分

本章金句

所有的工厂都是用来封装对象创建的

简单工厂虽然不是真正的设计模式,但是是一个简单而且常用的方法,可以将客户程序从具体类解耦

工厂方法使用继承,把对象的创建延时到子类

抽象工厂使用对象组合,对象的创建被实现在工厂接口的方法中
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息