隶属类别——对象创建型模式
提供一个创建一系列相关或互相依赖对象的接口,而无需明确指定它们的具体的类
Kit
考虑一组支持多种视感(look-and-feel)标准的用户界面工具包,例如Motif和Presentation Manager。不同的视感风格为诸如滚动条、窗口和按钮等用户界面”窗口组件“定义不同的外观和行为。为保证视感风格标准间的可移植性,一个应用不应该为一个特定的视感外观硬编码它的窗口组件。在整个应用中实例化特定既视感风格窗口组件类将使得以后很难改变视感风格。
为解决这一问题我们可以定义一个抽象的WidgeFactory类,这个类声明了一个用来创建每一个类基本窗口组件的接口。每一类窗口组件都有一个抽象类,而具体子类则实现了窗口组件的特定视感风格。对于每一个抽象窗口组件类,WidgetFactory接口都有一个返回新窗口组件对象的操作。客户调用这些操作以获得窗口组件实例,但客户并不知道他们他们正在使用的是哪些具体类。这样客户就不依赖于一般的视感风格,如下页图所示:
每一种视感标准都对应一个具体的WidgetFactory子类。每一子类实现那些勇于创建合适视感风格窗口组件的操作。例如,MotifWidgetFactory的CreateScrollBar操作实例化并返回一个Motif滚动条,而相应的PMWidgetFactory操作返回一个Presentation Manager的滚动条。客户仅通过WidgetFactory接口创建组件,它们并不知道哪些类实现了既定视感风格的窗口组件。换言之,客户仅与抽象类定义的接口交互,而不使用特定的具体类的接口。
WidgetFactory也增强了具体窗口组件类之间依赖关系。一个Motif的滚动条应该与Motif按钮、Motif正文编辑器一起使用,这一约束条件作为使用MotifWidgetFactory的结果被自动加上。
在以下情况可以使用Abstract Factory模式
AbstractFactory(WidgetFactory)
——声明一个创建抽象产品对象的操作接口
ConcreteFactory(MotifyWidgetFactorym PMWidgetFactory)
——实现创建具体产品的操作
AbstractProduct(Window,ScrollBar)
——为一类产品对象声明一个接口(并不是指实际接口,而指抽象方法,一个入口)
ConcreteProduct(MotifyWindow,MotifScrollBar)
——定义一个将被相应的具体工厂创建的产品对象
——实现AbstractProduct接口
Client
——仅使用有AbstractFactory和AbstractProduct类声明的接口。
Abstract Factory模式有下面的一些优点和缺点:
优点:
缺点:
下面是实现Abstract Factory模式的一些应该注意的点:
1.将工厂作为单件 一个应用中一般每个产品系列只需要一个ConcreteFactory的实例,因此工厂通常最好实现在一个Singleton。
2.创建产品 Abstract Factory仅声明一个创建产品的接口,真正创建产品是由ConcreteProduct子类实现的。最通常的一个办法是为每一个产品定义一个工厂方法。一个具体的工厂将为每个产品重定义该工厂方法以指定产品。虽然这样的实现很简单,但他却要求每个产品系列都要有一个新的具体工厂子类,即使这些产品系列的差别很小。
如果有多个可能的产品系列。具体工厂也可以使用Prototype来实现。具体工厂使用产品系列中的每一个产品的原型实例来初始化,且它通过复杂它的原型来创建新的产品。在基于原型的方法中,使得不是每个新的产品系列都需要一个新的具体工厂类。具体的Java例子 ?
3.定义可扩展的工厂 Abstract Factory通常为每一个它可以生产的产品定义一个操作。产品的种类被编码在操作型构中。增加一种新的产品要求编码AbtractFactory的接口以及所有与它相关的类。一个更灵活但不太安全的设计是给创建对象的操作增加一个参数。该参数指指定了将被创建的对象的种类。它可以是一个类标识符、一个整数、一个字符串,或其他任何可以标识这种产品的东西。实际上使用这种方法,Abstract Factory只需要一个”Make“操作和一个指示要创建对象的种类的参数。这是前面已经讨论过的基于原型的和基于类的抽象工厂的抽象工厂的技术。
与C++这样的静态类型语言相比,这一变化容易用在类似于Smalltalk这样的动态类型言语中。仅当所有的对象都有相同的抽象基类,或者当产品对象可以被请求它们的客户安全的强制转换成正确的类型时,你才能在C++中使用它,Factory的实现部分说明了怎么样在C++实现这样的参数化操作。
该方法即使不需要类型强制转化,但仍有一个本质的问题:所有的产品将返回类型所给定的相同的抽象接口返回给客户。客户将不能区分或者对一个产品的类别进行安全的假定。如果一个客户需要进行与特定子类相关的操作,而这个操作却不能通过抽象接口得到。虽然客户可以实施一个向下类型转化(例如在C++中用dynamic_cast),但这并不总是可行或安全的,因为向下类型转化可能会失败。这个是一个典型的高度灵活和可扩展接口的权衡折衷。
首先是创建AbstractFactory——PizzaIngredientFactory.java
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
}
上述提到对具体工厂使用单例,有利于节省成本,当下比较好用的单例应该是其他开发者在stackoverflow向我推荐的使用Enum作为单例,并且在某些需要同步的方法上使用Synchronized关键字。
ConcreteFactory——ChicagoPizzaIngredientFactory.java & ChicagoPizzaIngredientFactory.java
ChicagoPizzaIngredientFactory.java
public enum ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
CHICAGO_FACTORY_INSTANCE;
@Override
public synchronized Dough createDough() {
return new ThickCrustDough();
}
@Override
public synchronized Sauce createSauce() {
return new PlumTomatoSauce();
}
@Override
public synchronized Cheese createCheese() {
return new MozzarellaCheese();
}
@Override
public synchronized Veggies[] createVeggies() {
Veggies veggies[] = { new BlackOlives(),
new Spinach(),
new Eggplant() };
return veggies;
}
@Override
public synchronized Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public synchronized Clams createClam() {
return new FrozenClams();
}
}
ChicagoPizzaIngredientFactory.java
public enum NYPizzaIngredientFactory implements PizzaIngredientFactory {
NY_FACTORY_INSTANCE;
@Override
public synchronized Dough createDough() {
return new ThinCrustDough();
}
@Override
public synchronized Sauce createSauce() {
return new MarinaraSauce();
}
@Override
public synchronized Cheese createCheese() {
return new ReggianoCheese();
}
@Override
public synchronized Veggies[] createVeggies() {
Veggies[] veggies = {new Garlic(), new Onion(), new Mushroom(), new RedPepper()};
return veggies;
}
@Override
public synchronized Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public synchronized Clams createClam() {
return new FreshClams();
}
}
有AbstractProduct太多我这里只列举两个:
Dough.java
public interface Dough {
String toString();
}
Sauce.java
public interface Sauce {
String toString();
}
接着是ConcreteProduct:
ThickCrustDough.java
public class ThickCrustDough implements Dough {
@Override
public String toString() {
return "ThickCrust style extra thick crust dough";
}
}
ThinCrustDough.java
public class ThinCrustDough implements Dough{
@Override
public String toString() {
return "Thin Crust Dough";
}
}
PlumTomatoSauce.java
public class PlumTomatoSauce implements Sauce{
@Override
public String toString() {
return "Tomato sauce with plum tomatoes";
}
}
MarinaraSauce.java
public class MarinaraSauce implements Sauce{
@Override
public String toString() {
return "Marinara Sauce";
}
}
其他的原料类因为篇幅过大省略了。
接下来是调用的辅助类——PizzaStore.java
public abstract class PizzaStore {
protected abstract Pizza createPizza(String item);
public final 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.java
public class ChicagoPizzaStore extends PizzaStore {
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory =
ChicagoPizzaIngredientFactory.CHICAGO_FACTORY_INSTANCE;
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;
}
}
NYPizzaStore.java
public class NYPizzaStore extends PizzaStore{
protected Pizza createPizza(String item) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory =
NYPizzaIngredientFactory.NY_FACTORY_INSTANCE;
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;
}
}
然后是测试类Client——PizzaTestDrive.java
public class PizzaTestDrive {
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 + "\n");
pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a " + pizza + "\n");
}
}
以及对应的测试结果
--- Making a New York Style Cheese Pizza ---
Preparing New York Style Cheese Pizza
Bake for 25 minutes at 350
Cutting the pizza into diagonal slices
Place pizza in official PizzaStore box
Ethan ordered a ---- New York Style Cheese Pizza ----
Thin Crust Dough
Marinara Sauce
Reggiano Cheese
--- Making a Chicago Style Cheese Pizza ---
Preparing Chicago Style Cheese Pizza
Bake for 25 minutes at 350
Cutting the pizza into diagonal slices
Place pizza in official PizzaStore box
Joel ordered a ---- Chicago Style Cheese Pizza ----
ThickCrust style extra thick crust dough
Tomato sauce with plum tomatoes
Shredded Mozzarella
InterView使用"Kit"后缀来表示AbstractFactory类。它定义WidgetKit和DialogKit抽象工厂来生成与特定视感相关的用户界面对象。InterView还包括一个LayoutKit,它根据所需要的布局生成不同的组合(composition)对象。例如,一个概念上是水平的布局根据文档的定位(画像或是风景)可能需要不同的组成对象。
ET++使用Abstract Factory模式以达到在不同窗口系统(例如,X Windows和SunViem)间可移植性。Window System抽象基类定义一些接口,来创建表示窗口系统资源的对象。具体子类为某个特定的窗口系统实现这些接口。运行时刻,ET++创建一个具体WindowSystem子类的实例,以创建具体的系统资源对象。
《HeadFirst设计模式》
《设计模式:可复用面对对象软件的基础》