portaldacalheta.pt
  • Основен
  • Дизайн На Марката
  • Тенденции
  • Инструменти И Уроци
  • Технология
Back-End

Изчерпателното ръководство за модели за дизайн на JavaScript



Като добър разработчик на JavaScript се стремите да пишете чист, здравословен и поддържаем код. Решавате интересни предизвикателства, които, макар и уникални, не изискват непременно уникални решения. Вероятно сте открили, че пишете код, който изглежда подобно на решението на съвсем различен проблем, с който сте се справяли преди. Може да не го знаете, но сте използвали JavaScript модел на проектиране . Дизайнерските модели са решения за многократна употреба на често срещани проблеми в софтуерния дизайн.

Изчерпателното ръководство за модели за дизайн на JavaScript



По време на живота на който и да е език много такива решения за многократна употреба се правят и тестват от голям брой разработчици от общността на този език. Именно поради този комбиниран опит на много разработчици такива решения са толкова полезни, защото ни помагат да пишем код по оптимизиран начин, като в същото време решаваме проблема.



Основните предимства, които получаваме от дизайнерските модели, са следните:



  • Те са доказани решения: Тъй като дизайнерските модели често се използват от много разработчици, можете да сте сигурни, че те работят. И не само това, можете да сте сигурни, че те са били ревизирани многократно и вероятно са били приложени оптимизации.
  • Те са лесно за многократна употреба: Дизайнерските модели документират решение за многократна употреба, което може да бъде модифицирано за решаване на множество конкретни проблеми, тъй като те не са обвързани с конкретен проблем.
  • Те са изразителни: Моделите на дизайна могат да обяснят едно голямо решение доста елегантно.
  • Те улесняват комуникацията: Когато разработчиците са запознати с дизайнерските модели, те могат по-лесно да общуват помежду си относно потенциалните решения на даден проблем.
  • Те предотвратяват необходимостта от рефакторинг на код: Ако дадено приложение е написано с оглед на дизайнерски модели, често се случва, че по-късно няма да е необходимо да рефакторирате кода, тъй като прилагането на правилния модел на дизайн към даден проблем вече е оптимално решение.
  • Те намаляват размера на кодовата база: Тъй като дизайнерските модели обикновено са елегантни и оптимални решения, те обикновено изискват по-малко код от други решения.

Знам, че сте готови да влезете в този момент, но преди да научите всичко за дизайнерските модели, нека прегледаме някои основи на JavaScript.

Кратка история на JavaScript

JavaScript е един от най-популярните езици за програмиране за уеб разработка днес. Първоначално беше направен като нещо като „лепило“ за различни показани HTML елементи, известни като скриптов език от страна на клиента, за един от първоначалните уеб браузъри. Наречен Netscape Navigator, той може да показва само статичен HTML по това време. Както можете да предположите, идеята за такъв скриптов език доведе до войни в браузърите между големите играчи в индустрията за разработка на браузъри по това време, като Netscape Communications (днес Mozilla), Microsoft и други.



Всеки от големите играчи искаше да прокара собствената си реализация на този скриптов език, така че Netscape направи JavaScript (всъщност Brendan Eich го направи), Microsoft направи JScript и т.н. Както можете да си представите, разликите между тези внедрения бяха големи, така че разработката за уеб браузъри беше направена за всеки браузър, с най-добре гледани стикери, които се доставяха с уеб страница. Скоро стана ясно, че се нуждаем от стандартно решение за различни браузъри, което да унифицира процеса на разработка и да опрости създаването на уеб страници. Това, което са измислили, се нарича ECMAScript .

ECMAScript е стандартизирана спецификация на скриптовия език, която всички съвременни браузъри се опитват да поддържат и има множество внедрения (може да се каже диалекти) на ECMAScript. Най-популярният е темата на тази статия, JavaScript. От първоначалното си издание ECMAScript е стандартизирал много важни неща и за тези, които се интересуват повече от спецификата, има подробен списък на стандартизирани елементи за всяка версия на ECMAScript, наличен в Wikipedia. Поддръжката на браузъра за ECMAScript версии 6 (ES6) и по-нови все още са непълни и трябва да бъдат транслирани в ES5, за да бъдат напълно поддържани.



Какво е JavaScript?

За да разберем напълно съдържанието на тази статия, нека направим въведение в някои много важни езикови характеристики, които трябва да знаем, преди да се потопим в шаблоните за дизайн на JavaScript. Ако някой трябва да ви попита „Какво е JavaScript?“ може да отговорите някъде в редовете на:

JavaScript е лек, интерпретиран, обектно-ориентиран език за програмиране с първокласни функции, най-известни като скриптови езици за уеб страници.



Гореспоменатата дефиниция означава да се каже, че JavaScript кодът е с малък отпечатък на паметта, лесен за изпълнение и лесен за научаване със синтаксис, подобен на популярните езици като C ++ и Java. Това е скриптов език, което означава, че неговият код се интерпретира, вместо да се компилира. Той има поддръжка за процедурни, обектно-ориентирани и функционални стилове на програмиране, което го прави много гъвкав за разработчиците.

Досега разгледахме всички характеристики, които звучат като много други езици там, така че нека да разгледаме какво е специфичното за JavaScript по отношение на други езици. Ще изброя няколко характеристики и ще дам най-добрия опит да обясня защо те заслужават специално внимание.



конвертирайте заплатата на пълен работен ден в договорна ставка

JavaScript поддържа първокласни функции

По-рано тази характеристика ме затрудняваше да схвана, когато тепърва започвах с JavaScript, тъй като идвах от C / C ++ фон. JavaScript третира функциите като първокласни граждани, което означава, че можете да предавате функции като параметри на други функции, точно както бихте правили всяка друга променлива.

// we send in the function as an argument to be // executed from inside the calling function function performOperation(a, b, cb) { var c = a + b; cb(c); } performOperation(2, 3, function(result) { // prints out 5 console.log('The result of the operation is ' + result); })

JavaScript е базиран на прототип

Както е случаят с много други обектно-ориентирани езици, JavaScript поддържа обекти и един от първите термини, който идва на ум, когато се мисли за обекти, е класове и наследяване. Тук става малко сложно, тъй като езикът не поддържа класове в неговия обикновен език, а по-скоро използва нещо, наречено наследяване, базирано на прототип или екземпляр.



Точно сега, в ES6, е официалният термин клас , което означава, че браузърите все още не поддържат това (ако си спомняте, към момента на писане последната напълно поддържана версия на ECMAScript е 5.1). Важно е обаче да се отбележи, че въпреки че терминът „клас“ е въведен в JavaScript, той все още използва базирано на прототип наследство под капака.

Програмирането, базирано на прототип, е стил на обектно-ориентирано програмиране, при което повторното използване на поведението (известно като наследяване) се извършва чрез процес на повторно използване на съществуващи обекти чрез делегиране, които служат като прототипи. Ще се потопим в повече подробности с това, след като стигнем до раздела за дизайнерски модели на статията, тъй като тази характеристика се използва в много дизайнерски модели на JavaScript.

Цикли на събития в JavaScript

Ако имате опит в работата с JavaScript, със сигурност сте запознати с термина функция за обратно извикване . За тези, които не са запознати с термина, функцията за обратно извикване е функция, изпратена като параметър (не забравяйте, JavaScript третира функциите като първокласни граждани) на друга функция и се изпълнява след изстрелване на събитие. Това обикновено се използва за абониране за събития като щракване с мишката или натискане на бутон на клавиатурата.

Графично изображение на цикъла на събитията на JavaScript

Всеки път, когато събитие, към което има прикачен слушател, се задейства (в противен случай събитието се губи), изпраща се съобщение до опашката от съобщения, които се обработват синхронно, по начин FIFO (първи в първия изход ). Това се нарича цикъл на събития .

Всяко от съобщенията в опашката има свързана с него функция. След като съобщението бъде деактивирано, изпълнението изпълнява функцията изцяло, преди да обработи друго съобщение. Това означава, че ако дадена функция съдържа други извиквания на функции, всички те се изпълняват преди обработката на ново съобщение от опашката. Това се нарича изпълнение до завършване.

while (queue.waitForMessage()) { queue.processNextMessage(); }

queue.waitForMessage() синхронно чака нови съобщения. Всяко от обработваните съобщения има свой собствен стек и се обработва, докато стекът се изпразни. След като приключи, ново съобщение се обработва от опашката, ако има такова.

Може също да сте чували, че JavaScript не се блокира, което означава, че когато се извършва асинхронна операция, програмата е в състояние да обработва други неща, като например получаване на потребителски вход, докато чака асинхронната операция да завърши, без да блокира основната нишка за изпълнение. Това е много полезно свойство на JavaScript и цяла статия може да бъде написана точно по тази тема; това обаче е извън обхвата на тази статия.

Какви са дизайнерските модели?

Както казах преди, дизайнерските модели са решения за многократна употреба на често срещани проблеми в софтуерния дизайн. Нека да разгледаме някои от категориите дизайнерски модели.

Протомодели

Как се създава модел? Да приемем, че сте разпознали често срещан проблем и имате свое уникално решение за този проблем, което не е световно признато и документирано. Използвате това решение всеки път, когато се сблъскате с този проблем, и смятате, че то е многократно и че общността на разработчиците може да се възползва от него.

Веднага ли се превръща в модел? За щастие, не. Често човек може да има добри практики за писане на кодове и просто да сбърка нещо, което прилича на шаблон за такъв, когато всъщност това не е модел.

Как можете да разберете кога това, което смятате, че разпознавате, всъщност е модел на дизайн?

Като получавате мнения на други разработчици за това, като знаете за процеса на създаване на самия модел и като се запознаете добре със съществуващите модели. Има фаза, през която трябва да премине шаблон, преди да се превърне в пълноценен модел, и това се нарича прото-модел.

Протомоделът е бъдещ модел ако преминава определен период от тестване от различни разработчици и сценарии, където моделът се оказва полезен и дава коректни резултати. Има доста голям обем работа и документация - повечето от които са извън обхвата на тази статия -, за да се направи пълноценен модел, разпознат от общността.

Анти-модели

Тъй като моделът на дизайна представлява добра практика, анти-шаблонът представлява лоша практика.

Пример за анти-шаблон би бил модифицирането на Object прототип на клас. Почти всички обекти в JavaScript наследяват от Object (не забравяйте, че JavaScript използва наследяване, базирано на прототип), така че представете си сценарий, в който сте променили този прототип. Промени в Object прототип ще се види във всички обекти, които наследяват от този прототип— което би било най-много JavaScript обекти . Това е бедствие, което чака да се случи.

Друг пример, подобен на споменатия по-горе, е модифициране на обекти, които не притежавате. Пример за това би бил отменянето на функция от обект, използван в много сценарии в цялото приложение. Ако работите с голям екип, представете си объркването, което би причинило това; бързо бихте се сблъскали с именуване на сблъсъци, несъвместими реализации и кошмари за поддръжка.

Подобно на това как е полезно да знаете за всички добри практики и решения, също е много важно да знаете и за лошите. По този начин можете да ги разпознаете и да избегнете грешката отпред.

Категоризация на дизайнерския модел

Моделите на дизайна могат да бъдат категоризирани по няколко начина, но най-популярният е следният:

  • Творчески дизайнерски модели
  • Структурни дизайнерски модели
  • Поведенчески дизайнерски модели
  • Съвпадение дизайнерски модели
  • Архитектурна дизайнерски модели

Модели на творчески дизайн

Тези модели се занимават с механизми за създаване на обекти, които оптимизират създаването на обекти в сравнение с основен подход. Основната форма на създаване на обект може да доведе до проблеми с дизайна или до допълнителна сложност на дизайна. Креативните дизайнерски модели решават този проблем чрез някакъв контрол върху създаването на обекти. Някои от популярните дизайнерски модели в тази категория са:

  • Фабричен метод
  • Абстрактна фабрика
  • Строител
  • Прототип
  • Сингълтън

Структурни дизайнерски модели

Тези модели се занимават с обектни взаимоотношения. Те гарантират, че ако една част от системата се промени, цялата система няма нужда да се променя заедно с нея. Най-популярните модели в тази категория са:

  • Адаптер
  • Мост
  • Композитен
  • Декоратор
  • Фасада
  • Муха
  • Прокси

Модели за поведенчески дизайн

Този тип модели разпознават, прилагат и подобряват комуникацията между различни обекти в системата. Те помагат да се гарантира, че различните части на системата имат синхронизирана информация. Популярни примери за тези модели са:

  • Верига на отговорност
  • Команда
  • Итератор
  • Посредник
  • Спомен
  • Наблюдател
  • Щат
  • Стратегия
  • Посетител

Модели за едновременно проектиране

Този тип дизайнерски модели се занимават с многонишкови парадигми за програмиране. Някои от популярните са:

  • Активен обект
  • Ядрена реакция
  • Планировчик

Модели на архитектурен дизайн

Модели за проектиране, които се използват за архитектурни цели. Някои от най-известните са:

  • MVC (контролер за изглед на модел)
  • MVP (Model-View-Presenter)
  • MVVM (Model-View-ViewModel)

В следващия раздел ще разгледаме по-отблизо някои от гореспоменатите дизайнерски модели с примери, предоставени за по-добро разбиране.

Примери за дизайнерски модели

Всеки от дизайнерските модели представлява специфичен тип решение на конкретен тип проблем. Няма универсален набор от модели, който винаги да е най-подходящ. Трябва да научим кога даден модел ще се окаже полезен и дали ще осигури действителна стойност. След като се запознаем с моделите и сценариите, за които са най-подходящи, можем лесно да определим дали конкретен модел е подходящ или не за даден проблем.

Не забравяйте, че прилагането на грешен шаблон към даден проблем може да доведе до нежелани ефекти като ненужна сложност на кода, ненужни режийни разходи за производителност или дори до появата на нов анти-шаблон.

Това са всички важни неща, които трябва да имате предвид, когато мислите за прилагане на шаблон за дизайн към нашия код. Ще разгледаме някои от дизайнерските модели, които лично намирам за полезни, и вярваме, че всеки старши разработчик на JavaScript трябва да е запознат.

Конструктор модел

Когато се мисли за класически обектно-ориентирани езици, конструкторът е специална функция в клас, която инициализира обект с някакъв набор от стойности по подразбиране и / или изпратени стойности.

Често срещаните начини за създаване на обекти в JavaScript са трите следните начина:

// either of the following ways can be used to create a new object var instance = {}; // or var instance = Object.create(Object.prototype); // or var instance = new Object();

След създаването на обект има четири начина (от ES3) за добавяне на свойства към тези обекти. Те са следните:

// supported since ES3 // the dot notation instance.key = 'A key's value'; // the square brackets notation instance['key'] = 'A key's value'; // supported since ES5 // setting a single property using Object.defineProperty Object.defineProperty(instance, 'key', { value: 'A key's value', writable: true, enumerable: true, configurable: true }); // setting multiple properties using Object.defineProperties Object.defineProperties(instance, { 'firstKey': { value: 'First key's value', writable: true }, 'secondKey': { value: 'Second key's value', writable: false } });

Най-популярният начин за създаване на обекти са къдравите скоби и, за добавяне на свойства, точката или квадратните скоби. Всеки, който има опит с JavaScript, ги е използвал.

По-рано споменахме, че JavaScript не поддържа естествени класове, но поддържа конструктори чрез използването на ключова дума „new“ с префикс на извикване на функция. По този начин можем да използваме функцията като конструктор и да инициализираме нейните свойства по същия начин, както бихме направили с класически конструктор на език.

// we define a constructor for Person objects function Person(name, age, isDeveloper) { this.name = name; this.age = age; this.isDeveloper = isDeveloper || false; this.writesCode = function() { console.log(this.isDeveloper? 'This person does write code' : 'This person does not write code'); } } // creates a Person instance with properties name: Bob, age: 38, isDeveloper: true and a method writesCode var person1 = new Person('Bob', 38, true); // creates a Person instance with properties name: Alice, age: 32, isDeveloper: false and a method writesCode var person2 = new Person('Alice', 32); // prints out: This person does write code person1.writesCode(); // prints out: this person does not write code person2.writesCode();

Тук обаче все още има място за подобрение. Ако си спомняте, споменах по-рано, че JavaScript използва наследяване, базирано на прототип. Проблемът с предишния подход е, че методът writesCode се предефинира за всеки от случаите на Person конструктор. Можем да избегнем това, като зададем метода във прототипа на функцията:

// we define a constructor for Person objects function Person(name, age, isDeveloper) false; // we extend the function's prototype Person.prototype.writesCode = function() { console.log(this.isDeveloper? 'This person does write code' : 'This person does not write code'); } // creates a Person instance with properties name: Bob, age: 38, isDeveloper: true and a method writesCode var person1 = new Person('Bob', 38, true); // creates a Person instance with properties name: Alice, age: 32, isDeveloper: false and a method writesCode var person2 = new Person('Alice', 32); // prints out: This person does write code person1.writesCode(); // prints out: this person does not write code person2.writesCode();

Сега и двата случая на Person конструктор има достъп до споделен екземпляр на writesCode() метод.

Модулен модел

Що се отнася до особеностите, JavaScript не спира да учудва. Друго особено нещо за JavaScript (поне що се отнася до обектно-ориентираните езици) е, че JavaScript не поддържа модификатори за достъп. В класическия OOP език потребителят дефинира клас и определя правата на достъп за своите членове. Тъй като JavaScript в обикновената си форма не поддържа нито класове, нито модификатори на достъп, разработчиците на JavaScript измислиха начин да имитират това поведение, когато е необходимо.

Преди да влезем в спецификата на модела, нека поговорим за концепцията за затваряне. A закриване е функция с достъп до родителския обхват, дори след като родителската функция е затворена. Те ни помагат да имитираме поведението на модификаторите за достъп чрез обхват. Нека покажем това чрез пример:

// we used an immediately invoked function expression // to create a private variable, counter var counterIncrementer = (function() { var counter = 0; return function() { return ++counter; }; })(); // prints out 1 console.log(counterIncrementer()); // prints out 2 console.log(counterIncrementer()); // prints out 3 console.log(counterIncrementer());

Както можете да видите, използвайки IIFE, ние свързахме променливата брояч с функция, която беше извикана и затворена, но все още може да бъде достъпна от дъщерната функция, която я увеличава. Тъй като не можем да получим достъп до променливата на брояча отвъд израза на функцията, направихме я частна чрез манипулиране на обхвата.

Използвайки затварянията, можем да създаваме обекти с частни и обществени части. Те се наричат модули и са много полезни винаги, когато искаме да скрием определени части от обект и да изложим интерфейс само на потребителя на модула. Нека покажем това в пример:

// through the use of a closure we expose an object // as a public API which manages the private objects array var collection = (function() { // private members var objects = []; // public members return { addObject: function(object) { objects.push(object); }, removeObject: function(object) { var index = objects.indexOf(object); if (index >= 0) { objects.splice(index, 1); } }, getObjects: function() { return JSON.parse(JSON.stringify(objects)); } }; })(); collection.addObject('Bob'); collection.addObject('Alice'); collection.addObject('Franck'); // prints ['Bob', 'Alice', 'Franck'] console.log(collection.getObjects()); collection.removeObject('Alice'); // prints ['Bob', 'Franck'] console.log(collection.getObjects());

Най-полезното нещо, което този модел въвежда, е ясното разделяне на частните и публичните части на обект, което е концепция, много подобна на разработчиците, идващи от класически обектно-ориентиран фон.

Не всичко обаче е толкова перфектно. Когато искате да промените видимостта на даден член, трябва да промените кода, където и да сте използвали този член, поради различното естество на достъп до публични и частни части. Също така методите, добавени към обекта след тяхното създаване, не могат да получат достъп до частните членове на обекта.

Разкриване на модел на модул

Този модел е подобрение на модулния модел, както е илюстрирано по-горе. Основната разлика е, че записваме цялата логика на обекта в частния обхват на модула и след това просто излагаме частите, които искаме да бъдат публични, като връщаме анонимен обект. Също така можем да променим именуването на частни членове, когато картографираме частни членове на съответните им публични членове.

// we write the entire object logic as private members and // expose an anonymous object which maps members we wish to reveal // to their corresponding public members var namesCollection = (function() { // private members var objects = []; function addObject(object) { objects.push(object); } function removeObject(object) { var index = objects.indexOf(object); if (index >= 0) { objects.splice(index, 1); } } function getObjects() { return JSON.parse(JSON.stringify(objects)); } // public members return { addName: addObject, removeName: removeObject, getNames: getObjects }; })(); namesCollection.addName('Bob'); namesCollection.addName('Alice'); namesCollection.addName('Franck'); // prints ['Bob', 'Alice', 'Franck'] console.log(namesCollection.getNames()); namesCollection.removeName('Alice'); // prints ['Bob', 'Franck'] console.log(namesCollection.getNames());

Разкриващият модулен шаблон е един от поне трите начина, по които можем да приложим модулен шаблон. Разликите между разкриващия модулен модел и другите варианти на модулния модел са предимно в това как се препращат публичните членове. В резултат на това моделът на разкриващия модул е ​​много по-лесен за използване и модифициране; обаче може да се окаже крехка в определени сценарии, като използването на RMP обекти като прототипи във верига за наследяване. Проблемните ситуации са следните:

  1. Ако имаме частна функция, която се отнася до публична функция, не можем да заменим публичната функция, тъй като частната функция ще продължи да се отнася до частното изпълнение на функцията, като по този начин въвежда грешка в нашата система.
  2. Ако имаме публичен член, сочещ към частна променлива, и се опитваме да заменим публичния член извън модула, другите функции пак ще се отнасят до частната стойност на променливата, въвеждайки грешка в нашата система.

Единичен модел

Единичният модел се използва в сценарии, когато се нуждаем от точно един екземпляр на клас. Например, трябва да имаме обект, който съдържа някаква конфигурация за нещо. В тези случаи не е необходимо да се създава нов обект, когато обектът за конфигурация се изисква някъде в системата.

var singleton = (function() { // private singleton value which gets initialized only once var config; function initializeConfiguration(values){ this.randomNumber = Math.random(); values = values || {}; this.number = values.number || 5; this.size = values.size || 10; } // we export the centralized method for retrieving the singleton value return { getConfig: function(values) { // we initialize the singleton value only once if (config === undefined) { config = new initializeConfiguration(values); } // and return the same config value wherever it is asked for return config; } }; })(); var configObject = singleton.getConfig({ 'size': 8 }); // prints number: 5, size: 8, randomNumber: someRandomDecimalValue console.log(configObject); var configObject1 = singleton.getConfig({ 'number': 8 }); // prints number: 5, size: 8, randomNumber: same randomDecimalValue as in first config console.log(configObject1);

Както можете да видите в примера, генерираното произволно число винаги е същото, както и изпратените конфигурационни стойности.

Важно е да се отбележи, че точката за достъп за извличане на единичната стойност трябва да бъде само една и много добре позната. Недостатък на използването на този модел е, че е доста трудно да се тества.

Модел на наблюдател

Моделът на наблюдателя е много полезен инструмент, когато имаме сценарий, при който трябва да подобрим комуникацията между различни части на нашата система по оптимизиран начин. Той насърчава свободно свързване между обектите.

Има различни версии на този модел, но в най-основната му форма имаме две основни части на шаблона. Първият е субект, а вторият - наблюдатели.

Субектът обработва всички операции по дадена тема, за която наблюдателите се абонират. Тези операции абонират наблюдател за определена тема, отписват наблюдател от определена тема и уведомяват наблюдатели за определена тема, когато е публикувано събитие.

Има обаче вариант на този модел, наречен модел издател / абонат, който ще използвам като пример в този раздел. Основната разлика между класически модел на наблюдател и модел на издател / абонат е, че издателят / абонатът насърчава още по-свободно свързване, отколкото моделът на наблюдателя.

В модела на наблюдателя субектът държи препратките към абонираните наблюдатели и извиква методи директно от самите обекти, докато в модела издател / абонат имаме канали, които служат като комуникационен мост между абонат и издател. Издателят задейства събитие и просто изпълнява функцията за обратно извикване, изпратена за това събитие.

Ще покажа кратък пример за модел издател / абонат, но за тези, които се интересуват, може лесно да се намери онлайн пример за класически модел на наблюдател.

var publisherSubscriber = {}; // we send in a container object which will handle the subscriptions and publishings (function(container) { // the id represents a unique subscription id to a topic var id = 0; // we subscribe to a specific topic by sending in // a callback function to be executed on event firing container.subscribe = function(topic, f) { if (!(topic in container)) { container[topic] = []; } container[topic].push({ 'id': ++id, 'callback': f }); return id; } // each subscription has its own unique ID, which we use // to remove a subscriber from a certain topic container.unsubscribe = function(topic, id) { var subscribers = []; for (var subscriber of container[topic]) { if (subscriber.id !== id) { subscribers.push(subscriber); } } container[topic] = subscribers; } container.publish = function(topic, data) { for (var subscriber of container[topic]) { // when executing a callback, it is usually helpful to read // the documentation to know which arguments will be // passed to our callbacks by the object firing the event subscriber.callback(data); } } })(publisherSubscriber); var subscriptionID1 = publisherSubscriber.subscribe('mouseClicked', function(data) { console.log('I am Bob's callback function for a mouse clicked event and this is my event data: ' + JSON.stringify(data)); }); var subscriptionID2 = publisherSubscriber.subscribe('mouseHovered', function(data) { console.log('I am Bob's callback function for a hovered mouse event and this is my event data: ' + JSON.stringify(data)); }); var subscriptionID3 = publisherSubscriber.subscribe('mouseClicked', function(data) { console.log('I am Alice's callback function for a mouse clicked event and this is my event data: ' + JSON.stringify(data)); }); // NOTE: after publishing an event with its data, all of the // subscribed callbacks will execute and will receive // a data object from the object firing the event // there are 3 console.logs executed publisherSubscriber.publish('mouseClicked', {'data': 'data1'}); publisherSubscriber.publish('mouseHovered', {'data': 'data2'}); // we unsubscribe from an event by removing the subscription ID publisherSubscriber.unsubscribe('mouseClicked', subscriptionID3); // there are 2 console.logs executed publisherSubscriber.publish('mouseClicked', {'data': 'data1'}); publisherSubscriber.publish('mouseHovered', {'data': 'data2'});

Този модел на проектиране е полезен в ситуации, когато трябва да извършим множество операции върху едно изстреляно събитие. Представете си, че имате сценарий, при който трябва да осъществим множество AJAX повиквания към back-end услуга и след това да извършим други AJAX повиквания в зависимост от резултата. Ще трябва да вложите повикванията AJAX едно в друго, евентуално да влезете в ситуация, известна като ад за обратно извикване. Използването на модел издател / абонат е много по-елегантно решение.

Недостатък на използването на този модел е трудното тестване на различни части от нашата система. Няма елегантен начин да разберем дали абонираните части на системата се държат, както се очаква.

Модел на посредник

Накратко ще разгледаме модел, който също е много полезен, когато говорим за отделени системи. Когато имаме сценарий, при който множество части на системата трябва да комуникират и да бъдат координирани, може би добро решение би било да се въведе медиатор.

Медиаторът е обект, който се използва като централна точка за комуникация между различни части на системата и обработва работния процес между тях. Сега е важно да подчертаем, че той се справя с работния процес. Защо това е важно?

Тъй като има голяма прилика с модела издател / абонат. Може да се запитате, Добре, така че тези два модела помагат да се осъществи по-добра комуникация между обектите ... Каква е разликата?

Разликата е, че посредникът се справя с работния процес, докато издателят / абонатът използва нещо, наречено тип комуникация „запали и забрави“. Издателят / абонатът е просто агрегатор на събития, което означава, че той просто се грижи за задействане на събитията и уведомяване на правилните абонати кои събития са били стартирани. Агрегаторът на събития не се интересува какво се случва, след като дадено събитие е изстреляно, което не е случаят с посредник.

Хубав пример за посредник е тип интерфейс на съветника. Да предположим, че имате голям процес на регистрация за система, по която сте работили. Често, когато се изисква много информация от потребител, е добра практика да се раздели на няколко стъпки.

По този начин кодът ще бъде много по-чист (по-лесен за поддръжка) и потребителят няма да бъде претоварен от количеството информация, което се иска само за да завърши регистрацията. Посредникът е обект, който ще обработва стъпките за регистрация, като взема предвид различни възможни работни потоци, които могат да се случат поради факта, че всеки потребител потенциално може да има уникален процес на регистрация.

Очевидната полза от този модел на проектиране е подобрената комуникация между различните части на системата, която сега всички комуникират чрез посредника и по-чиста кодова база.

Недостатък би бил, че сега сме въвели една точка на отказ в нашата система, което означава, че ако медиаторът ни се провали, цялата система може да спре да работи.

Образец на прототип

Както вече споменахме в цялата статия, JavaScript не поддържа класове в естествената си форма. Наследяването между обекти се реализира с помощта на програмиране, базирано на прототип.

Позволява ни да създаваме обекти, които могат да служат като прототип на други обекти, които се създават. Прототипният обект се използва като план за всеки обект, който конструкторът създава.

Тъй като вече говорихме за това в предишните раздели, нека покажем прост пример за това как може да се използва този модел.

var personPrototype = { sayHi: function() { console.log('Hello, my name is ' + this.name + ', and I am ' + this.age); }, sayBye: function() { console.log('Bye Bye!'); } }; function Person(name, age) { name = name || 'John Doe'; age = age || 26; function constructorFunction(name, age) { this.name = name; this.age = age; }; constructorFunction.prototype = personPrototype; var instance = new constructorFunction(name, age); return instance; } var person1 = Person(); var person2 = Person('Bob', 38); // prints out Hello, my name is John Doe, and I am 26 person1.sayHi(); // prints out Hello, my name is Bob, and I am 38 person2.sayHi();

Обърнете внимание как наследяването на прототип също увеличава производителността, тъй като и двата обекта съдържат препратка към функциите, които са внедрени в самия прототип, вместо във всеки от обектите.

Команден модел

Командният модел е полезен в случаите, когато искаме да отделим обекти, изпълняващи командите, от обекти, издаващи командите. Например, представете си сценарий, при който нашето приложение използва голям брой повиквания на API услуги. След това, да кажем, че API услугите се променят. Ще трябва да модифицираме кода, където и да се извикат променените API.

какво е c и c++

Това би било чудесно място за внедряване на слой за абстракция, който да отдели обектите, извикващи API услуга, от обектите, които им казват кога да се обадите на услугата API. По този начин избягваме модификация на всички места, където имаме нужда да се обадим на услугата, а по-скоро трябва да сменим само обектите, които извършват самото обаждане, което е само на едно място.

Както при всеки друг модел, трябва да знаем кога точно има реална нужда от такъв модел. Трябва да сме наясно с компромиса, който правим, тъй като добавяме допълнителен слой за абстракция над API извикванията, което ще намали производителността, но потенциално ще спести много време, когато трябва да модифицираме обекти, изпълняващи командите.

// the object which knows how to execute the command var invoker = { add: function(x, y) { return x + y; }, subtract: function(x, y) { return x - y; } } // the object which is used as an abstraction layer when // executing commands; it represents an interface // toward the invoker object var manager = { execute: function(name, args) { if (name in invoker) { return invoker[name].apply(invoker, [].slice.call(arguments, 1)); } return false; } } // prints 8 console.log(manager.execute('add', 3, 5)); // prints 2 console.log(manager.execute('subtract', 5, 3));

Фасаден модел

Моделът на фасадата се използва, когато искаме да създадем абстракционен слой между това, което се показва публично, и това, което се изпълнява зад завесата. Използва се, когато се желае по-лесен или по-опростен интерфейс към подлежащ обект.

Чудесен пример за този модел биха били селектори от DOM библиотеки за манипулация като jQuery, Dojo или D3. Може би сте забелязали, използвайки тези библиотеки, че те имат много мощни селекторни функции; можете да пишете в сложни заявки като:

jQuery('.parent .child div.span')

Той опростява много функциите за подбор и въпреки че изглежда просто на повърхността, под капака е внедрена цяла сложна логика, за да работи това.

Също така трябва да сме наясно с компромиса между производителността и простотата. Желателно е да се избягва допълнителна сложност, ако не е достатъчно полезна. В случая с гореспоменатите библиотеки компромисът си заслужаваше, тъй като всички те са много успешни библиотеки.

Следващи стъпки

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

За да бъде статията сравнително кратка, няма да показваме повече примери. За тези, които се интересуват, голямо вдъхновение за тази статия дойде от книгата „Бандата на четирима“ Модели на проектиране: Елементи на многократно използвания обектно-ориентиран софтуер и Addy Osmani’s Изучаване на модели за дизайн на JavaScript . Горещо препоръчвам и двете книги.

Свързани: Като разработчик на JS, това е, което ме поддържа през нощта / осмисляне на объркването в клас ES6

Разбиране на основите

Какви са основните характеристики на JavaScript?

JavaScript е асинхронен, поддържа първокласни функции и е базиран на прототип.

Какво представляват дизайнерските модели и как те поддържат софтуерни архитекти?

Дизайнерските модели са решения за многократна употреба на често срещани проблеми в софтуерния дизайн. Те са доказани решения, лесно за многократна употреба и изразителни. Те намаляват размера на вашата кодова база, предотвратяват бъдещото рефакториране и правят кода ви по-лесен за разбиране от други разработчици.

Какво е JavaScript и как той оживя?

JavaScript е клиентски скриптов език за браузъри, създаден за първи път от Brendan Eich за Netscape Navigator от сегашната Mozilla.

Какво е ECMAScript?

ECMAScript е стандартизирана спецификация на скриптовия език, която всички съвременни браузъри се опитват да поддържат. Има множество внедрения на ECMAScript, най-популярният от които е JavaScript.

Какво е прото-модел?

Протомоделът е бъдещ модел, ако премине определен период от тестване от различни разработчици и сценарии, когато шаблонът се окаже полезен и дава коректни резултати.

Какво е анти-модел?

Ако моделът на дизайна представлява добра практика, тогава анти-шаблонът представлява лоша практика. Пример за анти-шаблон би бил модифицирането на прототипа на обектния клас. Промени в прототипа на обекта могат да се видят във всички обекти, които наследяват от този прототип (т.е. почти всички обекти в JavaScript).

Как да категоризираме дизайнерските модели?

Моделите на дизайна могат да бъдат творчески, структурни, поведенчески, паралелни или архитектурни.

Кои са няколко примера за дизайнерски модели?

Някои примери, разгледани в статията по-горе, включват модела на конструктора, модела на модула, модела на разкриващия модул, модела на единичен модел, модела на наблюдателя, модела на медиатора, шаблона на прототипа, шаблона на командата и фасадата.

Cheat Sheet за управление на проекти

Пъргав

Cheat Sheet за управление на проекти
ApeeScape си партнира с Treehoppr, за да улесни ангажирането на служителите в отдалечени екипи

ApeeScape си партнира с Treehoppr, за да улесни ангажирането на служителите в отдалечени екипи

Други

Популярни Публикации
Fastlane: Автоматизация на iOS в круиз контрол
Fastlane: Автоматизация на iOS в круиз контрол
Ефективни комуникационни стратегии за дизайнери
Ефективни комуникационни стратегии за дизайнери
Изследване на контролирани алгоритми за машинно обучение
Изследване на контролирани алгоритми за машинно обучение
10-те UX доставки, които топ дизайнерите използват
10-те UX доставки, които топ дизайнерите използват
Тест за използваемост за преобразуване: Спрете да следвате тенденциите. Започнете с данните
Тест за използваемост за преобразуване: Спрете да следвате тенденциите. Започнете с данните
 
Кеширане през пролетта с EhCache анотации
Кеширане през пролетта с EhCache анотации
Разбиване на топ 5 мита за отдалечените работници
Разбиване на топ 5 мита за отдалечените работници
Неформално въведение в DOCX
Неформално въведение в DOCX
Проектиране за интерактивна среда и интелигентни пространства
Проектиране за интерактивна среда и интелигентни пространства
Най-добрите UX инструменти (с инфографика)
Най-добрите UX инструменти (с инфографика)
Популярни Публикации
  • което прави една компания по време на етапа преди IPO
  • всяка фаза на ____ произвежда някакъв вид документация, която да премине към следващата фаза.
  • пружинен mvc с пружинен ботуш
  • какви са принципите на графичния дизайн
  • научете как да кодирате в c
Категории
  • Дизайн На Марката
  • Тенденции
  • Инструменти И Уроци
  • Технология
  • © 2022 | Всички Права Запазени

    portaldacalheta.pt