成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

不知道怎么封裝代碼?看看這幾種設計模式吧!

開發 前端
我們經常聽說:“寫代碼要有良好的封裝,要高內聚,低耦合”。那怎樣才算良好的封裝,我們為什么要封裝呢?其實封裝有這樣幾個好處,一起來看看吧。

[[339386]]

為什么要封裝代碼?

我們經常聽說:“寫代碼要有良好的封裝,要高內聚,低耦合”。那怎樣才算良好的封裝,我們為什么要封裝呢?其實封裝有這樣幾個好處:

  1.   封裝好的代碼,內部變量不會污染外部。
  2.   可以作為一個模塊給外部調用。外部調用者不需要知道實現的細節,只需要按照約定的規范使用就行了。
  3.   對擴展開放,對修改關閉,即開閉原則。外部不能修改模塊,既保證了模塊內部的正確性,又可以留出擴展接口,使用靈活。

怎么封裝代碼?

JS生態已經有很多模塊了,有些模塊封裝得非常好,我們使用起來很方便,比如jQuery,Vue等。如果我們仔細去看這些模塊的源碼,我們會發現他們的封裝都是有規律可循的。這些規律總結起來就是設計模式,用于代碼封裝的設計模式主要有工廠模式,創建者模式,單例模式,原型模式四種。下面我們結合一些框架源碼來看看這四種設計模式:

工廠模式

工廠模式的名字就很直白,封裝的模塊就像一個工廠一樣批量的產出需要的對象。常見工廠模式的一個特征就是調用的時候不需要使用new,而且傳入的參數比較簡單。但是調用次數可能比較頻繁,經常需要產出不同的對象,頻繁調用時不用new也方便很多。一個工廠模式的代碼結構如下所示: 

  1. function factory(type) {  
  2.   switch(type) {  
  3.     case 'type1':  
  4.       return new Type1();  
  5.     case 'type2':  
  6.       return new Type2();  
  7.     case 'type3':  
  8.       return new Type3();  
  9.   }  

上述代碼中,我們傳入了type,然后工廠根據不同的type來創建不同的對象。

實例: 彈窗組件

下面來看看用工廠模式的例子,假如我們有如下需求:

我們項目需要一個彈窗,彈窗有幾種:消息型彈窗,確認型彈窗,取消型彈窗,他們的顏色和內容可能是不一樣的。

針對這幾種彈窗,我們先來分別建一個類: 

  1. function infoPopup(content, color) {}  
  2. function confirmPopup(content, color) {}  
  3. function cancelPopup(content, color) {} 

如果我們直接使用這幾個類,就是這樣的: 

  1. let infoPopup1 = new infoPopup(content, color);  
  2. let infoPopup2 = new infoPopup(content, color);  
  3. let confirmPopup1 = new confirmPopup(content, color);  
  4. ... 

每次用的時候都要去new對應的彈窗類,我們用工廠模式改造下,就是這樣: 

  1. // 新加一個方法popup把這幾個類都包裝起來  
  2. function popup(type, content, color) {  
  3.   switch(type) {  
  4.     case 'infoPopup':  
  5.       return new infoPopup(content, color);  
  6.     case 'confirmPopup':  
  7.       return new confirmPopup(content, color);  
  8.     case 'cancelPopup':  
  9.       return new cancelPopup(content, color);  
  10.   }  

然后我們使用popup就不用new了,直接調用函數就行: 

  1. let infoPopup1 = popup('infoPopup', content, color); 

改造成面向對象

上述代碼雖然實現了工廠模式,但是switch始終感覺不是很優雅。我們使用面向對象改造下popup,將它改為一個類,將不同類型的彈窗掛載在這個類上成為工廠方法: 

  1. function popup(type, content, color) {  
  2.   // 如果是通過new調用的,返回對應類型的彈窗  
  3.   if(this instanceof popup) {  
  4.     return new this[type](content, color);  
  5.   } else {  
  6.     // 如果不是new調用的,使用new調用,會走到上面那行代碼  
  7.     return new popup(type, content, color);  
  8.   }  
  9.  
  10. // 各種類型的彈窗全部掛載在原型上成為實例方法  
  11. popup.prototype.infoPopup = function(content, color) {}  
  12. popup.prototype.confirmPopup = function(content, color) {}  
  13. popup.prototype.cancelPopup = function(content, color) {} 

封裝成模塊

這個popup不僅僅讓我們調用的時候少了一個new,他其實還把相關的各種彈窗都封裝在了里面,這個popup可以直接作為模塊export出去給別人調用,也可以掛載在window上作為一個模塊給別人調用。因為popup封裝了彈窗的各種細節,即使以后popup內部改了,或者新增了彈窗類型,或者彈窗類的名字變了,只要保證對外的接口參數不變,對外面都沒有影響。掛載在window上作為模塊可以使用自執行函數: 

  1. (function(){  
  2.      function popup(type, content, color) {  
  3.     if(this instanceof popup) {  
  4.       return new this[type](content, color);  
  5.     } else {  
  6.       return new popup(type, content, color);  
  7.     }  
  8.   }  
  9.   popup.prototype.infoPopup = function(content, color) {}  
  10.   popup.prototype.confirmPopup = function(content, color) {}  
  11.   popup.prototype.cancelPopup = function(content, color) {} 
  12.   window.popup = popup;  
  13. })()  
  14. // 外面就直接可以使用popup模塊了  
  15. let infoPopup1 = popup('infoPopup', content, color);  

jQuery的工廠模式

jQuery也是一個典型的工廠模式,你給他一個參數,他就給你返回符合參數DOM對象。那jQuery這種不用new的工廠模式是怎么實現的呢?其實就是jQuery內部幫你調用了new而已,jQuery的調用流程簡化了就是這樣: 

  1. (function(){  
  2.   var jQuery = function(selector) {  
  3.     return new jQuery.fn.init(selector);   // new一下init, init才是真正的構造函數  
  4.   }  
  5.   jQueryjQuery.fn = jQuery.prototype;     // jQuery.fn就是jQuery.prototype的簡寫  
  6.   jQuery.fn.init = function(selector) {  
  7.     // 這里面實現真正的構造函數 
  8.    }  
  9.   // 讓init和jQuery的原型指向同一個對象,便于掛載實例方法  
  10.   jQueryjQuery.fn.init.prototype = jQuery.fn;   
  11.   // 最后將jQuery掛載到window上  
  12.   window.$ = window.jQuery = jQuery;  
  13. })(); 

上述代碼結構來自于jQuery源碼,從中可以看出,你調用時省略的new在jQuery里面幫你調用了,目的是為了使大量調用更方便。但是這種結構需要借助一個init方法,最后還要將jQuery和init的原型綁在一起,其實還有一種更加簡便的方法可以實現這個需求: 

  1. var jQuery = function(selector) {  
  2.   if(!(this instanceof jQuery)) {  
  3.     return new jQuery(selector);  
  4.   }  
  5.   // 下面進行真正構造函數的執行  

上述代碼就簡潔多了,也可以實現不用new直接調用,這里利用的特性是this在函數被new調用時,指向的是new出來的對象,new出來的對象自然是類的instance,這里的this instanceof jQuery就是true。如果是普通調用,他就是false,我們就幫他new一下。

建造者模式

建造者模式是用于比較復雜的大對象的構建,比如Vue,Vue內部包含一個功能強大,邏輯復雜的對象,在構建的時候也需要傳很多參數進去。像這種需要創建的情況不多,創建的對象本身又很復雜的時候就適用建造者模式。建造者模式的一般結構如下: 

  1. function Model1() {}   // 模塊1  
  2. function Model2() {}   // 模塊2  
  3. // 最終使用的類  
  4. function Final() {  
  5.   this.model1 = new Model1();  
  6.   this.model2 = new Model2();  
  7.  
  8. // 使用時  
  9. var obj = new Final(); 

上述代碼中我們最終使用的是Final,但是Final里面的結構比較復雜,有很多個子模塊,Final就是將這些子模塊組合起來完成功能,這種需要精細化構造的就適用于建造者模式。

實例:編輯器插件

假設我們有這樣一個需求:

寫一個編輯器插件,初始化的時候需要配置大量參數,而且內部的功能很多很復雜,可以改變字體顏色和大小,也可以前進后退。

一般一個頁面就只有一個編輯器,而且里面的功能可能很復雜,可能需要調整顏色,字體等。也就是說這個插件內部可能還會調用其他類,然后將他們組合起來實現功能,這就適合建造者模式。我們來分析下做這樣一個編輯器需要哪些模塊:

  1.   編輯器本身肯定需要一個類,是給外部調用的接口
  2.   需要一個控制參數初始化和頁面渲染的類
  3.   需要一個控制字體的類
  4.   需要一個狀態管理的類 
  1. // 編輯器本身,對外暴露  
  2. function Editor() {  
  3.   // 編輯器里面就是將各個模塊組合起來實現功能  
  4.   this.initer = new HtmlInit();  
  5.   this.fontController = new FontController();  
  6.   this.stateController = new StateController(this.fontController);  
  7.  
  8. // 初始化參數,渲染頁面  
  9. function HtmlInit() { 
  10.   
  11. HtmlInit.prototype.initStyle = function() {}     // 初始化樣式  
  12. HtmlInit.prototype.renderDom = function() {}     // 渲染DOM  
  13. // 字體控制器  
  14. function FontController() {  
  15.  
  16. FontController.prototype.changeFontColor = function() {}    // 改變字體顏色  
  17. FontController.prototype.changeFontSize = function() {}     // 改變字體大小  
  18. // 狀態控制器  
  19. function StateController(fontController) {  
  20.   this.states = [];       // 一個數組,存儲所有狀態  
  21.   this.currentState = 0;  // 一個指針,指向當前狀態  
  22.   this.fontController = fontController;    // 將字體管理器注入,便于改變狀態的時候改變字體  
  23.  
  24. StateController.prototype.saveState = function() {}     // 保存狀態  
  25. StateController.prototype.backState = function() {}     // 后退狀態  
  26. StateController.prototype.forwardState = function() {}     // 前進狀態 

上面的代碼其實就將一個編輯器插件的架子搭起來了,具體實現功能就是往這些方法里面填入具體的內容就行了,其實就是各個模塊的相互調用,比如我們要實現后退狀態的功能就可以這樣寫: 

  1. StateController.prototype.backState = function() {  
  2.   var state = this.states[this.currentState - 1];  // 取出上一個狀態  
  3.   this.fontController.changeFontColor(state.color);  // 改回上次顏色  
  4.   this.fontController.changeFontSize(state.size);    // 改回上次大小  

單例模式

單例模式適用于全局只能有一個實例對象的場景,單例模式的一般結構如下: 

  1. function Singleton() {} 
  2.  Singleton.getInstance = function() {  
  3.   if(this.instance) {  
  4.     return this.instance;  
  5.   }  
  6.   this.instance = new Singleton();  
  7.   return this.instance;  

上述代碼中,Singleton類掛載了一個靜態方法getInstance,如果要獲取實例對象只能通過這個方法拿,這個方法會檢測是不是有現存的實例對象,如果有就返回,沒有就新建一個。

實例:全局數據存儲對象

假如我們現在有這樣一個需求:

我們需要對一個全局的數據對象進行管理,這個對象只能有一個,如果有多個會導致數據不同步。

這個需求要求全局只有一個數據存儲對象,是典型的適合單例模式的場景,我們可以直接套用上面的代碼模板,但是上面的代碼模板獲取instance必須要調getInstance才行,要是某個使用者直接調了Singleton()或者new Singleton()就會出問題,這次我們換一種寫法,讓他能夠兼容Singleton()和new Singleton(),使用起來更加傻瓜化: 

  1. function store() {  
  2.   if(store.instance) {  
  3.     return store.instance;  
  4.   }  
  5.   store.instance = this 

上述代碼支持使用new store()的方式調用,我們使用了一個靜態變量instance來記錄是否有進行過實例化,如果實例化了就返回這個實例,如果沒有實例化說明是第一次調用,那就把this賦給這個這個靜態變量,因為是使用new調用,這時候的this指向的就是實例化出來的對象,并且最后會隱式的返回this。

如果我們還想支持store()直接調用,我們可以用前面工廠模式用過的方法,檢測this是不是當前類的實例,如果不是就幫他用new調用就行了: 

  1. function store() {  
  2.   // 加一個instanceof檢測  
  3.   if(!(this instanceof store)) {  
  4.     return new store();  
  5.   }   
  6.   // 下面跟前面一樣的  
  7.   if(store.instance) {  
  8.     return store.instance;  
  9.   } 
  10.    store.instance = this
  11.  

然后我們用兩種方式調用來檢測下:

實例:vue-router

vue-router其實也用到了單例模式,因為如果一個頁面有多個路由對象,可能造成狀態的沖突,vue-router的單例實現方式又有點不一樣,下列代碼來自vue-router源碼: 

  1. let _Vue;  
  2. function install(Vue) {  
  3.   if (install.installed && _Vue === Vue) return;  
  4.   install.installed = true  
  5.   _Vue = Vue  

每次我們調用vue.use(vueRouter)的時候其實都會去執行vue-router模塊的install方法,如果用戶不小心多次調用了vue.use(vueRouter)就會造成install的多次執行,從而產生不對的結果。vue-router的install在第一次執行時,將installed屬性寫成了true,并且記錄了當前的Vue,這樣后面在同一個Vue里面再次執行install就會直接return了,這也是一種單例模式。

可以看到我們這里三種代碼都是單例模式,他們雖然形式不一樣,但是核心思想都是一樣的,都是用一個變量來標記代碼是否已經執行過了,如果執行過了就返回上次的執行結果,這樣就保證了多次調用也會拿到一樣的結果。

原型模式

原型模式最典型的應用就是JS本身啊,JS的原型鏈就是原型模式。JS中可以使用Object.create指定一個對象作為原型來創建對象: 

  1. const obj = {  
  2.   x: 1,  
  3.   func: () => {}  
  4.  
  5. // 以obj為原型創建一個新對象  
  6. const newObj = Object.create(obj);  
  7. console.log(newObj.__proto__ === obj);    // true  
  8. console.log(newObj.x);    // 1 

上述代碼我們將obj作為原型,然后用Object.create創建的新對象都會擁有這個對象上的屬性和方法,這其實就算是一種原型模式。還有JS的面向對象其實更加是這種模式的體現,比如JS的繼承可以這樣寫: 

  1. function Parent() {  
  2.   this.parentAge = 50 
  3.  
  4. function Child() {}  
  5. Child.prototype = new Parent();  
  6. ChildChild.prototype.constructor = Child;      // 注意重置constructor  
  7. const obj = new Child();  
  8. console.log(obj.parentAge);    // 50 

這里的繼承其實就是讓子類Child.prototype.__proto__的指向父類的prototype,從而獲取父類的方法和屬性。JS中面向對象的內容較多,我這里不展開了,有一篇文章專門講這個問題

總結

  1.  很多用起來順手的開源庫都有良好的封裝,封裝可以將內部環境和外部環境隔離,外部用起來更順手。
  2.  針對不同的場景可以有不同的封裝方案。
  3.  需要大量產生類似實例的組件可以考慮用工廠模式來封裝。
  4.  內部邏輯較復雜,外部使用時需要的實例也不多,可以考慮用建造者模式來封裝。
  5.  全局只能有一個實例的需要用單例模式來封裝。
  6.  新老對象之間可能有繼承關系的可以考慮用原型模式來封裝,JS本身就是一個典型的原型模式。
  7.  使用設計模式時不要生搬硬套代碼模板,更重要的是掌握思想,同一個模式在不同的場景可以有不同的實現方案。 

 

責任編輯:龐桂玉 來源: segmentfault
相關推薦

2021-06-27 21:37:27

緩存雪崩穿透

2021-04-13 11:10:09

Windows 10Windows微軟

2021-08-04 08:31:10

MySQL數據庫日志

2021-09-07 11:20:02

binlogMySQL數據庫

2019-07-23 17:52:59

Spring BootJava開發

2019-07-23 15:56:56

Spring Boot部署servlet

2021-01-21 05:52:11

斷言assert程序員

2021-06-03 08:05:46

VSCode 代碼高亮原理前端

2020-06-12 09:20:33

前端Blob字符串

2020-07-28 08:26:34

WebSocket瀏覽器

2018-07-13 15:15:09

2010-10-20 09:10:29

私有云

2014-10-21 11:17:41

蘋果設計

2022-07-17 06:53:24

微服務架構

2018-09-02 15:43:56

Python代碼編程語言

2021-08-10 23:09:55

區塊鏈數據技術

2021-10-22 09:41:26

橋接模式設計

2023-11-15 08:22:42

Java開發小技巧

2022-05-30 08:18:35

門面模式子系統對象

2021-10-22 22:35:00

Windows 10Windows微軟
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品毛片 | 欧美一区二区三区在线 | 欧美精品久久久久久久久久 | 精品伊人| 激情一区二区三区 | 国产综合精品一区二区三区 | 色免费在线视频 | 精一区二区 | 国产精品综合网 | 国产清纯白嫩初高生视频在线观看 | 九九热精品视频 | 中文字幕 国产 | 欧美一区2区三区3区公司 | 中文字幕黄色大片 | 精品美女 | 亚洲一二三区在线观看 | 逼逼视频 | www.久草| 91视视频在线观看入口直接观看 | 精品国产欧美一区二区三区成人 | 国产 日韩 欧美 制服 另类 | 久久久久久综合 | 女人毛片a毛片久久人人 | 99久久精品免费看国产高清 | 狠狠婷婷综合久久久久久妖精 | 国产91在线精品 | 国产欧美精品在线观看 | 91传媒在线观看 | 美女爽到呻吟久久久久 | 美国一级片在线观看 | 99视频在线 | 欧美日韩免费一区二区三区 | 999视频在线播放 | 亚洲视频二区 | 成在线人视频免费视频 | 亚洲导航深夜福利涩涩屋 | 欧美一区二区三区免费在线观看 | 亚洲瑟瑟| 欧美自拍一区 | 91激情电影 | 精产国产伦理一二三区 |