天天看点

js设计模式【详解】—— 工厂模式

目录

​​工厂模式定义​​

​​简单工厂【重点掌握】​​

​​演示范例——简单工厂​​

​​项目实战范例——简单工厂​​

​​工厂方法​​

​​抽象工厂​​

工厂模式定义

用途: 创建对象

核心思想:不暴露创建对象的具体逻辑,将逻辑封装在一个函数中,这个函数就可以被视为一个工厂。

分类(根据抽象程度的不同):简单工厂(又叫静态工厂模式),工厂方法和抽象工厂

简单工厂【重点掌握】

使用场景:创建的对象数量较少,对象的创建逻辑不复杂时使用。(在实际的前端业务中,最常用的简单工厂模式。如果不是超大型的项目,是很难有机会使用到工厂方法模式和抽象工厂方法模式的。)

优点:只需要一个正确的参数,就可以获取到你所需要的对象,而无需知道其创建的具体细节。

缺点:每增加新的构造函数还需要修改判断逻辑代码。

演示范例——简单工厂

范例功能:根据用户的权限来渲染不同的页面

实现思路:使用关键字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');      

抽象工厂

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('微博小李');      

继续阅读