Open/Closed Principle (OCP - Nguyên tắc Mở/Đóng)

“Các lớp nên MỞ để mở rộng nhưng ĐÓNG để sửa đổi.”

Ý tưởng chính: giữ cho mã hiện có không bị hỏng khi triển khai tính năng mới. Câu nói vui của tác giả: “Cái gì đang chạy rồi thì đừng sửa nó” - hạn chế tối đa sửa code cũ, ưu tiên mở rộng và “thay thế” nếu có thể.

Ví dụ: hệ thống tính phí vận chuyển

Ban đầu chỉ hỗ trợ xe tải, ShippingService phụ thuộc trực tiếp TruckShipping. Cần thêm tàu hỏa → thay vì sửa class hiện có, mở rộng bằng interface:

<?php
interface ShippingMethod {
    public function calculateCost($weight);
}
 
class TruckShipping implements ShippingMethod {
    public function calculateCost($weight) { return $weight * 1.5; }
}
 
class TrainShipping implements ShippingMethod {
    public function calculateCost($weight) { return $weight * 1.2; }
}
 
class ShippingService {
    private $shipping;
 
    // Dependency injection: nhận bất kỳ ShippingMethod nào
    public function __construct(ShippingMethod $shipping) {
        $this->shipping = $shipping;
    }
 
    public function calculate($weight) {
        return $this->shipping->calculateCost($weight);
    }
}
 
echo (new ShippingService(new TruckShipping()))->calculate(100); // 150
echo (new ShippingService(new TrainShipping()))->calculate(100); // 120

Cần thêm AirShipping? Chỉ tạo class mới implement ShippingMethod - không thay đổi class hiện có.

Khi nào cần OCP nhất

Website TMĐT được yêu cầu thêm phương thức vận chuyển mới - nếu mọi thứ về vận chuyển viết chung trong class Order, bạn phải tác động trực tiếp code cũ và hy vọng không hỏng gì (định lý Murphy: chắc chắn sẽ hỏng 😄). Lúc này Strategy Pattern sẽ giải cứu: trích xuất phương thức vận chuyển sang các class riêng, implement chung interface.

Liên quan