Общие шаблоны проектирования Javascript

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

"Practical Common LispАвтор Питер Сейбел однажды сказал, что если вам нужен узор, значит, что-то не так. Проблема, о которой он говорил, была общим решением, которое нужно было искать и обобщать из-за присущих языку недостатков.

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

Чернокнижники и теневые жрецы могут легко стать отличной поддержкой, в то время как вражеское заклинание, летящее по карте с Мейконом на спине, может быть немного неудобным. Переходя на программу, может потребоваться много усилий для реализации декораторов на статических языках, а js может в любой момент набрасывать методы на объекты, так что паттерн декоратор становится безвкусным в js.

Существует относительно немного книг по шаблонам проектирования Javascript.Pro javaScript Design Patterns— относительно классическая книга, но примеры в ней довольно многословны, поэтому я подытожу свое понимание на основе кода, который я написал в своей работе. Пожалуйста, не стесняйтесь исправлять меня, если я неправильно понимаю.

Одноэлементный шаблон

Определение шаблона singleton заключается в создании единственного экземпляра класса, но сам js является «бесклассовым» языком. Многие статьи о шаблонах проектирования js используют {} как синглтон, что едва ли имеет смысл. Поскольку в js есть много способов генерировать объекты, давайте рассмотрим еще один синглтон, который имеет больше смысла.

Существует такое распространенное требование, что при нажатии кнопки на странице должен появляться слой маски. Например, когда web.qq.com нажимает для входа.

Код для создания маски серого фона хорошо написан.

JavaScript

var createMask = function(){
 
   return document,body.appendChild(  document.createElement(div)  );
 
}

JavaScript

$( 'button' ).click( function(){
 
   Var mask  = createMask();
 
   mask.show();
 
})

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

Давайте посмотрим на второе решение. Создайте этот div в начале страницы, а затем используйте переменную для ссылки на него.

JavaScript

var mask = document.body.appendChild( document.createElement( ''div' ) );
 
$( ''button' ).click( function(){
 
   mask.show();
 
} )

Таким образом, на странице будет создан только один div слоя маски, но последует другая проблема, может быть, этот слой маски нам никогда не понадобится, тогда еще один div тратится впустую, а любая операция над узлом dom должна быть очень скупой.

Если вы можете использовать переменную, чтобы определить, был ли создан div?

JavaScript

var mask;
 
var createMask = function(){
 
if ( mask ) return mask;
 
else{
 
mask = document,body.appendChild(  document.createElement(div)  );
 
return mask;
 
}
 
}

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

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

JavaScript

var createMask = function(){
  var mask;
  return function(){
       return mask || ( mask = document.body.appendChild( document.createElement('div') ) )
  }
}()

Простое замыкание используется для обертывания маски переменной, по крайней мере, для функции createMask она закрыта.

Когда вы видите это, вы можете подумать, что шаблон singleton слишком прост. Действительно, некоторые шаблоны проектирования очень просты. Даже если вы никогда не обращали внимания на понятие шаблонов проектирования, вы неосознанно будете использовать некоторые шаблоны проектирования в своем обычном коде. Так же, как много лет назад, когда я понял, что такое повозка старика, я тоже подумал, что Нима оказалась повозкой старика.

23 шаблона проектирования в GOF также являются шаблонами, которые существовали и неоднократно использовались при разработке программного обеспечения в течение длительного времени.Если программист четко не осознает, что он использовал некоторые шаблоны, он может пропустить более подходящий дизайн в следующий раз (этот абзац Слова взяты из «Мира программ Мацумото Юкихиро»).

Возвращаясь к теме, предыдущий синглтон все еще имеет недостатки.Его можно использовать только для создания маскирующего слоя.Что делать, если мне нужно написать функцию для создания уникального объекта xhr?Можете ли вы найти общую обёртку синглтона.

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

JavaScript

var singleton = function( fn ){
    var result;
    return function(){
        return result || ( result = fn .apply( this, arguments ) );
    }
}
 
var createMask = singleton( function(){
 
return document.body.appendChild( document.createElement('div') );
 
 })

Используйте переменную для сохранения первого возвращаемого значения. Если ей было присвоено значение, переменная будет возвращена первой при последующих вызовах. Код, который фактически создает слой маски, передается в обработчик синглтона через функцию обратного вызова. Да. Это Метод на самом деле называется режимом моста, о режиме моста поговорим чуть позже.

Однако функция singleton не идеальна, ей по-прежнему требуется переменный результат для хранения ссылки на div.К сожалению, функциональной природы js недостаточно, чтобы полностью исключить объявления и операторы.

Две простые фабричные выкройки

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

Шаблон Simple Factory также очень полезен при создании объектов ajax.

До того, как я написал библиотеку, которая обрабатывает асинхронную вложенность ajax, адрес находился по адресуGitHub.com/alloy team/D…

Эта библиотека предоставляет несколько методов ajax-запросов, в том числе xhr object get, post, а также междоменные jsonp и iframe, для удобства эти методы абстрагированы в один и тот же интерфейс.

JavaScript

var request1 = Request('cgi.xx.com/xxx' , ''get' );
 
request1.start();
 
request1.done( fn );
 
var request2 = Request('cgi.xx.com/xxx' , ''jsonp' );
 
request2.start();
 
request2.done( fn );

Запрос на самом деле является фабричным методом в отношении того, генерировать ли экземпляр xhr или экземпляр jsonp, это определяется более поздним кодом.

По сути, в js так называемый конструктор — это тоже простая фабрика. Только что утвердили новый предмет одежды. Давайте снимем этот предмет одежды и заглянем внутрь.

С помощью этого кода в firefox, chrome и других браузерах можно отлично имитировать новые.

JavaScript

       function A( name ){
 
              this.name = name;
 
       }
 
       function ObjectFactory(){
 
              var obj = {},
 
                     Constructor = Array.prototype.shift.call( arguments );
 
obj.__proto__ =  typeof Constructor .prototype === 'number'  ? Object.prototype
 
:  Constructor .prototype;
 
              var ret = Constructor.apply( obj, arguments );
 
              return typeof ret === 'object' ? ret : obj;
 
       }
 
       var a = ObjectFactory( A, 'svenzeng' );
 
       alert ( a.name );  //svenzeng

Этот код исходит из описания new и конструктора в es5.Видно, что сам так называемый new — это просто процесс копирования и перезаписи объекта, а то, что будет сгенерировано, определяется параметрами, переданными при вызове ObjectFactory .

Режим трех наблюдателей

Шаблон наблюдателя (также известный как шаблон издателя-подписчика) должен быть одним из наиболее часто используемых шаблонов. Он широко используется во многих языках. Включая события dom, с которыми мы обычно связываемся. Это также наблюдение, реализованное между js и модератором dom. .

JavaScript

div.onclick  =  function click (){
 
   alert ( ''click' )
 
}

Просто подпишитесь на событие щелчка div.При нажатии на div будет запущена функция click.

Так что же такое паттерн наблюдателя? Давайте посмотрим на паттерн наблюдателя в жизни.

В Голливуде есть известная поговорка: «Не звони мне, я позвоню тебе». Это предложение объясняет все тонкости модели наблюдателя. где «я» — издатель, а «вы» — подписчик.

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

Шаблон наблюдателя вполне может обеспечить развязку между двумя модулями. Предположим, я разрабатываю в команде html5-игру, когда игра запускается, нужно загрузить некоторые графические материалы. После того, как эти изображения загружены, выполняется игровая логика.Предположим, это проект, требующий сотрудничества нескольких человек.Я завершил модули Gamer и Map, а мой коллега A написал загрузчик изображений loadImage.

Код для loadImage выглядит следующим образом

JavaScript

loadImage(  imgAry,  function(){
 
Map.init();
 
Gamer.init();
 
} )

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

JavaScript

loadImage(  imgAry,  function(){
 
Map.init();
 
Gamer.init();
 
Sount.init();
 
} )

Но мой коллега А который написал этот модуль уехал в заграничную командировку.Поэтому я ему позвонил,здравствуйте.Где ваша функция loadImage,можно ли ее изменить,и будут ли побочные эффекты после ее изменения?Как вы думаете всякие непростые Что, если бы мы могли написать:

JavaScript

loadImage.listen( ''ready', function(){
 
    Map.init();
 
})
 
loadImage.listen( ''ready', function(){
 
   Gamer.init();
 
})
 
loadImage.listen( ''ready', function(){
 
   Sount.init();
 
})

После завершения loadImage ему совершенно все равно, что будет дальше, потому что его работа уже сделана, дальше он просто подает сигнал.

loadImage.trigger («готово»);

Затем объект, который прослушивает событие «готово» loadImage, будет уведомлен. Как и в предыдущем примере с интервью. Интервьюеру все равно, куда пойдут обедать интервьюируемые после получения результатов интервью. Он отвечает только за то, чтобы поставить резюме интервьюера Соберите их вместе.Когда будут результаты собеседования, они будут уведомлены один за другим по номеру телефона, указанному в резюме.

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

JavaScript

Events = function() {
 
           var listen, log, obj, one, remove, trigger, __this;
 
           obj = {};
 
           __this = this;
 
           listen = function( key, eventfn ) {  //把简历扔盒子, key就是联系方式.
 
             var stack, _ref;  //stack是盒子
 
             stack = ( _ref = obj[key] ) != null ? _ref : obj[ key ] = [];
 
             return stack.push( eventfn );
 
           };
 
           one = function( key, eventfn ) {
 
             remove( key );
 
             return listen( key, eventfn );
 
           };
 
           remove = function( key ) {
 
             var _ref;
 
             return ( _ref = obj[key] ) != null ? _ref.length = 0 : void 0;
 
           };
 
           trigger = function() {  //面试官打电话通知面试者
 
             var fn, stack, _i, _len, _ref, key;
 
             key = Array.prototype.shift.call( arguments );
 
             stack = ( _ref = obj[ key ] ) != null ? _ref : obj[ key ] = [];
 
             for ( _i = 0, _len = stack.length; _i < _len; _i++ ) {
 
               fn = stack[ _i ];
 
               if ( fn.apply( __this,  arguments ) === false) {
 
                 return false;
 
               }
 
             }
 
             return {
 
                listen: listen,
 
                one: one,
 
                remove: remove,
 
                trigger: trigger
 
             }
 
           }

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

//подписчик

JavaScript

var adultTv = Event();
 
adultTv .listen(  ''play',  function( data ){
 
   alert ( "今天是谁的电影" + data.name );
 
});
 
//发布者
 
adultTv .trigger(  ''play',  { 'name': '麻生希' }  )

Четыре режима адаптера

В прошлом году, когда я разрабатывал dev.qplus.com, там был js-файл, в котором хранился идентификатор категории приложения. Структура идентификатора категории изначально была задумана как громоздкая. Поэтому я решил провести ее рефакторинг. Я определил ее в форма дерева json, вероятно, так:

JavaScript

var category = {
 
   music: {
 
id: 1,
 
children: [ , , , , ]
 
   }
 
}

На dev.qplus.com есть около 4 или 5 страниц, которые называют этот объект категории.Перед праздником Весны я взял недельный отпуск.После китайского Нового года я обнаружил, что в почтовом ящике было электронное письмо, и одноклассники тот, кто разработал базу данных, также повторил о категории..js. Я создал копию, и несколько других проектов использовали эту категорию.js. Я был ошеломлен, когда принял ее. Она полностью отличалась от структуры данных, которую я установил раньше.

Конечно, это негативный пример общения.Но следующий момент в том, что я использовал category.js, который я установил ранее в N файлах.И я попал в какую-то сложную связанную логику.Как изменить мой предыдущий код Что.Это однозначно не хочется все переписывать.Так что теперь переходник пригодится.

Просто используйте функцию для преобразования категории моего коллеги в ту, которую я определил ранее.

JavaScript

my.category = adapterCategory ( afu.category );

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

Поэтому режим адаптера часто используется для адаптации двух интерфейсов в программе.Например, вы используете пользовательскую js-библиотеку.Есть метод $id() для получения узла по id.В один прекрасный день вы думаете, что в jquery $ реализовать круче, но вы не хотите, чтобы ваши инженеры изучали новые библиотеки и синтаксис — этот адаптер сделает это за вас.

JavaScript

$id = function( id ){
 
  return jQuery( '#' + id )[0];
 
}

Режим пяти агентств

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

Например, я преследовал ММ и хотел послать ей букет цветов, но из-за стеснительности доверил отправить ей цветы хорошей подруге ММ.

Этот пример не очень хорош, по крайней мере, мы не видим большого использования прокси-режима, потому что лучший способ преследовать ММ — отправить BMW.

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

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

Другой подробный пример, до того, как я написал игру Street Fighter, адрес находится по адресукоманда сплава.GitHub.com/street fight…

В игре Лонгу необходимо принять события клавиатуры, чтобы выполнить соответствующие действия.

Поэтому я написал класс keyManage, который прослушивает изменения в keyManage в основном потоке игры.

JavaScript

var keyMgr = keyManage();
 
keyMgr.listen( ''change', function( keyCode ){
 
   console.log( keyCode );
 
});

На картинке Лонг выпускает Shenglongquan. Операция Shenglongquan вперед и вперед + удар. Но этот класс keyManage будет запускать ранее отслеживаемую функцию изменения всякий раз, когда происходит событие клавиатуры. Это означает, что можно получить только передний, задний и передний навсегда. , пробивает такие отдельные события нажатия клавиш и не может получить комбинацию клавиш.

Что ж, я решил переписать свой класс keyManage, чтобы он также поддерживал передачу комбинаций клавиш. Но если я буду писать html5-версию Double Dragon в будущем, это означает, что мне придется каждый раз переписывать keyManage. Я всегда чувствую, что такая функция должна быть Его можно абстрагировать в метод более низкого уровня, чтобы его можно было использовать в любой игре.

Таким образом, последний keyManage отвечает только за сопоставление событий клавиатуры, а действия, полученные Long, обрабатываются через прокси-объект.

JavaScript

var keyMgr = keyManage();
 
keyMgr.listen( ''change', proxy( function( keyCode ){
 
   console.log( keyCode );  //前下前+拳
 
)} );

Что касается того, как реализовать это в прокси, то играть можно совершенно бесплатно.

Другой пример: при вызове ajax-запроса, будь то различные библиотеки с открытым исходным кодом или Ajax-класс, написанный вами, для объекта xhr будет установлен прокси.Мы не можем часто манипулировать объектом xhr для отправки запросы, но это должно быть так.

JavaScript

var request = Ajax.get( 'cgi.xx.com/xxx' );
 
request.send();
 
request.done(function(){
 
});

Режим шести мостов

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

JavaScript

var singleton = function( fn ){
    var result;
    return function(){
        return result || ( result = fn .apply( this, arguments ) );
    }
}
 
var createMask = singleton( function(){
 
return document.body.appendChild( document.createElement('div') );
 
 })

singleton — это абстрактная часть, а createMask — часть реализации. Они могут изменяться независимо, не влияя друг на друга. Если вам нужно написать синглтон createScript, это совсем не сложно.

JavaScript

var createScript = singleton( function(){
 
return document.body.appendChild( document.createElement('script') );
 
 })

Другим распространенным примером является реализация функции forEach для перебора массива.

JavaScript

forEach = function( ary, fn ){
  for ( var i = 0, l = ary.length; i < l; i++ ){
    var c = ary[ i ];
    if ( fn.call( c, i, c ) === false ){
      return false;
    }
   }
}

Видно, что функция forEach не заботится о конкретной реализации в fn, логика в fn не будет затронута переписыванием функции forEach.

JavaScript

forEach( [1,2,3], function( i, n ){
 
 alert ( n*2 )
 
} )
 
forEach( [1,2,3], function( i, n ){
 
  alert ( n*3 )
 
} )

Семь режимов внешнего вида

Фасадный режим (фасадный режим) — относительно простой и повсеместно распространенный режим. Фасадный режим предоставляет высокоуровневый интерфейс, упрощающий вызов клиентов или подсистем.
Представлен простым фрагментом кода

JavaScript

var getName = function(){
  return ''svenzeng"
}
var getSex = function(){
   return 'man'
}

Если вам нужно вызвать функции getName и getSex отдельно, то это можно вызвать через высокоуровневый интерфейс getUserInfo.

JavaScript

var getUserInfo = function(){
  var info = a() + b();
  return info;
}

Вы можете спросить, почему вы не написали код для getName и getSex вместе в первую очередь, как это

JavaScript

var getNameAndSex = function(){
  return 'svenzeng" + "man";
}

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

Наконец, напишите пример шаблона внешнего вида, который мы все использовали.

JavaScript

var stopEvent = function( e ){   //同时阻止事件默认行为和冒泡
  e.stopPropagation();
  e.preventDefault();
}

Восемь моделей посетителей

Официальное определение GOF: шаблон посетителя — это операция, которая воздействует на каждый элемент в структуре объекта. Это позволяет определять новые операции, которые воздействуют на элементы без изменения их классов. Когда мы используем некоторые операции для обработки разных объектов, мы часто выбираем разные методы и процедуры обработки в зависимости от разных объектов. В реальном кодовом процессе мы можем обнаружить, что если все операции рассредоточены по различным объектам, всю систему станет трудно поддерживать и модифицировать. А добавление новых операций обычно требует перекомпиляции всех классов. Следовательно, чтобы решить эту проблему, мы можем выделить соответствующие операции в каждом классе и упаковать их в независимый объект, который мы называем посетителем. Используя посетителя, при выполнении некоторых операций над запрашиваемым элементом нужно только передать этот объект в качестве параметра текущему посетителю, и тогда посетитель будет выполнять сопутствующие операции на основе конкретной информации о посетителе.

По статистике только 5% людей в абзаце выше увидят последнее предложение. Таким образом, с точки зрения непрофессионала, шаблон посетителя сначала абстрагирует некоторые многократно используемые поведения в функцию (объект), которую мы называем посетителем. Если другие объекты хотят вызвать эту функцию, вам нужно только передать эти объекты в качестве параметров этой функции.В js мы часто передаем этот объект в функцию посетителя, вызывая или применяя.
Шаблон посетителя также известен как самый сложный для понимания из 23 шаблонов проектирования, обобщенных GOF. Однако в значительной степени это связано с тем, что "Шаблоны проектирования" написаны на основе C++ и Smalltalk. В строго типизированных языках требуется многократная перегрузка для достижения соответствия интерфейса посетителей.

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

Давайте сначала разберемся, что такое утиный тип, и расскажем историю:
Давным-давно жил-был император, которому нравилось слушать карканье уток, поэтому он призвал своих министров составить хор из тысячи уток. Министр поймал всех уток во всей стране, но в итоге все же одной не хватило. Однажды, наконец, явилась курица, которая вызвалась, и курица сказала, что она тоже может квакать, ну, в сеттинге этой истории она квакает. Позже стало очевидно, что курица смешалась с хором утки. — Император просто хочет услышать карканье, ему все равно, утка ты или курица.

Это концепция утиной типизации.В языке со слабой типизацией, таком как js, многие методы не выполняют определение типа объектов, а заботятся только о том, что эти объекты могут делать.
Методы прототипов конструкторов Array и String специально предназначены для посетителей. Эти методы не выполняют никакой проверки типа данных this. Вот почему аргументы могут притворяться массивом и вызывать метод push.

Посмотрите на код Array.prototype.push в движке v8:

JavaScript

function ArrayPush() {  var n = TO_UINT32( this.length );
  var m = %_ArgumentsLength();    for (var i = 0; i < m; i++) {    this[i+n] = %_Arguments(i);    //属性拷贝  }  this.length = n + m;             //修正length  return this.length;}

Как видите, метод ArrayPush не накладывает никаких явных ограничений на тип this, поэтому теоретически в посетитель ArrayPush можно передать любой объект.

Однако при выполнении кода все же существуют некоторые неявные ограничения, требования легко увидеть в приведенном выше примере:
1. В этом объекте можно хранить свойства. //Противоположный пример: данные типа значения
2. Свойство длины этого объекта доступно для записи. //Противоположный пример: объект функции, функция имеет свойство длины только для чтения, которое представляет количество формальных параметров.

Если эти два правила не соблюдены, код сообщит об ошибке во время выполнения, то есть Array.prototype.push.call( 1, 'first' ) и Array.prototoype.push.call( function(){}, ' first' ) не дает желаемого эффекта.

С помощью посетителя сделаем кое-что интересное: добавим к объекту метод push.

JavaScript

var Visitor = {}
Visitor .push  =  function(){
    return Array.prototype.push.apply( this, arguments );
}
var obj = {};
obj.push = Visitor .push;
obj.push( '"first" );
alert ( obj[0] )  //"first"
alert ( obj.length );  //1

Девять режимов стратегии

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

JavaScript

$( div ).animate( {"left: 200px"}, 1000, 'linear' );  //匀速运动
$( div ).animate( {"left: 200px"}, 1000, 'cubic' );  //三次方的缓动

Эти две строки кода предназначены для перемещения элемента div вправо на 200 пикселей за 1000 мс. Линейный (равномерная скорость) и кубический (кубическое смягчение) представляют собой инкапсуляцию режима стратегии.
Другой пример, в первой половине года я писал dev.qplus.com, на многих страницах будет форма с мгновенной валидацией, у каждого члена формы будут свои правила валидации.

Например, в поле имени необходимо убедиться, что оно не содержит пустых, конфиденциальных слов и слишком длинных символов. Конечно, для ее решения можно написать 3 if else, но масштабируемость и ремонтопригодность написания кода таким образом можно себе представить. Чем больше элементов в форме, тем больше случаев нужно проверить, и нет ничего невозможного в том, чтобы написать сотни if elses.
Таким образом, лучший подход — инкапсулировать каждое правило проверки отдельно с помощью шаблона стратегии. Нужно только указать имя этой стратегии, когда требуется тип аутентификации. так:

JavaScript

nameInput.addValidata({
   notNull: true,
   dirtyWords: true,
   maxLength: 30
})
而notNull,maxLength等方法只需要统一的返回true或者false,来表示是否通过了验证。
validataList = {
  notNull: function( value ){
     return value !== '';
  },
  maxLength: function( value, maxLen ){
     return value.length() > maxLen;
  }
}

Как видите, различные правила проверки можно легко изменить и заменить друг другом. Если однажды продакт-менеджер предложил изменить ограничение на количество символов до 60 символов. Это занимает всего 0,5 секунды, чтобы завершить работу.

Десять паттернов шаблонного метода

Метод шаблона состоит в том, чтобы предварительно определить набор алгоритмов, сначала абстрагировать инвариантную часть алгоритма для родительского класса, а затем отложить некоторые другие переменные шаги для реализации подклассом. Звучит немного похоже на фабричный шаблон (не на простой фабричный шаблон, упомянутый ранее).
Самое большое отличие состоит в том, что целью шаблона фабрики является окончательное получение объекта в соответствии с реализацией подкласса Шаблон метода шаблона фокусируется на управлении подклассом родительским классом.

Как описано в GOF, шаблонный подход приводит к обратной структуре управления, которую иногда называют «голливудским законом», т.е. «не находите нас, мы найдем вас». Это относится к операции родительского класса, вызывающего дочерний класс, а не наоборот.
Очень распространен сценарий в проекте компании, где архитекторы часто настраивают архитектуру и объявляют абстрактные методы. Следующие программисты переписывают эти абстрактные методы отдельно.

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

JavaScript

var Life = function(){
}
Life.prototype.init = function(){
   this.DNA复制();
   this.出生();
   this.成长();
   this.衰老();
   this.死亡();
}
this.prototype.DNA复制 = function(){
  &*$%&^%^&(&(&(&&(^^(*)  //看不懂的代码
}
Life.prototype.出生 = function(){
}
Life.prototype.成长 = function(){
}
Life.prototype.衰老 = function(){
}
Life.prototype.死亡 = function(){
}

Среди них репликация ДНК является инвариантной частью предопределенного алгоритма.Все подклассы не могут его переопределить.При необходимости мы можем записать его как защищенный тип.
А остальные функции сначала будут определяться как пустая функция (хук) в родительском классе, а затем переопределяться в подклассе, это так называемый переменный шаг в шаблонном методе.
Предположим, что существует подкласс класса Mammal, расширяющий класс Life.

JavaScript

var Mammal = function(){
}
Mammal.prototype = Life.prototype;   //继承Life

Затем перепишите две крючковые функции рождения и старения.

JavaScript

Mammal.prototope.出生 = function(){
  '胎生()
}
Mammal.prototype.成长 = function(){
  //再留给子类去实现
}
Mammal.prototope.衰老 = function(){
  自由基的过氧化反应()
}
Life.prototype.死亡 = function(){
 //再留给子类去实现
}
//再实现一个Dog类
var = Dog = function(){
}
//Dog继承自哺乳动物.
Dog.prototype = Mammal.prototype;
var dog = new Dog();
dog.init();

До сих пор жизнь щенка будет последовательно проходить через процессы репликации ДНК, рождения, роста, старения и смерти. Эти шаги решаются задолго до его рождения. К счастью, Бог не устроил все детали ее жизни. Это также может быть другой щенок, если переписать функцию роста.

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

JavaScript

var gameCenter = function(){
}
gameCenter.ptototype.init = function(){
  this.login();
  this.gameStart();
  this.end();
}
gameCenter.prototype.login= function(){
   //do something
}
gameCenter.prototype.gameStart= function(){
   //空函数, 留给子类去重写
}
gameCenter.prototype.end= function(){
  alert ( "欢迎下次再来玩" );
}

Далее создайте новую игру Дудижу, просто наследуйте GameCenter и перепишите ее функцию gameStart.

JavaScript

var 斗地主 = function(){
}
斗地主.prototype = gameCenter.prototype;  //继承
斗地主.prototype.gameStart = function(){
  //do something
}
(new 斗地主).init();

Итак, начинается новая игра.

Одиннадцатая промежуточная модель

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

Например, в целях безопасности покупатели и продавцы оружия находят доверенного посредника для проведения сделок. Покупатель A отдает деньги брокеру B, а затем получает оружие от брокера, а продавец C продает оружие брокеру и получает от брокера деньги обратно. После транзакции А даже не знает, является ли С обезьяной или мамонтом. Из-за наличия посредников А не обязательно должен покупать оружие С, это могут быть и D, E, F.

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

Шаблон посредника немного похож на шаблон прокси. Все они являются сторонними объектами для связи между двумя объектами. Конкретную разницу можно увидеть на рисунке ниже.

Прокси-режим:

модель посредника

В режиме прокси A должен знать все о B, а в режиме посредника A, B и C не заботятся о реализации E, F и G. А режим посредника может соединять любые объекты.

Переключиться обратно на mvc в программном мире, будь то Action of struts в j2ee, или Controler в backbone.js и spin.js в js, оба играют роль посредника.
Возьмем, к примеру, магистраль. Данные в режиме не определяются тем, какие представления в конечном итоге используются. Данные, необходимые для представления, также могут поступать из любого режима. Все отношения связывания определяются в контроллере. отношения «ко многим» на два относительно простых отношения «один ко многим».

Простой пример кода:

JavaScript

var mode1 = Mode.create(),  mode2 = Mode.create();
var view1 = View.create(),   view2 = View.create();
var controler1 = Controler.create( mode1, view1, function(){
  view1.el.find( ''div' ).bind( ''click', function(){
    this.innerHTML = mode1.find( 'data' );
  } )
})
var controler2 = Controler.create( mode2 view2, function(){
  view1.el.find( ''div' ).bind( ''click', function(){
    this.innerHTML = mode2.find( 'data' );
  } )
})

Двенадцать шаблонов итераторов

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

JavaScript

forEach = function( ary, fn ){  for ( var i = 0, l = ary.length; i < l; i++ ){    var c = ary[ i ];    if ( fn.call( c, i , c ) === false ){      return false;    }   }}
forEach( [ 1, 2, 3 ], function( i, n ){
  alert ( i );
})

Итератор объектов:

JavaScript

forEach = function( obj, fn ){  for ( var i in obj ){    var c = obj[ i ];    if ( fn.call( c, i, c ) === false ){      return false;    }   }}
forEach( {"a": 1,"b": 2}, function( i, n ){
  alert ( i );
})

Тринадцать комбинированных режимов

Шаблон композиции, также известный как шаблон часть-целое, объединяет все объекты в древовидную структуру. Это позволяет пользователям выполнять одни и те же операции со всеми членами, только работая с интерфейсом верхнего уровня.
Хорошим примером является объект jquery.Все знают, что объект jquery на самом деле представляет собой набор объектов. Например, на такой HTML-странице

JavaScript

<div>
   <span></span>
   <span></span>
</div>

Мы хотим отменить события, привязанные ко всем узлам, нам нужно написать так

JavaScript

var allNodes = document.getElementsByTagName("*");
var len = allNodes.length;
while( len-- ){
  allNodes.unbind("*");
}

Но так как используется jquery, я точно больше такими вещами заниматься не буду. Нам просто нужно $('body').unbind('*');
Когда каждый элемент реализует интерфейс развязки, просто вызовите развязку объекта верхнего уровня $('body' ), и метод развязки всех объединенных элементов может быть автоматически итерирован и вызван.
Другой конкретный пример — форма мгновенной проверки сайта dev.qplus.com.

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

JavaScript

if ( nameField.validata() && idCard.validata() && email.validata() && phone.validata() ){
   alert ( "验证OK" );
}

Вроде бы проблему условного стекирования веток с режимом просмотра мы с трудом решим, но реальная проблема в том, что мы не можем гарантировать количество полей в форме, может завтра продакт-менеджер позволит удалить одно или добавить два , Тогда это Метод обслуживания явно неприемлем.
Лучшей реализацией было бы иметь функцию form.validata, которая отвечает за диспетчеризацию фактических операций проверки данных для каждого составного объекта.
Функция form.validata будет проходить все поля, которые необходимо проверить по очереди. Если одно поле не проходит проверку, form.validata вернет false. Псевдокод выглядит следующим образом.

JavaScript

form.validata = function(){
  forEach( fields, function( index, field ){
    if ( field.validata() === false  ){
       return false;
    }
  })
  return true;
}

14 Режим заметок

Мемо-режим часто используется для кеширования данных в js, например, элемент управления пейджингом может хранить данные страницы с сервера и хранить их в кеше. Когда вы вернетесь на эту страницу позже, вы сможете использовать данные в кеше напрямую, без повторного запроса к серверу.
Реализация относительно проста, псевдокод:

JavaScript

var Page = function(){
   var page = 1,
      cache = {},
      data;
   return function( page ){
      if ( cache[ page ] ){
               data =  cache[ page ];
               render( data );
      }else{
               Ajax.send( 'cgi.xx.com/xxx', function( data ){
                   cache[ page ] = data;
                   render( data );
               })
      }
    }
}()

15. Модель цепочки ответственности
Модель цепочки ответственности заключается в том, что объект A инициирует запрос к другому объекту B. Если B не обрабатывает его, он может перенаправить запрос C, а если C не обрабатывает его, он может переслать запрос D. Пока объект не захочет обработать запрос.

Например, клиент просит начальника написать программу на php. Начальник не должен писать, а потом начальник передал начальнику отдела. Менеджер отдела не хотел писать, поэтому он передал его руководителю проекта. Руководитель проекта не может его написать, и он передается программисту. Наконец, это делает фермер-код.

В этом предположении есть несколько характеристик шаблона цепочки ответственности.

1 Начальник имеет дело только с руководителем отдела, руководитель отдела связывается только с руководителем проекта, а руководитель проекта только спрашивает фермеров о проблемах.
2 Если кодер не пишет, проект будет прерван.
3 Клиент не знает, кто в итоге написал программу.
Всплывание событий в js реализовано как цепочка ответственности. Событие запускается на узле, а затем распространяется на корневой узел, пока не будет перехвачено узлом.

16 Режим наилегчайшего веса

Режим облегченного веса в основном используется для уменьшения количества объектов, необходимых программе. В одном примере почти у каждого нашего одноклассника, работающего с интерфейсом, есть копия «Авторитетного руководства по JavaScript». С точки зрения экономии денег — около трех копий. достаточно.В книжном шкафу кафедры,кому нужно прочесть,поднимите,и верните после прочтения.Если есть 4 студента,которым надо читать одновременно,идите и купите другую книгу в это время .

В webqq, когда вы открываете список друзей QQ и вытягиваете его, для каждого друга будет создан div (если вы подсчитаете дочерние узлы в div, там гораздо больше одного элемента).

Если друзей QQ 1000, значит если тянуть от начала до конца, то создастся 1000 дивов.В это время в некоторых браузерах может быть анабиоз.Это просто операция по перелистыванию списка друзей.

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

Теперь на сцену может выйти режим легковеса. Как следует из названия, режим легковеса может предоставлять некоторые общие объекты для повторного использования. Если внимательно посмотреть на рисунок выше, нам нужно всего 10 элементов div для отображения информации о друзьях, то есть тех, которые появляются в поле зрения пользователя 10 divs.Эти 10 divs можно записать как облегчённый.
Псевдокод выглядит следующим образом.

JavaScript

 var getDiv = (function(){
    var created = [];
    var create = function(){
          return document.body.appendChild( document.createElement( 'div' ) );
    }
    var get = function(){
         if ( created.length ){
              return created.shift();
          }else{
                return create();
           }
     }
/* 一个假设的事件,用来监听刚消失在视线外的div,实际上可以通过监听滚                                     动条位置来实现 */
      userInfoContainer.disappear(function( div ){
              created.push( div );
        })
 })()
  var div = getDiv();
  div.innerHTML = "${userinfo}";

Принцип на самом деле очень простой.Помещаем только что спрятанный div в массив.Когда нужен div, сначала берем его из массива.Если в массиве больше нет,создаем новый.Div в этом Массив легковесный, каждый из них можно использовать как носитель любой пользовательской информации.

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

Режим семнадцати статусов

Шаблон состояния в основном может использоваться для такого рода сценариев.
1 Поведение объекта зависит от его состояния
2 Операция содержит огромный оператор условного перехода

Вспомните игру Street Fighter.

Лонг имеет различные состояния, такие как ходьба, атака, защита, падение, прыжок и т. д., и эти состояния связаны и взаимно сдерживаются. Например, при прыжке вы не можете ни атаковать, ни защищаться. Он не может ни атаковать, ни защищаться при падении, но может атаковать и прыгать при ходьбе.

Дополнить такой ряд логикой, если еще по здравому смыслу не обойтись, а число оценить невозможно, особенно при добавлении нового состояния, оно может быть изменено с 10-й строки кода на 900-ю строку.

JavaScript

if ( state === 'jump' ){
   if ( currState === 'attack' || currState === 'defense' ){
     return false;
   }
}else if ( state === 'wait' ){
   if ( currState === 'attack' || currState === 'defense' ){
     return true;
   }
}

Чтобы устранить эти if else и упростить модификацию и обслуживание, мы вводим класс состояния.

JavaScript

var StateManager = function(){
  var currState = 'wait';
  var states = {
    jump: function( state ){
    },
    wait: function( state ){
    },
    attack: function( state ){
    },
    crouch: function( state ){
    },
    defense: function( state ){
      if ( currState === 'jump'  ){
          return false;  //不成功,跳跃的时候不能防御
      }
    //do something;     //防御的真正逻辑代码, 为了防止状态类的代码过多, 应该把这些逻辑继续扔给真正的fight类来执行.
    currState = 'defense'; //  切换状态
    }
  }
  var changeState = function( state ){
    states[ state ] && states[ state ]();
  }
  return {
      changeState  : changeState
  }
}
var stateManager = StateManager();
stateManager.changeState( 'defense' );

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

 

/******************************Демаркационная линия 1********************* ** *************************/

Большинство из 23 шаблонов проектирования, предложенных GOF, уже написаны. Есть также некоторые, которые не применимы в js, или есть нативные реализации в js, поэтому я не стал вдаваться в подробности. Большинство примеров в этих двух статьях взяты из кода на работе и учебе или адаптированы из него. Мое мнение о шаблонах проектирования заключается в том, что вам не нужно специально изучать шаблоны проектирования.Обычно многие коды, с которыми мы сталкиваемся, уже содержат реализацию некоторых шаблонов проектирования. Мой процесс заключается в том, что после прочтения исходного кода прототипа и jquery я вернулся к книге шаблонов проектирования и обнаружил, что уже коснулся шесть или семь раз, прежде чем осознал это.

Кроме того, нет необходимости преднамеренно использовать некоторые шаблоны проектирования в реальном кодировании. Как и в tokyo hot 32, нет необходимости намеренно использовать определенную позу во время дружеского сеанса папапа. Все зависит от потребностей и чувств.

 

2 лайка26 Избранное22 комментария