工厂模式定义
用途: 创建对象
核心思想:不暴露创建对象的具体逻辑,将逻辑封装在一个函数中,这个函数就可以被视为一个工厂。
分类(根据抽象程度的不同):简单工厂(又叫静态工厂模式),工厂方法和抽象工厂
简单工厂【重点掌握】
使用场景:创建的对象数量较少,对象的创建逻辑不复杂时使用。(在实际的前端业务中,最常用的简单工厂模式。如果不是超大型的项目,是很难有机会使用到工厂方法模式和抽象工厂方法模式的。)
优点:只需要一个正确的参数,就可以获取到你所需要的对象,而无需知道其创建的具体细节。
缺点:每增加新的构造函数还需要修改判断逻辑代码。
演示范例——简单工厂
范例功能:根据用户的权限来渲染不同的页面
实现思路:使用关键字class定义类User,此时的User类就是一个简单工厂,调用工厂方法,传入不同的参数,便能得到拥有不同权限的用户实例
//User类 class User { //构造器 constructor(opt) { this.name = opt.name; this.viewPage = opt.viewPage; } //静态方法 static getInstance(role) { switch (role) { case 'superAdmin': return new User({ name: '超级管理员', viewPage: ['首页', '通讯录', '发现页', '应用数据', '权限管理'] }); break; case 'admin': return new User({ name: '管理员', viewPage: ['首页', '通讯录', '发现页', '应用数据'] }); break; case 'user': return new User({ name: '普通用户', viewPage: ['首页', '通讯录', '发现页'] }); break; default: throw new Error('参数错误, 可选参数:superAdmin、admin、user') } } } //调用 let superAdmin = User.getInstance('superAdmin'); let admin = User.getInstance('admin'); let normalUser = User.getInstance('user');
项目实战范例——简单工厂
范例功能:在登录的时候根据权限使用vue-router
提供的addRoutes
方法给予用户相对应的路由权限。
router/index.js
文件
//index.js import Vue from 'vue' import Router from 'vue-router' import Login from '../components/Login.vue' Vue.use(Router) export default new Router({ routes: [ //重定向到登录页 { path: '/', redirect: '/login' }, //登陆页 { path: '/login', name: 'Login', component: Login } ] })
router/
文件夹下新建一个routerFactory.js
文件,导出routerFactory
简单工厂函数,用于根据用户权限提供路由权限
//routerFactory.js import SuperAdmin from '../components/SuperAdmin.vue' import NormalAdmin from '../components/Admin.vue' import User from '../components/User.vue' import NotFound404 from '../components/404.vue' let AllRoute = [ //超级管理员页面 { path: '/super-admin', name: 'SuperAdmin', component: SuperAdmin }, //普通管理员页面 { path: '/normal-admin', name: 'NormalAdmin', component: NormalAdmin }, //普通用户页面 { path: '/user', name: 'User', component: User }, //404页面 { path: '*', name: 'NotFound404', component: NotFound404 } ] let routerFactory = (role) => { switch (role) { case 'superAdmin': return { name: 'SuperAdmin', route: AllRoute }; break; case 'normalAdmin': return { name: 'NormalAdmin', route: AllRoute.splice(1) } break; case 'user': return { name: 'User', route: AllRoute.splice(2) } break; default: throw new Error('参数错误! 可选参数: superAdmin, normalAdmin, user') } } export { routerFactory }
在登录页导入该方法,请求登录接口后根据权限添加路由:
//Login.vue import {routerFactory} from '../router/routerFactory.js' export default { //... methods: { userLogin() { //请求登陆接口, 获取用户权限, 根据权限调用this.getRoute方法 //.. }, getRoute(role) { //根据权限调用routerFactory方法 let routerObj = routerFactory(role); //给vue-router添加该权限所拥有的路由页面 this.$router.addRoutes(routerObj.route); //跳转到相应页面 this.$router.push({name: routerObj.name}) } } };
使用this.$router.addRoutes
方法添加的路由刷新后不能保存,所以会导致路由无法访问。通常的做法是本地加密保存用户信息,在刷新后获取本地权限并解密,根据权限重新添加路由。
工厂方法
核心要点:将创建实例推迟到子类中进行;
实现原理:使用new.target
来模拟出抽象类。new.target
指向直接被new
执行的构造函数,我们对new.target
进行判断,如果指向了该类则抛出错误来使得该类成为抽象类。
class User { constructor(name = '', viewPage = []) { if(new.target === User) { throw new Error('抽象类不能实例化!'); } this.name = name; this.viewPage = viewPage; } } class UserFactory extends User { constructor(name, viewPage) { super(name, viewPage) } create(role) { switch (role) { case 'superAdmin': return new UserFactory( '超级管理员', ['首页', '通讯录', '发现页', '应用数据', '权限管理'] ); break; case 'admin': return new UserFactory( '普通用户', ['首页', '通讯录', '发现页'] ); break; case 'user': return new UserFactory( '普通用户', ['首页', '通讯录', '发现页'] ); break; default: throw new Error('参数错误, 可选参数:superAdmin、admin、user') } } } let userFactory = new UserFactory(); let superAdmin = userFactory.create('superAdmin'); let admin = userFactory.create('admin'); let user = userFactory.create('user');
抽象工厂
抽象工厂模式是对类的工厂抽象用来创建产品类簇,不负责创建某一类产品的实例。
同样使用new.target
语法来模拟抽象类,并通过继承的方式创建出UserOfWechat
, UserOfQq
, UserOfWeibo
这一系列子类类簇。使用getAbstractUserFactor
来返回指定的类簇。
class User { constructor(type) { if (new.target === User) { throw new Error('抽象类不能实例化!') } this.type = type; } } class UserOfWechat extends User { constructor(name) { super('wechat'); this.name = name; this.viewPage = ['首页', '通讯录', '发现页'] } } class UserOfQq extends User { constructor(name) { super('qq'); this.name = name; this.viewPage = ['首页', '通讯录', '发现页'] } } class UserOfWeibo extends User { constructor(name) { super('weibo'); this.name = name; this.viewPage = ['首页', '通讯录', '发现页'] } } function getAbstractUserFactory(type) { switch (type) { case 'wechat': return UserOfWechat; break; case 'qq': return UserOfQq; break; case 'weibo': return UserOfWeibo; break; default: throw new Error('参数错误, 可选参数:superAdmin、admin、user') } } let WechatUserClass = getAbstractUserFactory('wechat'); let QqUserClass = getAbstractUserFactory('qq'); let WeiboUserClass = getAbstractUserFactory('weibo'); let wechatUser = new WechatUserClass('微信小李'); let qqUser = new QqUserClass('QQ小李'); let weiboUser = new WeiboUserClass('微博小李');
更多设计模式详见——js设计模式【详解】总目录
https://blog.csdn.net/weixin_41192489/article/details/116154815