S.O.L.I.D – Phần 2: Open / Closed principle

2 min read

Open/Closed Principle

Đối tượng hoặc thực thể nên mở rộng được nhưng đóng lại để sửa đổi.

Nguyên tắc Open/Closed (OCP) yêu cầu rằng các lớp nên mở rộng được để thêm chức năng, nhưng đóng lại để sửa đổi.

Sửa đổi có nghĩa là thay đổi mã của một lớp hiện có, và mở rộng có nghĩa là thêm chức năng mới.

Do đó, nguyên tắc này nói rằng chúng ta nên có thể thêm chức năng mới mà không cần sửa đổi mã hiện có. Điều này bởi vì mỗi khi chúng ta sửa đổi mã hiện có, sẽ có cơ hội để xuất hiện lỗi mới. Chúng ta nên tránh sửa đổi mã sản xuất.

Hãy xem xét một ví dụ. Trong ví dụ trước của chúng ta, InvoicePersistence, chúng ta đang lưu hóa đơn trong tệp. Bây giờ người quản lý/khách hàng của chúng ta muốn lưu hóa đơn vào Cơ sở dữ liệu cũng.

Chúng ta sẽ làm gì? Chúng ta sẽ thêm một hàm mới vào lớp InvoicePersistence, mà sẽ lưu hóa đơn vào Cơ sở dữ liệu.

class InvoicePersistence {
    let invoice: Invoice

    public init(invoice: Invoice) {
        self.invoice = invoice
    }

    public func saveToFile(filename: String) {
        // Creates a file with given name and writes the invoice
    }
    
    public func saveToDataBase(filename: String) {
        // save invoide in Database
    }
    
}

Đúng vậy, dễ như thế sao?

Nhưng đây là vi phạm rõ ràng của nguyên tắc Mở/Rồi Đóng. Để thêm tính năng này, chúng ta đang sửa đổi lớp hiện có, mà đã được kiểm tra và đã được QA kiểm tra, và mã đã được phát hành trên thị trường. Điều này có thể tạo ra lỗi mới và QA cần phải kiểm tra toàn bộ mã lại.

Làm thế nào chúng ta có thể ngăn chặn điều đó? Hãy xem xét ví dụ code sau đây.

protocol InvoicePersistence {
    func save(filename: String)
}

class FilePersistence: InvoicePersistence {
    let invoice: Invoice

    public init(invoice: Invoice) {
        self.invoice = invoice
    }

    public func save(filename: String) {
        // Creates a file with given name and writes the invoice
    }
}

Ở đây, chúng ta đã tạo một giao thức InvoicePersistence với phương thức save, và đã tạo một lớp FilePersistence để lưu hóa đơn vào tệp.

Bây giờ khi người quản lý/khách hàng của chúng ta muốn lưu hóa đơn vào Cơ sở dữ liệu cũng.

Dưới đây là điều chúng ta có thể làm:

class DatabasePersistence: InvoicePersistence {
    let invoice: Invoice

    public init(invoice: Invoice) {
        self.invoice = invoice
    }

    public func save(filename: String) {
        // save invoice in Database
    }
}

Chúng ta đã tạo một lớp DatabasePersistence mới mà kế thừa giao thức InvoicePersistence và trong lớp này, chúng ta có thể lưu hóa đơn vào Cơ sở dữ liệu mà không cần sửa đổi code hiện có.

Tài liệu tham khảo:

Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *