JavaScript设计模式第2篇:工厂模式

青毛狮子
• 阅读 2321

分类

这里工厂模式分为2类:简单工厂工厂方法,下一节会介绍第3类工厂模式:抽象工厂

简单工厂

定义

简单工厂:定义一个类来创建其他类的实例,根据参数的不同返回不同类的实例,通常这些类拥有相同的父类。

例子

假设现在有 3 款车,Benz、Audi 和 BMW,他们都继承自父类 Car,并且重写了父类方法 drive:

class Car {
    drive () {
        console.log('Car drive');
    }
}

class Benz extends Car {
    drive () {
        console.log(`Benz drive`)
    }
}

class Audi extends Car {
    drive () {
        console.log(`Audi drive`)
    }
}

class BMW extends Car {
    drive () {
        console.log(`BMW drive`)
    }
}

我们定义了一个父类 Car,包含一个方法 drive,Benz、Audi 和 BMW 继承自共同的父类 Car。

那么我们在实例化这 3 款车的时候,就需要如下调用:

let benz = new Benz();
let audi = new Audi();
let bmw = new BMW();

benz.drive();       // Benz drive
audi.drive();       // Audi drive
bmw.drive();        // BMW drive

这种写法就很繁琐,这时候就用到我们的简单工厂了,提供一个工厂类:

class SimpleFactory {
    static getCar (type) {
        switch (type) {
            case 'benz':
                return new Benz();
            case 'audi':
                return new Audi();   
            case 'bmw':
                return new BMW();  
        }
    }
}

简单工厂类 SimpleFactory 提供一个静态方法 getCar,我们再实例化 3 款车的时候,就变成下面这样了:


let benz = SimpleFactory.getCar('benz');
let audi = SimpleFactory.getCar('audi');
let bmw = SimpleFactory.getCar('bmw');

benz.drive();       // Benz drive
audi.drive();       // Audi drive
bmw.drive();        // BMW drive

这么一看,使用简单工厂后代码行数反而变多了,这种写法真的有优势吗?

我们要知道,设计模式并不是为了减少代码行数而出现的,它是为了使我们的代码更好扩展,更好维护,更方便使用而出现的。那么使用简单工厂后,有什么好处呢?

简单工厂,用户不需要知道具体产品的类名,只需要知道对应的参数即可,对于一些复杂的类名,可以减少用户的记忆量,同时用户无需了解这些对象是如何创建及组织的,有利于整个软件体系结构的优化。

所以,使用简单工厂,是将类的实例化交给工厂函数去做,对外提供统一的方法。我们要养成一个习惯,在代码中 new 是一个需要慎重考虑的操作,new 出现的次数越多,代码的耦合性就越强,可维护性就越差,简单工厂,就是在上面做了一层抽象,将 new 的操作封装了起来,向外提供静态方法供用户调用,这样就将耦合集中到了工厂函数中,而不是暴露在代码的各个位置。

缺陷

简单工厂有它的好处,必然也有它的缺点,比如下面这种情况:

我们又新增了一类车 Ferrai,就需要在简单工厂类 SimpleFactory 中再新增一个 case,添加 Ferrari 的实例化过程。每新增一类车,就要修改简单工厂类,这样做其实违背了设计原则中的开闭原则:对扩展开放,对修改封闭,同时如果每类车在实例化之前需要做一些处理逻辑的话,SimpleFactory 会变的越来越复杂。

所以简单工厂适用于产品类比较少并且不会频繁增加的情况,那么有什么方法能解决简单工厂存在的问题呢?

工厂方法

工厂方法模式是简单工厂的进一步优化,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂,也就是说每个对象都有一个与之对应的工厂。

定义

工厂方法模式又称为工厂模式,它的实质是“定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中执行”。

例子

光看概念还是很难理解,我们沿着上面的简单工厂来做修改,举例说明。

还是先定义一个父类 Car,提供一个方法 drive:

class Car {
    drive () {
        console.log('Car drive');
    }
}

每一款车都继承自父类 Car,并重写方法 drive:

class Benz extends Car {
    drive () {
        console.log(`Benz drive`)
    }
}

class Audi extends Car {
    drive () {
        console.log(`Audi drive`)
    }
}

class BMW extends Car {
    drive () {
        console.log(`BMW drive`)
    }
}

然后按照定义,我们提供一个创建对象的接口:

class IFactory {
    getCar () {
        throw new Error('不允许直接调用抽象方法,请自己实现');
    }
}

每款车都提供一个工厂类,实现自上述接口,因为 JavaScript 中没有 implements,所以用 extends 代替:

class BenzFactory extends IFactory {
    getCar () {
        return new Benz();
    }
}

class AudiFactory extends IFactory {
    getCar () {
        return new Audi();
    }
}

class BMWFactory extends IFactory {
    getCar () {
        return new BMW();
    }
}

这样当我们需要实例化每款车的时候,就按如下操作:

let benzFactory = new BenzFactory();
let benz = benzFactory.getCar();

let audiFactory = new AudiFactory();
let audi = audiFactory.getCar();

let bmwFactory = new BMWFactory();
let bmw = bmwFactory.getCar();

benz.drive();       // Benz drive
audi.drive();       // Audi drive
bmw.drive();        // BMW drive

我们再来对比一下简单工厂的实例化过程:

let benz = SimpleFactory.getCar('benz');
let audi = SimpleFactory.getCar('audi');
let bmw = SimpleFactory.getCar('bmw');

benz.drive();       // Benz drive
audi.drive();       // Audi drive
bmw.drive();        // BMW drive

我们用 UML 类图来描述一下工厂方法模式:

JavaScript设计模式第2篇:工厂模式

可以看到,同样是为了实例化一个对象,怎么就变得这么复杂了呢····我们来总结一下工厂方法模式的步骤:

  1. 定义产品父类 -- Car
  2. 定义子类实现父类,并重写父类方法 -- BenzCar、AudiCar、BMWCar
  3. 定义抽象接口,以及抽象方法 -- IFactory
  4. 定义工厂类,实现自抽象接口,并且实现抽象方法 -- BenzFactory、AudiFactory、BMWFactory
  5. new 工厂类,调用方法进行实例化

那么工厂方法增加了如此多的流程,提高了复杂度,究竟解决了简单工厂的什么问题呢?

通过上文可以知道,简单工厂在新增一款车的时候,需要修改简单工厂类 SimpleFactory,违背了设计模式中的“开闭原则”:对扩展开放,对修改封闭。

当工厂方法需要新增一款车的时候,比如 Ferrari,只需要定义自己的产品类 Ferrari 以及自己的工厂类 FerrariFactory:

class Ferrari extends Car {
    drive () {
        console.log(`Ferrari drive`)
    }
}

class FerrariFactory extends IFactory {
    getCar () {
        return new Ferrari();
    }
}

let ferrariFactory = new FerrariFactory();
let ferrari = ferrariFactory.getCar();

ferrari.drive();       // Ferrari drive

完全不用修改已有的抽象接口 IFactory,只需要扩展实现自己需要的就可以了,不会影响已有代码。这就是对扩展开放,对修改封闭

总结

  1. 简单工厂模式

    • 解决了用户多次自己实例化的问题,屏蔽细节,提供统一工厂,将实例化的过程封装到内部,提供给用户统一的方法,只需要传递不同的参数就可以完成实例化过程,有利于软件结构体系的优化;
    • 但不足之处是,增加新的子类时,需要修改工厂类,违背了“开闭原则”,并且工厂类会变得越来越臃肿;
    • 简单工厂模式适用于固定的,不会频繁新增子类的使用场景
  2. 工厂方法模式

    • 通过在上层再增加一层抽象,提供了接口,每个子类都有自己的工厂类,工厂类实现自接口,并且实现了统一的抽象方法,这样在新增子类的时候,完全不需要修改接口,只需要新增自己的产品类和工厂类就可以了,符合“开闭原则”;
    • 但不足之处也正是如此,持续的新增子类,导致系统类的个数将成对增加,在一定程度上增加了系统的复杂度,同时有更多的类需要编译和运行,会给系统代理一些额外的开销;
    • 工厂方法模式适用于会频繁新增子类的复杂场景;
点赞
收藏
评论区
推荐文章
happlyfox happlyfox
4年前
笑说设计模式-小白逃课被点名
关于我简介工厂模式(FactoryPattern)是最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,而是通过使用一个共同的接口来指向新创建的对象。分类工厂模式可以分为三种,其中简单工厂一般不被认为是一种设计模式,可以将其看成是工厂方法的一种特殊
zdd小小菜鸟 zdd小小菜鸟
2年前
创建型-2-抽象工厂模式( Abstract Factory Pattern )
创建型2抽象工厂模式(AbstractFactoryPattern)抽象工厂模式(AbstractFactoryPattern)是围绕一个超级工厂创建其他工厂tex该超级工厂又称为其他工厂的工厂在抽象
Wesley13 Wesley13
3年前
Java设计模式之三种工厂模式
工厂模式实现了创建者和调用者的分离,实现了更好的解耦。详细分类:1)简单工厂模式(静态工厂模式);2)工厂方法模式;3)抽象工厂模式面向对象设计的基本原则:1)      OCP(开闭原则,OpenClosedPrinciple):一个软件的实体应当对扩展开放,对修改关闭。2)      
Wesley13 Wesley13
3年前
Unity C# 设计模式(二)简单工厂模式
定义:简单工厂模式是属于创建型模式,又叫做静态工厂方法(StaticFactoryMethod)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该
Wesley13 Wesley13
3年前
Java描述设计模式(04):抽象工厂模式
一、抽象工厂模式1、生活场景汽车生产根据用户选择的汽车类型,指定不同的工厂进行生产,选择红旗轿车,就要使用中国工厂,选择奥迪轿车,就要使用德国工厂。2、抽象工厂模式1.抽象工厂模式:定义了一个interface用于创建相关对象或相互依赖的对象,而无需指明具体的类;2.抽象工厂模式可以
Wesley13 Wesley13
3年前
Java进阶篇设计模式之三
前言在上一篇(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnblogs.com%2Fxuwujing%2Fp%2F9363142.html)中我们学习了工厂模式,介绍了简单工厂模式、工厂方法和抽象工厂模式。本篇则介绍设计模式中属于创建型模式的建造者模式和原型模式。
Stella981 Stella981
3年前
Python之设计模式
一、设计模式分类a、创建型模式简单工厂模式一、内容不直接向客户端暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例。二、角色工厂角色(Creator)抽象产品角色(Product)具体产品角色(ConcreteProduct)
Wesley13 Wesley13
3年前
C#设计模式 —— 工厂模式
。  工厂模式同样是项目中最常用的设计模式,工厂模式中又分为简单工厂,工厂方法,抽象工厂。下面我们由简单的开始逐一介绍。1.简单工厂模式  简单工厂又被称为静态工厂,在设计模式中属于创建型模式。主要解决的问题是封装了实例化的过程,通过传入参数来获不同实例。下面我们举一个项目中可能会用到的例子。  假设我们程序的数据保存在几个不同
Wesley13 Wesley13
3年前
Java设计模式
模式描述工厂方法模式提供一个用于创建产品的接口,由实现类决定实现哪些产品。工厂方法模式使一个类的实例化延迟到子类,并且只适用于一个产品的等级结构。优点可以一定程度上解耦,消费者和产品实现类隔离开,只依赖产品接口(抽象产品),产品实现类如何改动与消费者完全无关。例子还是以之前简单工厂的手机为案例:/
Wesley13 Wesley13
3年前
23种设计模式(面向对象语言)
一、设计模式的分类总体来说设计模式分为三大类:创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。创建型模式是用来创建对象的模式,抽象了实例化的过程,帮助一个系统独立于其他关联对象的创建、组合和表示方式。所有的创建型模式都有两个主要功能:  1.将系统所使用的具体类的信息封装起来  2.隐藏
Wesley13 Wesley13
3年前
C++ 常用设计模式(学习笔记)
设计模式1、工厂模式在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。工厂模式作为一种创建模式,一般在创建复杂对象时,考虑使用;在创建简单对象时,建议直接new完成一个实例对象的创建。1.1、简单工厂模式主要特点是需要在工厂类中做判断,从而创造相应的产品,当