工厂设计模式

一、工厂设计模式介绍

在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。

复杂对象指的是类的构造函数参数过多等对类的构造有影响的情况,因为类的构造过于复杂,如果直接在其他业务类内使用,则两者的耦合过重,后续业务更改,就需要在任何引用该类的源代码内进行更改,光是查找所有依赖就很消耗时间了,更别说要一个一个修改了。

按实际业务场景划分,工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。

二、简单工厂模式

1.什么是简单工厂模式

我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。
在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern),本文给的案例没有将方法改为静态方法,读者可以自行尝试。

2.简单工厂模式的结构

简单工厂模式的主要角色如下:

  • 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
  • 抽象对象(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
  • 具体对象(Television、Fan、Computer):是简单工厂模式的创建目标。


    image.png

3.简单工厂模式的实现

public abstract class Product {

    private String name;

    /**
     * 实现功能
     */
    public abstract void doFunction();

    public void setName(String name) {
        this.name = name;
    }
}
public class Computer extends Product {
    @Override
    public void doFunction() {
        System.out.println("我可以用来打电脑游戏");
    }
}
public class Fan extends Product {
    @Override
    public void doFunction() {
        System.out.println("我可以用来吹风");
    }
}
public class Television extends Product {
    @Override
    public void doFunction() {
        System.out.println("我可以用来看电视");
    }
}

public class SimpleFactory {

    private static final String COMPUTER = "computer";
    private static final String FAN = "fan";
    private static final String TELEVISION = "television";

    public Product createProduct(String type){
        Product product = null;
        if (FAN.equals(type)) {
            product = new Fan();
            product.setName(type);
        } else if (TELEVISION.equals(type)) {
            product = new Television();
            product.setName(type);
        } else if (COMPUTER.equals(type)) {
            product = new Computer();
            product.setName(type);
        }
        return product;
    }
}

4.简单工厂模式优缺点与应用场景

  • 优点:
    1)工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
    2)客户端无需知道所创建具体产品的类名,只需知道参数即可。
    3)也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。

  • 缺点:
    1)简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
    2)使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
    3)系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂

  • 应用场景
    对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。

三、工厂方法模式

1.什么是工厂方法模式

前面我们介绍了简单工厂模式,提到了简单工厂模式违背了开闭原则,而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。
工厂方法模式(Factory Method Pattern)也被称为多态工厂模式,其定义了一个创建某种产品的接口,但由子类决定要实例化的产品是哪一个,从而把产品的实例化推迟到子类。

2.工厂方法设计模式的结构

工厂方法模式的主要角色如下:

  • 抽象工厂(AbstractFactory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 crateProduct() 来创建产品。
  • 具体工厂(FanFactory、ComputerFactory、TelevisionFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(Televison、Fan、Computer):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。


    image.png

3.工厂方法模式的实现

public abstract class Product {

    private String name;

    /**
     * 实现功能
     */
    public abstract void doFunction();

    public void setName(String name) {
        this.name = name;
    }
}
public class Computer extends Product {
    @Override
    public void doFunction() {
        System.out.println("我可以用来打电脑游戏");
    }
}
public class Fan extends Product {
    @Override
    public void doFunction() {
        System.out.println("我可以用来吹风");
    }
}
public class Television extends Product {
    @Override
    public void doFunction() {
        System.out.println("我可以用来看电视");
    }
}

public interface AbstractFactory {
    Product createProduct();
}
public class ComputerFactory implements AbstractFactory{
    @Override
    public Product createProduct() {
        return new Computer();
    }
}
public class FanFactory implements AbstractFactory{
    @Override
    public Product createProduct() {
        return new Fan();
    }
}
public class TelevisionFactory implements AbstractFactory{
    @Override
    public Product createProduct() {
        return new Television();
    }
}

4.工厂方法模式优缺点与应用场景

  • 优点:
    1)用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的2具体创建过程。
    2)灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
    3)典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

  • 缺点:
    1)类的个数容易过多,增加复杂度
    2)增加了系统的抽象性和理解难度
    3)抽象产品只能生产一种产品,此弊端可使用[抽象工厂模式解决。

  • 应用场景:
    1)客户只知道创建产品的工厂名,而不知道具体的产品名。
    2)创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
    3)客户不关心创建产品的细节,只关心产品的品牌

三、抽象工厂模式

1.什么是抽象工厂模式

本节要介绍的抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,图1所示的是海尔工厂和 TCL 工厂所生产的电视机与空调对应的关系图。


图1.png

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

2.抽象工厂模式的结构

工厂方法模式的主要角色如下:

  • 抽象工厂(AbstractFactory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂(TclFactory、GreeFactory):主要是实现抽象工厂中的创造产品的多个抽象方法,完成某个品牌的具体产品的创建。
  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品:实现了抽象产品角色所定义的内容。


    image.png

3.抽象工厂模式的实现

public interface IFan {
    void blow();
}
public interface ITelevision {
    void watch();
}
public class GreeFan implements IFan {
    @Override
    public void blow() {
        System.out.println("吹格力电风扇");
    }
}
public class TclFan implements IFan{
    @Override
    public void blow() {
        System.out.println("吹Tcl电风扇");
    }
}
public class GreeTelevision implements ITelevision{
    @Override
    public void watch() {
        System.out.println("看格力电视机");
    }
}
public class TclTelevision implements ITelevision {
    @Override
    public void watch() {
        System.out.println("看TCL电视机");
    }
}
public abstract class AbstractFactory {
    public void init() {
        System.out.println("都要执行的通用的工厂方法");
    }

    protected abstract IFan createFan();

    protected abstract ITelevision createTelevision();

}
public class GreeFactory extends AbstractFactory{
    @Override
    protected IFan createFan() {
        super.init();
        return new GreeFan();
    }

    @Override
    protected ITelevision createTelevision() {
        super.init();
        return new GreeTelevision();
    }
}
public class TclFactory extends AbstractFactory{
    @Override
    protected IFan createFan() {
        super.init();
        return new TclFan();
    }

    @Override
    protected ITelevision createTelevision() {
        super.init();
        return new TclTelevision();
    }
}

4.抽象工厂模式优缺点与应用场景

  • 优点
    1)抽象工厂模式除了具有工厂方法模式的优点外,还可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
    2)当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
    3)抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

  • 缺点
    1)当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。

  • 应用场景
    跟工厂方法模式应用场景类似,只是更加细化了。

五、工厂设计模式在实际中的应用

1.工厂设计模式在mybatis源码中的应用

源码详见cn.bugstack.mybatis.session.SqlSessionFactory

public interface SqlSessionFactory {

   SqlSession openSession();

}

源码详见cn.bugstack.mybatis.session.defaults.DefaultSqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
        Transaction tx = null;
        try {
            final Environment environment = configuration.getEnvironment();
            TransactionFactory transactionFactory = environment.getTransactionFactory();
            tx = transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.READ_COMMITTED, false);
            // 创建执行器
            final Executor executor = configuration.newExecutor(tx);
            // 创建DefaultSqlSession
            return new DefaultSqlSession(configuration, executor);
        } catch (Exception e) {
            try {
                assert tx != null;
                tx.close();
            } catch (SQLException ignore) {
            }
            throw new RuntimeException("Error opening session.  Cause: " + e);
        }
    }

}

微信图片_20220719230621.jpg
  • 工厂模式:简单工厂,是一种创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例对象的类型。
  • 场景介绍SqlSessionFactory 是获取会话的工厂,每次我们使用 Mybatis 操作数据库的时候,都会开启一个新的会话。在会话工厂的实现中负责获取数据源环境配置信息、构建事务工厂、创建操作SQL的执行器,并最终返回会话实现类。
  • 同类设计SqlSessionFactoryObjectFactoryMapperProxyFactoryDataSourceFactory

2.工厂设计模式在slf4j源码中的应用

我们来看一下slf4j中是如何应用工厂方法模式的:
首先slf4j定义一个抽象工厂ILoggerFactory

public interface ILoggerFactory {
    Logger getLogger(String var1);
}

接着定义了一个抽象产品Logger:

public interface Logger {
    ... ...
}

public class SubstituteLogger implements Logger {

    private final String name;
    private volatile Logger _delegate;
    private Boolean delegateEventAware;
    private Method logMethodCache;
    private EventRecodingLogger eventRecodingLogger;
    private Queue<SubstituteLoggingEvent> eventQueue;

    private final boolean createdPostInitialization;
    
    public SubstituteLogger(String name, Queue<SubstituteLoggingEvent> eventQueue, boolean createdPostInitialization) {
        this.name = name;
        this.eventQueue = eventQueue;
        this.createdPostInitialization = createdPostInitialization;
    }
)

具体工厂有很多,都实现了抽象工厂,这里展示其中一个SubstituteLoggerFactory

public class SubstituteLoggerFactory implements ILoggerFactory {

    boolean postInitialization = false;
    
    final Map<String, SubstituteLogger> loggers = new HashMap<String, SubstituteLogger>();

    final LinkedBlockingQueue<SubstituteLoggingEvent> eventQueue = new LinkedBlockingQueue<SubstituteLoggingEvent>();

    synchronized public  Logger getLogger(String name) {
        SubstituteLogger logger = loggers.get(name);
        if (logger == null) {
            logger = new SubstituteLogger(name, eventQueue, postInitialization);
            loggers.put(name, logger);
        }
        return logger;
    }
}

具体工厂用于生产具体的产品,SubstituteLogger就是一种具体的产品,从上边的代码中就可以看到,SubstituteLoggerFactory工厂在生产SubstituteLogger具体产品时,做了一定的处理。而真正使用时,只需要通过getLogger方法就可以得到具体产品,当然除了工厂设计模式这里还用到了单例设计模式,这就是设计模式的魅力。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容