为了账号安全,请及时绑定邮箱和手机立即绑定
慕课专栏

目录

索引目录

设计模式深度解析34讲

原价 ¥ 68.00

立即订阅
04 工厂模式 —— 别具匠心
更新时间:2019-11-26 09:43:54
世界上最宽阔的是海洋,比海洋更宽阔的是天空,比天空更宽阔的是人的胸怀。——雨果

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

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

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

1、模拟场景

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

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

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

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

优点:

  1. 一个调用者想创建某个对象,只需知道其名称即可

  2. 屏蔽具体行为实现,调用者只需关心产品接口,减轻调用者负担

  3. 拓展性高,如果想增加一个产品类,只需拓展一个工厂类即可

2、具体分类

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

图片描述

2.1 普通简单工厂

就是建立一个具体工厂类,对实现了同一接口的一些类进行实例的创建,首先看下 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(); // 发送快递
    }
}

2.2 多方法简单工厂

多方法简单工厂是在前者的基础上该进来的,普通工厂方法在使用时,如果 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(); // 发送快递
    }
}

2.3 静态方法简单工厂

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

这里我们直接看修改后的 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(); // 发送快递
    }
}

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

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

而工厂方法模式则是对该问题的进一步延伸解决,差异就是将原先存在于一个工厂类中的逻辑抽调出来,创建一个接口和多个工厂类。这样,一旦功能有新增,比如说我们要加一个 “发送导弹” 的功能,只需要加一个 “导弹发送工厂类”,该类实现 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(); // 发送快递
    }
}

4、总结

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

}
立即订阅 ¥ 68.00

你正在阅读课程试读内容,订阅后解锁课程全部内容

千学不如一看,千看不如一练

手机
阅读

扫一扫 手机阅读

设计模式深度解析34讲
立即订阅 ¥ 68.00

举报

0/150
提交
取消