1. 设计模式之工厂模式

1. 设计模式之工厂模式

一、介绍

每当我听到有人讨论设计模式时,我听到最多的概念好像就是「工厂模式」,他就像是背单词时候的「abandon」,它易于理解且经常用到,所以我也将它作为学习「设计模式」的第一步。

我们都知道,工厂模式是为了创建对象而存在的(主要是听到的太多了~)。对象是一个系统的基石,我们编写的功能都可以抽象成由一个个对象组合而成,请求是由一个个XmlHttpRequest对象执行的、页面由一个个DOM节点对象堆砌而成等等。我们在前端框架之中往往会对请求作一层层的封装(比如我们会在JQuery.ajax、axios之上再封装一层),那我们在生成这些对象的时候,会发现他们都有着相似之处,就像是由工厂生产出来的一个个产品,那我们封装的过程其实就和「工厂模式」很相近了。

工厂模式属于「创建型模式」 的一种,与之相关的有:简单工厂模式、工厂模式和抽象工厂模式。

二、简单工厂模式

简单工厂模式可以理解为「一家工厂根据不同的模具来生产产品」的模式,如下举例:

// 工厂class SimpleRequestFactory {    constructor() {}    createRequest(type) {        let req = null;        switch (type) {            case 'get':                req = new GetRequest(); // GetRequest为get请求的模具                break;            case 'post':                req = new PostRequest(); // PostRequest为post请求的模具                break;        }        return req;    }}// 工厂生产处get请求的产品const getRequestInstance = SimpleRequestFactory.createRequest('get');getRequestInstance.setUrl('https://xxx'); // 设置get请求的urlgetRequestInstance.setParams({id: 'xxx'}); // 设置get请求的参数getRequestInstance.request();// 工厂生产处post请求的产品const postRequestInstance = SimpleRequestFactory.createRequest('post');postRequestInstance.setUrl('https://xxx'); // 设置post请求的urlpostRequestInstance.setParams({id: 'xxx'}); // 设置post请求的参数getRequestInstance.request();

以上就是简单工厂模式运行模式,利用这种方式,我们就可以根据不同请求的需要,来不断生产request请求对象,我们并不需要关心request的实例对象是怎么生成的,我们只需要得到它、使用它即可。

所以「简单工厂模式」的特点就是:

  • 我们只需要知道我们要生产的产品的名称即可
  • 我们不需要知道产品怎么来的,我们只需要知道产品怎么用

由上面的代码我们也能看出,简单工厂模式是具有三大要素的:

  • 工厂类:由它根据模具来生产产品
  • 模具类(抽象类产品):它是所有产品的基石,我们根据它们来得到用户使用的产品
  • 产品对象:简单工厂模式的创建目标,用户最终使用的就是具体的产品对象

三、工厂模式

工厂模式是在简单工厂模式之上做了优化处理之后形成一种模式,我们先看有关于工厂模式的代码

class RequestFactory {    constructor() {}    createRequest() {        // 我只代表每种RequestFactory都要实现createRequest方法        // 而我不生产任何产品    }}// get请求的工厂class GetRequestFactory extends RequestFactory {    constructor() {}    createRequest() {        return new GetRequest();    }}// post请求的工厂class PostRequestFactory extends RequestFactory {    constructor() {}    createRequest() {        return new PostRequest();    }}// put请求的工厂class PutRequestFactory extends RequestFactory {    constructor() {}    createRequest() {        return new PutRequest();    }}// 生产get请求的产品const getRequestIns = GetRequestFactory.createRequest();getRequestIns.setUrl('https://xxx'); // 设置get请求的urlgetRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数getRequestIns.request();// 生产post请求的产品const postRequestIns = PostRequestFactory.createRequest();postRequestIns.setUrl('https://xxx'); // 设置get请求的urlpostRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数postRequestIns.request();// 生产put请求的产品const putRequestIns = PutRequestFactory.createRequest();putRequestIns.setUrl('https://xxx'); // 设置get请求的urlputRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数putRequestIns.request();

由上面的代码可以看出,我们把每一种请求的生产工厂都独立出来了,看似没有简单工厂模式方便,但是我们可以在生活之中找到例子辅助理解。

比如我们A公司是一家生产饮品的公司,我们最开始有一条生产线专门生产矿泉水,我们叫「XX山泉」,后来我们发现矿泉水做的不错,我们想多做一些产品,如「XX AD钙奶」和「XX 纯牛奶」,那我们应该怎么做呢?这时我们可能根据生活中看到的例子,可以想到,我们只要照搬A公司的基础部门(如行政),再成立几家公司,分别生产「XX AD钙奶」和「XX 纯牛奶」即可。那我们为什么不在A公司的基础上不断扩充生产线呢?因为一旦生产线越来越多,管理就越来越复杂,我们需要不断地折腾A公司,还不如复制一个抽象公司,然后专事专做。

上面的例子就是为了帮助我们理解「简单工厂模式」和「工厂模式」的区别的,那我们什么时候用哪种模式呢?我的理解就是:

  • 如果系统简单,需要生成的对象类型可数,就用「简单工厂模式」
  • 如果系统存在扩展的可能性,且我们无法预计未来扩展的规模,就用「工厂模式」

其实上面说的也关乎到设计模式中的一个原则——「开闭原则」。

再回到「工厂模式」之上,我们可以看到工厂模式的特点就是:

  • 符合「开闭原则」,易于扩展
  • 会增加系统的复杂度

「工厂模式」包含四大要素:

  • 抽象工厂:抽象工厂不是一家实际的公司,但是他拥有所有公司共同点
  • 实体工厂:实体工厂负责生产具体的产品
  • 模具(抽象产品):我们根据模具来生产产品
  • 实体产品:用户最终得到并使用实体产品

三、抽象工厂模式

此时肯定有朋友想到了一个问题:「现实中我们也并不是所有的工厂都只生产一类产品,牛奶工厂可以生产纯牛奶、酸奶等等」,这就是我们提到的抽象工厂模式了。示例代码如下

// 抽象工厂class RequestFactory {    constructor() {}    createRequest() {        // 我只代表每种RequestFactory都要实现createRequest方法        // 而我不生产任何产品    }}/** 看这 start **/// 抽象的get请求class GetRequest{    constructor() {}    request() {}}// 简单的get请求(不需要带参数的get请求)class SimpleGetRequest extends GetRequest{    constructor() {}    request() {}}// 普通的get请求class NormalGetRequest extends GetRequest{    constructor() {}    request() {}}// 抽象的post请求class PostRequest{    constructor() {}    request() {}}// 简单的post请求(不需要带参数的post请求)class SimplePostRequest{    constructor() {}    request() {}}// 普通的post请求class NormalPostRequest extends PostRequest{    constructor() {}    request() {}}/** 看这 end **/// get请求的工厂class GetRequestFactory extends RequestFactory {    constructor() {}    createSimpleRequest() {        return new SimpleGetRequest();    }    createNormalRequest() {        return new NormalGetRequest();    }}// post请求的工厂class PostRequestFactory extends RequestFactory {    constructor() {}    createSimpleRequest() {        return new SimplePostRequest();    }    createNormalRequest() {        return new NormalPostRequest();    }}// 生产get请求的产品const simpleGetRequestIns = GetRequestFactory.createSimpleRequest();simpleGetRequestIns.setUrl('https://xxx'); // 设置get请求的urlsimpleGetRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数simpleGetRequestIns.request();const normalGetRequestIns = GetRequestFactory.createNormalRequest();normalGetRequestIns.setUrl('https://xxx'); // 设置get请求的urlnormalGetRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数normalGetRequestIns.request();// 生产post请求的产品const simplePostRequestIns = PostRequestFactory.createSimpleRequest();simplePostRequestIns.setUrl('https://xxx'); // 设置get请求的urlsimplePostRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数simplePostRequestIns.request();const normalPostRequestIns = PostRequestFactory.createNormalRequest();normalPostRequestIns.setUrl('https://xxx'); // 设置get请求的urlnormalPostRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数normalPostRequestIns.request();

通过上面的代码,我们可以看到,抽象工厂模式之于工厂模式的不同就是在工厂模式的基础上,对产品也进行了一层抽象,从而实现了一个实体工厂也能生产多个产品的功能。

「抽象工厂模式」的好处就是:

  • 易于交换产品系列,我们只需要初始化工厂,就可以随意切换生产的产品
  • 它让具体的创建实例过程和客户端分离,客户端通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。

「抽象工厂模式」有五大要素:

  • 抽象工厂
  • 实体工厂
  • 抽象模具
  • 模具
  • 实体产品

创建型模式: 创建型模式抽象了实例化的过程,他们帮助一个系统独立于如何创建、组合和表示它的那些对象。创建型模式都会将关于该系统使用哪些具体的类的信息封装起来。允许客户用结构和功能差别很大的「产品」对象配置一个系统。配置可以是静态的,即在编译时指定,也可以是动态的,就是运行时再指定。

开闭原则: 开放-封闭原则是说软件实体(类、模块、函数等等)应该是可以扩展、但是不可修改[ASD]。


参考
  • 大话设计模式 - 程杰

  • 工厂模式与抽象工厂模式的区别 - 贾不假

个人博客

北落师门的博客

免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部