Шаблоны проектирования JavaScript и практика — Factory Pattern

внешний интерфейс Шаблоны проектирования JavaScript Vue.js

1 Что такое заводской образец?

Фабричный шаблон является одним из наиболее распространенных шаблонов проектирования, используемых для создания объектов. Мы не выставляем конкретную логику создания объектов, а инкапсулируем логику в функцию, тогда эту функцию можно рассматривать как фабрику. Фабричный узор можно разделить на:简单工厂,工厂方法а также抽象工厂.

Если вы познакомились только с языком JavaScript, концепция абстракции может показаться немного расплывчатой, потому что JavaScript всегда былabstractкак зарезервированное слово без его реализации. Если вы плохо понимаетеАннотацияконцепции, то трудно понять сходства и различия трех методов в фабричном шаблоне. Итак, давайте кратко опишем понятия абстракции и фабрики в сценарии.

Представьте, что у вашей подруги скоро день рождения, вы хотите знать, чего она хочет, поэтому вы спрашиваете ее: «Дорогая, что ты хочешь на день рождения?» Так уж случилось, что ваша девушка любит кошек, и ее больше всего очаровал супермилый шотландский вислоухий кот на Douyin, а еще она очень хочет кошку в том же стиле, что и интернет-знаменитость.

Поэтому она отвечает вам: «Дорогой, я хочу животное».

Вы спокойно спросили ее: «Какое животное ты хочешь?»

Твоя девушка сказала: "Я хочу кошачьих".

В это время вы задаетесь вопросом, кошки включают тигров, львов, леопардов, рысей и всех видов котят Как мне узнать, что вы хотите?

Итак, вы спрашиваете свою девушку: «Какую кошку ты хочешь?»

"Это глупо, чего еще ты хочешь? Это должен быть котенок. Можем ли мы еще вырастить тигров, как местные тираны в Дубае!", - ответила твоя девушка.

«Хорошо, а какую породу кошек вы хотите?» — спросите вы.

«Я хочу иностранных пород, а не китайских кошек», — высокомерно ответила твоя девушка.

В это время вы почти рухнули.Как программист, вы больше не можете выносить этот вид выжимания зубной пасты вопроса, поэтому вы умоляли: «Дорогой, просто скажи мне, какой сорт и цвет ты хочешь, Насколько большой кот?»

Твоя девушка думает о коте в TikTok и отвечает: «Хочу одного灰色из,不超过1Лет от роду苏格兰短耳猫! "

Итак, вы пришли на крупнейший оптовый рынок домашних животных в стране в день рождения своей девушки, выбрали «серого, шотландского короткоухого кота не старше 1 года» и подарили своей девушке, сделав вашу девушку интернет-знаменитостью. того самого кота!

Кошку, которую вы, наконец, купили и подарили своей девушке, можно рассматривать как экземпляр объекта, а оптовый рынок домашних животных можно рассматривать как фабрику, мы можем думать о ней как о функции, существуют различные виды фабричных функций. Животное, так как ты получил экземпляр? Потому что вы передали правильные параметры оптовому рынку домашних животных:“color: 灰色”,“age: 不超过1岁”,"breed:苏格兰短耳",“category: 猫". В предыдущем разговоре ваша девушка ответила "животные", "кошачьи", "иностранные породы" так, что вы не поняли, чего она хочет, потому что она слишком абстрактна.

Она ответила на общие черты большой группы животных, а не конкретных животных, мыслительный процесс извлечения одной или нескольких общих черт сложных вещей является абстракцией..

Теперь, когда мы поняли концепцию абстракции, давайте взглянем на три метода реализации фабричного шаблона, упомянутые ранее:简单工厂模式,工厂方法模式,抽象工厂模式.

1.1 Простой заводской шаблон

简单工厂模式он же静态工厂模式, который определяется фабричным объектом для создания экземпляра определенного класса объектов продукта. В основном используется для создания объектов одного класса.

В реальных проектах нам часто нужно отображать разные страницы в соответствии с разрешениями пользователя.Некоторые страницы, принадлежащие пользователям с разрешениями высокого уровня, не могут быть просмотрены пользователями с разрешениями низкого уровня. Так мы можем сохранять страницы, которые пользователь может видеть в конструкторе пользователей с разными уровнями привилегий. Создайте экземпляр пользователя на основе разрешений. При переписывании шаблона простой фабрики с помощью ES6 мы больше не используем конструкторы для создания объектов, а используем новый синтаксис класса и используем ключевое слово static для инкапсуляции простой фабрики в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');

UserЭто простая фабрика, в этой функции есть 3 экземпляра, которые соответствуют пользователям с разными разрешениями. Когда мы вызываем фабричную функцию, нам просто нужно передатьsuperAdmin, admin, userОдин из трех необязательных параметров получает соответствующий объект экземпляра.

Преимущество простой фабрики в том, что вам нужен только правильный параметр, чтобы получить нужный вам объект, не зная специфики его создания. Однако функция содержит логику создания (конструктор) и код логики оценки всех объектов.Каждый раз, когда добавляется новый конструктор, код логики оценки необходимо модифицировать. Когда наш объект не выше 3, а 30 или более, эта функция станет огромной суперфункцией, и ее будет сложно поддерживать.Поэтому простая фабрика может воздействовать только на небольшое количество созданных объектов и используется, когда логика создания объекта несложна..

1.2 Шаблон фабричного метода

Первоначальная цель шаблона фабричного метода состоит в том, чтобыРабота по фактическому созданию объекта отложена на подкласс, поэтому основной класс становится абстрактным классом. Но в JavaScript сложно создавать абстрактные классы, подобные традиционным объектно-ориентированным. Так что в JavaScript нам нужно только обратиться к его основной идее. Мы можем думать о фабричном методе как о фабричном классе, который создает экземпляры объектов. Хотя ES6 тоже не реализуетabstract, но мы можем использовать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');

1.3 Шаблон абстрактной фабрики

Выше описаны простой фабричный шаблон и фабричный метод, оба из которых напрямую генерируют экземпляры, но абстрактный фабричный шаблон отличается от него.对产品类簇создание.

в примере вышеsuperAdmin,admin,userТри роли пользователя, где пользователь может быть зарегистрирован с разными учетными записями в социальных сетях, например:wechat,qq,weibo. Тогда эти три типа учетных записей социальных сетей являются соответствующими кластерами. В абстрактной фабрике кластер классов обычно определяется родительским классом, а некоторые абстрактные методы определяются в родительском классе, а затем дочерний класс наследует родительский класс через абстрактную фабрику. так,Абстрактная фабрика на самом деле является методом реализации подклассов для наследования родительского класса..

Упомянутые выше абстрактные методы — это методы, которые объявлены, но не могут использоваться. Обычно используется в других традиционных объектно-ориентированных языках.abstractобъявить, но в JavaScript,abstract— зарезервированное слово, но мы можем имитировать абстрактные классы, вызывая ошибки в методах класса.

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

2 Проект фактического применения заводского режима

В реальном клиентском бизнесе чаще всего используется простой фабричный шаблон. Если это не очень большой проект, трудно иметь возможность использовать шаблон фабричного метода и шаблон абстрактного фабричного метода. Ниже я представляю фактическое использование в проекте Vue.简单工厂模式Приложения.

в обычномvue + vue-routerВ проектах мы обычно пишем все маршруты кrouter/index.jsв этом файле. Следующий код, я думаю, будет очень знаком разработчикам vue, всего с 5 маршрутами страниц:

// index.js

import Vue from 'vue'
import Router from 'vue-router'
import Login from '../components/Login.vue'
import SuperAdmin from '../components/SuperAdmin.vue'
import NormalAdmin from '../components/Admin.vue'
import User from '../components/User.vue'
import NotFound404 from '../components/404.vue'

Vue.use(Router)

export default new Router({
  routes: [
    //重定向到登录页
    {
      path: '/',
      redirect: '/login'
    },
    //登陆页
    {
      path: '/login',
      name: 'Login',
      component: Login
    },
    //超级管理员页面
    {
      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
    }
  ]
})

Когда дело доходит до страницы управления правами, обычно необходимо открыть страницу с фиксированным доступом в соответствии с правами при входе пользователя в систему и выполнить переход на страницу с соответствующими правами. Но если мы все же пойдем по-старому и пропишем все маршруты доrouter/index.jsВ этом файле, если пользователь с низким уровнем привилегий знает маршрут с высоким уровнем привилегий, он может перейти на страницу с высоким уровнем привилегий, введя URL-адрес в браузере. Поэтому мы должны использовать его в соответствии с разрешениями при входе в систему.vue-routerкоторый предоставилaddRoutesМетод дает пользователю соответствующее разрешение маршрутизации. В настоящее время вы можете использовать простой фабричный метод для преобразования приведенного выше кода.

существуетrouter/index.jsдокумент, мы предоставляем только/loginЭто одна страница маршрутизации.

//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Маршрут, добавленный методом, не может быть сохранен после обновления, поэтому маршрут будет недоступен. Обычной практикой является локальное шифрование и сохранение информации о пользователе, получение локальных разрешений и их расшифровка после обновления, а также повторное добавление маршрутов в соответствии с разрешениями. Я не буду вдаваться в подробности здесь, потому что это не имеет ничего общего с фабричным шаблоном.

3 Резюме

Все три фабричных шаблона, упомянутые выше, как и шаблон синглтона, являются творческими шаблонами проектирования. Простой фабричный паттерн также называют статическим фабричным методом, который используется для создания экземпляра определенного объекта продукта, который используется для создания одного объекта; фабричный паттерн заключается в том, чтобы отложить создание экземпляров до подклассов; абстрактная фабрика шаблон используется для абстрагирования фабрики класса, для создания кластера класса продуктов и не отвечает за создание экземпляров определенного класса продуктов. В реальном бизнесе необходимо выбрать соответствующий режим в соответствии с фактической сложностью бизнеса. Для небольших интерфейсных приложений гибкое использование простых фабрик может фактически решить большинство проблем.

3.1 Когда используется заводской шаблон?

Новая операция просто инкапсулирована.

3.2 Каковы преимущества заводского шаблона?

Например:

  • Вы идете покупать гамбургер, заказываете напрямую, забираете еду, а сами не готовите (покупатель не обращает внимания на то, как сделан бургер).

  • магазин хочет封装Делайте работу гамбургера, и делайте это непосредственно перед покупателем (продавец не скажет вам, как это сделать, и не будет настолько глуп, чтобы дать вам кусок хлеба, немного сливок и немного салата, чтобы приготовить его). сами)

Снаружи не может заботиться о том, как генерируется внутренний конструктор, просто вызовите фабричный метод для создания экземпляра;

Конструктор и создатель разделены по принципу открытого-закрытого.

3.3 Некоторые практические примеры

  • jQuery$(selector)jQuery$('div')а такжеnew $('div')Который прост в использовании, очевидно, прямой$()наиболее удобно, потому что$()это уже заводской метод;
class jQuery {
    constructor(selector) {
        super(selector)
    }
    //  ....
}

window.$ = function(selector) {
    return new jQuery(selector)
}

  • РеагироватьcreateElement()

React.createElement()метод является фабричным методом

  • Асинхронные компоненты для Vue

пройти черезpromiseПутьresolveиз компонента

Ссылка: https://segmentfault.com/a/1190000014196851