自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何寫出優(yōu)雅的 JS 代碼?使用 SOLID 原則

開發(fā) 前端
設(shè)計(jì)模式的六個(gè)原則的首字母聯(lián)合起來(lái)(兩個(gè) L 算做一個(gè))就是 SOLID (solid,穩(wěn)定的),其代表的含義就是這六個(gè)原則結(jié)合使用的好處:建立穩(wěn)定、靈活、健壯的設(shè)計(jì)。下面我們來(lái)分別看一下這六大設(shè)計(jì)原則。

本文轉(zhuǎn)載自微信公眾號(hào)「大遷世界」,轉(zhuǎn)載本文請(qǐng)聯(lián)系大遷世界公眾號(hào)。

設(shè)計(jì)模式的六大原則有:

  • Single Responsibility Principle:?jiǎn)我宦氊?zé)原則
  • Open Closed Principle:開閉原則
  • Liskov Substitution Principle:里氏替換原則
  • Law of Demeter:迪米特法則
  • Interface Segregation Principle:接口隔離原則
  • Dependence Inversion Principle:依賴倒置原則

把這六個(gè)原則的首字母聯(lián)合起來(lái)(兩個(gè) L 算做一個(gè))就是 SOLID (solid,穩(wěn)定的),其代表的含義就是這六個(gè)原則結(jié)合使用的好處:建立穩(wěn)定、靈活、健壯的設(shè)計(jì)。下面我們來(lái)分別看一下這六大設(shè)計(jì)原則。

[[326031]]

單一職責(zé)原則(SRP)

單一功能原則 :?jiǎn)我还δ茉瓌t 認(rèn)為對(duì)象應(yīng)該僅具有一種單一功能的概念。

換句話說(shuō)就是讓一個(gè)類只做一種類型責(zé)任,當(dāng)這個(gè)類需要承擔(dān)其他類型的責(zé)任的時(shí)候,就需要分解這個(gè)類。在所有的SOLID原則中,這是大多數(shù)開發(fā)人員感到最能完全理解的一條。嚴(yán)格來(lái)說(shuō),這也可能是違反最頻繁的一條原則了。單一責(zé)任原則可以看作是低耦合、高內(nèi)聚在面向?qū)ο笤瓌t上的引申,將責(zé)任定義為引起變化的原因,以提高內(nèi)聚性來(lái)減少引起變化的原因。責(zé)任過(guò)多,可能引起它變化的原因就越多,這將導(dǎo)致責(zé)任依賴,相互之間就產(chǎn)生影響,從而極大的損傷其內(nèi)聚性和耦合度。單一責(zé)任,通常意味著單一的功能,因此不要為一個(gè)模塊實(shí) 現(xiàn)過(guò)多的功能點(diǎn),以保證實(shí)體只有一個(gè)引起它變化的原因。

「不好的寫法」

  1. class UserSettings { 
  2.   constructor(user) { 
  3.     this.user = user; 
  4.   } 
  5.  
  6.   changeSettings(settings) { 
  7.     if (this.verifyCredentials()) { 
  8.       // ... 
  9.     } 
  10.   } 
  11.  
  12.   verifyCredentials() { 
  13.     // ... 
  14.   } 

「好的寫法」

  1. class UserAuth { 
  2.   constructor(user) { 
  3.     this.user = user; 
  4.   } 
  5.  
  6.   verifyCredentials() { 
  7.     // ... 
  8.   } 
  9.  
  10. class UserSettings { 
  11.   constructor(user) { 
  12.     this.user = user; 
  13.     this.auth = new UserAuth(user); 
  14.   } 
  15.  
  16.   changeSettings(settings) { 
  17.     if (this.auth.verifyCredentials()) { 
  18.       // ... 
  19.     } 
  20.   } 

開放閉合原則 (OCP)

軟件實(shí)體應(yīng)該是可擴(kuò)展,而不可修改的。也就是說(shuō),對(duì)擴(kuò)展是開放的,而對(duì)修改是封閉的。這個(gè)原則是諸多面向?qū)ο缶幊淘瓌t中最抽象、最難理解的一個(gè)。

  • 通過(guò)增加代碼來(lái)擴(kuò)展功能,而不是修改已經(jīng)存在的代碼。
  • 若客戶模塊和服務(wù)模塊遵循同一個(gè)接口來(lái)設(shè)計(jì),則客戶模塊可以不關(guān)心服務(wù)模塊的類型,服務(wù)模塊可以方便擴(kuò)展服務(wù)(代碼)。
  • OCP支持替換的服務(wù),而不用修改客戶模塊。

說(shuō)大白話就是:你不是要變化嗎?,那么我就讓你繼承實(shí)現(xiàn)一個(gè)對(duì)象,用一個(gè)接口來(lái)抽象你的職責(zé),你變化越多,繼承實(shí)現(xiàn)的子類就越多。

「不好的寫法」

  1. class AjaxAdapter extends Adapter { 
  2.   constructor() { 
  3.     super(); 
  4.     this.name = "ajaxAdapter"
  5.   } 
  6.  
  7. class NodeAdapter extends Adapter { 
  8.   constructor() { 
  9.     super(); 
  10.     this.name = "nodeAdapter"
  11.   } 
  12.  
  13. class HttpRequester { 
  14.   constructor(adapter) { 
  15.     this.adapter = adapter; 
  16.   } 
  17.  
  18.   fetch(url) { 
  19.     if (this.adapter.name === "ajaxAdapter") { 
  20.       return makeAjaxCall(url).then(response => { 
  21.         // transform response and return 
  22.       }); 
  23.     } else if (this.adapter.name === "nodeAdapter") { 
  24.       return makeHttpCall(url).then(response => { 
  25.         // transform response and return 
  26.       }); 
  27.     } 
  28.   } 
  29.  
  30. function makeAjaxCall(url) { 
  31.   // request and return promise 
  32.  
  33. function makeHttpCall(url) { 
  34.   // request and return promise 

「好的寫法」

  1. class AjaxAdapter extends Adapter { 
  2.   constructor() { 
  3.     super(); 
  4.     this.name = "ajaxAdapter"
  5.   } 
  6.  
  7.   request(url) { 
  8.     // request and return promise 
  9.   } 
  10.  
  11. class NodeAdapter extends Adapter { 
  12.   constructor() { 
  13.     super(); 
  14.     this.name = "nodeAdapter"
  15.   } 
  16.  
  17.   request(url) { 
  18.     // request and return promise 
  19.   } 
  20.  
  21. class HttpRequester { 
  22.   constructor(adapter) { 
  23.     this.adapter = adapter; 
  24.   } 
  25.  
  26.   fetch(url) { 
  27.     return this.adapter.request(url).then(response => { 
  28.       // transform response and return 
  29.     }); 
  30.   } 

里氏替換原則(LSP)

里氏替換原則 :里氏替換原則 認(rèn)為“程序中的對(duì)象應(yīng)該是可以在不改變程序正確性的前提下被它的子類所替換的”的概念。

LSP則給了我們一個(gè)判斷和設(shè)計(jì)類之間關(guān)系的基準(zhǔn):需不需 要繼承,以及怎樣設(shè)計(jì)繼承關(guān)系。

當(dāng)一個(gè)子類的實(shí)例應(yīng)該能夠替換任何其超類的實(shí)例時(shí),它們之間才具有is-A關(guān)系。繼承對(duì)于「OCP」,就相當(dāng)于多態(tài)性對(duì)于里氏替換原則。子類可以代替基類,客戶使用基類,他們不需要知道派生類所做的事情。這是一個(gè)針對(duì)行為職責(zé)可替代的原則,如果S是T的子類型,那么S對(duì)象就應(yīng)該在不改變?nèi)魏纬橄髮傩郧闆r下替換所有T對(duì)象。

客戶模塊不應(yīng)關(guān)心服務(wù)模塊的是如何工作的;同樣的接口模塊之間,可以在不知道服務(wù)模塊代碼的情況下,進(jìn)行替換。即接口或父類出現(xiàn)的地方,實(shí)現(xiàn)接口的類或子類可以代入。

「不好的寫法」

  1. class Rectangle { 
  2.   constructor() { 
  3.     this.width = 0
  4.     this.height = 0
  5.   } 
  6.  
  7.   setColor(color) { 
  8.     // ... 
  9.   } 
  10.  
  11.   render(area) { 
  12.     // ... 
  13.   } 
  14.  
  15.   setWidth(width) { 
  16.     this.width = width; 
  17.   } 
  18.  
  19.   setHeight(height) { 
  20.     this.height = height; 
  21.   } 
  22.  
  23.   getArea() { 
  24.     return this.width * this.height; 
  25.   } 
  26.  
  27. class Square extends Rectangle { 
  28.   setWidth(width) { 
  29.     this.width = width; 
  30.     this.height = width
  31.   } 
  32.  
  33.   setHeight(height) { 
  34.     this.width = height
  35.     this.height = height; 
  36.   } 
  37.  
  38. function renderLargeRectangles(rectangles) { 
  39.   rectangles.forEach(rectangle => { 
  40.     rectangle.setWidth(4); 
  41.     rectangle.setHeight(5); 
  42.     const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20. 
  43.     rectangle.render(area); 
  44.   }); 
  45.  
  46. const rectangles = [new Rectangle(), new Rectangle(), new Square()]; 
  47. renderLargeRectangles(rectangles); 

「好的寫法」

  1. class Shape { 
  2.   setColor(color) { 
  3.     // ... 
  4.   } 
  5.  
  6.   render(area) { 
  7.     // ... 
  8.   } 
  9.  
  10. class Rectangle extends Shape { 
  11.   constructor(width, height) { 
  12.     super(); 
  13.     this.width = width; 
  14.     this.height = height; 
  15.   } 
  16.  
  17.   getArea() { 
  18.     return this.width * this.height; 
  19.   } 
  20.  
  21. class Square extends Shape { 
  22.   constructor(length) { 
  23.     super(); 
  24.     this.length = length; 
  25.   } 
  26.  
  27.   getArea() { 
  28.     return this.length * this.length; 
  29.   } 
  30.  
  31. function renderLargeShapes(shapes) { 
  32.   shapes.forEach(shape => { 
  33.     const area = shape.getArea(); 
  34.     shape.render(area); 
  35.   }); 
  36.  
  37. const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)]; 
  38. renderLargeShapes(shapes); 

接口隔離原則(ISP)

接口隔離原則 :接口隔離原則 認(rèn)為“多個(gè)特定客戶端接口要好于一個(gè)寬泛用途的接口”的概念。

不能強(qiáng)迫用戶去依賴那些他們不使用的接口。換句話說(shuō),使用多個(gè)專門的接口比使用單一的總接口總要好。

這個(gè)原則起源于施樂(lè)公司,他們需要建立了一個(gè)新的打印機(jī)系統(tǒng),可以執(zhí)行諸如裝訂的印刷品一套,傳真多種任務(wù)。此系統(tǒng)軟件創(chuàng)建從底層開始編制,并實(shí)現(xiàn)了這些 任務(wù)功能,但是不斷增長(zhǎng)的軟件功能卻使軟件本身越來(lái)越難適應(yīng)變化和維護(hù)。每一次改變,即使是最小的變化,有人可能需要近一個(gè)小時(shí)的重新編譯和重新部署。這 是幾乎不可能再繼續(xù)發(fā)展,所以他們聘請(qǐng)羅伯特Robert幫助他們。他們首先設(shè)計(jì)了一個(gè)主要類Job,幾乎能夠用于實(shí)現(xiàn)所有任務(wù)功能。只要調(diào)用Job類的 一個(gè)方法就可以實(shí)現(xiàn)一個(gè)功能,Job類就變動(dòng)非常大,是一個(gè)胖模型啊,對(duì)于客戶端如果只需要一個(gè)打印功能,但是其他無(wú)關(guān)打印的方法功能也和其耦合,ISP 原則建議在客戶端和Job類之間增加一個(gè)接口層,對(duì)于不同功能有不同接口,比如打印功能就是Print接口,然后將大的Job類切分為繼承不同接口的子 類,這樣有一個(gè)Print Job類,等等。

「不好的寫法」

  1. class DOMTraverser { 
  2.   constructor(settings) { 
  3.     this.settings = settings; 
  4.     this.setup(); 
  5.   } 
  6.  
  7.   setup() { 
  8.     thisthis.rootNode = this.settings.rootNode; 
  9.     this.animationModule.setup(); 
  10.   } 
  11.  
  12.   traverse() { 
  13.     // ... 
  14.   } 
  15.  
  16. const $ = new DOMTraverser({ 
  17.   rootNode: document.getElementsByTagName("body"), 
  18.   animationModule() {} // Most of the time, we won't need to animate when traversing. 
  19.   // ... 
  20. }); 

「好的寫法」

  1. class DOMTraverser { 
  2.   constructor(settings) { 
  3.     this.settings = settings; 
  4.     this.options = settings.options; 
  5.     this.setup(); 
  6.   } 
  7.  
  8.   setup() { 
  9.     thisthis.rootNode = this.settings.rootNode; 
  10.     this.setupOptions(); 
  11.   } 
  12.  
  13.   setupOptions() { 
  14.     if (this.options.animationModule) { 
  15.       // ... 
  16.     } 
  17.   } 
  18.  
  19.   traverse() { 
  20.     // ... 
  21.   } 
  22.  
  23. const $ = new DOMTraverser({ 
  24.   rootNode: document.getElementsByTagName("body"), 
  25.   options: { 
  26.     animationModule() {} 
  27.   } 
  28. }); 

依賴倒置原則(DIP)

依賴倒置原則:依賴倒置原則 認(rèn)為一個(gè)方法應(yīng)該遵從“依賴于抽象而不是一個(gè)實(shí)例” 的概念。依賴注入是該原則的一種實(shí)現(xiàn)方式。

依賴倒置原則(Dependency Inversion Principle,DIP)規(guī)定:代碼應(yīng)當(dāng)取決于抽象概念,而不是具體實(shí)現(xiàn)。

  • 高層模塊不要依賴低層模塊
  • 高層和低層模塊都要依賴于抽象;
  • 抽象不要依賴于具體實(shí)現(xiàn)
  • 具體實(shí)現(xiàn)要依賴于抽象
  • 抽象和接口使模塊之間的依賴分離

類可能依賴于其他類來(lái)執(zhí)行其工作。但是,它們不應(yīng)當(dāng)依賴于該類的特定具體實(shí)現(xiàn),而應(yīng)當(dāng)是它的抽象。這個(gè)原則實(shí)在是太重要了,社會(huì)的分工化,標(biāo)準(zhǔn)化都 是這個(gè)設(shè)計(jì)原則的體現(xiàn)。顯然,這一概念會(huì)大大提高系統(tǒng)的靈活性。如果類只關(guān)心它們用于支持特定契約而不是特定類型的組件,就可以快速而輕松地修改這些低級(jí) 服務(wù)的功能,同時(shí)最大限度地降低對(duì)系統(tǒng)其余部分的影響。

「不好的寫法」

  1. class InventoryRequester { 
  2.   constructor() { 
  3.     this.REQ_METHODS = ["HTTP"]; 
  4.   } 
  5.  
  6.   requestItem(item) { 
  7.     // ... 
  8.   } 
  9.  
  10. class InventoryTracker { 
  11.   constructor(items) { 
  12.     this.items = items; 
  13.  
  14.     // BAD: We have created a dependency on a specific request implementation. 
  15.     // We should just have requestItems depend on a request method: `request` 
  16.     this.requester = new InventoryRequester(); 
  17.   } 
  18.  
  19.   requestItems() { 
  20.     this.items.forEach(item => { 
  21.       this.requester.requestItem(item); 
  22.     }); 
  23.   } 
  24.  
  25. const inventoryTracker = new InventoryTracker(["apples", "bananas"]); 
  26. inventoryTracker.requestItems(); 

「好的寫法」

  1. class InventoryTracker { 
  2.   constructor(items, requester) { 
  3.     this.items = items; 
  4.     this.requester = requester; 
  5.   } 
  6.  
  7.   requestItems() { 
  8.     this.items.forEach(item => { 
  9.       this.requester.requestItem(item); 
  10.     }); 
  11.   } 
  12.  
  13. class InventoryRequesterV1 { 
  14.   constructor() { 
  15.     this.REQ_METHODS = ["HTTP"]; 
  16.   } 
  17.  
  18.   requestItem(item) { 
  19.     // ... 
  20.   } 
  21.  
  22. class InventoryRequesterV2 { 
  23.   constructor() { 
  24.     this.REQ_METHODS = ["WS"]; 
  25.   } 
  26.  
  27.   requestItem(item) { 
  28.     // ... 
  29.   } 
  30. const inventoryTracker = new InventoryTracker( 
  31.   ["apples", "bananas"], 
  32.   new InventoryRequesterV2() 
  33. ); 
  34. inventoryTracker.requestItems(); 

 

責(zé)任編輯:趙寧寧 來(lái)源: 大遷世界
相關(guān)推薦

2021-01-04 07:57:07

C++工具代碼

2019-09-20 15:47:24

代碼JavaScript副作用

2022-03-11 12:14:43

CSS代碼前端

2020-05-08 14:45:00

JS代碼變量

2021-12-07 08:16:34

React 前端 組件

2020-07-15 08:17:16

代碼

2020-05-11 15:23:58

CQRS代碼命令

2021-09-01 08:55:20

JavaScript代碼開發(fā)

2013-06-07 14:00:23

代碼維護(hù)

2021-11-30 10:20:24

JavaScript代碼前端

2022-02-08 19:33:13

技巧代碼格式

2022-02-17 10:05:21

CSS代碼前端

2020-12-19 10:45:08

Python代碼開發(fā)

2020-05-19 15:00:26

Bug代碼語(yǔ)言

2021-12-13 14:37:37

React組件前端

2017-10-24 15:28:27

PHP代碼簡(jiǎn)潔SOLID原則

2022-10-24 08:10:21

SQL代碼業(yè)務(wù)

2017-08-10 14:55:33

前端JavaScript 函數(shù)

2015-09-28 10:49:59

代碼程序員

2019-06-24 10:26:15

代碼程序注釋
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)