Java工厂方法

每日一言

没有什么是完美的,这个世界并不完美,所以才显得美丽。——《钢之炼金术师》

什么是工厂方法(Factory Method)

Java中的工厂方法是一种创建对象的设计模式,属于创建模式之一。工厂方法通过定义一个创建对象的接口,让子类决定实例化哪一个类。这样做的好处是将对象的创建过程封装起来,使得客户端代码不需要知道具体的类,而是通过工厂方法来获取所需的对象。

主要特点:

  • 定义接口:工厂方法定义了创建对象的接口,但将实际创建对象的工作交给子类去完成。
  • 子类决定对象:通过继承工厂类并实现工厂方法,子类可以决定具体创建哪个类的实例。
  • 解耦对象创建:客户端不需要关心具体的类,只需要通过工厂方法来创建对象,从而实现了代码的解耦和扩展性。

如何写好一个工厂方法

第一步:定义产品的抽象接口或抽象类

产品是由工厂创建的对象(实例),并且他们都实现了同一个抽象接口或父类。这个抽象类中应该包含所有产品至少应该实现的接口。

📌 举个例子:

假设你有一个发送消息的系统,支持多种通知方式:

通知方式 类名 属于“产品”吗?
发送邮件 EmailNotification ✅ 是产品
发送短信 SMSNotification ✅ 是产品
发送微信 WeChatNotification ✅ 是产品

它们都有一个共同的“父类”或“接口”:

1
2
3
interface Notification {
void send(String message);
}

第二步:创建多个具体的产品实现类

这些类是实际的产品。也就是说我们需要实现我们所需要的每个产品的具体实现。

1
2
3
4
5
6
7
8
9
10
11
12
class EmailNotification implements Notification {
public void send(String message) {
System.out.println("发送邮件: " + message);
}
}

class SMSNotification implements Notification {
public void send(String message) {
System.out.println("发送短信: " + message);
}
}

第三步:定义一个抽象工厂或接口

抽象工厂描述了创建产品的方法:

1
2
3
abstract class NotificationFactory {
public abstract Notification createNotification();
}

也可以是接口:

1
2
3
interface NotificationFactory {
Notification createNotification();
}

第四步:实现具体的工厂子类

每个子类负责创建一个具体的产品:

1
2
3
4
5
6
7
8
9
10
11
class EmailFactory extends NotificationFactory {
public Notification createNotification() {
return new EmailNotification();
}
}

class SMSFactory extends NotificationFactory {
public Notification createNotification() {
return new SMSNotification();
}
}

第五步:添加一个工厂选择器(用于根据不同类型返回不同工厂)

1
2
3
4
5
6
7
8
9
10
NotificationFactoryProvider {
public static NotificationFactory getFactory(String type) {
switch (type.toLowerCase()) {
case "email": return new EmailFactory();
case "sms": return new SMSFactory();
default: throw new IllegalArgumentException("未知通知类型");
}
}
}

第六步:客户端使用工厂选择器获取对象

1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
//客户端不关心具体的实现类,只关心工厂和接口
NotificationFactory factory = NotificationFactoryProvider.getFactory("email");
Notification notification = factory.createNotification();
notification.send("Hello, this is a test email!");
}
}

充分运用了多态的特性,当我们提前不知道需要那种通知方式时,通过使用工厂方法,在运行环境中动态调节我们生产的产品。

易扩展性

接下来我们继续通过这个例子体现工厂方法的易扩展性:

新增微信通知

工厂方法的生产方式在增加类型时,只动类,不懂客户端。即不需要修改Main函数中的内容。

我们先新增微信通知这个产品:

1
2
3
4
5
class WeChatNotification implements Notification {
public void send(String message) {
System.out.println("发送微信: " + message);
}
}

然后增加微信类的创建工厂:

1
2
3
4
5
class WeChatFactory extends NotificationFactory {
public Notification createNotification() {
return new WechatNotification();
}
}

然后我们在工厂选择器中增加微信选项:

1
2
3
4
5
6
7
8
9
10
11
NotificationFactoryProvider {
public static NotificationFactory getFactory(String type) {
switch (type.toLowerCase()) {
case "email": return new EmailFactory();
case "sms": return new SMSFactory();
case "wechat": return new WeChatFactory; //只需增加这一行
default: throw new IllegalArgumentException("未知通知类型");
}
}
}

这样当客户端接收到字段wechat时,就会生成微信工厂,并生成微信产品。

简单工厂

对于上面的例子,似乎并不能很好的利用到工厂方法的特性,反而让程序过度封装显得冗杂。因此我们引入简单工厂,作为在相对简单的情况中使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
interface Notification {
void send(String message);
}
class EmailNotification implements Notification {
public void send(String message) {
System.out.println("发送邮件: " + message);
}
}

class SMSNotification implements Notification {
public void send(String message) {
System.out.println("发送短信: " + message);
}
}

public class Main {
public static void main(String[] args) {
//客户端不关心具体的实现类,只关心工厂和接口
String type = "email"; // 可以是 "email" 或 "sms"
Notification notification = createNotification(type);
notification.send("Hello, this is a test message!");
}
public static Notification createNotification(String type) {
switch(type) {
case "email":
return new EmailNotification();
case "sms":
return new SMSNotification();
default:
throw new IllegalArgumentException("Unknown notification type: " + type);
}
}
}

省去了给每个商品创建工厂的步骤,使用一个大工厂直接生成对应商品。

抽象工厂(Abstract Factroy)

抽象工厂是工厂方法的升级版,使用于一组产品的创建。

跨平台的GUI框架

如果我们想要开发一个跨平台的GUI框架,可以生成:

  • 按钮(Button)
  • 输入框(Textbox)

由于Windows系统和Mac系统UI风格不一致,你希望:

  • 在Window系统下使用Windows风格组件
  • 在Mac系统下使用Mac风格的组件

第一步:抽象产品族

我们给每一个产品创建一个接口:

1
2
3
4
5
6
7
8
9
10
// 抽象产品A:按钮
interface Button {
void render();
}

// 抽象产品B:文本框
interface Textbox {
void render();
}

第二步:具体产品实现(Mac和Windows两种风格)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class WindowsButton implements Button {
public void render() {
System.out.println("渲染 Windows 风格按钮");
}
}

class WindowsTextbox implements Textbox {
public void render() {
System.out.println("渲染 Windows 风格文本框");
}
}

class MacButton implements Button {
public void render() {
System.out.println("渲染 Mac 风格按钮");
}
}

class MacTextbox implements Textbox {
public void render() {
System.out.println("渲染 Mac 风格文本框");
}
}

第三步:抽象工厂接口

1
2
3
4
interface GUIFactory {
Button createButton();
Textbox createTextbox();
}

第四步:具体工厂类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton();
}

public Textbox createTextbox() {
return new WindowsTextbox();
}
}

class MacFactory implements GUIFactory {
public Button createButton() {
return new MacButton();
}

public Textbox createTextbox() {
return new MacTextbox();
}
}

第五步:客户端使用(不关心具体产品是哪一套)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public static void main(String[] args) {
GUIFactory factory = getFactory("mac"); // 或 "windows"

Button button = factory.createButton();
Textbox textbox = factory.createTextbox();

button.render();
textbox.render();
}

public static GUIFactory getFactory(String type) {
if (type.equalsIgnoreCase("windows")) {
return new WindowsFactory();
} else if (type.equalsIgnoreCase("mac")) {
return new MacFactory();
} else {
throw new IllegalArgumentException("不支持的类型");
}
}
}


Java工厂方法
http://blog.ulna520.com/2025/04/13/Java工厂方法_20250413_221855/
Veröffentlicht am
April 13, 2025
Urheberrechtshinweis