上一节我们学了类间关系以及UML类图,现在我们来看看工厂方法模式。

工厂

在面向对象程序设计中,工厂是一个用来创建其他对象的对象。它提供了一种抽象的构造方法,用于实现不同的对象创建方案。工厂对象通常包含一个或多个方法,这些方法用于创建工厂所能生成的不同类别的对象。这些方法可能会接收参数,以指定对象创建的方式,并最终返回创建的对象。

当对象的创建涉及到复杂的控制过程、配置或其他操作时,工厂对象可以负责将这些细节封装起来,使客户端代码保持简洁,专注于业务逻辑。

不同的设计模式,如工厂方法模式、抽象工厂模式、生成器模式、单例模式等,都应用了工厂的概念。这些模式通过不同的方式组织和实现工厂,以满足不同的设计需求,提高代码的可维护性和可复用性。

工厂方法模式

工厂方法模式(Factory method pattern)是一种实现了 “工厂” 概念的面向对象设计模式,属于创建型模式。工厂方法模式用于处理在不指定对象具体类别的情况下创建对象的问题。

模式意图:定义一个创建对象的接口,但让实现这个接口的具体类来决定实例化哪些类。工厂方法让类的实例化推迟到子类中进行

类图 | 结构与角色分析

先看类图:

下面是角色分析。

Product

  • Product(产品):抽象类,定义了工厂方法模式中生成的那些实例所持有的属性和方法,但具体的处理则由子类ConcreteProduct角色来决定。
1
2
3
4
5
6
/**
* 产品:工厂方法所创建的对象的抽象类
*/
public abstract class Product {
//可以定义Product的属性和方法
}

Creator

  • Creator(创建者):声明工厂方法factoryMethod,负责生成Product。但具体的处理则由子类ConcreteCreator角色来决定。注意!Creator对于实际负责生成实例ConcreteCreator角色一无所知,它唯一知道的就是可以调用Product角色,生成Product实例对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 创建者,声明工厂方法
*/
public abstract class Creator {
/**
* 创建 Product 的工厂方法
* @return Product对象
*/
public abstract Product factoryMethod();

/**
* 示意方法,实现某些功能的方法
*/
public void someOperation() {
//通常在这些方法实现中,需要调用工厂方法来获取Product对象
Product product = factoryMethod();
}
}

注意,这里将 factoryMethod 方法定义为抽象方法,因此子类必须实现这个方法来创建真正的实例。

ConcreteProduct

  • ConcreteProduct(具体的产品):具体产品角色。
1
2
3
4
5
6
/**
* 具体的 Product 对象
*/
public class ConcreteProduct extends Product {
// 实现Product要求的方法
}

ConcreteCreator

  • ConcreteCreator (具体的创建者):属于具体加工这一方,负责生成具体的产品,返回一个具体的Product对象。
1
2
3
4
5
6
public class ConcreteCreator extends Creator {
public Product factoryMethod() {
// 重写工厂方法,返回一个具体的Product对象
return new ConcreteProduct();
}
}

举例 | 电视机工厂生产电视机的例子

类图

客户端使用

我们首先来看客户端的使用效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 客户端代码,使用工厂创建产品并调用其方法
public class Client {
public static void main(String[] args) {
// 创建海尔工厂实例
Factory haierFactory = new HaierFactory();

// 使用工厂创建产品,这里是海尔电视
TV haierTv = haierFactory.createProduct();

// 调用产品的方法
haierTv.play();
}
}

运行效果:

1
2
3
(1)海尔工厂正在生产海尔电视机...
(2)已创建“海尔电视机”...
(3)海尔电视机播放中...

产品角色(Product)

  • TV表示工厂方法模式中的抽象产品“电视机”,由抽象类来实现(也可以定义为接口)。
1
2
3
4
5
6
abstract class TV {  //角色1:Product 
/**
* 抽象TV类:表示抽象产品(电视机),在该类中仅声明 play 抽象方法,表示电视机在播放
*/
public abstract void play();
}

工厂角色(Creator)

  • Factory是一个抽象类,声明创建产品对象的抽象方法createProduct,具体处理让子类去实现。
1
2
3
4
5
6
7
abstract class Factory {  //角色2:Creator
/**
* 电视机工厂生产电视机产品
* @return
*/
public abstract TV createProduct();
}

具体产品角色(ConcreteProduct)

  • HaierTV是产品角色TV的实现类,是工厂方法创建的实际对象。
1
2
3
4
5
6
7
8
9
10
11
class HaierTV extends TV {  //角色3:ConcreteProduct

public HaierTV(){
System.out.println("(2)已创建“海尔电视机”...");
}

@Override
public void play(){
System.out.println("(3)海尔电视机播放中...");
}
}

具体工厂角色(ConcreteCreator)

  • HaierFactory是工厂角色Factory的具体实现类,实现了创建产品的方法createProduct,生成具体的产品对象HaierTV
1
2
3
4
5
6
7
class HaierFactory extends Factory { //角色4:ConcreteCreator
@Override
public TV createProduct(){
System.out.println("(1)海尔工厂正在生产海尔电视机...");
return new HaierTV();
}
}

模式优点

工厂方法模式通过将产品的实例化延迟到具体的工厂类,实现了客户端与具体产品的解耦。

1
2
3
4
5
6
7
8
class HaierFactory extends Factory {
@Override
public TV createProduct() {
System.out.println("(1)海尔工厂正在生产海尔电视机...");
// 这里返回具体的产品实例
return new HaierTV();
}
}

客户端通过调用具体工厂的createProduct方法来获取产品实例,而无需关心具体产品的创建过程。

新增产品时,只需新增一个具体工厂类继承自抽象工厂,重写抽象工厂中的抽象方法,这样可以在不修改已有具体工厂类的情况下引进新的产品,符合开闭原则,降低了耦合度。

开闭原则:对修改封闭,对扩展开放。

新增产品

比如现在,我们引入新的产品:索尼电视机。

1
2
3
4
5
6
7
8
9
10
11
class SonyTV extends TV {  //角色:ConcreteProduct

public SonyTV() {
System.out.println("(2)已创建“索尼电视机”...");
}

@Override
public void play(){
System.out.println("(3)索尼电视机播放中...");
}
}

以及具体的工厂类:

1
2
3
4
5
6
7
8
class SonyFactory extends Factory { //角色:ConcreteCreator

@Override
public TV createProduct(){
System.out.println("(1)索尼工厂正在生产索尼电视机...");
return new SonyTV();
}
}

现在,我们来客户端中测试一把:

1
2
3
4
5
6
7
8
9
10
11
public class Client{ //客户端
public static void main(String[] args){
Factory haierFactory = new HaierFactory();
TV haierTv = haierFactory.createProduct();
haierTv.play();
System.out.println("----------");
Factory sonyFactory = new SonyFactory();
TV sonyTv = sonyFactory.createProduct();
sonyTv.play();
}
}

运行效果:

1
2
3
4
5
6
7
(1)海尔工厂正在生产海尔电视机...
(2)已创建“海尔电视机”...
(3)海尔电视机播放中...
----------
(1)索尼工厂正在生产索尼电视机...
(2)已创建“索尼电视机”...
(3)索尼电视机播放中...

类图如下:

总结

“在工厂方法模式中,父类决定实例的生成方式,但并不决定所要生成的具体的类,具体的处理全部交给子类负责。将生成实例的框架(framework)和实际负责生成实例的类进行解耦”。

我们总结一下,对于工厂方法模式来说,它就是封装一个创建对象的抽象类(或者说接口),由该抽象类的子类来决定实例化哪一个产品类,因此我们说工厂方法模式将类的实例化延迟到子类中完成。产品类可以有很多个,作为客户端(调用者)来说,只需调用工厂方法返回产品即可(我要什么你就给我什么)。