23种设计模式23种设计模式
首页
介绍
  • 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰器模式
  • 外观模式
  • 享元模式
  • 代理模式
  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式
🚀 编程指南
首页
介绍
  • 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰器模式
  • 外观模式
  • 享元模式
  • 代理模式
  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式
🚀 编程指南
  • 创建型模式

    • 单例模式 (Singleton)
    • 工厂方法模式 (Factory Method)
    • 抽象工厂模式 (Abstract Factory)
    • 建造者模式 (Builder)
    • 原型模式 (Prototype)

工厂方法模式 (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│
└───────┘ └───────┘       └─────────┘ └────────┘

✅ 适用场景

  1. 不知道具体产品类:客户端只知道工厂接口,不关心具体实现
  2. 子类决定实例化:让子类来决定创建什么对象
  3. 产品族扩展:新增产品只需新增工厂类,符合开闭原则

⚠️ 优缺点

优点:

  • 解耦:客户端和具体产品分离
  • 扩展性好:新增产品只需新增类
  • 符合开闭原则

缺点:

  • 类的数量增多(每个产品都需要一个工厂类)
  • 增加了系统复杂度

小结

工厂方法模式的核心:把 new 对象的工作交给工厂,客户端只和工厂接口打交道。

简单记忆:你要什么,告诉工厂,工厂给你造。

👉 下一篇:抽象工厂模式

Prev
单例模式 (Singleton)
Next
抽象工厂模式 (Abstract Factory)