创建者模式:工厂方法模式

复制代码
生动讲解工厂方法模式

想象你要开一家"日志打印店":
1. 客户只需要说:"我要打印日志"
2. 不用关心是用打印机(文件日志)还是刻石碑(数据库日志)
3. 你根据订单类型自动选择对应的生产工具

这就是工厂方法模式的核心:**让子类决定创建什么对象**,客户端只需面向接口编程。

---

### 一、工厂方法模式四大核心组件

#### 1. 🧩 抽象产品(Logger接口)
```java
// 相当于"印刷品标准"
public interface Logger {
    void writeLog(); // 所有日志都必须支持打印功能
}
```

#### 2. 🖨️ 具体产品(FileLogger/DataBaseLogger)
```java
// 文件日志 - 相当于"纸质印刷"
public class FileLogger implements Logger {
    public void writeLog() {
        System.out.println("正在使用文件日志记录器记录日志");
    }
}

// 数据库日志 - 相当于"石碑雕刻"
public class DataBaseLogger implements Logger {
    public void writeLog() {
        System.out.println("正在使用数据库日志记录器记录日志");
    }
}
```

#### 3. 🏭 抽象工厂(LoggerFactory接口)
```java
// 印刷厂生产标准
public interface LoggerFactory {
    Logger createLogger(); // 所有工厂必须能生产日志产品
}
```

#### 4. 🔧 具体工厂(FileLoggerFactory/DataBaseLoggerFactory)
```java
// 文件日志工厂 - 专门生产纸质印刷品
public class FileLoggerFactory implements LoggerFactory {
    public Logger createLogger(){
        System.out.println("🖨️ 启动文件印刷流水线");
        return new FileLogger();
    }
}

// 数据库日志工厂 - 专门生产石碑雕刻
public class DataBaseLoggerFactory implements LoggerFactory {
    public Logger createLogger() {
        System.out.println("⛏️ 启动石碑雕刻工坊");
        return new DataBaseLogger();
    }
}
```

---

### 二、工作流程(客户下单场景)
```java
public class Client {
    public static void main(String[] args) {
        // 1. 获取工厂配置(客户选择印刷方式)
        LoggerFactory factory = XMLUtil.getBean();

        // 2. 创建日志产品(工厂生产)
        Logger logger = factory.createLogger();

        // 3. 使用产品(客户收货使用)
        logger.writeLog();
    }
}
```

#### 配置文件(订单需求单)
```xml
<!-- config.xml -->
<config>
    <!-- 切换此处即可更换日志类型 -->
    <className>创建者模式.工厂方法模式.具体工厂.FileLoggerFactory</className>
</config>
```

#### 配置解析器(订单处理中心)
```java
public class XMLUtil {
    public static LoggerFactory getBean() {
        // 1. 读取配置文件(接收订单)
        // 2. 解析XML获取类名(理解需求)
        String className = "创建者模式.工厂方法模式.具体工厂.FileLoggerFactory";

        // 3. 反射创建工厂(分配对应工厂)
        Class<?> clazz = Class.forName(className);
        return (LoggerFactory) clazz.newInstance();
    }
}
```

---

### 三、实际开发应用场景

#### 场景1:支付网关切换
```java
// 抽象产品
interface Payment {
    void pay(double amount);
}

// 具体产品
class Alipay implements Payment { /* 支付宝实现 */ }
class WechatPay implements Payment { /* 微信支付实现 */ }

// 抽象工厂
interface PaymentFactory {
    Payment create();
}

// 客户端
public class PaymentClient {
    public void processOrder(String paymentType) {
        PaymentFactory factory = PaymentConfig.getFactory(paymentType);
        Payment payment = factory.create();
        payment.pay(100.00);
    }
}
```

#### 场景2:跨数据库支持
```java
// 抽象产品
interface DatabaseConnection {
    void connect();
}

// 具体产品
class MySQLConnection implements DatabaseConnection { /* ... */ }
class OracleConnection implements DatabaseConnection { /* ... */ }

// 配置文件切换
<database>
    <type>MySQL</type>
    <!-- 或 <type>Oracle</type> -->
</database>
```

#### 场景3:多格式导出功能
```java
class ExportFactory {
    public static Exporter getExporter(String format) {
        switch(format) {
            case "PDF": return new PDFExporterFactory();
            case "EXCEL": return new ExcelExporterFactory();
            default: throw new IllegalArgumentException("不支持的格式");
        }
    }
}
```

---

### 四、六大核心优势

1. **解耦王者**:客户端只依赖抽象(Logger),不依赖具体实现
   ```mermaid
   graph LR
   Client-->|依赖|Logger
   Client-->|依赖|LoggerFactory
   LoggerFactory-->|生产|Logger
   ```

2. **开闭原则典范**:新增产品只需扩展,无需修改已有代码
   ```java
   // 新增云日志产品
   class CloudLogger implements Logger { /* ... */ }
   class CloudLoggerFactory implements LoggerFactory { /* ... */ }
   ```

3. **可配置性**:通过配置文件实现零代码修改切换实现
   ```xml
   <!-- 切换为云日志 -->
   <className>com.example.CloudLoggerFactory</className>
   ```

4. **职责清晰**:每个工厂只负责创建一种产品

5. **可测试性**:轻松Mock工厂进行单元测试

6. **可扩展性**:天然支持产品族扩展(结合抽象工厂模式)

---

### 五、五大避坑指南

1. **避免过度设计**
   ✅ 简单场景用简单工厂:
   ```java
   class LoggerFactory {
       public static Logger create(String type) {
           switch(type) {
               case "FILE": return new FileLogger();
               case "DB": return new DataBaseLogger();
           }
       }
   }
   ```
   ❌ 不要为只有1-2种实现的场景上完整工厂模式

2. **防止循环依赖**
   工厂不应依赖具体产品,应依赖抽象产品
   ```java
   // 正确示范 ✅
   class FileLoggerFactory implements LoggerFactory {
       public Logger createLogger() { // 返回接口
           return new FileLogger();
       }
   }
   ```

3. **性能考量**
   频繁创建对象时考虑对象池:
   ```java
   class DatabaseLoggerFactory {
       private static Queue<Logger> pool = new ConcurrentLinkedQueue<>();

       public Logger createLogger() {
           Logger logger = pool.poll();
           if(logger == null) {
               logger = new DatabaseLogger();
           }
           return logger;
       }
   }
   ```

4. **异常处理**
   工厂方法应明确处理创建失败:
   ```java
   public Logger createLogger() throws CreateException {
       try {
           return new DatabaseLogger();
       } catch (DBConnectionException e) {
           throw new CreateException("数据库连接失败", e);
       }
   }
   ```

5. **避免反射滥用**
   配置化创建时要防御非法类:
   ```java
   // XMLUtil改进
   if(!LoggerFactory.class.isAssignableFrom(clazz)) {
       throw new IllegalArgumentException("非法工厂类");
   }
   ```

---

### 六、三种演进形态

| 模式类型       | 适用场景                          | 示例                     |
|----------------|-----------------------------------|--------------------------|
| 简单工厂       | 产品类型固定且较少                | `LoggerFactory.create(type)` |
| **工厂方法**   | 需要灵活扩展产品                  | 本文示例                 |
| 抽象工厂       | 创建相关产品族(如不同主题UI组件)| `ThemeFactory.createButton()/createMenu()` |

---

### 七、最佳实践总结

1. **识别变化点**:将易变的创建逻辑封装进工厂
2. **面向接口编程**:客户端代码只与抽象产品/工厂交互
3. **配置化创建**:对频繁切换的实现使用配置文件+反射
4. **工厂命名规范**:使用`XXXFactory`统一后缀
5. **结合单例模式**:工厂本身通常实现为单例

```java
// 典型应用框架
DatabaseConnection conn = ConnectionFactory.getFactory(DatabaseType.MYSQL)
                                           .createConnection();
```

> 🌟 **黄金法则**:
> 当你的代码中出现`new`关键字时,问问自己:
> "这个对象的创建逻辑未来会变化吗?"
> 如果答案是肯定的,就该请出工厂方法模式了!

工厂方法就像制造业的"标准化生产线",让产品制造与使用分离。正确使用能让你的代码像乐高积木一样灵活组装,应对各种需求变化!
相关推荐
wellc35 分钟前
SpringBoot集成Flowable
java·spring boot·后端
Hui Baby1 小时前
springAi+MCP三种
java
hsjcjh1 小时前
【MySQL】C# 连接MySQL
java
敖正炀1 小时前
LinkedBlockingDeque详解
java
wangyadong3171 小时前
datagrip 链接mysql 报错
java
untE EADO1 小时前
Tomcat的server.xml配置详解
xml·java·tomcat
ictI CABL2 小时前
Tomcat 乱码问题彻底解决
java·tomcat
敖正炀2 小时前
DelayQueue 详解
java
敖正炀2 小时前
PriorityBlockingQueue 详解
java
shark22222222 小时前
Spring 的三种注入方式?
java·数据库·spring