Love Frontend
Сообщество
фронтенд разработчиков
EN

Шаблон проектирования Фабрика в ES6

Дата публикации: 04.10.2017

Я пытаюсь получить максимальную отдачу от новшеств в ES6 (ES2015). И я пишу новую библиотеку, в которой мне потребовалась Фабрика. Я заинтересовался, как я могу выгодно использовать новые вещи, чтобы сделать это. В то же время не забывая, что код на ES5 будет также валиден и спокойно используется рядом с ES6

Я искал и гуглил, потому что было много умных кодеров, которые обычно задают вопросы такого типа и я получил умные ответы. Хотя было довольно трудно получить результат и я не мог найти что-нибудь, что объединяло бы обе области: ES6 + Factory-pattern (Шаблон Фабрика). Может быть потому что никто не писал об этом или просто потому что обе области имеют множество ссылок на себя и популярные посты о других технологиях или о ES5 показывались в топе поисковой выдачи и новые посты еще не поднялись в ранге.

Так я добавил что-то от себя. Но прежде, чем погрузится в предмет, нам необходимы общие понимания того, что такое паттерн (шаблон) проектирования Фабрика. Существует 3 понятия шаблона Фабрики:

Простая фабрика (simple Factory)

Простая фабрика - это объект, который инкапсулирует создание другого объекта. В ES6 это может быть конструктором, вызванным new()

 //Definition of class class User {     constructor(typeOfUser){         this._canEditEverything = false;         if (typeOfUser === "administrator") {             this._canEditEverything = true;         }     }     get canEditEverything() { return this._canEditEverything; } } //Instatiation let u1 = new User("normalGuy"); let u2 = new User("administrator");  

Это и есть простая фабрика, как видите, очень простая. Здесь применяется только один класс. Хорошо подходит для множества ситуаций.

Фабричный метод - Factory method

Фабричный метод определяет один метод, создания экземпляров, который переопределен подклассом, который решает, что вернуть. Фабрики и Продукты должны соответствовать интерфейсу клиентов, чтобы быть доступными для их использования.

Продолжая использовать new вместо создания новых методов, в ES6 это может быть реализовано расширяющимися классами (extend)

 //Class class User {     constructor(){         this._canEditEverything = false;     }     get canEditEverything() { return this._canEditEverything; } } //Sub-class class Administrator extends User {     constructor() {         super();         this._canEditEverything = true;     } } //Instatiation let u2 = new Administrator(); u2.canEditEverything; //true  

Абстрактная Фабрика - Abstract Factory

Шаблон абстрактная Фабрика предоставляет интерфейс для создания семейства связанных или зависимых объектов без точного определения их конкретных классов.

Важное слово здесь - семейства. Продолжая наш пример это может быть реализовано в ES6 как interface (class) и много конкретных классов (подклассов). В TypeScript (как и в C#) interface существует как определение (без какой-либо реализации) и классы реализуют его. Но в JS (включая ES6) этого нету, это причина по которой мы используем класс и подклассы, которые наследуют его. У нас могут быть подклассы "Administrator", "Editor", "Publisher" и т.д.

Регистрация конкретного объекта на лету (run-time)

Всё идёт нормально, но мне нужно было что-то более полезное. Мне нужно было:

Так я сделал ES6 класс со статичным методом для регистрации и создания классов. Что-то вроде этого:

 class Factory {     constructor() {     }     static register(clazzname, clazz) {         if ((!Factory._registeredTypes.has(clazzname) &&             clazz.prototype instanceof User)) {             Factory._registeredTypes.add(clazzname, clazz);             ...         } else { ... }     }     static create(clazzname, ...options) {         if (!Factory._registeredTypes.has(clazzName)) {             console.error("!!!");             return null;         }         let clazz = this._registeredTypes.get(clazzName);         let instance = new clazz(...options);         return instance;     } } Factory._registeredTypes = new Map();  

Обратите внимание на две вещи: (1) использование метода с типом static и (2) привзяка map() к свойствам внизу. В ES6 вы можете использовать static метод (его не обязательно использовать для создания экземпляра класса), он не будет доступен экземпляру и он не имеет доступа к экземпляру через this. Но тут нету "static" свойств. Таким образом, мы должны создать их в (2).

Это работает хорошо. И кто-то из вас возможно заметит, что это как настоящая реализация паттерна. Но я так не думаю.

Возможно это дело стиля, но я предпочитаю объекты вместо классов или функций в данном случае. В итоге я использовал простой объект:

 const Factory = {     registeredTypes: new Map(),     register(clazzname, clazz) {         if (!(Factory.registeredTypes.has(clazzname) &&             clazz.prototype instanceof User)) {             Factory._registeredTypes.add(clazzname, clazz);         } else { ... }     },     create(clazzname, ...options) {         if (!Factory.registeredTypes.has(clazzName)) {             console.error("!!!");             return null;         }         let clazz = this.registeredTypes.get(clazzName);         let instance = new clazz(...options);         return instance;     } }  

Это всё, после двух дней выяснений как реализовать это в моей новой ES6 библиотеке, я пришел к довольно стандартному решению!

Оставьте свой e-mail чтобы получать уведомления о свежих статьях.