工厂方法模式 (Factory Method)
📖 通俗理解
想象你要买一台手机:
- 你不需要知道手机是怎么生产的
- 你只需要告诉店员"我要苹果手机"或"我要华为手机"
- 店员(工厂)就会给你对应的手机
工厂方法模式就是:定义一个创建对象的接口,让子类决定实例化哪个类。把创建对象的工作交给"工厂",我们只管用。
🎯 解决什么问题?
问题场景:假设你的代码里到处都是 new 对象的代码:
// 到处都是这样的代码
Phone phone = new IPhone();
Phone phone2 = new HuaweiPhone();
问题:
- 如果产品类的构造方法改了,所有
new的地方都要改 - 客户端和具体产品类紧密耦合
- 新增产品时,改动量大
解决:把创建对象的代码封装到工厂里,客户端只和工厂打交道。
🌰 生活中的例子
- 手机工厂:苹果工厂生产苹果手机,华为工厂生产华为手机
- 披萨店:北京店做北京口味披萨,上海店做上海口味披萨
- 汽车4S店:不同4S店卖不同品牌的车
💻 Java 代码实现
场景:手机工厂
我们要创建不同品牌的手机,使用工厂方法模式。
第一步:定义产品接口
/**
* 产品接口:手机
*/
public interface Phone {
void call(); // 打电话
void sendSMS(); // 发短信
}
第二步:实现具体产品
/**
* 具体产品:苹果手机
*/
public class IPhone implements Phone {
@Override
public void call() {
System.out.println("用苹果手机打电话...");
}
@Override
public void sendSMS() {
System.out.println("用苹果手机发短信...");
}
}
/**
* 具体产品:华为手机
*/
public class HuaweiPhone implements Phone {
@Override
public void call() {
System.out.println("用华为手机打电话...");
}
@Override
public void sendSMS() {
System.out.println("用华为手机发短信...");
}
}
/**
* 具体产品:小米手机
*/
public class XiaomiPhone implements Phone {
@Override
public void call() {
System.out.println("用小米手机打电话...");
}
@Override
public void sendSMS() {
System.out.println("用小米手机发短信...");
}
}
第三步:定义工厂接口
/**
* 工厂接口
*/
public interface PhoneFactory {
Phone createPhone();
}
第四步:实现具体工厂
/**
* 苹果手机工厂
*/
public class IPhoneFactory implements PhoneFactory {
@Override
public Phone createPhone() {
System.out.println("苹果工厂生产手机...");
return new IPhone();
}
}
/**
* 华为手机工厂
*/
public class HuaweiPhoneFactory implements PhoneFactory {
@Override
public Phone createPhone() {
System.out.println("华为工厂生产手机...");
return new HuaweiPhone();
}
}
/**
* 小米手机工厂
*/
public class XiaomiPhoneFactory implements PhoneFactory {
@Override
public Phone createPhone() {
System.out.println("小米工厂生产手机...");
return new XiaomiPhone();
}
}
第五步:客户端使用
public class Client {
public static void main(String[] args) {
// 我要买苹果手机
PhoneFactory factory1 = new IPhoneFactory();
Phone iPhone = factory1.createPhone();
iPhone.call();
iPhone.sendSMS();
System.out.println("-------------------");
// 我要买华为手机
PhoneFactory factory2 = new HuaweiPhoneFactory();
Phone huawei = factory2.createPhone();
huawei.call();
huawei.sendSMS();
}
}
输出结果:
苹果工厂生产手机...
用苹果手机打电话...
用苹果手机发短信...
-------------------
华为工厂生产手机...
用华为手机打电话...
用华为手机发短信...
🔥 简单工厂(工厂方法的简化版)
实际开发中,如果产品种类不多,可以用更简单的简单工厂:
/**
* 简单工厂
* 用一个工厂类来创建所有产品
*/
public class SimplePhoneFactory {
public static Phone createPhone(String type) {
switch (type) {
case "iphone":
return new IPhone();
case "huawei":
return new HuaweiPhone();
case "xiaomi":
return new XiaomiPhone();
default:
throw new IllegalArgumentException("不支持的手机类型: " + type);
}
}
}
// 使用
public class Client {
public static void main(String[] args) {
Phone phone1 = SimplePhoneFactory.createPhone("iphone");
Phone phone2 = SimplePhoneFactory.createPhone("huawei");
phone1.call();
phone2.call();
}
}
简单工厂的缺点
每次新增产品都要修改工厂类,违反了开闭原则。但对于产品种类稳定的场景,简单工厂更实用。
🔥 实战案例:日志记录器
/**
* 日志接口
*/
public interface Logger {
void info(String message);
void error(String message);
void debug(String message);
}
/**
* 文件日志
*/
public class FileLogger implements Logger {
private String filePath;
public FileLogger(String filePath) {
this.filePath = filePath;
}
@Override
public void info(String message) {
System.out.println("[FILE-INFO] " + filePath + ": " + message);
}
@Override
public void error(String message) {
System.out.println("[FILE-ERROR] " + filePath + ": " + message);
}
@Override
public void debug(String message) {
System.out.println("[FILE-DEBUG] " + filePath + ": " + message);
}
}
/**
* 控制台日志
*/
public class ConsoleLogger implements Logger {
@Override
public void info(String message) {
System.out.println("[CONSOLE-INFO] " + message);
}
@Override
public void error(String message) {
System.err.println("[CONSOLE-ERROR] " + message);
}
@Override
public void debug(String message) {
System.out.println("[CONSOLE-DEBUG] " + message);
}
}
/**
* 日志工厂接口
*/
public interface LoggerFactory {
Logger createLogger();
}
/**
* 文件日志工厂
*/
public class FileLoggerFactory implements LoggerFactory {
private String filePath;
public FileLoggerFactory(String filePath) {
this.filePath = filePath;
}
@Override
public Logger createLogger() {
return new FileLogger(filePath);
}
}
/**
* 控制台日志工厂
*/
public class ConsoleLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
return new ConsoleLogger();
}
}
使用方式:
public class Application {
private Logger logger;
public Application(LoggerFactory factory) {
this.logger = factory.createLogger();
}
public void run() {
logger.info("应用启动...");
logger.debug("初始化配置...");
logger.info("应用运行中...");
}
public static void main(String[] args) {
// 开发环境用控制台日志
Application devApp = new Application(new ConsoleLoggerFactory());
devApp.run();
System.out.println("====================");
// 生产环境用文件日志
Application prodApp = new Application(new FileLoggerFactory("/var/log/app.log"));
prodApp.run();
}
}
📊 类图结构
┌─────────────────┐ ┌─────────────────┐
│ <<interface>> │ │ <<interface>> │
│ Product │ │ Factory │
├─────────────────┤ ├─────────────────┤
│ + operation() │ │ + create() │
└────────▲────────┘ └────────▲────────┘
│ │
┌────┴────┐ ┌────┴────┐
│ │ │ │
┌───┴───┐ ┌───┴───┐ ┌────┴────┐ ┌───┴────┐
│ProductA│ │ProductB│ │FactoryA│ │FactoryB│
└───────┘ └───────┘ └─────────┘ └────────┘
✅ 适用场景
- 不知道具体产品类:客户端只知道工厂接口,不关心具体实现
- 子类决定实例化:让子类来决定创建什么对象
- 产品族扩展:新增产品只需新增工厂类,符合开闭原则
⚠️ 优缺点
优点:
- 解耦:客户端和具体产品分离
- 扩展性好:新增产品只需新增类
- 符合开闭原则
缺点:
- 类的数量增多(每个产品都需要一个工厂类)
- 增加了系统复杂度
小结
工厂方法模式的核心:把 new 对象的工作交给工厂,客户端只和工厂接口打交道。
简单记忆:你要什么,告诉工厂,工厂给你造。
👉 下一篇:抽象工厂模式
