> Just like we have grammar rules and linguistic structures to frame our words and feelings into comprehensible sentences, we have design patterns and principles to shape our code. > > ... > > SOLID, DRY, KISS, and YAGNI are not merely principles but are cornerstones of crafting good code. > > [Good code is like a love letter to the next developer who will maintain it.](https://addyosmani.com/blog/good-code/) 文章中提及的 SOLID, DRY, KISS, YAGNI 都是設計原則,這些設計原則目的都是為了提高程式碼可讀性、提升可維護性。 ## KISS - Keep It Simple & Stupid > The simplest explanation tends to be the right one. **讓你的程式碼盡可能簡單。** Martin Flower 在 [Refactoring](https://martinfowler.com/books/refactoring.html) 這本書中有一句經典的話: "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." 任何一個傻瓜都能寫出計算機可以理解的程式,只有寫出人類容易理解的程式才是優秀的工程師。其實不光是程式,這個原則也可以延伸到產品的設計、業務的設計、專案結構的設計上。 ```js // 不符合 KISS function validateUserInput(input) { // 驗證邏輯包含多層巢狀條件 if (input && input.length >= 5 && input.length <= 20 && /^[A-Za-z0-9]+$/.test(input)) { return true; } return false; } // 符合 KISS function validateUserInput(input) { // 驗證邏輯簡單明瞭 return /^[A-Za-z0-9]+$/.test(input); } ``` ## DRY - Don't Repeat Yourself > A basic strategy for reducing complexity to managable units is to divide a system into pieces. **避免程式中過多重複的程式碼**。 如果是相同的邏輯在不同的地方使用,可以抽象出來複用,而不是將一樣的程式碼每個地方都寫一遍。 ```js // 不符合 DRY function calculateCircleArea(radius) { return Math.PI * radius * radius; } function calculateCircleCircumference(radius) { return 2 * Math.PI * radius; } // 符合 DRY function calculateCircleArea(radius) { const pi = Math.PI; const area = pi * radius * radius; const circumference = 2 * pi * radius; return { area, circumference }; } ``` 但以這個例子來說,修改後的函數又不太符合 KISS 了,所以實際開發中需要在 DRY, KISS 之間找到平衡。 ## YAGNI - You Ain't Gonna Need It > Coding is about building things. **只有當你需要的時候才去實現功能,不要過早設計、優化。** 這個原則鼓勵開發時專注於及時提供最重要的功能,透過避免實現不必要的功能以優化開發時間、降低複雜性並提高靈活性。 ## SOLID SOLID 原則又包含了五個原則: - **S**RP — Single Responsibility Principle - **O**CP — Open Closed Principle - **L**SP — Liskov Substitution Principle - **I**SP — Interface Segregation Principle - **D**IP — Dependency Inversion Principle ### SRP — Single Responsibility Principle **單一責任原則。** 一個類別或模組應該僅有一個單一的責任,這有助於確保程式碼結構清晰,更容易維護。 可以通過將不同的功能抽離到不同的類別或模組中來實現。 ```js // 不符合 SRP class User { constructor(name) { this.name = name; } calculateTotalScore() { // 計算使用者的總分數 } sendEmail() { // 發送郵件給使用者 } } // 符合 SRP class User { constructor(name) { this.name = name; } calculateTotalScore() { // 計算使用者的總分數 } } class EmailService { sendEmail(user) { // 發送郵件給使用者 } } ``` ### OCP — Open Closed Principle **開放/封閉原則。** - close: 要有一個不會改變的抽象 - open: 要能夠提供彈性的實作 大概意思為當需要添加新功能或進行變更時,不應該修改現有的程式碼,而應該通過擴展現有程式碼來實現。 ```js // 定義一個抽象類別 class Shape { constructor() { if (new.target === Shape) { throw new Error("Shape 類別是抽象的,不能直接實例化。"); } } // 定義抽象方法 calculateArea() { throw new Error("子類別必須實現 calculateArea 方法。"); } } // 具體的子類別實現抽象方法 class Circle extends Shape { constructor(radius) { super(); this.radius = radius; } calculateArea() { return Math.PI * this.radius * this.radius; } } class Rectangle extends Shape { constructor(width, height) { super(); this.width = width; this.height = height; } calculateArea() { return this.width * this.height; } } ``` > 推薦閱讀:[深入淺出開放封閉原則 Open-Closed Principle](https://www.jyt0532.com/2020/03/19/ocp/) ### LSP — Liskov Substitution Principle **里氏替換原則。** 子類別應該能夠替換其父類別,而不會影響系統的正確性。 這有助於確保繼承層次結構的一致性。 ```js // 不符合 LSP class Bird { fly() { // 實現飛行邏輯 } } class Ostrich extends Bird { // 鴕鳥無法飛行,但必須實現 fly 方法 } // 符合 LSP class Bird { // 父類別不強制實現 fly 方法 } class Sparrow extends Bird { fly() { // 實現飛行邏輯 } } class Ostrich extends Bird { // 鴕鳥不需要實現 fly 方法 } ``` ### ISP — Interface Segregation Principle **介面分離原則。** 不應強迫類別實現它們不需要使用的介面。 這有助於避免過多的依賴和對不必要方法的實現。 ```js // 不符合 ISP class Worker { work() { // 執行工作 } eat() { // 這個方法不應該存在於 Worker 類別 } } // 符合 ISP class Worker { work() { // 執行工作 } } class Eater { eat() { // 執行進食 } } ``` ### DIP — Dependency Inversion Principle **相依反轉原則。** 高層次模組不應該依賴於低層次模組,兩者都應該依賴於抽象。 這有助於實現**鬆散耦合**。 ```js // 不符合 DIP class LightBulb { turnOn() { // 打開燈泡 } } class Switch { constructor(bulb) { this.bulb = bulb; } operate() { this.bulb.turnOn(); } } // 符合 DIP class Device { operate() {} } class LightBulb extends Device { turnOn() { // 打開燈泡 } } class Switch { constructor(device) { this.device = device; } operate() { this.device.operate(); } } ```
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.