工厂模式


定义

工厂模式,是设计模式中最为常见的模式之一。属于创建型模式,提供创建对象的最佳方式。

工厂模式,顾名思义,一个模型,用来大规模的生产同类产品。该模式将对象的具体实例过程抽象化,并不关心具体的创建过程。通常,工厂模式被用来定义一个对象模型,之后,便可快速规模化实例化对象。

简单工厂模式的实质:一个工厂类根据传入的参数,动态决定应该创建哪一类产品类(这些产品类均继承自一个父类或接口)实例。

模拟场景

本来创建对象用 new 就行了,为什么要用函数或类将 new 的过程封装起来呢?

这里,我们引入两个角色,一个是类的设计者,一个是类的使用者。类的使用者只关心 “这个对象做这事”、“那个对象做那件事”,而不关心 “这件事如何去做?”,通常,类的设计者才会去关心 “如何去做”。

举个栗子,类的设计者设计了 “阿猫”、“阿狗” 两个类,类的使用者需要创建 “阿猫 1”、“阿狗 2” 两个对象,如果不使用工厂模式,类的使用者就需要明确知道 “阿猫”、“阿狗” 两个类,这无疑增加了类使用者的负担。使用工厂来代替创建这两对象,权责分开,比方说,这些动物都有 “吃东西” 的行为,这时,“吃东西” 具体行为是什么样子,这是类的设计者关心的,而何时进行该行为,是类的使用者关心的。

再比如,我们经常使用的数据库中间件,我们无需关心具体的底层实现类,只需将用户名密码等连接信息传过去,就会直接获取到相应的数据库连接实例,这个角度,就可以将数据库中间件看作一个大的工厂。

优点:

  • 一个调用者想创建某个对象,只需知道其名称即可
  • 屏蔽具体行为实现,调用者只需关心产品接口,减轻调用者负担
  • 拓展性高,如果想增加一个产品类,只需拓展一个工厂类即可

具体分类

一般来说,工厂模式分为三种:普通简单工厂模式、多方法简单工厂、静态方法简单工厂。这三种模式从上到下逐步抽象,并且根据一般性。

普通简单工厂

就是建立一个具体工厂类,对实现了同一接口的一些类进行实例的创建,首先看下 uml 类图(这里以发送短信、邮件、快递为例):

首先,创建一个三者共有的接口:

public interface Sender {
    public void Send();
}

然后,创建实现类:

public class EmailSender implements Sender{
    @Override
    public void Send() {
        System.out.println("发送邮件");
    }
}
public class SmsSender implements Sender{
    @Override
    public void Send() {
        System.out.println("发送短信");
    }
}

public class ExpressSender implements Sender {
    @Override
    public void Send() {
        System.out.println("发送快递");
    }
}

再然后,我们创建一个工厂类来产出这几种 “产品”:

public class SendFactory {
    public Sender produce(String type) {
        if (type == null) {
            return null;
        } else if ("email".equalsIgnoreCase(type)) {
            return new EmailSender();
        } else if ("sms".equalsIgnoreCase(type)) {
            return new SmsSender();
        } else if ("express".equalsIgnoreCase(type)) {
            return new ExpressSender();
        } else {
            return null;
        }
    }
}

接下来,我们试着调用下测试:

public class Main {

    public static void main(String[] args) {
        SendFactory sendFactory = new SendFactory();
        Sender senderSms = sendFactory.produce("sms");
        senderSms.Send(); // 发送短信

        Sender senderEmail = sendFactory.produce("email");
        senderEmail.Send(); // 发送邮件

        Sender senderExpress = sendFactory.produce("express");
        senderExpress.Send(); // 发送快递
    }
}

多方法简单工厂

多方法简单工厂是在前者的基础上该进来的,普通工厂方法在使用时,如果 type 类型传递错误则不能正确创建对象,多方法直接将 produce 中的逻辑展开到具体的方法中,从而避免该问题。接下来看下我们的改进:

针对上面代码,我们只需调整 SendFactory 类即可:

public class SendFactory {
    public Sender produceSms(){
        return new SmsSender();
    }

    public Sender produceEmail(){
        return new EmailSender();
    }

    public Sender produceExpress() {
        return new ExpressSender();
    }
}

接下来,我们进行如下简单测试:

public class Main {

    public static void main(String[] args) {
        SendFactory sendFactory = new SendFactory();
        Sender senderEmail = sendFactory.produceEmail();
        senderEmail.Send(); // 发送邮件

        Sender senderSms = sendFactory.produceSms();
        senderSms.Send(); // 发送短信

        Sender senderExpress = sendFactory.produceExpress();
        senderExpress.Send(); // 发送快递
    }
}

静态方法简单工厂

普通工厂模式和多方法工厂模式有一个弊端,就是需要频繁的实例化工厂类,一般我们会将 “多方法” 设置为静态的,从而避免类的频繁实例化,拿来即用。


这里我们直接看修改后的 SendFactory 类:

public class Main {

    public static void main(String[] args) {
        //SendFactory sendFactory = new SendFactory();
        Sender senderEmail = SendFactory.produceEmail();
        senderEmail.Send(); // 发送邮件

        Sender senderSms = SendFactory.produceSms();
        senderSms.Send(); // 发送短信

        Sender senderExpress = SendFactory.produceExpress();
        senderExpress.Send(); // 发送快递
    }
}

简单工厂的延申 — 工厂方法模式

上面介绍的简单工厂模式有个比较明显的弊端:工厂类集中了所有实例的创建逻辑,明显违背高内聚的责任分配原则,违背了闭包规则。

而工厂方法模式则是对该问题的进一步延伸解决,差异就是将原先存在于一个工厂类中的逻辑抽调出来,创建一个接口和多个工厂类。这样,一旦功能有新增,比如说我们要加一个 “发送导弹” 的功能,只需要加一个 “导弹发送工厂类”,该类实现 produce 接口返回实例化的 “导弹发送类”,再在 “导弹发送类” 中,实现具体的发送逻辑即可,无需修改之前的业务代码,拓展性较好。


首先,我们还是创建一个 Sender 接口:

public interface Sender {
    public void Send();
}

然后我们创建几个具体的实现类:

public class SmsSender implements Sender{
    @Override
    public void Send() {
        System.out.println("发送短信");
    }
}
public class ExpressSender implements Sender {
    @Override
    public void Send() {
        System.out.println("发送快递");
    }
}
public class EmailSender implements Sender{
    @Override
    public void Send() {
        System.out.println("发送邮件");
    }
}

然后,我们统一一下工厂类的接口行为:

public interface Provider {
    public Sender produce();
}

继续,定义几个工厂实现上面这种 “行为约束”:

public class ExpressSendFactory implements Provider {
    @Override
    public Sender produce() {
        return new ExpressSender();
    }
}
public class EmailSendFactory implements Provider{
    @Override
    public Sender produce() {
        return new EmailSender();
    }
}
public class SmsSendFactory implements Provider {
    @Override
    public Sender produce() {
        return new SmsSender();
    }
}

测试类:

public class Main {

    public static void main(String[] args) {
        Provider providerSms = new SmsSendFactory();
        Sender senderSms = providerSms.produce();
        senderSms.Send(); // 发送短信

        Provider providerEmail = new EmailSendFactory();
        Sender senderEmail = providerEmail.produce();
        senderEmail.Send(); // 发送邮件

        Provider providerExpress = new ExpressSendFactory();
        Sender senderExpress = providerExpress.produce();
        senderExpress.Send(); // 发送快递
    }
}

总结

工厂方法模式中,核心的工厂类(这里为 Provider 接口)不再负责所有产品的创建,而是将具体创建的工作交给子类去做,该核心类仅扮演抽象工厂的角色,负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应该被实例化的细节,拓展性较简单工厂模式提升明显。


Author: Re:0
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Re:0 !
 Previous
抽象工厂模式 抽象工厂模式
这回我们讲下抽象工厂模式,抽象工厂模式是工厂模式(简单工厂、工厂方法)中最具抽象和一般性的一种形态。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。 定义抽象工厂模式的定义:为创建一组
2022-03-07
Next 
责任链模式 责任链模式
定义什么是责任链模式?生活中我们经常遇到这样的问题,比如请假审批需要层层上报处理、遇到问题各个部门甩赖扯皮,像这种,在事情没有被处理之前,会经过一系列阶段,类似于“踢皮球”似的。同样地,当一个请求到达时,在程序无法直接决定由哪个对象负责处理
2022-03-05
  TOC