每日一言
没有什么是完美的,这个世界并不完美,所以才显得美丽。——《钢之炼金术师》
什么是工厂方法(Factory Method)
Java中的工厂方法是一种创建对象的设计模式,属于创建模式之一。工厂方法通过定义一个创建对象的接口,让子类决定实例化哪一个类。这样做的好处是将对象的创建过程封装起来,使得客户端代码不需要知道具体的类,而是通过工厂方法来获取所需的对象。
主要特点:
- 定义接口:工厂方法定义了创建对象的接口,但将实际创建对象的工作交给子类去完成。
- 子类决定对象:通过继承工厂类并实现工厂方法,子类可以决定具体创建哪个类的实例。
- 解耦对象创建:客户端不需要关心具体的类,只需要通过工厂方法来创建对象,从而实现了代码的解耦和扩展性。
如何写好一个工厂方法
第一步:定义产品的抽象接口或抽象类
产品是由工厂创建的对象(实例),并且他们都实现了同一个抽象接口或父类。这个抽象类中应该包含所有产品至少应该实现的接口。
📌 举个例子:
假设你有一个发送消息的系统,支持多种通知方式:
| 通知方式 | 类名 | 属于“产品”吗? |
|---|---|---|
| 发送邮件 | EmailNotification | ✅ 是产品 |
| 发送短信 | SMSNotification | ✅ 是产品 |
| 发送微信 | WeChatNotification | ✅ 是产品 |
它们都有一个共同的“父类”或“接口”:
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);
}
}
第三步:定义一个抽象工厂或接口
抽象工厂描述了创建产品的方法:
abstract class NotificationFactory {
public abstract Notification createNotification();
}
也可以是接口:
interface NotificationFactory {
Notification createNotification();
}
第四步:实现具体的工厂子类
每个子类负责创建一个具体的产品:
class EmailFactory extends NotificationFactory {
public Notification createNotification() {
return new EmailNotification();
}
}
class SMSFactory extends NotificationFactory {
public Notification createNotification() {
return new SMSNotification();
}
}
第五步:添加一个工厂选择器(用于根据不同类型返回不同工厂)
NotificationFactoryProvider {
public static NotificationFactory getFactory(String type) {
switch (type.toLowerCase()) {
case "email": return new EmailFactory();
case "sms": return new SMSFactory();
default: throw new IllegalArgumentException("未知通知类型");
}
}
}
第六步:客户端使用工厂选择器获取对象
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函数中的内容。
我们先新增微信通知这个产品:
class WeChatNotification implements Notification {
public void send(String message) {
System.out.println("发送微信: " + message);
}
}
然后增加微信类的创建工厂:
class WeChatFactory extends NotificationFactory {
public Notification createNotification() {
return new WechatNotification();
}
}
然后我们在工厂选择器中增加微信选项:
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时,就会生成微信工厂,并生成微信产品。
简单工厂
对于上面的例子,似乎并不能很好的利用到工厂方法的特性,反而让程序过度封装显得冗杂。因此我们引入简单工厂,作为在相对简单的情况中使用:
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风格的组件
第一步:抽象产品族
我们给每一个产品创建一个接口:
// 抽象产品A:按钮
interface Button {
void render();
}
// 抽象产品B:文本框
interface Textbox {
void render();
}
第二步:具体产品实现(Mac和Windows两种风格)
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 风格文本框");
}
}
第三步:抽象工厂接口
interface GUIFactory {
Button createButton();
Textbox createTextbox();
}
第四步:具体工厂类
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();
}
}
第五步:客户端使用(不关心具体产品是哪一套)
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("不支持的类型");
}
}
}