Factory Pattern
The Simple Factory
Example1
When we meet code like this: We’ve got several concrete classes being instantiated, and the decision of which to instantiate is made at runtime depending on some set of conditions.
当我们遇到像这样的代码:我们有几个具体的类被实例化,而实例化哪个类是在运行时根据一些条件做出的。
1Pizza orderPizza(String type) {
2 Pizza pizza;
3 if (type.equals("cheese")) {
4 pizza = new CheesePizza();
5 } else if (type.equals("greek")) {
6 pizza = new GreekPizza();
7 } else if (type.equals("pepperoni")) {
8 pizza = new PepperoniPizza();
9 } else {
10 pizza = null; // or handle unknown type
11 }
12 if (pizza != null) {
13 ...
14 }
15 return pizza;
16}
What’s wrong with “new”?
问题不在于 new 操作符本身,而在于它会将代码与“具体”的类实现紧密地耦合在一起。
- 真正的罪魁祸首是“变化” (CHANGE):当你在代码中直接使用 new 来创建一个具体类的实例时(例如 new Dog()),你的代码就焊死在了 Dog 这个具体的实现上。
- 违反“开闭原则”:理想的设计应该是“对扩展开放,对修改关闭”。但如果将来系统需要变化,比如要换成使用 Cat 类,你就必须找到所有 new Dog() 的地方,然后修改代码。这就意味着你的代码对于“修改”并不是关闭的。为了扩展功能(增加一个新的类),你被迫要修改旧有的代码。
- 缺乏灵活性:这种硬编码(hard-coding)的方式使得系统难以适应变化。而文中的解决方案是提倡“面向接口编程”,通过多态来隔离变化,从而让系统在不修改现有代码的情况下,就能适应新的具体类。
We can use the Simple Factory Pattern to encapsulate the instantiation logic in a separate class.
我们可以使用简单工厂模式将实例化逻辑封装在一个单独的类中。
1public class SimplePizzaFactory {
2 public Pizza createPizza(String type) {
3 Pizza pizza = null;
4
5 if (type.equals("cheese")) {
6 pizza = new CheesePizza();
7 } else if (type.equals("pepperoni")) {
8 pizza = new PepperoniPizza();
9 } else if (type.equals("clam")) {
10 pizza = new ClamPizza();
11 } else if (type.equals("veggie")) {
12 pizza = new VeggiePizza();
13 }
14
15 return pizza;
16 }
17}
What’s the advantage of this?
Factory need use static method?
Depends on the context.
If the factory is a utility class that provides a simple way to create objects without maintaining any state, then a static method is appropriate.
However, it can’t be subclassed or have the behavior of the create(...)
method changed through inheritance.
If the factory needs to maintain state or configuration, then it should be an instance method.
Using the Simple factory in a pizza store:
1public class PizzaStore {
2 SimplePizzaFactory factory;
3
4 public PizzaStore(SimplePizzaFactory factory) {
5 this.factory = factory;
6 }
7
8 public Pizza orderPizza(String type) {
9 Pizza pizza;
10 pizza = factory.createPizza(type);
11
12 pizza.prepare();
13 pizza.bake();
14 pizza.cut();
15 pizza.box();
16
17 return pizza;
18 }
19}
The Simple Factory Defined
PizzaStore
持有SimplePizzaFactory
的引用SimplePizzaFactory
负责创建Pizza
对象Pizza
是一个抽象类或接口或是一个类(我们说“implement an interface”并不是说一定要定义一个抽象的类或是一个Interface,一个具体的类作为具体的类的父类也是OK的),CheesePizza
、PepperoniPizza
、ClamPizza
和VeggiePizza
都是Pizza
的子类,是具体的实现类PizzaStore
通过SimplePizzaFactory
的createPizza
方法来创建具体的Pizza
对象
Factory Method Pattern
Example2
当我们需要有不同种类的PizzaStore
时,我们可以对 Example1 做一些更改:把 SimplePizzaFactory
中的createPizza
方法放进 PizzaStore
中,并且把PizzaStore
设置为抽象类,createPizza
方法变成抽象方法。
1public abstract class PizzaStore {
2 public Pizza orderPizza(String type) {
3 Pizza pizza;
4 pizza = createPizza(type);
5
6 pizza.prepare();
7 pizza.bake();
8 pizza.cut();
9 pizza.box();
10
11 return pizza;
12 }
13
14 protected abstract Pizza createPizza(String type);
15}
PizzaStore 是一个抽象类,它定义了一个抽象的 createPizza 方法。披萨的创建逻辑被延迟到 PizzaStore 的具体子类中去实现
这样,我们就可以创建不同的 PizzaStore
子类来实现不同的 createPizza
方法,例如:
1public class NYPizzaStore extends PizzaStore {
2 @Override
3 protected Pizza createPizza(String type) {
4 if (type.equals("cheese")) {
5 return new NYStyleCheesePizza();
6 } else if (type.equals("pepperoni")) {
7 return new NYStylePepperoniPizza();
8 } else if (type.equals("clam")) {
9 return new NYStyleClamPizza();
10 } else if (type.equals("veggie")) {
11 return new NYStyleVeggiePizza();
12 } else {
13 return null; // or handle unknown type
14 }
15 }
16}
17
18public class ChicagoPizzaStore extends PizzaStore {
19 @Override
20 protected Pizza createPizza(String type) {
21 if (type.equals("cheese")) {
22 return new ChicagoStyleCheesePizza();
23 } else if (type.equals("pepperoni")) {
24 return new ChicagoStylePepperoniPizza();
25 } else if (type.equals("clam")) {
26 return new ChicagoStyleClamPizza();
27 } else if (type.equals("veggie")) {
28 return new ChicagoStyleVeggiePizza();
29 } else {
30 return null; // or handle unknown type
31 }
32 }
33}
Factory Method Pattern 的好处是:
实现了产品的实现和使用的解耦:PizzaStore
不需要知道具体的 Pizza
实现类,只需要知道如何使用 createPizza
方法来创建 Pizza
对象。
Factory Method Pattern defined
The Factory Method Pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. 工厂方法模式定义了一个用于创建对象的接口,但允许子类决定实例化哪一个类。工厂方法使一个类将实例化推迟到子类。
- Creator 和 Product 都是抽象层
- 创建 具体对象实例(ConcreteProduct) 的定义位于 Creator 的子类实现中,创建的都必须满足 Product 的抽象接口
The Simple Factory vs. Factory Method
对于 The Simple Factory 的主要目的是避免 if-else
模块反复在代码中出现,将构造的代码提出到一个单独的类中;而 Factory Method 的主要目的是主抽象类不实现具体的创建逻辑,而是将创建逻辑交给子类去实现。
The Simple Factory Example
1// 主要目的是避免反复在代码中出现 if-else 判断模块
2// SimplePaymentFactory:这是简单工厂模式的核心,负责所有 Payment 对象的创建
3public class SimplePaymentFactory {
4 // 静态方法通常是简单工厂的典型实现,因为不需要实例化工厂对象
5 public static Payment createPayment(String type) {
6 if ("paypal".equalsIgnoreCase(type)) {
7 return new PayPalPayment();
8 } else if ("credit".equalsIgnoreCase(type)) {
9 return new CreditCardPayment();
10 } else if ("personal_paypal".equalsIgnoreCase(type)) { // 新增的判断分支
11 return new PersonalPayPalPayment();
12 } else if ("business_paypal".equalsIgnoreCase(type)) { // 新增的判断分支
13 return new BusinessPayPalPayment();
14 }
15 // 如果传入了未知的类型,可以抛出异常或返回 null
16 throw new IllegalArgumentException("Unknown payment type: " + type);
17 }
18}
19
20// 调用方式
21Payment payment = SimplePaymentFactory.createPayment("paypal");
22payment.process();
Factory Method Example
1// 抽象类定义框架,子类决定具体如何创建对象
2public abstract class PaymentStore {
3 public Payment orderPayment(String type) {
4 Payment payment = createPayment(type);
5 payment.process();
6 return payment;
7 }
8 protected abstract Payment createPayment(String type);
9}
10
11// 具体子类实现创建逻辑
12// 假设有不同类型的 PayPal 支付
13public class PayPalPaymentStore extends PaymentStore {
14 @Override
15 protected Payment createPayment(String type) {
16 // 子类根据传入的 type,在 PayPal 支付的家族中选择具体类型
17 if ("personal".equals(type)) {
18 return new PersonalPayPalPayment();
19 } else if ("business".equals(type)) {
20 return new BusinessPayPalPayment();
21 }
22 // 如果 type 不是预期的,可以抛出异常或返回 null
23 throw new IllegalArgumentException("Unknown PayPal payment type: " + type);
24 }
25}
26
27// 具体子类实现创建逻辑,用于信用卡支付
28public class CreditCardPaymentStore extends PaymentStore {
29 @Override
30 protected Payment createPayment(String type) {
31 // 子类根据传入的 type,在信用卡支付的家族中选择具体类型
32 if ("visa".equals(type)) {
33 return new VisaCreditCardPayment();
34 } else if ("mastercard".equals(type)) {
35 return new MasterCardCreditCardPayment();
36 }
37 // 如果 type 不是预期的,可以返回 null 或抛出异常
38 throw new IllegalArgumentException("Unknown Credit Card type: " + type);
39 }
40}
41
42// 调用方式
43PaymentStore store = new PayPalPaymentStore();
44Payment personalPay = store.orderPayment("personal");
45Payment businessPay = store.orderPayment("business");
46
47PaymentStore creditCardStore = new CreditCardPaymentStore();
48Payment visaPay = creditCardStore.orderPayment("visa");
49Payment masterCardPay = creditCardStore.orderPayment("mastercard");
The Dependency Inversion Principle
Example3
For example: When we do not use the Factory Method Pattern, we might have code like this:
例如:当我们不使用工厂方法模式时,我们可能会有这样的代码:
1public class DependentPizzaStore {
2 public Pizza createPizza(String style, String type) {
3 Pizza pizza = null;
4 if (style.equals("NY")) {
5 if (type.equals("cheese")) {
6 pizza = new NYStyleCheesePizza();
7 } else if (type.equals("veggie")) {
8 pizza = new NYStyleVeggiePizza();
9 } else if (type.equals("clam")) {
10 pizza = new NYStyleClamPizza();
11 } else if (type.equals("pepperoni")) {
12 pizza = new NYStylePepperoniPizza();
13 }
14 } else if (style.equals("Chicago")) {
15 if (type.equals("cheese")) {
16 pizza = new ChicagoStyleCheesePizza();
17 } else if (type.equals("veggie")) {
18 pizza = new ChicagoStyleVeggiePizza();
19 } else if (type.equals("clam")) {
20 pizza = new ChicagoStyleClamPizza();
21 } else if (type.equals("pepperoni")) {
22 pizza = new ChicagoStylePepperoniPizza();
23 }
24 } else {
25 System.out.println("Error: invalid type of pizza");
26 return null;
27 }
28
29 pizza.prepare();
30 pizza.bake();
31 pizza.cut();
32 pizza.box();
33 return pizza;
34 }
35}
我们可以计算出 DependentPizzaStore
依赖了多少个具体的类:
NYStyleCheesePizza
NYStyleVeggiePizza
NYStyleClamPizza
NYStylePepperoniPizza
ChicagoStyleCheesePizza
ChicagoStyleVeggiePizza
ChicagoStyleClamPizza
ChicagoStylePepperoniPizza
当我们在一个类中直接使用new来创建具体的类实例时,这个类就依赖于这些具体的类。这样做会导致代码的耦合度过高,违反了依赖倒置原则(Dependency Inversion Principle)。
How to fix it?
- 在设计中,我们只让
PizzaStore
依赖于抽象的Pizza
类,而不是具体的NYStyleCheesePizza
、ChicagoStyleCheesePizza
等具体类 - 所有具体的披萨类都“反向依赖”于抽象的
Pizza
类,因为每个具体的类都必须遵守Pizza
类的接口或抽象类的定义
Where’s the “inversion” in Dependency Inversion Principle?
我们仔细看前两张图,第一章的箭头由上至下依次依赖;而第二张图无论是PizzaStore
还是具体的披萨类,都依赖于抽象的Pizza
类。也就是说,依赖关系被“倒置”了。
- Old BAD design:
PizzaStore
->具体 Pizza
- New GOOD design:
PizzaStore
->抽象 Pizza
<-具体 Pizza
The Dependency Inversion Principle Defined
Design Principle - Dependency Inversion Principle : Depend upon abstractions. Do not depend upon concrete classes. 依赖于抽象。不要依赖于具体类
The following guidelines can help you avoid OO designs that violate the Dependency Inversion Principle:
- No variable should hold a reference to a concrete class.
任何变量都不应该持有对具体类的引用- 如果要使用new创建一个具体的类,请用工厂模式代替
- No class should derive from a concrete class.
不应从具体类派生任何类- 如果从一个具体的类派生,那么这个类就依赖于这个具体的类。应该从抽象类或接口派生
- No method should override an implemented method of any of its base classes.
任何方法都不应覆盖其任何基类的已实现方法- 如果你覆盖率一个基类中的一个已经实现的方法,换言之你的基类不是真正的基类(换言之基类不是一个真正的抽象)
- 一个真正的抽象应该被所有子类所共享,因此对于基类的派生不应该覆盖基类实现的方法!
这些只是一些指导原则,而不是规则。它们可以帮助你避免违反依赖倒置原则的面向对象设计,但并不是绝对的。当我们违反这些规则时候,我们可以有充足的理由,例如将来一段时间内不会改变这些类,或者这些类是一些工具类等。
Abstract Factory Pattern
Reworking Example3
我们发现在 Example3 中的代码有几个特点
- 多层嵌套: 在
createPizza
方法中有多层嵌套的if-else
语句,外层判断披萨店的地点,内层判断依赖于地点的披萨配料,导致代码依赖于具体的披萨店和披萨配料 - 内层重复代码:对于每个地方的披萨店,配料都是相似的,例如无论
NY
还是Chicago
,都需要判断是cheese
、veggie
、clam
还是pepperoni
,判断后生成对应的实例 - 修改思路:构建抽象类
Cheese
、Veggie
、Clam
、Pepperoni
,抽象工厂Factory
,每个地点的PizzaStore
都可以使用相同的抽象工厂来创建披萨配料,而不是依赖于具体的配料类
1public class NYPizzaStore extends PizzaStore {
2 protected Pizza createPizza(String item) {
3 Pizza pizza = null;
4 PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
5
6 if (item.equals("cheese")) {
7 pizza = new CheesePizza(ingredientFactory);
8 pizza.setName("New York Style Cheese Pizza");
9 } else if (item.equals("veggie")) {
10 pizza = new VeggiePizza(ingredientFactory);
11 pizza.setName("New York Style Veggie Pizza");
12 } else if (item.equals("clam")) {
13 pizza = new ClamPizza(ingredientFactory);
14 pizza.setName("New York Style Clam Pizza");
15 } else if (item.equals("pepperoni")) {
16 pizza = new PepperoniPizza(ingredientFactory);
17 pizza.setName("New York Style Pepperoni Pizza");
18 }
19
20 return pizza;
21 }
22}
23
24// Abstract Factory
25
26public interface PizzaIngredientFactory {
27 // 注意这些方法定义的返回值是接口或抽象类,而不是具体类!
28 public Dough createDough();
29 public Sauce createSauce();
30 public Cheese createCheese();
31 public Veggies[] createVeggies();
32 public Pepperoni createPepperoni();
33 public Clams createClam();
34}
35
36public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
37 @Override
38 public Dough createDough() {
39 // 工厂方法返回具体的 Dough 实现
40 return new ThinCrustDough();
41 }
42
43 @Override
44 public Sauce createSauce() {
45 return new MarinaraSauce();
46 }
47
48 @Override
49 public Cheese createCheese() {
50 return new ReggianoCheese();
51 }
52
53 @Override
54 public Veggies[] createVeggies() {
55 Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
56 return veggies;
57 }
58
59 @Override
60 public Pepperoni createPepperoni() {
61 return new SlicedPepperoni();
62 }
63
64 @Override
65 public Clams createClam() {
66 return new FreshClams();
67 }
68}
69
70public abstract class Pizza {
71 String name;
72 Dough dough;
73 Sauce sauce;
74 Veggies veggies[];
75 Cheese cheese;
76 Pepperoni pepperoni;
77 Clams clam;
78
79 abstract void prepare();
80
81 void bake() {
82 System.out.println("Bake for 25 minutes at 350");
83 }
84
85 void cut() {
86 System.out.println("Cutting the pizza into diagonal slices");
87 }
88
89 void box() {
90 System.out.println("Place pizza in official PizzaStore box");
91 }
92}
93
94public class CheesePizza extends Pizza {
95 PizzaIngredientFactory ingredientFactory;
96
97 public CheesePizza(PizzaIngredientFactory ingredientFactory) {
98 this.ingredientFactory = ingredientFactory;
99 }
100
101 void prepare() {
102 System.out.println("Preparing " + name);
103 dough = ingredientFactory.createDough();
104 sauce = ingredientFactory.createSauce();
105 cheese = ingredientFactory.createCheese();
106 }
107}
- 我们使用了Factory Method Pattern,对
PizzaStore
进行了抽象,createPizza
方法被定义为抽象方法,每个地点拥有具体的子类。例如NYPizzaStore
是其中一个具体的子类。这样避免了外层的地点判断。 - 我们抽象了
Pizza
,并且创建了CheesePizza
等实例,可以在各个地方通用使用,解决了内层判断中的重复代码问题(也就是在每个不同地点都有cheese
等配料的判断) - 对于不同地点的
CheesePizza
等实例,我们的构建方法是不同的,在之前我们是使用NYStyleCheesePizza
和ChicagoStyleCheesePizza
等具体类来区分。事实上对于不同cheese我们只是配料有一点差异。现在我们使用了PizzaIngredientFactory
来抽象不同地点生产的不同配料(Abstract Factory)。例如子类NYPizzaIngredientFactory
代表纽约的披萨配料。并且在NYPizzaStore
中实例化NYPizzaIngredientFactory
并注入到CheesePizza
中完成对NYStyleCheesePizza
的等价构建。
特点:
- 所有工厂都需要实现
PizzaIngredientFactory
接口,所以所有的工厂都可以生产Dough
、Sauce
、Cheese
、Veggies
、Pepperoni
和Clams
等配料,但是每个工厂生产的具体的配料是不同的 - 相同的配料都需要实现相同的接口,例如
ThickCrustDough
和ThinCrustDough
都需要实现Dough
接口 - 若某个
SUPER
披萨(Pizza类实例)需要Dough
、Cheese
配料,则从外部注入某个PizzaIngredientFactory
并获取Dough
、Cheese
配料,而不是依赖具体的工厂。
Abstract Factory Pattern defined
The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.
抽象工厂模式提供一个接口,用于创建一系列相关或依赖对象而无需指定它们的具体类。
特点:
- Factory和Product都必须构建抽象层,Client依赖的都是抽象层(通过注入获得工厂,内部实际方法使用Product的抽象而不是具体类)
- ConcreteFactory都满足Factory的抽象接口,所以所有的具体Factory都能生产相同的Product抽象
- ConcreteProduct都满足Product的抽象接口,所以所有的具体Product都能被Client使用
- ConcreteFactory应该new具体的ConcreteProduct,但是定义上是返回抽象的Product
- Factory可以保证所有的Product都是相关的(例如同一个工厂生产的Product都是同一个系列的),而不是不同系列的产品
Abstract Factory vs. Factory Method
- Factory Method 和 Abstract Factory 的目的都是为了创建对象,Factory Method使用继承来改变产品的创建方式,而 Abstract Factory 使用组合来创建一组相关的产品。
- Factory Method 使用扩展子类的方式来构建不同的产品
- Abstract Factory 通过实现工厂接口来创建一组相关的产品
- Factory Method 的扩展的子类都是 “for one product”,而 Abstract Factory 的扩展的子类是 “for a family of products”
- 如果要添加一个新的产品类型,Factory Method 只需要添加一个新的子类,而 Abstract Factory 需要添加一个新的工厂类和一个新的产品类。
- Abstract Factory可以保证当一个client使用某个工厂时,所有的产品都是相关的(a family of products)