您的位置:首页 > 其它

【head first 设计模式学习笔记】工厂模式

2017-11-19 14:04 489 查看
简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式。其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性。

简单工厂

简单工厂模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例。

考虑如下场景:一个披萨店要制作各种各样的披萨,甚至还要开分店。其中涉及到订购披萨的步骤。我们可以把orderPizza中的关于制作pizza的部分单独提取到一个类中。

public class PizzaStore {
SimplePizzaFactory factory;

public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}

public Pizza orderPizza(String type) {
Pizza pizza;

pizza = factory.createPizza(type); //制作pizza由工厂类来完成

pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();

return pizza;
}

}

以下是 SimplePizzaFactory

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是SimplePizzaFactory的客户,通过SimplePizzaFactory来取得pizza的实例。

一个PizzaStore在制作pizza时等于有了一个供应商一样的东西专门去制作,而PizzaStore不必要去关心这些的细节。这本来是可以在orderPizza中实现的,现在单独形成类时为了可以服务更多的商店(比如同时经营的连锁店)或者不同的吃法(比如送货上门)。这样的实现称为简单工厂模式,也可以使用静态方法来定义这个工厂方法,当然这样的实现有个缺点就是无法通过继承改变创建方法的行为。另外简单工厂不是一个设计模式,而只是一种编程习惯。

工厂方法

考虑更多加盟店的需求:他们需要他们自己风格的口味,但是加盟店的一些业务流程又必须严格按照总店进行处理。

我们要把createPizza放回到PizzaStore中,将其作为抽象方法,现在的PizzaStore就是各个加盟店的框架

public abstract class PizzaStore {

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

ChicagoPizzaStore能根据客户需求来生产pizza,ChicagoPizzaStore是PizzaStore的子类,现在我们就是由子类来决定如何制作pizza的。

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、ChicagoStyleVeggiePizza等Pizza类不一一在这里列出。

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

顺便贴一下Pizza类的代码

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

void prepare() {
System.out.println("Prepare " + name);
System.out.println("Tossing dough...");
System.out.println("Adding sauce...");
System.out.println("Adding toppings: ");
for (String topping : toppings) {
System.out.println("   " + topping);
}
}

void bake() {
System.out.println("Bake for 25 minutes at 350");
}

void cut() {
System.out.println("Cut 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 (String topping : toppings) {
display.append(topping + "\n");
}
return display.toString();
}
}

现在我们来订一份ChicagoStyleCheesePizza

public class PizzaTestDrive {

public static void main(String[] args) {
PizzaStore chicagoStore = new ChicagoPizzaStore();

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

通过工厂方法,我们实现了让各个不同地区的pizza店生产自己特定风格的pizza。如果还有其他加盟店,那么我们可以让新的类继承PizzaStore,这个子类提供createPizza方法来制作他们自己风格的pizza即可。

工厂方法模式让子类来决定需要创建的对象的是什么,就如这里我们让PizzaStore的子类创建自己风格的pizza。我们这里所说的“决定”,并非允许子类本身在运行时做决定,而是说在编写创建类(我们这里的PizzaStore)时,不需要知道实际创建的产品(Pizza)是什么,如果我们选择PizzaStore的子类ChicagoPizzaStore,将要生产的产品就被决定了。

在代码中使用new创建其他的类就是对其他类的依赖,减少对于具体类的依赖是件好事情,这就引出我们第六个设计原则:要依赖抽象,不要依赖具体类——这有点类似于我们第一个原则,但是这里更强调不能让高层组件依赖低层组件(PizzaStore是个高层组件,他的行为由Pizza来决定,PizzaStore创建所有不同的Pizza对象,而准备、烘焙等由pizza本身来完成),而且,两者都应该依赖于抽象。在这个例子中,XXXPizza 
--> Pizza <-- PizzaStore ,而Pizza则是一个抽象类。

在设计系统时,先从底部向上抽象(抽象出Pizza类),然后撇开具体的类,在抽象层面拓展(设计PizzaStore),有几个原则可以帮助我们规范使用这个原则,这些并不是都要做到,只要尽力即可:
变量不可以持有具体类的引用(如果使用new就会持有具体类的引用,可以使用工厂模式来避免)。
不要让类派生自具体类(都是从抽象类或接口派生出来的)。
不要覆盖基类中已经实现的方法。(这样会导致继承的复杂度上升,同时说明基类不合适被继承——基类中已实现的方法该由子类共享)

抽象工厂模式:

现在我们回到pizza本身,对pizza不同地区的加盟店其使用的原料可能不同,比如不同的酱料、不同的面团、不同的海鲜佐料等,这时我们需要一个

生产原料的工厂,即抽象工厂类PizzaIngredientFactory来生产原料

public interface PizzaIngredientFactory {

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

}


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

既然pizza原料变了,自然就pizza的准备prepare方法就变了,pizza类的prepare方法应该被抽象。
public abstract class Pizza {
String name;

Dough dough;
Sauce sauce;
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni;
Clams clam;

abstract void prepare(); //prepare方法为抽象方法,具体由pizza的子类实现

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();
}
}
列出一个Pizza的实现类如下:
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();
}
}

PizzaStore类如下
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.g
c2cc
etName() + " ---");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}

具体的生产Pizza方法createPizza由PizzaStore的子类实现。
比如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 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");

}
}

抽象工厂是应对产品族概念的。对每个不同地区的PizzaStore来说,他们使用的原料不同,我们用原料工厂PizzaIngredientFactory
的实现类(不同的工厂原料不同)来给特定地区的pizza店提供原料。

抽象工厂方法提供了一个接口(此例中为PizzaIngredientFactory),用于创建相关或者依赖的对象家族,而不需要明确指定具体类,每个家族成员(此例为NYPizzaIngredientFactory)都负责创建一个具体的产品。与工厂方法的区别在于:工厂方法使用继承,针对的是类,利用工厂方法创建对象要扩展一个类,并覆盖它的工厂方法(例如这里的createPizza)——用来创建对象,工厂方法的实质在于通过子类创建对象;抽象工厂方法使用的是组合(ingredientFactory),针对的是一族对象,要使用这个工厂要首先将其实例化,然后将它传入一些针对抽象类型所写的代码中,优点在于可以把一群相关的产品集合起来,具体的工厂都是由工厂方法(prepare)创建的(这就是工厂方法和抽象工厂方法的联系)。


三者的不同点:1、简单工厂简单工厂方法中,包括一个“抽象产品类”(该类可以是接口Interface,也可以是实际的类Class),所有需要的产品类都是该“抽象产品类”的子类(如果是接口的话,那么就是说所有产品类都继承了该接口)。简单工厂一般只包含一个具体的工厂类,由该工厂类生成所有的产品类的对象。生成产品类的方法,其内部一般是类似于switch的结构,根据输入的标志,选择创建不同类型的对象。由于不知道创建的对象到底是哪个类的,所以方法的返回值的类型是“抽象产品类”。 2、工厂方法抽象工厂中,包括“抽象工厂类”和“抽象产品类”,同时包含不只一个工厂类。所有的工厂类都必须是“抽象工厂类”的子类,所有的产品都必须是“抽象产品类”的子类。和简单工厂比起来,工厂方法一般是从抽象工厂开始的。一般都是在抽象工厂类中提供一个静态方法,由该方法根据输入的标志,生成不同的具体工厂类,然后由具体的产品类生成具体的产品。注意,一个具体工厂类只能生成一种具体的产品类的对象,不同的具体工厂生成不同的产品,而不是像简单工厂中那样,一个工厂类可以生成多种不同产品类的对象。可以这么理解,在选择不同的具体工厂类的时候,就选择了生成的产品,相对于简单工厂,相当于将选择产品的动作提前了。因为不知道创建的具体工厂类到底是哪一个,所以生成具体工厂类的静态方法的返回值的类型是“抽象工厂类”。具体工厂类生成产品类的方法,返回值的类型也要求是“抽象产品类”(因为前端调用的时候,需要使用同样的代码来访问)。 
3、抽象工厂抽象工厂和工厂方法很类似,区别如下: 工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。   
一个抽象工厂类,可以派生出多个具体工厂类。   
每个具体工厂类只能创建一个具体产品类的实例。 
抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。   
一个抽象工厂类,可以派生出多个具体工厂类。   
每个具体工厂类可以创建多个具体产品类的实例。      

参考:
http://www.cnblogs.com/gnuhpc/archive/2012/12/17/2822403.html
http://zyjustin9.iteye.com/blog/2094960
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: