Java设计模式再相识 (三)——工厂方法(Factory Method)模式

11 篇文章 0 订阅
11 篇文章 0 订阅

在项目的开发中,我们经常会遇到需要创建复杂对象的场景,例如一个参数繁多的类的构造函数。这时如果我们不使用任何设计模式进行开发,项目将变得十分复杂,面对常常变动的需求,我们可能需要一个一个修改参数,并在任何引用该类的地方都做一次修改,最后只会变得乱成一团无法维护。那么该如何解决这个问题呢?

答案是使用工厂模式。在需要创建复杂对象,并频繁使用时,我们就可以选择工厂模式。

工厂模式的三种实现形式

工厂模式有三种实现形式

  • 简单工厂(静态工厂)
  • 工厂方法(本节核心)
  • 抽象工厂

注:简单工厂并不算在GoF 23种设计模式中

本节,我们将深入理解工厂方法模式及其应用。

简单工厂(静态工厂)

在介绍工厂方法模式之前,我先对简单工厂进行介绍。在简单工厂模式中,创建实例的方法通常为静态方法,因此简单工厂模式又被成为静态工厂方法。简单工厂只有一个具体的工厂类,专门用于生产特定专一的产品,在需要生产多种产品时,需要将多个产品定义到同一个工厂中进行生产。简单工厂名副其实,十分简单。这样的好处就是产品创建方便,客户直接提需求,工厂直接实现,不需要经过任何产品线。但问题也来了,在需要大量创建不同产品时,简单工厂就会变得“不堪重负”。回归到软件中,简单工厂会增加系统的复杂性,在多个产品的情况下,单个类职责过重,软件将变得难以维护。每增加一个产品就要增加一个工厂类,违背了“开闭原则”。

简单工厂的示例

我们有苹果树和梨树和一家工厂,这家工厂要负责将水果树上的所有水果都摘下来。


package com.yeliheng.factory.simplefactory;

public class Main {
    public static void main(String[] args) {
        Apple apple = (Apple) SimpleFactory.make(0);
        Banana banana = (Banana) SimpleFactory.make(1);
        apple.show();
        banana.show();
    }

    //水果产品
    public interface Fruit {
        void show();
    }

    //具体产品
    static class Apple implements Fruit {
        public void show() {
            System.out.println("苹果树上长苹果...");
        }
    }

    //具体产品
    static class Banana implements Fruit {
        public void show() {
            System.out.println("香蕉树上长香蕉...");
        }
    }

    //工厂生产
    static class SimpleFactory {
        public static Fruit make(int kind) {
            switch (kind) {
                case 0:
                    return new Apple();
                case 1:
                    return new Banana();
            }
            return null;
        }
    }
}

通过代码示例我们可以看到每生产一种具体的水果,就要定义一个新的静态产品类让水果能被工厂生产。工厂类的代码将会随着产品的增加越来越复杂。

为了解决这个问题,工厂方法(Factory Method)模式出现了。

工厂方法

工厂方法模式在简单工厂模式的基础上进一步抽象,可以使系统拓展新的具体类时,原来的工厂代码不必修改,满足开闭原则。拓展新的需求,创建新产品,只需要在原来的基础上增加一个工厂类,这样就可以有多个工厂为我们服务。并且上层应用只需了解抽象的产品类,无需对其他类进行拓展。这也同时满足了迪米特法则、依赖倒置原则和里氏替换原则。但工厂方法模式同样存在缺点。它将会让工厂类数目繁多,降低系统代码的可读性。并且在抽象产品中,只能产出一种具体产品。(这个问题可以使用抽象工厂解决,下节介绍)。

工厂方法模式由以下结构组成:

  • 抽象工厂(AbstractFactory):提供了产品的抽象类或接口。
  • 具体工厂(ConcreteFactory):具体的创建类,实现具体生产产品的方法。
  • 抽象产品(Product):产品的抽象创建类。
  • 具体产品(ConcreteProduct):将抽象产品进行实现。

实际应用场景

  • 日志工厂
  • 文件存储

示例

我们来实现一个日志工厂,该日志工厂具有以下三个功能:

  1. 打印日志到控制台
  2. 打印日志到文件

我们按照上文所述的结构一一实现工厂方法:

  1. 抽象产品

Logger.java


package com.yeliheng.factory.factorymethod;

/**
 * 日志产品的抽象
 */
public interface Logger {
    //日志级别
    void debug();

    void info();

    void warning();

    void error();

}

我们在抽象产品中定义了四个产品接口,可对应四种不同级别的日志。

  1. 抽象工厂

LoggerFactory.java


package com.yeliheng.factory.factorymethod;

public interface LoggerFactory {
    Logger getLogger();
}

在抽象工厂中,我们去使用Logger,定义一个getLogger()方法来便于待会具体工厂获取Logger实例。

  1. 具体产品

我们实现两个具体产品类,一个是输出到控制台的产品类,一个是输出到文件的产品类。这里为了代码的简洁,就不详细地写出输出到文件的逻辑,仅使用输出进行简单举例。

ConsoleLogger.java

package com.yeliheng.factory.factorymethod;

public class ConsoleLogger implements Logger{
    @Override
    public void debug() {

    }

    @Override
    public void info() {
        System.out.println("[INFO] 这条日志被输出到[控制台]");
    }

    @Override
    public void warning() {

    }

    @Override
    public void error() {

    }
}

FileLogger.java

package com.yeliheng.factory.factorymethod;

public class FileLogger implements Logger{
    @Override
    public void debug() {

    }

    @Override
    public void info() {
        System.out.println("[INFO] 这条日志被输出到[文件]");
    }

    @Override
    public void warning() {

    }

    @Override
    public void error() {

    }
}

  1. 实现具体的工厂

ConsoleLoggerFactory.java

package com.yeliheng.factory.factorymethod;

/**
 * 具体工厂
 */
public class ConsoleLoggerFactory implements LoggerFactory{
    @Override
    public Logger getLogger() {
        return new ConsoleLogger();
    }
}

我们重写了实现了抽象工厂:LoggerFactory,并重写了getLogger方法,让其返回一个ConsoleLogger实例。

FileLoggerFactory.java


package com.yeliheng.factory.factorymethod;

public class FileLoggerFactory implements LoggerFactory{
    @Override
    public Logger getLogger() {
        return new FileLogger();
    }
}

原理相同。

  1. 最后,我们可以开始使用我们通过工厂方法模式创建的日志工厂了。

Main.java

package com.yeliheng.factory.factorymethod;

public class Main {
    public static void main(String[] args) {
        //输出到控制台
        LoggerFactory consoleLoggerFactory = new ConsoleLoggerFactory();
        Logger logger1 = consoleLoggerFactory.getLogger();
        logger1.info();
        //输出到文件
        LoggerFactory fileLoggerFactory = new FileLoggerFactory();
        Logger logger2 = fileLoggerFactory.getLogger();
        logger2.info();

    }
}

可以看到最终输出如下:

output.png

细心的你可能会发现,这个写法与slf4j日志库的写法十分相似。没错,查看slf4j源码可以发现,其使用了大量工厂模式实现强大的日志框架。

小结

到此,我们使用工厂方法模式完成了一个简单的日志工厂,下节我们将使用抽象方法模式继续完善本节的例子。

源码参考:Github

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值