JavaScript设计模式——工厂模式
在介绍工厂模式之前,首先我们要理解一下什么是设计模式?什么是设计原则?
设计模式:
通常在我们解决问题的时候,很多时候不是只有一种方式,我们通常有多种方式来解决;但是肯定会有一种通用且高效的解决方案,这种解决方案在软件开发中我们称它为设计模式;
设计模式并不是一种固定的公式,而是一种思想,是一种解决问题的思路;恰当的使用设计模式,可以实现代码的复用和提高可维护性;
设计原则:
通常在做很多事情的时候,都会有一定的规范制约;在软件开发的过程中,我们可以将设计原则视为一种开发规范,但不是必须要遵循的,只是不遵循的话,代码后期的维护和复用都会变得很糟糕;
遵循设计原则可以帮助我们写出高内聚、低耦合的代码,当然代码的复用性、健壮性、可维护性也会更好;
有哪些设计模式:
在程序设计中我们通常认为是有23种设计模式,根据分类分别为:
创建型:
- 抽象工厂模式
- 工厂方法模式
- 建造者模式
- 原型模式
- 单例模式(又称单体模式)
结构型:
- 适配器模式
- 桥接模式
- 组合模式
- 装饰者模式
- 外观模式
- 享元模式
- 代理模式
行为型:
- 职责链模式
- 命令模式
- 解释器模式
- 迭代器模式
- 中介者模式
- 备忘录模式
- 观察者模式(又称发布/订阅模式)
- 状态模式
- 策略模式
- 访问者模式
- 模板方法模式
有哪些设计原则:
根据英文首单词我们又称为S.O.L.I.D.设计原则,一共有5种设计原则,根据分类分别为:
1、S(Single responsibility principle)——单一职责原则
一个程序或一个类或一个方法只做好一件事,如果功能过于复杂,我们就拆分开,每个方法保持独立,减少耦合度;
2、O(Open Closed Principle)——开放封闭原则
对扩展开放,对修改封闭;增加新需求的时候,我们需要做的是增加新代码,而非去修改源码;
例如:我们在使用vue框架的时候,有很多第三方插件我们可以去使用,在使用的时候我们通常都是直接在vue-cli中增加引入代码,而非去修改vue源码来达到支持某种功能的目的;
3、L(Liskov Substitution Principle, LSP)——里氏置换原则
子类能覆盖父类,父类能出现的地方子类就能出现;(在JS中没有类概念,使用较少)
4、I (Interface Segregation Principle)——接口独立原则
保持接口的单一独立,类似于单一原则,不过接口独立原则更注重接口;(在JS中没有接口概念)
5、D(Dependence Inversion Principle ,DIP)——依赖倒置原则
面向接口编程,依赖于抽象而不依赖于具体,使用方只关注接口而不需要关注具体的实现;(JS中没有接口概念)
作为前端开发人员来说,我们用的最多的设计原则是S(单一职责原则).O(开放封闭原则),所以在程序设计的时候我们重点关注这两个即可 ;
设计模式——工厂模式:
所谓工厂模式就是像工厂一样重复的产生类似的产品,工厂模式只需要我们传入正确的参数,就能生产类似的产品;
工厂模式根据抽象程度依次分为简单工厂模式、工厂方法模式、抽象工厂模式;
简单工厂模式:
在我们的生活中很多时候就有这样的场景,像在网站中有的页面是需要根据账号等级来决定是否有浏览权限的;账号等级越高可浏览的就越多,反之就越少;
// JS设计模式之简单工厂 function factory(role){ function superAdmin(){ this.name="超级管理员"; this.viewPage=["首页","发现页","通讯录","应用数据","权限管理"]; } function admin(){ this.name="管理员"; this.viewPage=["首页","发现页","通讯录","应用数据"]; } function user(){ this.name="普通用户"; this.viewPage=["首页","发现页","通讯录"]; } switch(role){ case "superAdmin": return new superAdmin(); break; case "admin": return new admin(); break; case "user": return new user(); break; } } let superAdmin = factory("superAdmin"); console.log(superAdmin); let admin = factory("admin"); console.log(admin); let user = factory("user"); console.log(user);
上述代码中,factory就是一个工厂,factory有三个函数分别是对应不同的产品,switch中有三个选项,这三个选项相当于三个模具,当匹配到其中的模具之后,将会new一个构造函数去执行生产工厂中的function;但是我们发现上面的简单工厂模式会有一定的局限性,就是如果我们需要去添加新的产品的时候,我们需要去修改两处位置(需要修改function和switch)才能达到添加新产品的目的;
下面我们将简单工厂模式进行改良:
// JS设计模式之简单工厂改良版 function factory(role){ function user(opt){ this.name = opt.name; this.viewPage = opt.viewPage; } switch(role){ case "superAdmin": return new user({name:"superAdmin",viewPage:["首页","发现页","通讯录","应用数据","权限管理"]}); break; case "admin": return new user({name:"admin",viewPage:["首页","发现页","通讯录","应用数据"]}); break; case "normal": return new user({name:"normal",viewPage:["首页","发现页","通讯录"]}); } } let superAdmin = factory("superAdmin"); console.log(superAdmin); let admin = factory("admin"); console.log(admin); let normal = factory("normal"); console.log(normal);
经过上面的修改之后,我们工厂里面的函数相当于一个万能摸具,switch里面给我什么,我就加工成什么样的;自然就解决了添加商品需要修改两处代码的问题;
工厂方法模式:
工厂方法模式是将创建对象的工作推到子类中进行;也就是相当于工厂总部不生产产品了,交给下辖分工厂进行生产;但是进入工厂之前,需要有个判断来验证你要生产的东西是否是属于我们工厂所生产范围,如果是,就丢给下辖工厂来进行生产,如果不行,那么要么新建工厂生产要么就生产不了;
// JS设计模式之工厂方法模式 function factory(role){ if(this instanceof factory){ var a = new this[role](); return a; }else{ return new factory(role); } } factory.prototype={ "superAdmin":function(){ this.name="超级管理员"; this.viewPage=["首页","发现页","通讯录","应用数据","权限管理"]; }, "admin":function(){ this.name="管理员"; this.viewPage=["首页","发现页","通讯录","应用数据"]; }, "user":function(){ this.name="普通用户"; this.viewPage=["首页","发现页","通讯录"]; } } let superAdmin = factory("superAdmin"); console.log(superAdmin); let admin = factory("admin"); console.log(admin); let user = factory("user"); console.log(user);
工厂方法模式关键核心代码就是工厂里面的判断this是否属于工厂,也就是做了分支判断,这个工厂只做我能生产的产品,如果你的产品我目前做不了,请找其他工厂代加工;
抽象工厂模式:
如果说上面的简单工厂和工厂方法模式的工作是生产产品,那么抽象工厂模式的工作就是生产工厂的;
举个例子:代理商找工厂进行合作,但是工厂没有实际加工能力来进行代加工某产品;无奈又签署了合同,这时,工厂上面的集团公司就出面了,集团公司承认该工厂是该集团下属公司,所以集团公司就重新建造一个工厂来进行代加工某商品以达到履行合约;
//JS设计模式之抽象工厂模式 let agency = function(subType, superType) { //判断抽象工厂中是否有该抽象类 if(typeof agency[superType] === 'function') { function F() {}; //继承父类属性和方法 F.prototype = new agency[superType] (); console.log(F.prototype); //将子类的constructor指向子类 subType.constructor = subType; //子类原型继承父类 subType.prototype = new F(); } else { throw new Error('抽象类不存在!') } } //鼠标抽象类 agency.mouseShop = function() { this.type = '鼠标'; } agency.mouseShop.prototype = { getName: function(name) { // return new Error('抽象方法不能调用'); return this.name; } } //键盘抽象类 agency.KeyboardShop = function() { this.type = '键盘'; } agency.KeyboardShop.prototype = { getName: function(name) { // return new Error('抽象方法不能调用'); return this.name; } } //普通鼠标子类 function mouse(name) { this.name = name; this.item = "买我,我线长,玩游戏贼溜" } //抽象工厂实现鼠标类的继承 agency(mouse, 'mouseShop'); //子类中重写抽象方法 // mouse.prototype.getName = function() { // return this.name; // } //普通键盘子类 function Keyboard(name) { this.name = name; this.item = "行,你买它吧,没键盘看你咋玩"; } //抽象工厂实现键盘类的继承 agency(Keyboard, 'KeyboardShop'); //子类中重写抽象方法 // Keyboard.prototype.getName = function() { // return this.name; // } //实例化鼠标 let mouseA = new mouse('联想'); console.log(mouseA.getName(), mouseA.type,mouseA.item); //联想 鼠标 //实例化键盘 let KeyboardA = new Keyboard('联想'); console.log(KeyboardA.getName(), KeyboardA.type,KeyboardA.item); //联想 键盘
抽象工厂模式一般用于严格要求以面向对象思想进行开发的超大型项目中,我们一般常规的开发的话一般就是简单工厂和工厂方法模式会用的比较多一些;
大白话解释:简单工厂模式就是你给工厂什么,工厂就给你生产什么;
工厂方法模式就是你找工厂生产产品,工厂是外包给下级分工厂来代加工,需要先评估一下能不能代加工;能做就接,不能做就找其他工厂;
抽象工厂模式就是工厂接了某项产品订单但是做不了,上级集团公司新建一个工厂来专门代加工某项产品;