Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产亚洲精品久久久久久午夜,韩国在线一区,91精品国产91久久久久久

          整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          前端開發(fā),用英雄聯(lián)盟的方式講解JavaScript設(shè)

          前端開發(fā),用英雄聯(lián)盟的方式講解JavaScript設(shè)計模式

          造函數(shù)模式

          簡介

          在JavaScript里,構(gòu)造函數(shù)通常是認(rèn)為用來實現(xiàn)實例的特殊的構(gòu)造函數(shù)。通過new關(guān)鍵字來調(diào)用定義的構(gòu)造函數(shù),你可以告訴JavaScript你要創(chuàng)建一個新對象并且新對象的成員聲明都是構(gòu)造函數(shù)里定義的。在構(gòu)造函數(shù)內(nèi)部,this關(guān)鍵字引用的是新創(chuàng)建的對象。

          作為一個老聯(lián)盟fans,一定要親手實現(xiàn)一下設(shè)計模式也可以融會貫通。

          現(xiàn)在打算創(chuàng)建一個英雄聯(lián)盟對象,需要地圖,英雄,士兵,野怪,還有開始游戲的按鈕。

          function LOL(maps, heros, soldier, monster) {
              this.maps=maps
              this.heros=heros
              this.soldier=soldier
              this.monster=monster
              this.start=function() {
                  return '地圖:' + this.maps + '\n對戰(zhàn)英雄:' + this.heros.join() + '\n小兵類型:' + this.soldier + '\n野怪:' + this.monster + '\n'
              }
          }
          
          
          var game1=new LOL('召喚師峽谷', ['影流之主', '詭術(shù)妖姬'], '超級兵', '紅buff')
          var game2=new LOL('大亂斗', ['影流之主', '詭術(shù)妖姬'], '超級兵', '紅buff')
          console.log(game1.start())
          console.log(game2.start())

          這樣寫代碼,每局游戲需要重新創(chuàng)建一個英雄聯(lián)盟實例,

          這樣使用構(gòu)造器,有多少個game就需要多少個start函數(shù)方法,如果共用一個start方法,可以節(jié)約很多內(nèi)存

          function LOL(maps, heros, soldier, monster) {
              this.maps=maps
              this.heros=heros
              this.soldier=soldier
              this.monster=monster
          }
          LOL.prototype.start=function() {
               return '地圖:' + this.maps + '\n對戰(zhàn)英雄:' + this.heros.join() + '\n小兵類型:' + this.soldier + '\n野怪:' + this.monster + '\n'
          }
          
          
          var game1=new LOL('召喚師峽谷', ['影流之主', '詭術(shù)妖姬'], '超級兵', '紅buff')
          var game2=new LOL('大亂斗', ['影流之主', '詭術(shù)妖姬'], '超級兵', '紅buff')
          
          console.log(game1.start())
          console.log(game2.start())

          如果讓start方法變成大家通用的就好了,因此把LOL.prototype.start改寫,這樣所以的LOL實例就可以共用一個方法,從原型鏈上繼承即可

          上面的方式可以節(jié)省內(nèi)存,start實例函數(shù)可以在所有LOL對象的實例中使用

          如果不使用new 也可以有其他方式創(chuàng)建對象

          function LOL(maps, heros, soldier, monster) {
              this.maps=maps
              this.heros=heros
              this.soldier=soldier
              this.monster=monster
              this.start=function() {
                  return '地圖:' + this.maps + '\n對戰(zhàn)英雄:' + this.heros.join() + '\n小兵類型:' + this.soldier + '\n野怪:' + this.monster + '\n'
              }
          }
          
          
          var game3=new Object();
          LOL.call(game3, "扭曲叢林", ['影流之主', '劍圣'], '遠(yuǎn)程兵', '大龍');
          console.log(game3.start())
          
          //也可以不使用new ,通過call方法在game3的作用域調(diào)用LOL

          這種方式雖然可以創(chuàng)建新的構(gòu)造函數(shù),但卻不能繼承LOL原型上的函數(shù)

          如果直接運行LOL()函數(shù)(不使用new的情況下),由于this指向的是window對象,因此start方法會變成window.start()

          如果強制要求函數(shù)使用new 方法也可以如下創(chuàng)建:

          function LOL(maps, heros, soldier, monster) {
              if (!(this instanceof LOL)) {
                  return new LOL(maps, heros, soldier, monster);
              }
              this.maps=maps
              this.heros=heros
              this.soldier=soldier
              this.monster=monster
          }

          通過判斷this的instanceof,就可以知道究竟是來自new方法,還是說是直接調(diào)用。如果是直接調(diào)用的話,判斷條件為true,還是會return一個新的實例。

          e.g:

          var s=new String("lol");
          var n=new Number(101);
          var b=new Boolean(true);
          // s n b返回的是實例對象
          //String{
          //  0: 'l',
          //  1: 'o',
          //  2: 'l'
          //  }
          
          //可以直接給變量賦值,或者不加new 關(guān)鍵詞

          外觀模式

          簡介

          外觀模式最大的體現(xiàn)其實就是入口,比如init()函數(shù),把一些內(nèi)部的函數(shù)都放在這個門面之下,只需要調(diào)用這個門面函數(shù),其他亂七八糟的功能都可以實現(xiàn)。

          現(xiàn)在有一個英雄,叫做亞索,我希望給他一些配置,比如技能,衣服等


          function YaSuo() {
          }
          function Qskill (hero) {
              hero.prototype.Qskill=function() {
                  console.log('hasaki!!')
              }
          }
          function Wskill (hero) {
              hero.prototype.Wskill=function() {
                  console.log('風(fēng)墻')
              }
          }
          function Eskill (hero) {
              hero.prototype.Eskill=function() {
                  console.log('快樂')
              }
          }
          function Rskill (hero) {
              hero.prototype.Rskill=function() {
                  console.log('痛里唉該痛')
              }
          }
          function Skin (hero, skin) {
              hero.prototype.skin=skin
          }
          
          function CreateYasuo () {
              Qskill(YaSuo)
              Wskill(YaSuo)
              Eskill(YaSuo)
              Rskill(YaSuo)
              Skin(YaSuo, 'originSkin')
              return new YaSuo()
          }
          CreateYasuo()
          // 創(chuàng)建成功 外觀模式啟動

          通過上面的代碼,成功創(chuàng)建了一個美麗的亞索。我們最后只需要了解,外觀模式不僅簡化類中的接口,而且對接口與調(diào)用者也進(jìn)行了解耦。外觀模式經(jīng)常被認(rèn)為開發(fā)者必備,它可以將一些復(fù)雜操作封裝起來,并創(chuàng)建一個簡單的接口用于調(diào)用

          說白了就是用一個接口封裝其它的接口。

          外觀模式優(yōu)點就是易使用。缺點則是,當(dāng)連續(xù)使用外觀模式創(chuàng)建的接口時,可能會產(chǎn)生性能問題。

          e.g.


          var addMyEvent=function (el, ev, fn) {
              if (el.addEventListener) {
                  el.addEventListener(ev, fn, false);
              } else if (el.attachEvent) {
                  el.attachEvent('on' + ev, fn);
              } else {
                  el['on' + ev]=fn;
              }
          };

          這是最常見對監(jiān)聽事件的處理,前端必會。其中的addMyEvent就是對其他三個接口的封裝,產(chǎn)生了一個門面,也就是外觀模式。

          代理模式

          簡介

          其實代理模式我們生活中接觸的很多了。比如es6中的proxy對象,還有我們平時上網(wǎng)用的VPN。那其實代理模式,就是讓一個對象幫助其他的對象來做事。

          比如我現(xiàn)在想創(chuàng)建一個英雄,名字叫做卡莉斯塔,俗稱滑板鞋。這個英雄有個特點,當(dāng)她放R技能的時候,會把一個對象拉過來到自己身邊幾秒,代理這個對象的走路行為,禁止他釋放技能等等,那這就要用到代理模式了。

          // 聲明走路動作
          function Walk (hero) { // 代理期間執(zhí)行的操作
              return function() {console.log(hero + ' is walk')}
          }
          function Kalisita () { // proxy
              this.walk=Walk('Kalisita')
              this.Rskill=function(hero) { // 傳入要拉取的英雄
                  this.walk=function() {
                      Walk('Kalisita')() // 既需要自己走
                      hero.walk() // 還需要帶著人一起走
                  }
              }
          }
          function HeroA () { // 被代理走路的英雄
              this.walk=Walk('heroA')
          }
          
          var k=new Kalisita()
          var a=new HeroA()
          
          k.walk() // Kalisita is walk
          a.walk() // heroA is walk
          
          k.Rskill(a) // k把a的walk事件代理了, 現(xiàn)在k觸發(fā)walk的同時,也會帶著a一起walk哦
          k.walk()
          // Kalisita is walk
          // heroA is walk

          代理模式主要用于幾點,

          • 要獲取本來沒有的對對象操作的權(quán)限
          • 要獲取遠(yuǎn)程文件,需要代理模式作為跳板
          • 根據(jù)需要創(chuàng)建開銷很大的對象,通過它來存放實例化需要很長時間的真實對象,比如瀏覽器的渲染的時候先顯示問題,而圖片可以慢慢顯示(就是通過虛擬代理代替了真實的圖片,此時虛擬代理保存了真實圖片的路徑和尺寸。
          • 只當(dāng)調(diào)用真實的對象時,代理處理另外一些事情。例如C#里的垃圾回收,使用對象的時候會有引用次數(shù),如果對象沒有引用了,GC就可以回收它了。

          詳情可以參考《大話設(shè)計模式》

          而我們前端代碼中用的比較多的,應(yīng)該就是vue.js中對data中數(shù)據(jù)響應(yīng)式的代理。vue3中也將使用大量ES6支持的Proxy對象來改寫。

          e.g.

          通過代理,嘗試設(shè)置私有屬性

          function getPrivateProps(obj, filterFunc) {
            return new Proxy(obj, {
              get(obj, prop) {
                if (!filterFunc(prop)) {
                  let value=Reflect.get(obj, prop);
                  // 如果是方法, 將this指向修改原對象
                  if (typeof value==='function') {
                    value=value.bind(obj);
                  }
                  return value;
                }
              },
              set(obj, prop, value) {
                if (filterFunc(prop)) {
                  throw new TypeError(`Cant set property ${prop}`);
                }
                return Reflect.set(obj, prop, value);
              },
              has(obj, prop) {
                return filterFunc(prop) ? false : Reflect.has(obj, prop);
              },
              ownKeys(obj) {
                return Reflect.ownKeys(obj).filter(prop=> !filterFunc(prop));
              },
              getOwnPropertyDescriptor(obj, prop) {
                return filterFunc(prop) ? undefined : Reflect.getOwnPropertyDescriptor(obj, prop);
              }
            });
          }
          
          function propFilter(prop) {
            return prop.indexOf('_')===0;
          }

          總結(jié)

          本次分享了三種設(shè)計模式,分別為構(gòu)造函數(shù)模式,外觀模式,代理模式;都是日常開發(fā)很常用的設(shè)計模式。

          其中es6proxy可以訪問阮老師博客查看詳細(xì)api,具體使用也可以借鑒vue源碼。代理模式雖然很好用,但也不是任何時候都建議使用,如果你的代碼需要獲得某些對象的權(quán)限,不妨可以使用一下代理模式。在比較簡單的場景,可能就沒有必要了。

          外觀模式是最常用的,畢竟每一個js文件總是需要一個入口的。無論是main函數(shù)還是init函數(shù),都是起到一個外觀包裝的作用。當(dāng)然外觀模式并不是必須作為一個文件入口存在,只要能把重復(fù)的代碼提煉出來,就是一個合理的外觀模式。

          構(gòu)造函數(shù)模式就不多說了,簡單好用。

          復(fù)制代碼是危險的。如果有兩段相同的代碼,幾乎可以說一定是有問題的,因為每次改動,要維護(hù)兩段代碼

          盡量減少IO操作,如操作數(shù)據(jù)庫,網(wǎng)絡(luò)發(fā)送,甚至printf ,這些操作比直接操作內(nèi)存,慢很多倍、

          修改Bug時,一定要從最簡單的基本的地方開始檢查,不要檢查到最底層沒問題,發(fā)現(xiàn)是傳入的某個參數(shù)是錯的。先不要懷疑系統(tǒng)的部分。

          設(shè)計架構(gòu),同時了解細(xì)節(jié),有些Bug,調(diào)起來可能費時費力,甚至花個二三天,其實當(dāng)時寫的時候,只要稍微注意,就可以輕松避免。避免Bug的代價與找出并修改Bug的代價,實在是差太多了。

          把一段長代碼,分成很多小函數(shù),便于維護(hù),連自己都不愿看,不愿改的代碼,百分百有問題。

          寫程序時,先把流程搞清楚。把各個流程用的函數(shù)寫清楚,函數(shù)可以留空,這樣編程就變成了填空題。

          做新功能時,把數(shù)據(jù)結(jié)構(gòu)的設(shè)計,放在較重要的位置

          希望這次分享能夠既有趣,又讓你得到收獲,謝謝閱讀。

          源自:https://juejin.im/post/5ebe65a0f265da7bd76bb626

          聲明:文章著作權(quán)歸作者所有,如有侵權(quán),請聯(lián)系小編刪除。

          閩南網(wǎng)]

          lol揮別2018起航2019活動正式開啟,不少玩家想知道召喚師圖標(biāo)免費領(lǐng)取方法。

          lol揮別2018起航2019活動介紹:

          揮別2018起航2019

          活動時間:即日起-1月14日

          領(lǐng)取截止時間:2019年1月21日15:59

          活動入口:點擊進(jìn)入

          圖標(biāo)領(lǐng)取地址:https://lol.qq.com/act/a20180929awards/index.html#box2

          起航2019召喚師圖標(biāo)寶箱介紹:

          1、一般活動結(jié)束后10個工作日內(nèi)可在本頁面領(lǐng)取。

          2、請在截止時間前領(lǐng)取道具,逾期作廢。

          3、領(lǐng)取之前請先進(jìn)入一次游戲商城才能收到道具。

          4、領(lǐng)取之后請重新進(jìn)入游戲查收道具。

          關(guān)于lol揮別2018起航2019活動的介紹就到這。

          大家好,這是第三篇作者對于設(shè)計模式的分享了,前兩篇可以參考:

          手寫一下JavaScript的幾種設(shè)計模式 (工廠模式,單例模式,適配器模式,裝飾者模式,建造者模式)

          用英雄聯(lián)盟的方式講解JavaScript設(shè)計模式(一)! (構(gòu)造函數(shù)模式,外觀模式,代理模式)

          設(shè)計模式在編程開發(fā)中用途十分廣泛,每一個模式描述了一個在我們周圍不斷重復(fù)發(fā)生的問題,以及解決問題的核心!很多的時候,對于我們其實如何選擇適合的設(shè)計模式,才更加消耗時間。從之前的文章,每一個設(shè)計模式都會有一到兩個例子,既可以給自己以后開發(fā)回憶設(shè)計模式提供幫助,也希望可以給讀者一些啟發(fā)。

          策略模式


          簡介

          策略模式定義了算法家族,分別封裝起來,讓他們之間可以互相替換,此模式讓算法的變化不會影響到使用算法的客戶。

          那聽起來云山霧繞,怎么都涉及到 算法 了 ?難道我一個前端是時候進(jìn)攻算法大軍了嗎。其實并不是,用一個超級常見的例子就可以解釋!

          讓我們又回到英雄聯(lián)盟,當(dāng)我們第一次登陸英雄聯(lián)盟的時候,需要輸入一個新的姓名吧?起名規(guī)則起碼得有以下這幾條:

          • 名字長度
          • 名字是否有非法字符
          • 是否重名
          • 不能為空

          其中具體的設(shè)定,只有開發(fā)者才知道了,身為玩家只能注意到這幾點,那策略模式怎么體現(xiàn)在這里的呢?首先我們實現(xiàn)一個顯而易見功能的例子:

          var validator={
              validate: function (value, type) {
                  switch (type) {
                      case 'isNonEmpty ':
                          {
                              return true; // 名字不能為空
                          }
                      case 'isNoNumber ':
                          {
                              return true; // 名字 不是 純數(shù)字
                              break;
                          }
                      case 'isExist ':
                          {
                              return true; // 名字已存在
                          }
                      case 'isLength':
                          {
                              return true; // 長度合理
                          }
                  }
              }
          };
          復(fù)制代碼

          上述代碼可以實現(xiàn)一個表單驗證系統(tǒng),剛創(chuàng)建角色起名字的時候驗證那里的功能,只需要傳入相應(yīng)的參數(shù)就可以。

          validator.validate('測試名字', 'isNumber') // false

          雖然可以得到理想的結(jié)果,但這種寫法有十分嚴(yán)重的缺點,最重要的,每次增加或修改規(guī)則時,需要修改整個validate函數(shù),這不符合開放封閉原則,增加邏輯,讓函數(shù)更加復(fù)雜不可控。

          那真正適合的代碼應(yīng)該怎么寫呢?

          var validator={
              // 所有驗證規(guī)則處理函數(shù)存放的地方
              types: {},
              validate: function (str, types) {
                  this.messages=[];
                  var checker, result, msg, i;
                  for (i in types) {
                      var type=types[i];
                      checker=this.types[type]; // 獲取驗證規(guī)則的驗證類
                      if (!checker) { // 如果驗證規(guī)則類不存在,拋出異常
                          throw {
                              name: "ValidationError",
                              message: "No handler to validate type " + type
                          };
                      }
          
                      result=checker.validate(str); // 使用查到到的單個驗證類進(jìn)行驗證
                      if (!result) {
                          msg="Invalid value for *" + type + "*, " + checker.instructions;
                          this.messages.push(msg);
                      }
                  }
                  
                  return this.hasErrors();
              },
          
              // 是否有message錯誤信息
              hasErrors: function () {
                  return this.messages.length !==0;
              }
          };
          復(fù)制代碼

          上面的代碼定義了validator對象以及validate函數(shù),函數(shù)內(nèi)部會對傳入的字符串,檢測類型數(shù)組進(jìn)行處理。如果存在規(guī)則,進(jìn)行判斷,并把錯誤信息發(fā)送到this.message。如果不存在規(guī)則,自然的就不需要繼續(xù)執(zhí)行,拋出error即可。

          // 驗證給定的值是否不為空
          validator.types.isNonEmpty={
              validate: function (value) {
                  return value !=="";
              },
              instructions: "傳入的值不能為空"
          };
          
          // 驗證給定的值是否 不是 純數(shù)字
          validator.types.isNoNumber={
              validate: function (value) {
                  return isNaN(value); // 偽寫法,因為isNaN會誤判布爾值和空字符串等,因此并不能作為真正判斷純數(shù)字的依據(jù)
              },
              instructions: "傳入的值不能是純數(shù)字"
          };
          
          // 驗證給定的值是否存在
          validator.types.isExist={
              validate: function (value) {
                  // $.ajax() ...
                  return true;
              },
              instructions: "給定的值已經(jīng)存在"
          };
          
          // 驗證給定的值長度是否合理
          validator.types.isLength={
              validate: function (value) {
                  var l=value.toString().length
                  if ( l > 2 && l < 10) {
                      return true;
                  } else {
                      return false;
                  }
              },
              instructions: "長度不合理,請長度在2-10個字符內(nèi)"
          };
          復(fù)制代碼

          上面對types規(guī)則進(jìn)行了補充,定義了幾種規(guī)則,至此,對于名稱校驗,簡單的設(shè)定就敲完了。接下來要準(zhǔn)備的就是一個能夠在英雄聯(lián)盟合理的名字進(jìn)行驗證:

          var types=['isExist', 'isLength', 'isNoNumber', 'isNonEmpty']; // 決定想要的規(guī)則,無論增加或者減少,原函數(shù)都不需要改動
          function check (name, types) {
              validator.validate(name, types);
              if (validator.hasErrors()) {
                  console.log(validator.messages.join("\n"));
              } else {
                  console.log('驗證通過!')
              }
          }
          check('okckokckokck', types) // 長度不合理,請長度在2-10個字符內(nèi)
          check('老faker', types) // true
          check('00001', types) // 傳入的值不能是純數(shù)字
          復(fù)制代碼

          首先設(shè)定好想要的規(guī)則,用一個types數(shù)組囊括進(jìn)來,之后定義一個check函數(shù),把結(jié)果處理封裝一下,最后傳入?yún)?shù),無論想要檢測什么規(guī)則,都不需要修改原函數(shù)。現(xiàn)在無論我想檢測faker可不可以注冊,還是一個空字符串,都可以傳入規(guī)則,進(jìn)行使用。如果想添加新的規(guī)則,只需要在validator.types上續(xù)寫對象就可以,方便清晰,結(jié)構(gòu)明朗。

          核心思想就是把復(fù)雜的算法結(jié)構(gòu),分別封裝起來,讓他們之間可以互相替換,上面的代碼就很好的體現(xiàn)了 互相替換 ,因為無論我怎么去修改想要的規(guī)則,都不需要改動原本的代碼。

          橋接模式

          簡介

          在系統(tǒng)沿著多個維度變化的同時,又不增加其復(fù)雜度并已達(dá)到解耦。將抽象部分與它的實現(xiàn)部分分離,使它們都可以獨立地變化。簡單的說:橋接模式最主要的特點是實現(xiàn)層(如元素綁定的事件)與抽象層(如修飾頁面UI邏輯)解耦分離

          下面依然是一個例子:

          假如我們還在英雄聯(lián)盟的世界里,每一場游戲最終都會有一個結(jié)局,無論勝利還是失敗,都會彈出一個窗口,告訴你 —— Victory或者是Defeat

          function GameMessage (type) { // 抽象 與 實現(xiàn) 的 橋梁
              this.fn=type ? new Victory() : new Defeat()
          }
          GameMessage.prototype.show=function() {
              this.fn.show()
          }
          
          function Defeat() { // 抽象層
              this.show=function() {
                  console.log('im loser')
              }
          }
          
          function Victory() { // 抽象層
              this.show=function() {
                  console.log('im winner')
              }
          }
          
          // 實現(xiàn)層
          function getResult() {
              var switchVD=Math.ceil(Math.random()*10) > 5 // 勝利失敗一半一半
              return new GameMessage(switchVD)
          }
          var result1=getResult()
          var result2=getResult()
          var result3=getResult()
          result1.show()
          result2.show()
          result3.show()
          
          復(fù)制代碼

          首先我們創(chuàng)建了一個GameMessage的函數(shù),我們都知道勝利失敗都有一半的概率,因此定義了switchVD變量,模擬一個隨機事件,同時每次結(jié)果調(diào)用一次getResult函數(shù),獲取最新結(jié)果。

          橋接模式體現(xiàn)在GameMessage函數(shù)上,將抽象的 Victory() 以及 Defeat() 與 我們獲取結(jié)果的 getResult() 實現(xiàn)解耦。函數(shù)之間不糅合邏輯,但又通過橋梁函數(shù),連接在一起。

          這么寫的好處就是,兩者都可以獨立的變化,互不打擾。畢竟如果揉在一起,可能邏輯如下:

          function Defeat() { // 抽象層
              this.show=function() {
                  console.log('im loser')
              }
          }
          
          function Victory() { // 抽象層
              this.show=function() {
                  console.log('im winner')
              }
          }
          
          var switchVD=Math.ceil(Math.random()*10) > 5
          if (switchVD) {
              var result=new Victory()
          } else {
              var result=new Defeat()
          }
          
          result.show() // loser or winner
          復(fù)制代碼

          上述代碼可以輕松的看到,如果沒有橋接模式,直接把實現(xiàn)層,渲染層糅合在一起,會依賴上下文。倘若獲取不到上下文的環(huán)境,很容易出現(xiàn)問題。

          小結(jié)

          橋接模式在日常開發(fā)中,會在不經(jīng)意間頻繁使用,目的也是為了讓代碼結(jié)構(gòu)清晰,將不同邏輯的代碼互相解耦。便于日后維護(hù),開發(fā)時也更能區(qū)分模塊,看的舒服,自然效率也高。

          橋接模式關(guān)鍵是要理解抽象部分與實現(xiàn)部分的分離,使得二者可以獨立的變化,而不必拘泥于形式。靈活的變化,適用場景的多變就非常適合使用這種模式來實現(xiàn)。橋接模式最重要的是找到代碼中不同的變化緯度。

          狀態(tài)模式

          簡介

          狀態(tài)模式(State)允許一個對象在其內(nèi)部狀態(tài)改變的時候改變它的行為,對象看起來似乎修改了它的類。 其實就是用一個對象或者數(shù)組記錄一組狀態(tài),每個狀態(tài)對應(yīng)一個實現(xiàn),實現(xiàn)的時候根據(jù)狀態(tài)挨個去運行實現(xiàn)。

          優(yōu)點:

          1. 一個狀態(tài)對應(yīng)一個行為,直觀清晰,增改方便。
          2. 狀態(tài)與狀態(tài)間,行為與行為間彼此獨立互不干擾。
          3. 避免對象條件判斷語句過多。
          4. 不用執(zhí)行不必要的判斷語句。

          缺點:

          1. 需要將事物的不同狀態(tài)以及對應(yīng)的行為拆分出來,有時候會過度設(shè)計。
          2. 必然會增加事物類和動作類的個數(shù),動作類再根據(jù)單一原則,分別拆成幾個類,會反而使得代碼混亂。

          比如下面我們定義一個英雄的狀態(tài),名字叫亞索,其中亞索可能同時有好幾個狀態(tài)比如 邊走邊攻擊 —— 我們俗稱的“走A”,還有可能釋放技能之后接一個“B鍵回家”的操作,當(dāng)然最有可能的是eqw閃r行云流水的操作收獲一個人頭,再接一個ctrl+f6等。


          如果對這些操作一個個進(jìn)行處理判斷,需要多個if-else或switch不僅丑陋不說,而且在遇到有組合動作的時候,實現(xiàn)就會更為冗余。那么我們這里的復(fù)雜操作,可以使用 狀態(tài)模式 來實現(xiàn)。

          狀態(tài)模式 的思路是:首先創(chuàng)建一個狀態(tài)對象或者數(shù)組,在對象內(nèi)部存儲需要操作的狀態(tài)數(shù)組或?qū)ο螅缓鬆顟B(tài)對象提供一些接口,可以更改狀態(tài)以及執(zhí)行動作。

          那現(xiàn)在有一個英雄叫做亞索!下面代碼,我們就用亞索的狀態(tài)來實現(xiàn)一下傳說中的狀態(tài)模式:

          function YasuoState() {
              //存儲當(dāng)前即將執(zhí)行動作的狀態(tài)!
              this.currentstate=[];
          
              this.Actions={
                  walk : function(){
                      console.log('walk');
                  },
                  attack : function(){
                      console.log('attack');
                  },
                  magic : function(){
                      console.log('magic');
                  },
                  backhome : function(){
                      console.log('backhome');
                  }
              };
          }
            
          YasuoState.prototype.changeState=function() {
              //清空當(dāng)前的動作
              this.currentstate=[];
              Object.keys(arguments).forEach((i)=> this.currentstate.push(arguments[i]))
              return this;
          }
          YasuoState.prototype.YasuoActions=function() {
              //當(dāng)前動作集合中的動作依次執(zhí)行
              this.currentstate.forEach((k)=> this.Actions[k] && this.Actions[k]())
              return this;
          }
          
          
            
          var yasuoState=new YasuoState();
            
          yasuoState.changeState('walk','attack').YasuoActions().changeState('walk').YasuoActions().YasuoActions();
          復(fù)制代碼

          上面代碼成功實現(xiàn)了亞索的狀態(tài)模式,我們假設(shè)他有走路、攻擊、釋放技能、回家?guī)讉€狀態(tài),其中這幾個狀態(tài)其實是可以同時輸入指令的,要不然那些職業(yè)選手的高光操作就會在 技能銜接 而出現(xiàn)的卡頓 香消玉殞。

          狀態(tài)模式最常見的就是日常的例子 —— 紅綠燈,每當(dāng)切換狀態(tài)的時候,執(zhí)行一次動作。

          至于英雄聯(lián)盟中,最常見的就是邊走邊攻擊,在輸入命令后,首先改變了我們對象的狀態(tài)yasuoState.changeState('magic','backhome'),然后因為在代碼中有return this;,可以鏈?zhǔn)秸{(diào)用接下來的行為,于是我們讓它依次執(zhí)行剛才輸入的狀態(tài)。接下來又一次改變了狀態(tài)changeState('walk'),并且進(jìn)行執(zhí)行。可以看到執(zhí)行了兩次,由于狀態(tài)并沒有再次改變,因此只需要重復(fù)執(zhí)行就可以保證我們的英雄一直往前走下去了。

          希望狀態(tài)模式可以幫助你解決絕大多數(shù),需要切換狀態(tài)的操作。遇到類似的問題時,可以迅速拿出成熟可靠的狀態(tài)模式解決之。

          總結(jié)

          本次分享的三種模式,都可以在英雄聯(lián)盟中找到影子,因為我喜歡這款游戲,所以很輕松可以找到其中使用的設(shè)計模式:

          • 策略模式 —— 行為模式,當(dāng)業(yè)務(wù)需要很多種判斷,甚至組合計算時,為了方便擴(kuò)展修改或使用它。
          • 橋接模式 —— 結(jié)構(gòu)型模式,構(gòu)建需要經(jīng)常擴(kuò)展功能的對象時,經(jīng)常會遇到。
          • 狀態(tài)模式 —— 行為模式,希望業(yè)務(wù)根據(jù)狀態(tài)改變而發(fā)生改變,最常見的現(xiàn)實例子就是紅綠燈。

          設(shè)計模式主要可以幫助我們解決,開發(fā)中對代碼的設(shè)計問題,那我們?nèi)绾握业胶线m的對象,并應(yīng)用合適的設(shè)計模式呢?

          借用書中的幾個提示吧:

          尋找合適的對象

          決定對象的粒度

          決定好這個對象設(shè)計的接口

          把對象需要的具體函數(shù)實現(xiàn)

          合理的運用代碼復(fù)用機制

          設(shè)計的代碼應(yīng)該可以支持變化,要對變化有預(yù)見性

          大概是這幾種,在 javascript 中涉及編譯的場景較少,就不敘述了。

          設(shè)計模式是軟件開發(fā)人員在軟件開發(fā)過程中面臨的一般問題的解決方案。這些解決方案是眾多軟件開發(fā)人員經(jīng)過相當(dāng)長的一段時間的試驗和錯誤總結(jié)出來的。

          根據(jù)上面的幾條規(guī)則,在開發(fā)接口和函數(shù)的時候,時刻注意,就可以避免大多數(shù)代碼設(shè)計上的問題,對于以后的維護(hù)也會有巨大的幫助。下一個接受代碼的人,也會十分感激你的,讀代碼其實和讀書一樣,你現(xiàn)在偷懶寫的代碼可能無所謂,后面接手的人會瘋狂吐槽。相反如果你優(yōu)雅的實現(xiàn),像我,就會心里由衷的佩服,看到整齊的函數(shù),注釋明朗的功能,不得不說,高手確實是高手啊,短短 200 行,讓人跪服,就突出一個詞 —— 優(yōu)雅。



          相關(guān)參考

          • tom大叔博客
          • 《設(shè)計可復(fù)用的設(shè)計模式》

          作者:黃梵高
          鏈接:https://juejin.im/post/5ec791fdf265da770d3daabd


          主站蜘蛛池模板: 国产麻豆剧果冻传媒一区| 精品国产一区二区三区在线观看 | 高清一区二区三区视频| 国产一区二区三区内射高清| 久久福利一区二区| 精品国产毛片一区二区无码| 精品免费国产一区二区三区| 老熟妇仑乱一区二区视頻| 久久久精品一区二区三区| 久久亚洲中文字幕精品一区| 日本韩国一区二区三区| 日韩人妻精品一区二区三区视频| 国产一区二区视频在线观看| 国产福利电影一区二区三区久久老子无码午夜伦不 | 国产一区二区三区美女| 亚洲一区视频在线播放| 国产av福利一区二区三巨| 国产亚洲自拍一区| 中文字幕亚洲乱码熟女一区二区 | 亚洲一区二区三区国产精华液| 色婷婷av一区二区三区仙踪林| 伊人久久精品一区二区三区| 亚洲Av无码国产一区二区| 国产乱码精品一区二区三区| 日韩美一区二区三区| 亚洲第一区在线观看| 国产av成人一区二区三区| 日韩精品区一区二区三VR| 99精品国产高清一区二区麻豆| 日韩精品无码久久一区二区三| 精品一区精品二区制服| 日本一区二三区好的精华液| 日韩一区二区三区视频久久| 亚洲乱码日产一区三区| 精品一区精品二区| 国产丝袜美女一区二区三区| 亚洲第一区二区快射影院| 久久久精品一区二区三区| 无码人妻精品一区二区蜜桃| 91午夜精品亚洲一区二区三区| 日本v片免费一区二区三区 |