工厂模式(Factory Pattern)

229 阅读4分钟

概念

工厂模式(Factory Pattern)可以说是最常见的设计模式,该设计模式数据创建型模式,提供了一种简单、快速、高校而安全的创建对象的方式。工厂模式在接口中定义了创建对象的方法,从而将具体的创建对象过程在子类中实现,用户只需要通过接口创建需要的对象即可,不必去关注创建对象的具体过程,不同的子类可以根据需求实现不同的方法。

通俗来讲,工厂模式其本质是用工厂方法代替new操作创建一种实例化对象的方法,提供一种方便创建拥有同种类型接口的产品的复杂对象。

简介

为什么需要工厂模式?
  • 解耦:将对象的创建与使用分离,若不分离情况下,首先违反了设计模式的开闭原则,其次当需求变动,代码上需要使用另一个子类则必定需修改源代码。当将对象的创建与使用分离后,A类调用B类,则A类仅调用B类方法,B类的具体实例化操作则交由工厂完成。

  • 降低代码重复:想想这样一种场景,创建一个对象A,其中属性很多,并且很多地方都要用到A对象,那么就会出现很多重复代码,不仅方法长度变长,后期其他人员维护时也要去看每个实例化的具体实现,维护成本较高。若将A对象交由工厂管理,减少了重复代码,也方便后期对A对象创建过程的维护。(当然,实际开发中可以将一些创建过程写到构造函数中,同样可以降低重复代码,并且构造函数本身也为初始化对象使用,但这样会导致构造函数过于复杂,不符合设计原则。)

  • 减少初始化逻辑导致的错误:因为工厂管理了对象的创建逻辑,创建者并不需要了解具体过程,仅仅使用即可,减少了使用者因为创建逻辑导致的错误。

  • 增加代码的封装性、可读性,清楚明了的代码结构。

适用场景

当我们明确的计划不同条件下返回不同实例时。

使用时需注意的点

作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

伪代码

以创建手机为例,假定手机的品牌有小米和华为两种类型,我们要实现的是根据不同的传入参数实例化不同的手机,其UML设计图如下:

classDiagram
Phone <|-- HuaWei
Phone <|-- XiaoMi
class Phone{
    <<interface>>
    +brand() String
}
class HuaWei{
<<impl>>
+brand() String
}
class XiaoMi{
<<impl>>
+brand() String
}
HuaWei <|-- Factory : <create>
XiaoMi <|-- Factory : <create>
class Factory {
    <<class>>
    +createPhone() Phone
}
Factory <|-- TestDemo
class TestDemo{
    <<class>>
    +main() void
}

伪代码实现如下:

public class SimpleFactory {

    /**
     * 定义Phone接口,定义brand方法返回手机品牌
     */
    public interface Phone {
        String brand();
    }

    /**
     *  华为手机实现类
     */
    public static class HuaWei implements Phone {
        @Override
        public String brand() {
            return "HuaWei phone";
        }
    }

    /**
     * 小米手机实现类
     */
    public static class XiaoMi implements Phone {
        @Override
        public String brand() {
            return "XiaoMi phone";
        }
    }

    /**
     * 手机枚举
     */
    public enum PhoneEnum{
        HuaWei("HuaWei"),
        XiaoMi("XiaoMi");

        private String name;

        PhoneEnum(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    /**
     * 手机工厂
     */
    public static class Factory {
        public Phone createPhone (String phoneName) {
            if (PhoneEnum.HuaWei.getName().equals(phoneName)) {
                return new HuaWei();
            } else if (PhoneEnum.XiaoMi.getName().equals(phoneName)) {
                return new XiaoMi();
            } else {
                return null;
            }
        }
    }

    /**
     * main方法
     */
    public static void main(String[] args) {
        Factory factory = new Factory();
        Phone huawei = factory.createPhone("HuaWei");
        Phone xiaomi = factory.createPhone("XiaoMi");
        System.out.println(huawei.brand());
        System.out.println(xiaomi.brand());

    }

输出结果:

image.png

拓展

既然工厂模式这么好用,那么大佬的源码中是怎么用的呢?这里举一个简单的拓展:
在Spring整合Mybatis的时候,Mybatis 使用SqlSessionFactoryBean 实现了 spring的FactoryBean

image.png

在定义sqlSeeesionFactory时 ,实现类class不是自身的类,而是使用了SqlSessionFactoryBean工厂类,属于工厂模式。

也就是说SqlSeeesionFactoryBean 就是SqlSeeesionFactory的工厂Bean,是创建SqlSeeesionFactory 实例用的,而 SqlSeeesionFactory 是SqlSeeesion的工厂,获取到SqlSeeesion就等于有了Connection,然后实现具体的数据库操作。

感兴趣的同学可以在idea中查看一下源码,在这里我就不深入挖掘了。

结尾

工厂模式的思想很容易掌握,其实同学们如果在中小型的互联网公司写过一段时间代码,就很容易发现主干逻辑,或者分支逻辑中存在着一堆实例化代码,实例化了一个类然后set相应的值,这样写起来运行自然没有问题,但是对于后续需求改动的拓展性和其他开发人员的维护性是及其不友好的。

我一直认为编写软件,工期内完成功能实现,确保不出现bug及性能问题这种代码并不一定是好的代码。拥有很强的健壮性、拓展性、可维护性的代码才是我们开发人应该追求的代码。

我是loger,欢迎点赞~