设计模式 02 工厂模式

工厂模式(Factory Pattern)属于创建型模式

概述

在工厂模式中,创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

如果需要创建一个对象,最简单的方式就是直接 new 一个。而工厂模式代替了传统的直接 new 的形式,那么为什么要替代呢?如果所有的对象都通过 new 的方式去创建,那么当程序中大量使用此对象时,突然有一天这个对象的构造方法或是类名发生了修改,那就得逐个去进行修改。

根据迪米特法则,应该尽可能地少与其他类进行交互,所以可以将那些需要频繁出现的对象创建,封装到一个工厂类中。当需要对象时,直接调用工厂类中的工厂方法来生成对象,这样,就算类出现了变动,只需要修改工厂中的代码即可,而不是大面积地进行修改。同时,可能某些对象的创建并不只是一个 new 就可以搞定,可能还需要更多的步骤来准备构造方法需要的参数。

代码实现

普通工厂模式

下面以水果工厂生产水果为例介绍普通工厂模式:

1、定义水果类

/**
 * 水果类
 */
public class Fruit {

    private final String name;

    public Fruit(String name){
        this.name = name;
    }

}

2、定义苹果类

/**
 * 苹果类
 */
public class Apple extends Fruit{

    public Apple() {
        super("苹果");
    }

}

3、定义橘子类

/**
 * 橘子类
 */
public class Orange extends Fruit{

    public Orange() {
        super("橘子");
    }

}

4、定义水果工厂

/**
 * 水果工厂
 */
public class FruitFactory {
    
    /**
     * 这里就直接来一个静态方法根据指定类型进行创建
     * @param type 水果类型
     * @return 对应的水果对象
     */
    public static Fruit getFruit(String type) {
        switch (type) {
            case "苹果":
                return new Apple();
           	case "橘子":
                return new Orange();
            default:
                return null;
        }
    }
    
}

5、调用

// 直接问工厂要,而不是自己去创建
Apple apple = (Apple) FruitFactory.getFruit("苹果");
Orange orange = (Orange) FruitFactory.getFruit("橘子");

不过这样还是有一些问题,前面提到了开闭原则,一个软件实体,比如类、模块和函数应该对扩展开放,对修改关闭。

此时如果需要新增一种水果,比如桃子,那么就得去修改工厂提供的工厂方法了,这样是不太符合开闭原则的。因为工厂实际上是针对于调用方提供的,所以应该尽可能对修改关闭。

工厂方法模式

所以,我们就利用对扩展开放,对修改关闭的性质,将简单工厂模式改进为工厂方法模式,既然不让改,那么就看看如何去使用扩展的形式。

这里还是以水果工厂生产水果介绍工厂方法模式:

1、定义水果工厂

/**
 * 水果工厂
 * <p>将水果工厂抽象为抽象类,添加泛型 T 由子类指定水果类型
 */
public abstract class FruitFactory<T extends Fruit> {

    /**
     * 不同的水果工厂,通过此方法生产不同的水果
     * @return 水果对象
     */
    public abstract T getFruit();

}

2、定义苹果工厂

/**
 * 苹果工厂
 */
public class AppleFactory extends FruitFactory<Apple>{

    /**
     * 获取苹果对象
     * @return 苹果对象
     */
    @Override
    public Apple getFruit() {
        return new Apple();
    }
    
}

3、定义橘子工厂

/**
 * 橘子工厂
 */
public class OrangeFactory extends FruitFactory<Orange> {

    /**
     * 获取橘子对象
     * @return 橘子对象
     */
    @Override
    public Orange getFruit() {
        return new Orange();
    }
    
}

这样,就可以使用不同类型的工厂来生产不同类型的水果了,如果新增了水果类型,直接创建一个新的工厂类就行,不需要修改之前已经编写好的内容。

4、调用

// 使用对应的工厂生产对应的对象
Apple apple = new AppleFactory().getFruit();
Orange orange = new OrangeFactory().getFruit();

这样,就简单实现了工厂方法模式,通过工厂来屏蔽对象的创建细节,使用者只需要关心如何去使用对象即可。

优缺点

优点

1、一个调用者想创建一个对象,只要知道其名称就可以了。

2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。

3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点

每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。

使用场景

1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。

2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。

3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。

注意事项

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


参考

https://www.bilibili.com/video/BV1mc411h719?p=3&vd_source=299f4bc123b19e7d6f66fefd8f124a03

posted @ 2022-07-10 11:47  天航星  阅读(29)  评论(0编辑  收藏  举报