OOP 範式的引入普及了繼承、多型、抽象和封裝等關鍵程式設計概念。 OOP 很快就成為一種廣為接受的程式設計範例,並以多種語言(例如 Java、C、C#、JavaScript 等)實作。隨著時間的推移,物件導向程式系統變得越來越複雜,但其軟體仍然難以改變。為了提高軟體可擴展性並降低程式碼剛性,Robert C. Martin(又名 Bob 叔叔)在 2000 年代初引入了 SOLID 原則。
SOLID 是一個縮寫詞,由一組原則組成——單一責任原則、開閉原則、里氏替換原則、介面隔離原則和依賴倒置原則——幫助軟體工程師設計和編寫可維護、可擴展和靈活的軟體程式碼。它的目的是什麼?提高遵循物件導向程式設計(OOP)範式開發的軟體的品質。
在本文中,我們將深入研究 SOLID 的所有原則,並說明如何使用最受歡迎的 Web 程式語言之一 JavaScript 來實現它們。
單一職責原則(SRP)
SOLID中的第一個字母代表單一責任原則。這原則顯示類別或模組應該只執行一個角色。
簡單地說,一個類別應該有單一的責任或單一的改變理由。如果一個類別處理多個功能,則更新一個功能而不影響其他功能會變得很棘手。隨後的複雜情況可能會導致軟體效能故障。為了避免此類問題,我們應該盡力編寫模組化軟體,其中關注點是分離的。
如果一個類別的職責或功能太多,修改起來就會很頭痛。透過使用單一責任原則,我們可以編寫模組化、更易於維護且不易出錯的程式碼。以人物模型為例:
class Person { constructor(name, age, height, country){ this.name = name this.age = age this.height = height this.country = country } getPersonCountry(){ console.log(this.country) } greetPerson(){ console.log("Hi " + this.name) } static calculateAge(dob) { const today = new Date(); const birthDate = new Date(dob); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if (monthDiff <p>上面的程式碼看起來沒問題,對吧?不完全是。範例程式碼違反了單一責任原則。 Person 類別不是可以建立 Person 的其他實例的唯一模型,它還具有其他職責,例如calculateAge、greetPerson 和getPersonCountry。 </p> <p>Person 類別處理的這些額外職責使得僅更改程式碼的一個方面變得困難。例如,如果您嘗試重構calculateAge,您也可能被迫重構Person 模型。根據我們的程式碼庫的緊湊和複雜程度,重新配置程式碼而不導致錯誤可能很困難。 </p> <p>讓我們試著修改錯誤。我們可以將職責分成不同的類,如下:<br> </p><pre class="brush:php;toolbar:false">class Person { constructor(name, age, height, country){ this.name = name this.age = age this.height = height this.country = country } getPersonCountry(){ console.log(this.country) } greetPerson(){ console.log("Hi " + this.name) } static calculateAge(dob) { const today = new Date(); const birthDate = new Date(dob); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if (monthDiff <p>正如您從上面的範例程式碼中看到的,我們已經分離了我們的職責。 Person 類別現在是一個模型,我們可以用它來建立一個新的 person 物件。 PersonUtils 類別只有一項職責-計算一個人的年齡。 PersonService 類別處理問候語並向我們顯示每個人的國家。 </p> <p>如果我們願意,我們仍然可以進一步減少這個過程。遵循SRP,我們希望將類別的責任解耦到最低限度,以便在出現問題時,可以輕鬆地進行重構和調試。 </p> <p>透過將功能劃分為單獨的類,我們遵循單一職責原則並確保每個類別負責應用程式的特定方面。 </p> <p>在我們繼續下一個原則之前,應該注意的是,遵守 SRP 並不意味著每個類別應該<em>嚴格</em>包含單一方法或功能。 </p> <p>但是,堅持單一責任原則意味著我們應該有意識地為類別分配功能。一個班級所進行的每件事在任何意義上都應該是密切相關的。我們必須小心,不要讓多個類別分散在各處,並且我們應該盡一切努力避免在程式碼庫中出現臃腫的類別。 </p> <h2> 開閉原則(OCP) </h2> <p>開閉原則指出軟體元件(類別、函數、模組等)應該對擴充開放,並對修改封閉。我知道你在想什麼——是的,這個想法一開始可能看起來很矛盾。但 OCP 只是要求軟體的設計方式允許擴充而不必修改原始碼。 </p> <p>OCP 對於維護大型程式碼庫至關重要,因為指南允許您引入新功能,而幾乎沒有破壞程式碼的風險。當出現新需求時,您不應修改現有的類別或模組,而應透過新增元件來擴充相關類別。執行此操作時,請務必檢查新組件是否不會為系統引入任何錯誤。 </p> <p>OC 原理可以使用 ES6 類別繼承功能在 JavaScript 中實作。 </p> <p>以下程式碼片段說明如何使用前面提到的 ES6 class 關鍵字在 JavaScript 中實現開閉原則:<br> </p> <pre class="brush:php;toolbar:false">class Person { constructor(name, dateOfBirth, height, country){ this.name = name this.dateOfBirth = dateOfBirth this.height = height this.country = country } } class PersonUtils { static calculateAge(dob) { const today = new Date(); const birthDate = new Date(dob); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if(monthDiff <p>上面的程式碼工作正常,但它僅限於計算矩形的面積。現在想像一下有一個新的計算要求。舉例來說,我們需要計算圓的面積。我們必須修改 shapeProcessor 類別來滿足這一點。但是,遵循 JavaScript ES6 標準,我們可以擴展此功能以考慮新形狀的區域,而不必修改 shapeProcessor 類別。 </p> <p>我們可以這樣做:<br> </p><pre class="brush:php;toolbar:false">class Person { constructor(name, age, height, country){ this.name = name this.age = age this.height = height this.country = country } getPersonCountry(){ console.log(this.country) } greetPerson(){ console.log("Hi " + this.name) } static calculateAge(dob) { const today = new Date(); const birthDate = new Date(dob); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if (monthDiff <p>在上面的程式碼片段中,我們使用 extends 關鍵字擴充了 Shape 類別的功能。在每個子類別中,我們重寫了area()方法的實作。遵循這個原則,我們可以增加更多的形狀和處理區域,而無需修改 ShapeProcessor 類別的功能。 </p> <h3> 為什麼 OCP 很重要? </h3>
- 減少錯誤:OCP 透過避免系統修改來幫助避免大型程式碼庫中的錯誤。
- 鼓勵軟體適應性:OCP 還提高了在不破壞或更改原始程式碼的情況下向軟體添加新功能的便利性。
- 測試新功能:OCP 提倡程式碼擴充而非修改,使新功能更容易作為一個單元進行測試,而不影響整個程式碼庫。
里氏替換原則
里氏替換原則指出子類別的物件應該能夠替換超類別的物件而不破壞程式碼。讓我們用一個例子來解釋它是如何運作的:如果 L 是 P 的子類,那麼 L 的對象應該替換 P 的對象,而不會破壞系統。這僅僅意味著子類別應該能夠以不破壞系統的方式重寫超類別方法。
在實務上,里氏替換原則確保遵守以下條件:
- 子類別應該重寫父類別的方法而不破壞程式碼
- 子類別不應偏離父類別的行為,這表示子類別只能添加功能,而不能更改或刪除父類別的功能
- 與父類別實例一起工作的程式碼應該與子類別實例一起工作,而不需要知道該類別已更改
是時候用 JavaScript 程式碼範例來說明里氏替換原理了。看看:
class Person { constructor(name, dateOfBirth, height, country){ this.name = name this.dateOfBirth = dateOfBirth this.height = height this.country = country } } class PersonUtils { static calculateAge(dob) { const today = new Date(); const birthDate = new Date(dob); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if(monthDiff <p>在上面的程式碼片段中,我們建立了兩個子類別(Bicycle 和 Car)和一個超類別(Vehicle)。出於本文的目的,我們為超類別實作了一個方法 (OnEngine)。 </p> <p>LSP 的核心條件之一是子類別應該覆蓋父類別的功能而不破壞程式碼。記住這一點,讓我們看看我們剛剛看到的程式碼片段是如何違反里氏替換原則的。實際上,汽車有發動機並且可以打開發動機,但自行車從技術上講沒有發動機,因此無法打開發動機。因此,Bicycle 無法在不破壞程式碼的情況下重寫 Vehicle 類別中的 OnEngine 方法。 </p><p>我們現在已經確定了違反里氏替換原則的程式碼部分。 Car 類別可以重寫超類別中的 OnEngine 功能,並以區別於其他車輛(例如飛機)的方式實現它,並且程式碼不會中斷。 Car 類滿足里氏替換原則。 </p> <p>在下面的程式碼片段中,我們將說明如何建立符合里氏替換原則的程式碼:<br> </p> <pre class="brush:php;toolbar:false">class Person { constructor(name, age, height, country){ this.name = name this.age = age this.height = height this.country = country } getPersonCountry(){ console.log(this.country) } greetPerson(){ console.log("Hi " + this.name) } static calculateAge(dob) { const today = new Date(); const birthDate = new Date(dob); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if (monthDiff <p>這是具有通用功能「移動」的 Vehicle 類別的基本範例。人們普遍認為所有車輛都會移動;它們只是透過不同的機制移動。我們要說明 LSP 的一種方法是重寫 move() 方法並以描述特定車輛(例如汽車)如何移動的方式實現它。 </p> <p>為此,我們將創建一個 Car 類別來擴展 Vehicle 類別並重寫 move 方法以適應汽車的移動,如下所示:<br> </p> <pre class="brush:php;toolbar:false">class Person { constructor(name, dateOfBirth, height, country){ this.name = name this.dateOfBirth = dateOfBirth this.height = height this.country = country } } class PersonUtils { static calculateAge(dob) { const today = new Date(); const birthDate = new Date(dob); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if(monthDiff <p>我們仍然可以在另一個子車輛類別(例如飛機)中實作 move 方法。 </p> <p>我們的做法如下:<br> </p> <pre class="brush:php;toolbar:false">class Rectangle { constructor(width, height) { this.width = width; this.height = height; } area() { return this.width * this.height; } } class ShapeProcessor { calculateArea(shape) { if (shape instanceof Rectangle) { return shape.area(); } } } const rectangle = new Rectangle(10, 20); const shapeProcessor = new ShapeProcessor(); console.log(shapeProcessor.calculateArea(rectangle));
在上面的兩個範例中,我們說明了繼承和方法重寫等關鍵概念。
注意:允許子類別實作父類別中已定義的方法的程式設計功能稱為方法重寫。
讓我們做一些家事工作並將所有東西放在一起,就像這樣:
class Shape { area() { console.log("Override method area in subclass"); } } class Rectangle extends Shape { constructor(width, height) { super(); this.width = width; this.height = height; } area() { return this.width * this.height; } } class Circle extends Shape { constructor(radius) { super(); this.radius = radius; } area() { return Math.PI * this.radius * this.radius; } } class ShapeProcessor { calculateArea(shape) { return shape.area(); } } const rectangle = new Rectangle(20, 10); const circle = new Circle(2); const shapeProcessor = new ShapeProcessor(); console.log(shapeProcessor.calculateArea(rectangle)); console.log(shapeProcessor.calculateArea(circle));
現在,我們有 2 個子類別繼承並重寫父類別的單一功能,並根據它們的要求實現它。這個新的實作不會破壞程式碼。
介面隔離原則(ISP)
介面隔離原則規定,任何客戶端都不應被迫依賴它不使用的介面。它希望我們創建與特定客戶端相關的更小、更具體的接口,而不是擁有一個大型、單一的接口,迫使客戶端實現他們不需要的方法。
保持介面緊湊使得程式碼庫更易於調試、維護、測試和擴充。如果沒有ISP,大型介面的某個部分的更改可能會迫使程式碼庫的不相關部分發生更改,從而導致我們進行程式碼重構,這在大多數情況下取決於程式碼庫的大小可能是一項艱鉅的任務。
JavaScript 與 Java 等基於 C 的程式語言不同,它沒有內建的介面支援。然而,有一些技術可以在 JavaScript 中實作介面。
介面是類別必須實作的一組方法簽章。
在 JavaScript 中,您將介面定義為具有方法名稱和函數簽名的對象,如下所示:
class Person { constructor(name, age, height, country){ this.name = name this.age = age this.height = height this.country = country } getPersonCountry(){ console.log(this.country) } greetPerson(){ console.log("Hi " + this.name) } static calculateAge(dob) { const today = new Date(); const birthDate = new Date(dob); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if (monthDiff <p>要在 JavaScript 中實現接口,請創建一個類別並確保它包含與接口中指定的名稱和簽名相同的方法:<br> </p> <pre class="brush:php;toolbar:false">class Person { constructor(name, dateOfBirth, height, country){ this.name = name this.dateOfBirth = dateOfBirth this.height = height this.country = country } } class PersonUtils { static calculateAge(dob) { const today = new Date(); const birthDate = new Date(dob); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if(monthDiff <p>現在我們已經弄清楚如何在 JavaScript 中建立和使用介面。我們需要做的下一件事是說明如何在 JavaScript 中隔離接口,以便我們可以看到它們如何組合在一起並使程式碼更易於維護。 </p> <p>在下面的範例中,我們將使用印表機來說明介面隔離的原理。 </p> <p>假設我們有印表機、掃描器和傳真機,讓我們建立一個定義這些物件功能的介面:<br> </p> <pre class="brush:php;toolbar:false">class Rectangle { constructor(width, height) { this.width = width; this.height = height; } area() { return this.width * this.height; } } class ShapeProcessor { calculateArea(shape) { if (shape instanceof Rectangle) { return shape.area(); } } } const rectangle = new Rectangle(10, 20); const shapeProcessor = new ShapeProcessor(); console.log(shapeProcessor.calculateArea(rectangle));
在上面的程式碼中,我們創建了一系列分離或隔離的接口,以反對使用定義所有這些功能的大型接口。透過將這些功能分解為更小的部分和更具體的接口,我們允許不同的客戶端僅實現他們需要的方法,並保留所有其他部分。
下一步,我們將建立實作這些介面的類別。遵循介面隔離原則,每個類別只會實作它需要的方法。
如果我們想要實作一個只能列印文件的基本印表機,我們可以透過printerInterface實作print()方法,如下所示:
class Shape { area() { console.log("Override method area in subclass"); } } class Rectangle extends Shape { constructor(width, height) { super(); this.width = width; this.height = height; } area() { return this.width * this.height; } } class Circle extends Shape { constructor(radius) { super(); this.radius = radius; } area() { return Math.PI * this.radius * this.radius; } } class ShapeProcessor { calculateArea(shape) { return shape.area(); } } const rectangle = new Rectangle(20, 10); const circle = new Circle(2); const shapeProcessor = new ShapeProcessor(); console.log(shapeProcessor.calculateArea(rectangle)); console.log(shapeProcessor.calculateArea(circle));
該類別僅實作PrinterInterface。它不實現掃描或傳真方法。透過遵循介面隔離原則,客戶端(在本例中為 Printer 類別)降低了其複雜性並提高了軟體的效能。
依賴倒置原則(DIP)
現在我們的最後一個原則:依賴倒置原則。此原則表示較高層級的模組(業務邏輯)應該依賴抽象,而不是直接依賴較低層級的模組(具體)。它幫助我們減少程式碼依賴性,並為開發人員提供在更高層級修改和擴展應用程式的靈活性,而不會遇到複雜性。
為什麼依賴倒置原則支持抽象而不是直接依賴?這是因為抽象的引入減少了更改的潛在影響,提高了可測試性(模擬抽象而不是具體實現),並在程式碼中實現了更高程度的靈活性。這條規則使得透過模組化方法擴展軟體元件變得更容易,也幫助我們在不影響高層邏輯的情況下修改低層元件。
遵守 DIP 使程式碼更易於維護、擴展和擴展,從而阻止因程式碼變更而可能出現的錯誤。它建議開發人員在類別之間使用鬆散耦合而不是緊密耦合。一般來說,透過採用優先考慮抽象而不是直接依賴的思維方式,團隊將獲得適應和添加新功能或更改舊組件的敏捷性,而不會造成連鎖反應。在 JavaScript 中,我們可以使用依賴注入方法來實作 DIP,如下所示:
class Person { constructor(name, age, height, country){ this.name = name this.age = age this.height = height this.country = country } getPersonCountry(){ console.log(this.country) } greetPerson(){ console.log("Hi " + this.name) } static calculateAge(dob) { const today = new Date(); const birthDate = new Date(dob); let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); if (monthDiff <p>在上面的基本範例中,Application 類別是依賴資料庫抽象的高階模組。我們建立了兩個資料庫類別:MySQLDatabase 和 MongoDBDatabase。資料庫是低階模組,它們的實例被注入到應用程式運行時中,而無需修改應用程式本身。 </p> <h2> 結論 </h2> <p>SOLID 原則是可擴展、可維護和穩健的軟體設計的基本構建塊。這套原則可以幫助開發人員編寫乾淨、模組化且適應性強的程式碼。 </p> <p>SOLID 原則促進內聚功能、無需修改的可擴展性、物件替換、介面分離以及對特定依賴項的抽象化。請務必將 SOLID 原則整合到您的程式碼中,以防止錯誤並獲得其所有好處。 </p> <hr> <h2> LogRocket:透過了解上下文更輕鬆地調試 JavaScript 錯誤 </h2> <p>偵錯程式碼總是一項乏味的任務。但你越了解自己的錯誤,就越容易糾正它們。 </p> <p>LogRocket 讓您以新的、獨特的方式理解這些錯誤。我們的前端監控解決方案追蹤使用者與 JavaScript 前端的互動,使您能夠準確查看使用者的操作導致了錯誤。 </p> <p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/000/173538566094173.jpg?x-oss-process=image/resize,p_40" class="lazy" alt="SOLID principles for JavaScript"></p> <p>LogRocket 記錄控制台日誌、頁面載入時間、堆疊追蹤、帶有標頭正文的慢速網路請求/回應、瀏覽器元資料和自訂日誌。了解 JavaScript 程式碼的影響從未如此簡單! </p> <p>免費試用。 </p>
以上是JavaScript 的堅實原則的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python和JavaScript的主要區別在於類型系統和應用場景。 1.Python使用動態類型,適合科學計算和數據分析。 2.JavaScript採用弱類型,廣泛用於前端和全棧開發。兩者在異步編程和性能優化上各有優勢,選擇時應根據項目需求決定。

選擇Python還是JavaScript取決於項目類型:1)數據科學和自動化任務選擇Python;2)前端和全棧開發選擇JavaScript。 Python因其在數據處理和自動化方面的強大庫而備受青睞,而JavaScript則因其在網頁交互和全棧開發中的優勢而不可或缺。

Python和JavaScript各有優勢,選擇取決於項目需求和個人偏好。 1.Python易學,語法簡潔,適用於數據科學和後端開發,但執行速度較慢。 2.JavaScript在前端開發中無處不在,異步編程能力強,Node.js使其適用於全棧開發,但語法可能複雜且易出錯。

javascriptisnotbuiltoncorc; sanInterpretedlanguagethatrunsonenginesoftenwritteninc.1)JavascriptwasdesignedAsignedAsalightWeight,drackendedlanguageforwebbrowsers.2)Enginesevolvedfromsimpleterterpretpretpretpretpreterterpretpretpretpretpretpretpretpretpretcompilerers,典型地,替代品。

JavaScript可用於前端和後端開發。前端通過DOM操作增強用戶體驗,後端通過Node.js處理服務器任務。 1.前端示例:改變網頁文本內容。 2.後端示例:創建Node.js服務器。

選擇Python還是JavaScript應基於職業發展、學習曲線和生態系統:1)職業發展:Python適合數據科學和後端開發,JavaScript適合前端和全棧開發。 2)學習曲線:Python語法簡潔,適合初學者;JavaScript語法靈活。 3)生態系統:Python有豐富的科學計算庫,JavaScript有強大的前端框架。

JavaScript框架的強大之處在於簡化開發、提升用戶體驗和應用性能。選擇框架時應考慮:1.項目規模和復雜度,2.團隊經驗,3.生態系統和社區支持。

引言我知道你可能會覺得奇怪,JavaScript、C 和瀏覽器之間到底有什麼關係?它們之間看似毫無關聯,但實際上,它們在現代網絡開發中扮演著非常重要的角色。今天我們就來深入探討一下這三者之間的緊密聯繫。通過這篇文章,你將了解到JavaScript如何在瀏覽器中運行,C 在瀏覽器引擎中的作用,以及它們如何共同推動網頁的渲染和交互。 JavaScript與瀏覽器的關係我們都知道,JavaScript是前端開發的核心語言,它直接在瀏覽器中運行,讓網頁變得生動有趣。你是否曾經想過,為什麼JavaScr


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

SublimeText3 Linux新版
SublimeText3 Linux最新版