跳至正文
来两杯美式
返回

设计模式之工厂模式:简单工厂、工厂方法与抽象工厂

By 来两杯美式
发布于

这是 2020 年 3 月整理的设计模式专题第一篇。工厂模式是创建型模式里最常被问到的,面试几乎必考”三种工厂的区别”。本文以慕课网课程为例,从最朴素的简单工厂出发,一步步演进到工厂方法、抽象工厂,讲清每种模式的定位与取舍。

代码前置条件

以慕课网课程为例,一门课程需要有视频。抽象类 Video,其有两个实现类 JavaVideoPythonVideo

public abstract class Video {
    public abstract void produce();
}

public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制 Java 课程视频");
    }
}

public class PythonVideo extends Video {
    @Override
    public void produce() {
        System.out.println("录制 Python 课程视频");
    }
}

简单工厂(Simple Factory)

简单说明

简单工厂并不属于 23 种设计模式,而是一种编码风格,简单的实现代码。

public class SimpleVideoFactory1 {

    public static Video getVideo(int type) {
        if (type == 1) {
            return new JavaVideo();
        } else if (type == 2) {
            return new PythonVideo();
        } else {
            return null;
        }
    }
}

问题:上面的工厂,在 Video 有新的子类(比如 FeVideo)时,需要修改工厂的代码,不符合开闭原则

优化:用 Class 替代 Type

public class SimpleVideoFactory2 {

    public static <T extends Video> Video getVideo(Class<T> clazz) {
        try {
            return clazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
}

局限点:这种方式的局限在于不容易创建复杂对象——如果对象构造需要传参、依赖注入或复杂的初始化流程,newInstance() 就无能为力了。

工厂方法(Factory Method)

简单说明

工厂方法是 23 种设计模式之一,主要定义:

定义一个创建对象的接口,让实现这个接口的类来决定实例化哪个类,把类的实例化推迟到子类中

实现

声明一个创建产品的接口类(或者抽象类),再为每个产品创建单独的工厂。使用时根据实例化的具体工厂生产特定产品。需要扩展产品时,额外创建新的产品工厂即可。

/**
 * 描述:工厂方法抽象类
 */
public abstract class FactoryMethodVideoFactory {

    public abstract Video getVideo();
}

/**
 * 描述:工厂方法实现类:Java 视频工厂
 */
public class FactoryMethodJavaVideoFactory extends FactoryMethodVideoFactory {
    @Override
    public Video getVideo() {
        return new JavaVideo();
    }
}

// 使用:想创建哪种视频,就实例化哪个工厂
FactoryMethodVideoFactory factory = new FactoryMethodJavaVideoFactory();
Video video = factory.getVideo();
video.produce();

开闭原则体现:新增一种视频时,只需要新增 FeVideo + FactoryMethodFeVideoFactory不改动已有代码

抽象工厂(Abstract Factory)

简单说明

抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,能将一个系列的产品族统一到一起创建,在一定程度上解决工厂方法模式中类爆炸的情况。但是在产品组扩展新的产品时比较困难,需要修改抽象工厂的接口。

前置条件升级

以前的课程,只有一个 Video 对象即可。随着业务发展,现在要求有 Video 和手记 Article

此时引入抽象工厂模式:抽象工厂可以创建同一个产品组的产品——例如 java 相关的 VideoArticle,就是一个产品族

实现

/**
 * 描述:定义一个课程的接口,有获取课程视频和手记的方法
 */
public interface CourseFactory {

    Video getVideo();

    Article getArticle();
}

/**
 * 描述:Java 课程工厂(Java 产品族)
 */
public class JavaCourseFactory implements CourseFactory {
    @Override
    public Video getVideo() {
        return new JavaVideo();
    }

    @Override
    public Article getArticle() {
        return new JavaArticle();
    }
}

/**
 * 描述:Python 课程工厂(Python 产品族)
 */
public class PythonCourseFactory implements CourseFactory {
    @Override
    public Video getVideo() {
        return new PythonVideo();
    }

    @Override
    public Article getArticle() {
        return new PythonArticle();
    }
}

// 使用:产品族被"绑定",不会出现 JavaVideo + PythonArticle 拼错的情况
CourseFactory factory = new JavaCourseFactory();
Video video = factory.getVideo();
Article article = factory.getArticle();

产品族 vs 产品等级

                   产品等级结构(同一种类的不同实现)
                 Video  ←——————   Article
                 /  \               /  \
            JavaVideo PythonVideo JavaArticle PythonArticle
             \______ Java 产品族 ______/   ← 抽象工厂按"族"创建
             \______ Python 产品族 ______/

三种工厂对比

模式核心思想开闭原则优点缺点
简单工厂一个工厂方法 + if/else 判断类型不满足(新增产品要改工厂)实现简单,代码量少扩展需修改工厂;不适合复杂对象创建
工厂方法每个产品一个工厂,创建推迟到子类满足(新增产品只加新工厂)符合开闭原则,职责单一类数量膨胀(类爆炸),理解难度增加
抽象工厂一个工厂创建一族相关产品对产品族满足,对新增产品线不满足产品族统一创建,避免拼错新增产品线需改接口及所有实现

选型思路

现实中的应用

场景对应模式
java.util.Calendar.getInstance()工厂方法(不同地区返回不同实例)
java.util.logging.Logger / SLF4J LoggerFactory工厂方法
Spring IOC 容器创建 Bean工厂方法(BeanFactory
BeanFactorygetBean 按类型/名字创建简单工厂 + 反射
MyBatis SqlSessionFactory工厂方法
跨平台 UI 组件(Windows/Mac 各自一套按钮+文本框)抽象工厂(产品族 = 平台)

小结


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
查看系列全部文章
  1. 01.软件设计原则:SOLID + KISS / YAGNI / LOD 全景
  2. 02.设计模式之原型模式,及深浅拷贝
  3. 03.设计模式之工厂模式:简单工厂、工厂方法与抽象工厂
  4. 04.设计模式之建造者模式:复杂对象的组装艺术
  5. 05.设计模式之适配器模式:让不兼容的接口协同工作
  6. 06.设计模式之桥接模式:抽象与实现分离,各自独立变化
  7. 07.设计模式之装饰者模式:动态增强,比继承更有弹性
  8. 08.设计模式之代理模式:静态代理与 JDK 动态代理
  9. 09.设计模式之外观模式:统一门面,简化子系统调用
  10. 10.设计模式之享元模式:共享细粒度对象,降低内存占用
  11. 11.设计模式之策略模式:算法家族,自由切换
  12. 12.设计模式之模板方法模式:固定骨架,可变步骤
  13. 13.设计模式之观察者模式:一对多依赖,状态变更自动通知
  14. 14.设计模式之责任链模式:请求逐级传递,动态组合处理者
  15. 15.设计模式之状态模式:状态变,行为变
  16. 16.设计模式之迭代器模式:不暴露内部实现,顺序访问集合

上一篇
DDD 复杂业务系统设计(五):聚合与聚合根,一个“选课单”应该装多少东西
下一篇
DDD 复杂业务系统设计(四):实体与值对象,把“学员”和“地址”放进代码的两种姿势