javaScript设计模式 -- 工厂模式与抽象工厂模式

一、工厂模式

工厂模式是用来创建对象的一种常用的设计模式。在使用该模式时,我们不去暴露创建对象的具体逻辑,而是将逻辑封装到一个函数中,那么该函数就会被视为一个工厂,从而能够解决创建相似对象的问题。工厂模式可以分为:简单工厂工厂方法抽象工厂

1. 简单工厂模式

简单工厂模式又叫静态工厂模式。它是由一个工厂对象决定某一种产品对象类的实例。主要是用来创建同一类对象。

在实际中的例子中,我们常常需要根据用户的权限来渲染不同的页面。譬如低权限的用户无法看到高权限用户能看到内容。我们可以用一个User类,通过传入的参数对不同用户身份进行判断,从而得到拥有不同权限的User实例。

class User {
       constructor(opt) {
           this.identity = opt.identity
           this.viewPage = opt.viewPage
       }

       static getInstance(identity) {
           switch (identity) {
               case 'simple':
                   return new User({ identity: 'simple', viewPage: ['首页', '通讯录', '发现页'] });
                   break;
               case 'admin':
                   return new User({ identity: 'admin', viewPage: ['首页', '通讯录', '发现页', '应用数据'] });
                   break;
               default:
                   break;
           }
       }
   }

   let simpleUser = User.getInstance('simple')
   let adminUser = User.getInstance('admin')
   console.log(simpleUser, adminUser)

在这里插入图片描述
在上述实现中,User类实际上就是一个简单工厂,我们只需要通过给类的静态方法getInstance传入规定的参数(simple/admin)便可以控制不同用户看到的页面内容。解决了生成相似实例的问题(都是同属于User类,但拥有不同的权限)

简单工厂的优点在于,只需要一个正确的参数,便可以获取到我们所需要的对象,而无需知道创建该对象的具体细节。将类的实例化交给工厂函数去做,对外提供统一的方法。在代码中new是一个需要慎重考虑的操作,new出现的次数越多,代码的耦合性就越强,可维护性就越差,简单工厂便是在上面做了一层抽象,将new的操作封装了起来,向外提供静态方法供用户调用,这样就将耦合集中到了工厂函数中,而不是暴露在代码的各个位置。
但当我们需要新增一个类型的实例时(以上述例子说明),必须要去修改工厂的静态方法,且当用户身份很多时,getInstance方法便会变得非常庞大,会因此变得难以维护。故简单工厂只能作用于创建的对象身份少且对象创建逻辑不复杂时。

2. 工厂方法模式

在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有对象,而是针对不同的对象提供不同的工厂,也就是说每个对象都有一个与之对应的工厂。
工厂方法模式实际上是将实际创建对象的工作推迟到子类中。这样工厂核心类便变成了抽象类。由于js中没有实现abstract,故我们需要使用new.target来模拟抽象类(new.target直接指向new执行的构造函数),我们可以对new.target进行判断,如果它指向工厂核心类,则抛出错误。

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

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

   class BYD extends Car {
       drive() {
           console.log('BYD drive')
       }
   }

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

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

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

   class BYDFactory extends IFactory {
       getCar() {
           return new BYD();
       }
   }

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

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

   let bydFactory = new BYDFactory();
   let byd = bydFactory.getCar();

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

   benz.drive();       // Benz drive
   byd.drive();       //BYD drive
   bmw.drive();        // BMW drive

上面例子中,我们通过不同工厂来创建不同类型的汽车,当有新的类型的汽车需要被IFactory工厂接纳时,只需要为其创建属于自己的工厂类,即创建一个新的类去继承IFactory并创建实现Car类的汽车类。这样便不会因为新的类型去修改IFactory工厂类的内容,也更加符合开放封闭原则

3. 抽象工厂模式

抽象工厂模式实际上和工厂方法模式很类似,不过抽象工厂模式是针对某一个产品簇,而工厂方法模式是针对某一个产品。举例而言:对于一个工厂,不止可以生产汽车,也可以生产电器,这样的话便需要创建一个针对产品簇的抽象工厂,而当有新的品牌加入时,直接实现该抽象工厂即可。但我们也需要注意的是,为了符合开放封闭原则,我们需要创建抽象产品类,
抽象工厂模式的官方定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类

抽象工厂模式包含以下4种角色:

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

来看一个例子

//抽象工厂模式
    
     //抽象引擎产品类
     class Engine {
        start () {
            throw new Error('不能调用抽象方法,请自己实现');
        }
    }

    //具体引擎产品类
    class BenzEngine extends Engine {
        start() {
            console.log('Benz engine')
        }
    }

    class BYDEngine extends Engine {
        start() {
            console.log('BYD engine')
        }
    }


    //抽象汽车产品类
    class Car {
        drive() {
            throw new Error('不能调用抽象方法,请自己实现');
        }
    }

    //具体汽车产品类
    class BenzCar extends Car {
        drive() {
            console.log('Benz drive')
        }
    }

    class BYDCar extends Car {
        drive() {
            console.log('BYD drive')
        }
    }

    //抽象工厂类
    class AutoMakerFactory {
        createCar() {
            throw new Error('不能调用抽象方法,请自己实现')
        }

        createEngine() {
            throw new Error('不能调用抽象方法,请自己实现')
        }
    }

    //具体工厂类
    class BenzFactory extends AutoMakerFactory {
        createCar() {
            return new BenzCar();
        }
        createEngine() {
            return new BenzEngine();
        }
    }

    class BYDFactory extends AutoMakerFactory {
        createCar() {
            return new BYDCar();
        }
        createEngine() {
            return new BYDEngine();
        }
    }

    let benzFactory = new BenzFactory();
    let benzCar = benzFactory.createCar();
    let benzEngine = benzFactory.createEngine();

    let bydFactory = new BYDFactory();
    let bydCar = bydFactory.createCar();
    let bydEngine = bydFactory.createEngine();

    benzCar.drive()
    benzEngine.start()

    bydCar.drive()
    bydEngine.start()

    console.log(benzCar,benzEngine,bydCar,bydEngine)

在上述例子中,如果我们需要新增一个工厂AUDI,我们需要做什么呢:
① 创建AUDI具体工厂类(实现AutoMakerFactory抽象工厂类)

class AUDIFactory extends AutoMakerFactory {
       createCar() {
           return new AUDICar();
       }
       createEngine() {
           return new AUDIEngine();
       }
   }

② 创建AUDI具体产品类(AUDICarAUDIEngine

class AUDICar extends Car {
       drive() {
           console.log('AUDICar drive')
       }
   }

   class AUDIEngine extends Engine {
       start() {
           console.log('AUDIEngine start')
       }
   }

③ 实例化

   let audiFactory = new AUDIFactory();
   let audiCar = audiFactory.createCar();
   let audiEngine = audiFactory.createEngine();
   audiCar.drive()
   audiEngine.start()

在这里插入图片描述
从上除例子可以看出,当新增一个工厂类时,无需修改抽象工厂类的代码,只需要添加新的工厂类,让其实现抽象工厂类即可,符合"开闭原则"。我们可以在具体工厂类中去实例化不同产品类对象,这样便可实现一个工厂类对应一个产品簇。当然它的缺点也和工厂方法一样,不断地添加新产品会导致类越来越多,增加系统复杂度。

4. 总结

  • 简单工厂模式

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

    • 通过在上层再增加一层抽象,提供了接口,每个子类都有自己的工厂类,工厂类实现自接口,并且实现了统一的抽象方法,这样在新增子类的时候,完全不需要修改接口,只需要新增自己的产品类和工厂类就可以了,符合“开闭原则”;
    • 但不足之处也正是如此,持续的新增子类,导致系统类的个数将成对增加,在一定程度上增加了系统的复杂度,同时有更多的类需要编译和运行,会给系统代理一些额外的开销;
    • 工厂方法模式适用于会频繁新增子类的复杂场景;
  • 抽象工厂模式

    • 抽象工厂隔离了具体类地生成,使得客户并不需要知道什么被创建,而且每次可以通过具体工厂类创建一个产品簇中的多个对象,增加或替换产品簇比较方便,增加新的具体工厂和产品簇也很方便;
    • 但不足之处和工厂方法一致,持续的新增子类,导致系统类的个数将成对增加,在一定程度上增加了系统的复杂度,同时有更多的类需要编译和运行,会给系统代理一些额外的开销;
    • 抽象工厂适用情况:① 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节;② 属于同一个产品簇的产品将在一起使用;③ 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体实现

参考文章:
JavaScript设计模式与实践–工厂模式
JavaScript设计模式第2篇:工厂模式
JavaScript设计模式第3篇:抽象工厂模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值