阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈

SOLID 原則是一組指導原則,可協助軟體開發人員設計健壯、可擴充且可維護的系統。這些原則由 Robert C. Martin(Bob 叔叔)提出,對於物件導向程式設計建立靈活且可重複使用的程式碼至關重要。

在這篇文章中,我們將深入研究每個 SOLID 原則,解釋其目的,並提供 Java 範例來演示其應用程式。

1.單一職責原則(SRP)

定義:一個類別應該只有一個改變的理由。這意味著一個類別應該只有一項工作或職責。

為什麼建議零售價很重要

當一個類別具有多個職責時,對一項職責的變更可能會影響或破壞程式碼的其他部分。透過遵守 SRP,我們確保了更好的可維護性和可測試性。

例子

// Violating SRP: A class that handles both user authentication and database operations.
class UserManager {
    public void authenticateUser(String username, String password) {
        // Authentication logic
    }

    public void saveUserToDatabase(User user) {
        // Database logic
    }
}

// Following SRP: Separate responsibilities into distinct classes.
class AuthService {
    public void authenticateUser(String username, String password) {
        // Authentication logic
    }
}

class UserRepository {
    public void saveUserToDatabase(User user) {
        // Database logic
    }
}

在此範例中,AuthService 處理身份驗證,UserRepository 管理資料庫操作。每個類別都有一個單一的職責,使程式碼更乾淨、更模組化。

  1. 開閉原理(OCP)

定義:類別應該對擴充開放,但對修改關閉。這意味著您應該能夠在不更改現有程式碼的情況下新增功能。

為什麼 OCP 很重要

當您修改現有程式碼時,您可能會面臨引入錯誤的風險。 OCP 透過繼承或組合來促進功能擴展,而不是改變原始實作。

例子

// Violating OCP: Adding a new discount type requires modifying the existing code.
class DiscountCalculator {
    public double calculateDiscount(String discountType, double amount) {
        if ("NEWYEAR".equals(discountType)) {
            return amount * 0.10;
        } else if ("BLACKFRIDAY".equals(discountType)) {
            return amount * 0.20;
        }
        return 0;
    }
}

// Following OCP: Use polymorphism to add new discount types without changing existing code.
interface Discount {
    double apply(double amount);
}

class NewYearDiscount implements Discount {
    public double apply(double amount) {
        return amount * 0.10;
    }
}

class BlackFridayDiscount implements Discount {
    public double apply(double amount) {
        return amount * 0.20;
    }
}

class DiscountCalculator {
    public double calculateDiscount(Discount discount, double amount) {
        return discount.apply(amount);
    }
}

現在,新增的折扣類型只需建立一個實作 Discount 介面的新類別。

3.里氏替換原理(LSP)

定義:子類型必須可以替換其基底類型,而不改變程式的正確性。

為什麼 LSP 很重要

使用多態性時,違反 LSP 可能會導致意外的行為和錯誤。派生類別必須遵守其基底類別定義的契約。

例子

// Violating LSP: A subclass changes the behavior of the parent class in an unexpected way.
class Bird {
    public void fly() {
        System.out.println("Flying...");
    }
}

class Penguin extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException("Penguins can't fly!");
    }
}

// Following LSP: Refactor the hierarchy to honor substitutability.
abstract class Bird {
    public abstract void move();
}

class FlyingBird extends Bird {
    public void move() {
        System.out.println("Flying...");
    }
}

class Penguin extends Bird {
    public void move() {
        System.out.println("Swimming...");
    }
}

透過重新設計層次結構,FlyingBird 和 Penguin 在替換 Bird 時都能正確運作。

  1. 介面隔離原則(ISP)

定義:不應強迫客戶端實作他們不使用的介面。相反,建立更小、更具體的介面。

為什麼 ISP 很重要

大型介面迫使實作類別包含它們不需要的方法。這會導致程式碼臃腫和不必要的依賴關係。

例子

// Violating ISP: A large interface with unrelated methods.
interface Worker {
    void work();
    void eat();
}

class Robot implements Worker {
    public void work() {
        System.out.println("Working...");
    }

    public void eat() {
        // Robots don't eat, but they're forced to implement this method.
        throw new UnsupportedOperationException("Robots don't eat!");
    }
}

// Following ISP: Split the interface into smaller, focused interfaces.
interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

class Robot implements Workable {
    public void work() {
        System.out.println("Working...");
    }
}

class Human implements Workable, Eatable {
    public void work() {
        System.out.println("Working...");
    }

    public void eat() {
        System.out.println("Eating...");
    }
}

現在,Robot只實作了Workable接口,避免了不必要的方法。

  1. 依賴倒置原則(DIP)

定義:高層模組不應該依賴低層模組。兩者都應該依賴抽象。

為什麼 DIP 很重要

對具體實現的直接依賴使程式碼變得僵化且難以測試。 DIP 提倡使用抽象(介面)來解耦元件。

例子

// Violating DIP: High-level class depends on a low-level implementation.
class MySQLDatabase {
    public void connect() {
        System.out.println("Connecting to MySQL...");
    }
}

class UserService {
    private MySQLDatabase database;

    public UserService() {
        this.database = new MySQLDatabase(); // Tight coupling
    }

    public void performDatabaseOperation() {
        database.connect();
    }
}

// Following DIP: High-level class depends on an abstraction.
interface Database {
    void connect();
}

class MySQLDatabase implements Database {
    public void connect() {
        System.out.println("Connecting to MySQL...");
    }
}

class UserService {
    private Database database;

    public UserService(Database database) {
        this.database = database; // Depend on abstraction
    }

    public void performDatabaseOperation() {
        database.connect();
    }
}

// Usage
Database db = new MySQLDatabase();
UserService userService = new UserService(db);
userService.performDatabaseOperation();

透過這種設計,您可以輕鬆地交換資料庫實作(例如,PostgreSQL、MongoDB),而無需修改 UserService 類別。

結論

SOLID 原則是建立可維護、可擴展且健壯的軟體的強大工具。快速回顧一下:

  1. SRP:一類,一責。

  2. OCP:在不修改現有程式碼的情況下擴充功能。

  3. LSP:子類型必須可替換其基本類型。

  4. ISP:偏好較小的、集中的介面。

  5. DIP:取決於抽象,而不是具體實作。

透過應用這些原則,您的程式碼將更容易理解、測試並適應不斷變化的需求。從小處開始,根據需要進行重構,並逐漸將這些原則融入您的開發過程中!


原文出處:https://dev.to/be11amer/understanding-solid-principles-in-software-design-2b3


共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。

阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈