抽象工厂模式

上一篇文章我们介绍了 工厂方法模式,并用一个 ILogger 的例子展示了工厂方法模式的核心思想:暴露一个抽象的工厂方法让子类来实现,子类就可以根据自身的需求来创建不同的对象。

抽象工厂模式(Abstract Factory Pattern)也包含「工厂」的概念,但是代码实现上和工厂方法模式有较大的区别。

两者的核心区别在于:工厂方法每个工厂只负责创建一种产品,而抽象工厂一个工厂负责创建一整族相关产品,确保它们互相兼容。

实现方式上也不同:工厂方法通常依赖子类继承来重写创建方法,抽象工厂则是将工厂对象作为参数传入。

ILogger 的例子对比讲解一下就明白了:

工厂方法模式 中,我们实现了一个 Application 类,并暴露了一个抽象方法 createLogger()我们实现了三个 Application 子类,分别实现 createLogger() 方法即可定制自己的日志记录器。

如果用本文介绍的抽象工厂模式来实现,需要创建一个抽象工厂接口:

// 抽象工厂接口
interface ILoggerFactory {
    ILogger createLogger();
}

然后实现三个具体的工厂类,每个工厂类都创建一种不同的日志记录器:

// 控制台日志具体工厂
class ConsoleLoggerFactory implements ILoggerFactory {
    @Override
    public ILogger createLogger() {
        return new ConsoleLogger();
    }
}

// 文件日志具体工厂
class FileLoggerFactory implements ILoggerFactory {
    @Override
    public ILogger createLogger() {
        return new FileLogger("application.log");
    }
}

// 远程日志具体工厂
class RemoteLoggerFactory implements ILoggerFactory {
    @Override
    public ILogger createLogger() {
        return new RemoteLogger("http://remote-server.com");
    }
}

日志接口 ILogger 及其实现类 ConsoleLogger, FileLogger, RemoteLogger工厂方法模式 中的实现完全相同,这里不再重复。

最后,可以这样实现我们的应用程序,只需要在构造函数中传入一个具体工厂类即可:

class Application {
    private ILogger logger;

    public Application(ILoggerFactory loggerFactory) {
        // 通过工厂创建日志记录器
        this.logger = loggerFactory.createLogger();
    }

    public void doSomething() {
        logger.log("Start doing something...");
    }

    public static void main(String[] args) {
        // 根据环境选择不同的工厂
        ILoggerFactory loggerFactory;
        String env = System.getenv("ENV");
        if ("dev".equals(env)) {
            loggerFactory = new ConsoleLoggerFactory();
        } else if ("test".equals(env)) {
            loggerFactory = new FileLoggerFactory();
        } else if ("prod".equals(env)) {
            loggerFactory = new RemoteLoggerFactory();
        } else {
            throw new IllegalArgumentException("Invalid environment: " + env);
        }

        // 将工厂传入应用程序
        Application app = new Application(loggerFactory);
        app.doSomething();
    }
}

上面这个简单的例子就展示了抽象工厂模式,主要有以下几个关键角色:

  • 抽象产品(AbstractProduct):定义了抽象工厂所创建的对象的接口,即 ILogger 接口。
  • 具体产品(ConcreteProduct):实现了抽象产品接口的具体对象,即 ConsoleLoggerFileLoggerRemoteLogger 类。
  • 抽象工厂(AbstractFactory):抽象工厂接口,即 ILoggerFactory 接口,声明了创建方法。
  • 具体工厂(ConcreteFactory):实现抽象工厂接口的具体工厂类,即 ConsoleLoggerFactoryFileLoggerFactoryRemoteLoggerFactory 类。

不过上述示例中,抽象工厂只有一个 createLogger 方法,过于简单了,这个场景用抽象工厂模式有些大材小用。

在实际场景中,抽象工厂通常会包含多个创建方法,用于创建一系列「相关的」对象,下面举例说明。