工厂方法设计模式是一种创建设计模式,它提供了一个用于在超类中创建对象的接口,但允许子类更改将要创建的对象的类型。
它在以下情况下特别有用:
● 要创建的对象的确切类型直到运行时才知道。
● 对象创建逻辑复杂、重复或需要封装。
● 您希望遵循开放/封闭原则 — 开放用于扩展,关闭用于修改。
当您有多个类型相似的对象时,您可以从基本条件逻辑(如 or 语句)开始决定要创建哪个对象。 if-elseswitch
但随着应用程序的增长,这种方法会变得僵化、更难测试,并且将代码与特定类紧密耦合,这违反了关键设计原则。
Factory 方法允许您创建不同的对象,而无需将代码紧密耦合到特定类。
让我们通过一个真实世界的示例,看看如何应用工厂方法模式来构建更具可扩展性和可维护性的对象创建工作流程。
想象一下,您正在为向用户发送通知的 Web 应用程序构建后端。
起初,这很简单——您只发送电子邮件通知。
一个类可以处理这个问题。
public class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending email: " + message);
}
}
为了在我们的服务中使用它,我们创建了电子邮件通知对象并调用了该 方法。send()
public class NotificationService {
public void sendNotification(String message){
EmailNotification email = new EmailNotification();
email.send(message);
}
一切都很好。
但随之而来的是新的要求:支持短信通知。
因此,您可以添加一个新类,并通过添加一个新块来更新您的 NotificationService 类,以创建 SMS 通知对象,并发送该对象。
public class NotificationService {
public void sendNotification(String type, String message) {
if (type.equals("EMAIL")) {
EmailNotification email = new EmailNotification();
email.send(message); // 补充发送邮件的调用
} else if (type.equals("SMS")) {
SMSNotification sms = new SMSNotification();
sms.send(message);
} else if (type.equals("Push")) {
PushNotification push = new PushNotification(); // 修正变量
push.send(message); // 修正方法调用对象
}
}
}
稍微复杂一些,但仍然可以管理。
几周后,产品想要向移动设备发送推送通知。
public class NotificationService {
public void sendNotification(String type, String message) {
if (type.equals("EMAIL")) {
EmailNotification email = new EmailNotification();
email.send(message);
} else if (type.equals("SMS")) {
SMSNotification sms = new SMSNotification();
sms.send(message); // 补充SMS的发送调用
} else if (type.equals("Push")) {
PushNotification push = new PushNotification(); // 修正变量名和构造函数
push.send(message);
} else if (type.equals("Slack")) {
SlackNotification slack = new SlackNotification(); // 修正变量名和构造函数
slack.send(message);
} else if (type.equals("WhatsApp")) {
WhatsAppNotification whatsapp = new WhatsAppNotification(); // 修正变量名
whatsapp.send(message);
}
}
}
逻辑中的另一个分支。
然后,营销人员想要尝试使用 Slack 警报。然后是WhatsApp。
public class NotificationService {
public void sendNotification(String type, String message) {
if (type.equals("EMAIL")) {
EmailNotification email = new EmailNotification();
email.send(message);
} else if (type.equals("SMS")) {
SMSNotification sms = new SMSNotification();
sms.send(message);
} else if (type.equals("Push")) {
PushNotification push = new PushNotification(); // 修正变量名和赋值符号
push.send(message);
} else if (type.equals("Slack")) {
SlackNotification slack = new SlackNotification(); // 修正变量名
slack.send(message);
} else if (type.equals("WhatsApp")) {
WhatsAppNotification whatsapp = new WhatsAppNotification(); // 修正变量名
whatsapp.send(message); // 修正分号为英文
}
}
}
现在,您的通知代码开始看起来像一个巨大的控制塔。它负责创建各种通知,了解每种通知的工作原理,并根据类型决定发送哪些通知。
这成为维护的噩梦:
● 每次添加新的通知通道时,都必须修改相同的核心逻辑。
● 测试变得很麻烦,因为逻辑与对象创建交织在一起。
● 它违反了关键的设计原则,尤其是开放/封闭原则,即类应该开放以进行扩展,但关闭以进行修改。
● 如果您在团队中工作,合并冲突会变得很常见,因为多个开发人员尝试在同一位置添加或更改通知类型。
让我们将“创建什么的决定”与如何使用它的逻辑分开。
这就是简单工厂的用武之地。
简单工厂不是《四人帮》(GoF)一书中的正式设计模式,但它是现实世界代码库中最实用和最广泛使用的重构技术之一。
这个想法是这样的:
● 您可以创建一个单独的类(“工厂”),其唯一工作是集中和封装对象创建。
● 通知服务不再需要知道要实例化哪个具体类。它只是要求工厂提供正确类型的通知。
让我们将对象创建提取到一个单独的类中:
public class SimpleNotificationFactory {
public static Notification createNotification(String type) {
return switch (type) {
case "EMAIL" -> new EmailNotification();
case "SMS" -> new SMSNotification();
case "PUSH" -> new PushNotification();
default -> throw new IllegalArgumentException("Unknown type: " + type);
};
}
}
现在,所有创建逻辑都集中在一个位置,通知发送逻辑变得干净、可读,并且可以扩展。
现在 更干净了:NotificationService
public class NotificationService {
public void sendNotification(String type, String message) {
Notification notification = SimpleNotificationFactory.createNotification(type);
notification.send(message);
}
}
使用这种方法:
● 您的核心逻辑变得更加集中——它只使用通知,不构建通知。
● 添加新的通知类型变得更加容易 - 您只需修改工厂,而不是整个系统。
● 测试和维护变得更加简单——每个部件都能很好地完成一件事。
所以你已经用一个简单的工厂清理了东西。您的通知逻辑现在更加精简和模块化。但是,随着您的产品不断增长并且您不断添加新的通知类型,有些事情又开始感觉不对劲。
为什么?
因为你的代码开始看起来与你刚刚重构的臃肿代码出奇地相似。每次引入新类型(如 Slack、WhatsApp 或应用内通知)时,您都会立即回到修改工厂的 或 语句。NotificationFactoryswitchif-else
这不是很开放/封闭,不是吗?
您的系统更好,但如果不进行修改,它仍然无法进行扩展。您仍在对决策逻辑进行硬编码并将创建集中在一个地方。
这时你就会意识到——你需要更进一步。您需要赋予每种类型的通知自己的责任,以了解如何创建自身。
这正是工厂方法设计模式的目的。
工厂方法模式采用 对象创建的概念并将其移交给子类。您不是由一个中央工厂决定要创建什么,而是将责任委托给确切知道他们需要生产什么的专用类。
简单来说:
● 每个子类都定义了自己的实例化对象的方式。
● 基类定义了用于 创建该对象的通用接口,但不知道该对象是什么。
● 基类还经常定义常见行为,以某种方式使用创建的对象。
所以现在,与其拥有:
if type == "EMAIL" → return new EmailNotification()
if type == "SMS" → return new SMSNotification()
你有:
// EmailNotificationCreator knows it should return new EmailNotification
// SMSNotificationCreator knows it should return new SMSNotification
您的创建逻辑是去中心化的。
想想一个送餐平台。您下订单。如果系统设计得像一个简单的工厂,那么有一个集中式厨房来决定是烹饪披萨、寿司还是汉堡。
但通过工厂方法,每家餐厅(Pizza Place、Sushi Bar、Burger Joint)都有自己的厨房,并且知道如何准备食物。平台只是要求合适的厨房来处理它。
- 1. 产品(例如通知):工厂方法创建的对象的接口或抽象类。
- 2. ConcreteProduct(例如 EmailNotification、SMSNotification):实现 Product 接口的具体类。
- 3. Creator(例如 NotificationCreator):声明工厂方法的抽象类(或接口),该方法返回 Product 类型的对象。它还可能定义工厂方法的默认实现。Creator 还可以有其他方法使用工厂方法创建的产品。
- 4. ConcreteCreator(例如,EmailNotificationCreator、SMSNotificationCreator):覆盖工厂方法以返回特定 ConcreteProduct 实例的子类。
让我们缩小并查看大局。
到目前为止,您已经看到了臃肿的 连锁店和中心工厂的缺点。您已经看到随着系统的发展,它们如何成为痛点。使用工厂方法设计模式,我们将其翻转过来。if-else
我们没有将决策的负担放在一个地方,而是 以干净、有组织的方式在整个系统中分配对象创建职责。
让我们分解一下在通知系统上下文中的实际情况:
public interface Notification {
public void send(String message);
}
public class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending email: " + message);
}
}
public class PushNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending push notification: " + message);
}
}
public class SMSNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending SMS: " + message);
}
}
我们创建一个抽象类来声明工厂方法 ,并可选择包括共享行为,例如定义使用任何对象提供的任何对象发送通知的高级逻辑 。createNotification()send() createNotification()
public abstract class NotificationCreator {
// 工厂方法 - 由子类实现具体创建逻辑
public abstract Notification createNotification();
// 通用逻辑 - 使用工厂方法创建的对象
public void send(String message) {
Notification notification = createNotification();
notification.send(message);
}
}
将此类视为模板:
● 它不知道它正在发送什么通知,但它知道如何发送它。
● 它将通知类型的选择推迟到其子类。
这是关键:抽象创建者定义的是流程,而不是细节。
现在是令人兴奋的部分。
您可以创建特定的类,例如 、 、 等。EmailNotificationCreatorSMSNotificationCreatorPushNotificationCreator
public class EmailNotificationCreator extends NotificationCreator {
@Override
public Notification createNotification() {
return new EmailNotification();
}
}
public class PushNotificationCreator extends NotificationCreator {
@Override
public Notification createNotification() {
return new PushNotification();
}
}
public class SMSNotificationCreator extends NotificationCreator {
@Override
public Notification createNotification() {
return new SMSNotification();
}
}
每个都扩展了抽象创建者并实现了返回其特定通知类型的方法。createNotification()
这就是工厂方法魔法发生的地方:
● EmailNotificationCreator →返回new EmailNotification()
● SMSNotificationCreator →返回new SMSNotification()
● PushNotificationCreator →返回new PushNotification()
不再有条件。每个类都知道它需要创建什么,核心系统不需要关心。
现在,如果您想发送电子邮件,请使用 。对于短信,请使用 。系统知道流程,每个子类都知道细节。EmailNotificationCreatorSMSNotificationCreator
假设您想添加 Slack 通知。
使用旧设置,您必须:
● 修改工厂
● 添加新 案例 if-elseswitch
● 破坏现有逻辑的风险
使用工厂方法模式,只需:
● 创建新类SlackNotificationCreator
● 实现返回 createNotification()new SlackNotification()
做。
不修改现有类。
没有回归的风险。
没有巨大的工厂文件尖叫求救。
以下是您的应用如何使用此体系结构:
public class FactoryMethodDemo {
public static void main(String[] args) {
NotificationCreator creator;
// 发送邮件通知
creator = new EmailNotificationCreator();
creator.send("Welcome to our platform!");
// 发送短信通知
creator = new SMSNotificationCreator();
creator.send("Your OTP is 123456");
// 发送推送通知
creator = new PushNotificationCreator();
creator.send("You have a new follower!");
}
}
每行:
● 创建合适的创建者
● 调用共享 方法send()
● 内部委托给正确的 类型Notification
假设您现在想要支持 Slack 通知。
在简单工厂方法中,您必须:
● 打开工厂文件
● 添加新分支或 分支if-elseswitch
● 希望没有坏事
但在这里呢?
只需添加两个新类:
public class SlackNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending SLACK: " + message);
}
}
class SlackNotificationCreator extends NotificationCreator {
@Override
public Notification createNotification() {
return new SlackNotification();
}
}
做。无需修改现有代码。无回归风险。无耦合。
您现在可以这样使用它:
creator = new SlackNotificationCreator();
creator.send("Standup in 10 minutes!");
其他阅读材料:
https://pan.baidu.com/s/1c1oQItiA7nZxz8Rnl3STpw?pwd=yftc
https://pan.quark.cn/s/dec9e4868381