1. 概述
工厂模式,就是将对象的创建交给一个工厂,能够更加便利地管理对象的创建。工厂模式分为三种,简单工厂模式、工厂方法模式、抽象工厂模式,我们通过一个例子来逐步进行了解。
2. 三种工厂模式
案例:我们需要实现一个披萨店的类,店里卖两种口味的pizza,有芝士cheese口味的,和胡椒pepper口味的。根据用户输入的类型,是cheese还是pepper,然后制造出相应的pizza,过程中需要打印pizza的制作过程过程包括prepare、bake、cut、box。
注意:在这里披萨店是作为客户端
Pizza抽象类:
// 将Pizza做成抽象的
public abstract class Pizza {
protected String name;
// 不同的Pizza原材料不同,所以这个方法定义为抽象的,要到子类中由子类自己实现
public abstract void prepare();
public void bake() {
System.out.println(name + " baking;");
}
public void cut() {
System.out.println(name + " cutting;");
}
public void box() {
System.out.println(name + " boxing;");
}
public void setName(String name) {
this.name = name;
}
}
不同类型的pizza需要继承Pizza类然后重写prepare方法,因为不同类型的pizza的材料是不同的
2.1 传统方式
传统方式,直接在Pizza店中制作pizza,根据用户需要的Pizza类型,直接制作出相应的Pizza。
传统方式的UML图为: PizzaStore的代码实现:
public class PizzaStore {
// 订购pizza
public OrderPizza() {
Pizza pizza = null;
String orderType; // 订购pizza的类型
Scanner scanner = new Scanner(System.in);
do {
System.out.println("请输入pizza的类型");
orderType = scanner.nextLine();
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName("希腊披萨");
} else if (orderType.equals("chees")) {
pizza = new CheesPizza();
pizza.setName("芝士披萨");
} else if (orderType.equals("pepper")) {
pizza = new PepperPizza();
pizza.setName("辣椒披萨");
} else {
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
}
代码应该很容易理解
分析优缺点:
- 很好理解,简单易操作
- 缺点是违背了OCP原则,也就是对扩展开放,对修改关系。因为当我们需要增加新的类型的pizza的时候,那么我们对PizzaStore类中的orderPizza方法进行修改,要增加一个else if判断对种类进行判断然后添加相应的代码
- 如果有多个地方有制作pizza的代码,那么就需要全部都进行修改
2.2 简单工厂模式
简单工厂模式,很好理解,也就是将制作pizza的过程直接交由一个工厂类来实现。
我们画出简单工厂模式的UML类图: 从UML图能够看出PizzaStore类直接使用PizzaFactory类的createPizza方法来获取Pizza对象,这就好像pizza店都直接从工厂拿货进行售卖
代码实现:
public class SimpleFactory {
public Pizza createPizza(String orderType) {
System.out.println("使用简单工厂模式");
Pizza pizza = null;
switch (orderType) {
case "greek":
pizza = new GreekPizza();
pizza.setName("希腊披萨");
break;
case "cheese":
pizza = new CheesePizza();
pizza.setName("芝士披萨");
break;
case "pepper":
pizza = new PepperPizza();
pizza.setName("辣椒披萨");
break;
}
return pizza;
}
// 简单工厂模式也叫作静态工厂模式
}
创建了一个简单工厂,实现了一个createPizza方法,能够根据需要的pizza类型来制作pizza 那么我们的PizzaStore类就修改如下:
public class PizzaStore {
// 定义一个简单工厂对象
private SimpleFactory simpleFactory;
public void setSimpleFactory(SimpleFactory simpleFactory) {
this.simpleFactory = simpleFactory;
}
public void orderPizza() {
Scanner scanner = new Scanner(System.in);
String pizzaType;
Pizza pizza;
do {
System.out.println("输入pizza类型");
pizzaType = scanner.nextLine();
pizza = simpleFactory.createPizza(pizzaType);
if (pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("订购失败");
break;
}
}
while (true);
}
}
可以看到,PizzaStore类直接使用工厂类得到了一个Pizza对象,而自己不会再对pizza类型进行判断,直接告诉工厂我需要什么,让工厂进行判断从而生产
简单工厂相较于传统模式,将对象的创建交给一个工厂类来实现,当我增加了新的pizza种类,我不需要再修改PizzaStore的代码,我只需要修改工厂类的代码即可。这样当我有多个PizzaStore,也不需要修改多出,只需要修改一处即可。
当然也可以将工厂类的方法定义为静态的方法
2.3 工厂方法模式
现在我有了新的需求,我需要制作北京的芝士披萨,和北京的胡椒披萨,还有伦敦的芝士披萨,和伦敦的胡椒披萨。
首先我们仍然考虑使用简单工厂模式,那么就需要创建不同的简单工厂类,比如BJPizzaFactory、LDPizzaFactory。这样就需要两个工厂,为了简化,我们让两个工厂继承同一个工厂类,工厂类中定义一个抽象的创建对象方法,让子类去做具体的实现。这就变成了工厂方法模式
工厂方法模式的核心是将对象的实例化推迟到了子类
抽象工厂类的代码:
public abstract class PizzaFactory {
// 定义一个抽象方法,createPizza,让各个工厂子类自己实现
abstract Pizza createPizza(String pizzaType);
// 构造器
public void orderPizza() {
Scanner scanner = new Scanner(System.in);
String pizzaType;
Pizza pizza;
do {
System.out.println("输入pizza类型");
pizzaType = scanner.nextLine();
// 直接调用,获得pizza
pizza = createPizza(pizzaType);
if (pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("订购失败");
break;
}
}
while (true);
}
}
实现类的代码:
public class BJPizzaFactory extends PizzaFactory {
@Override
Pizza createPizza(String pizzaType) {
Pizza pizza = null;
if (pizzaType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (pizzaType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
public class LDPizzaFactory extends PizzaFactory {
@Override
Pizza createPizza(String pizzaType) {
Pizza pizza = null;
if (pizzaType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (pizzaType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
然后store类直接调用工厂类的方法即可
2.4 抽象工厂模式
- 抽象工厂模式,定义了一个interface用于创建相关或有依赖关系的对象蔟,而无需指明具体的类
- 抽象工厂模式可以将简单工厂模式和工厂方法模式进行了整合
- 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者进一步的抽象)
- 将工厂抽象为两层,AbsFactory(抽象工厂)和具体实现的工厂子类,程序员可以根据创建对象类型使用对应的工厂子类,这样将单个的简单工厂变成了工厂簇,更利于代码的维护和扩展
- 类图: 抽象工厂模式的代码:
public interface AbsFactory {
Pizza createPizza(String pizzaType);
}
然后子类实现接口
public class BJPizzaFactory implements AbsFactory {
@Override
public Pizza createPizza(String pizzaType) {
Pizza pizza = null;
if (pizzaType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (pizzaType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
然后PizzaStore的代码
public class PizzaStore {
private AbsFactory factory;
public void setFactory(AbsFactory absFactory) {
this.factory = absFactory;
}
public void orderPizza() {
Scanner scanner = new Scanner(System.in);
String pizzaType;
Pizza pizza;
while (true) {
System.out.println("请输入pizza类型");
pizzaType = scanner.nextLine();
pizza = factory.createPizza(pizzaType);
if (pizza == null) {
break;
}
pizza.prepare();
}
}
}
注意这里使用的factory对象是接口对象
3. Java源码中的工厂模式
Java中的Calendar类就是采用的工厂模式进行设计的。
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
这个方法根据不同的地区类型来创建不同的对象并返回
3. 小结
- 工厂模式的意义:将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦,从而提高醒目的扩展和维护性
- 设计模式符合依赖抽象原则