「设计模式」工厂模式

162 阅读5分钟

一、概述

工厂模式,是我们日常开发中,最常用的设计模式之一,工厂模式在Java程序体系可以说随处可见。它提供了一种创建对象的最佳方式,在创建对象的时候,不会对客户端暴露创建逻辑,并且通过使用一个共同的接口来创建新的对象。

工厂模式有3种不同的实现方式:

  • 简单工厂模式:又叫做静态工厂方法模式。该模式是通过传⼊相关的类型来返回相应的类,这种方式比较单一,可扩展性相对较差。简单工厂模式看为工厂方法模式的一种特例,两者归为一类。
  • 工厂方法模式:一个抽象产品类,可以派生出多个具体产品类。 一个抽象工厂类,可以派生出多个具体工厂类。每个具体工厂类只能创建一个具体产品类的实例。
  • 抽象工厂模式:多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类可以创建多个具体产品类的实例。

二、优缺点

简单工厂模式

  • 优点

(1)实现简单,可扩展。

(2)实现了对象创建与业务逻辑的分离。

(3)客户端无需知道所创建的具体产品类和类名。

  • 缺点

(1)实例化对象的逻辑全部封装在一个工厂类里,每次需求变化都要单独修改工厂类(违反了开闭原则),而且出了异常可能没法正常工作。

(2)不方便扩展子类。

工厂方法模式

  • 优点

符合开放-关闭原则(简称开-闭原则)将创建对象的逻辑与任务交给了工厂类。

  • 缺点

每次新增产品,产品类都需要创建对应工厂类,增加了系统的开销。

抽象工厂模式

  • 优点

复杂的抽象关联关系使得在类的内部对一系列产品组的管理很方便。

  • 缺点

增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了开闭原则。

三、实现方式

简单工厂模式

UML类图设计

产品手机抽象类

public interface Phone {
    void produce();
}

小米手机类

public class XiaomiPhone implements Phone{
    @Override
    public void produce() {
        System.out.println("生产->小米手机");
    }
}

华为手机类

public class HuaweiPhone implements Phone{
    @Override
    public void produce() {
        System.out.println("生产->华为手机");
    }
}

简单工厂类

public class PhoneFactory {
    public static Phone producePhone(String type){
        switch (type){
            case "xiaomi":
                return new XiaomiPhone();
            case "huawei":
                return new HuaweiPhone();
            default:
                return null;
        }
    }
}

代码如上,违反了开闭原则,不太容易扩展新产品类,如果产品改变,需要改变工厂的具体方法。

工厂方法模式

工厂方法模式与简单工厂模式相比,是在业务类的基础上封装了一层工厂。

UML类图设计

image.png

手机工厂抽象类

public interface PhoneFactory {
    void producePhone();
}

手机工厂类

//小米手机工厂
public class XiaomiFactory implements PhoneFactory{
    @Override
    public void producePhone() {
        XiaomiPhone xiaomiPhone = new XiaomiPhone();
        xiaomiPhone.produce();
    }
}
//华为手机工厂
public class HuaweiFactory implements PhoneFactory{
    @Override
    public void producePhone() {
        HuaweiPhone huaweiPhone = new HuaweiPhone();
        huaweiPhone.produce();
    }
}

代码如上,工厂方法模式有工厂名不易修改,稳定的优点,但是若要增加新产品(如:平板),对于产品簇问题,代码会出现成倍增产、类爆炸的现象。

抽象工厂模式

抽象工厂模式主要是来解决产品簇问题。

产品簇是啥呢?

通俗点讲,比如苹果厂只生产苹果产品,而苹果产品包括了电脑,手机,耳机等,这些一系列的产品就是产品簇。

在工厂方法模式下,小米、华为只能生产手机这个产品,现在需要扩展平板产品,按照工厂方法模式设计的话,还需要写一个平板抽象类,具体的再去实现,那么之后再去生产别的产品呢,抽象工厂类会越来越多,实现的类也会越来越多。

现在增加一个平板产品,相当于增加了6个类。

则使用抽象工厂后,只需要增加3个类,而且产品簇越多,效果越明显。

抽象工厂类

public interface AbstractFactory {
    //生产手机
    void producePhone();
    //生产平板
    void producePad();
}

小米工厂类

public class XiaomiFactory implements AbstractFactory{
    @Override
    public void producePhone() {
        XiaomiPhone xiaomiPhone = new XiaomiPhone();
        xiaomiPhone.produce();
    }

    @Override
    public void producePad() {
        XiaomiPad xiaomiPad = new XiaomiPad();
        xiaomiPad.produce();
    }
}

华为工厂类

public class HuaweiFactory implements AbstractFactory{
    @Override
    public void producePhone() {
        HuaweiPhone huaweiPhone = new HuaweiPhone();
        huaweiPhone.produce();
    }

    @Override
    public void producePad() {
        HuaweiPad huaweiPad = new HuaweiPad();
        huaweiPad.produce();
    }
}

代码如上,抽象工厂模式的优点是将工厂类的方法减少,适用于增加工厂的情况。缺点是当产品等级发生变化时(比如在增加一个手表),都需要修改以前工厂产品的代码(每个工厂都需要多加一个),违反开闭原则。

所以产品等级总是变化的时候,那么抽象工厂就不合适。

四、常见应用场景

  • Spring中的BeanFactory是简单工厂模式。
  • Spring中的FactoryBean是工厂方法模式。
  • 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
  • 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
  • 设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。