Hiểu về nguyên lý SOLID trong lập trình .NET

Hiểu về nguyên lý SOLID trong lập trình .NET

April 22, 2025 0 By Nam Vu

Nguyên tắc SOLID chắc có vô vàn bài viết nói về nó. Bài viết này mình một lần nữa tổng hợp và đưa ra ý chính giản lược giúp bạn nhớ ngắn gọn, anh em có thể tìm hiểu sâu hơn nhé

Nguyên tắc SOLID giúp lập trình viên viết mã dễ mở rộng và tránh các lỗi phổ biến trong lập trình. Những nguyên tắc này được giới thiệu bởi Robert C. Martin và đã trở thành một phần quan trọng của lập trình hướng đối tượng. Trong bối cảnh phát triển .NET, tuân theo SOLID giúp mã nguồn trở nên linh hoạt, dễ bảo trì và mở rộng hơn.

Dưới đây là năm nguyên tắc thiết kế SOLID:

1. Single Responsibility Principle (SRP) – Nguyên tắc trách nhiệm đơn lẻ

Định nghĩa: Một class chỉ nên có một lý do để thay đổi, nghĩa là nó chỉ nên có một trách nhiệm duy nhất. Điều này giúp mã nguồn dễ hiểu và bảo trì hơn.

Ý tưởng chính: Một class chỉ nên làm một việc và làm tốt việc đó.

Ví dụ thực tế: Một đầu bếp chỉ tập trung vào nấu ăn, không quản lý nhà hàng hay giao hàng.

Ví dụ C# trước khi áp dụng SRP:

public class Report
{
    public void GenerateReport() { }
    public void SaveToFile() { }
}

Class Report có hai trách nhiệm: tạo báo cáo và lưu báo cáo, vi phạm SRP.

Sau khi áp dụng SRP:

public class Report
{
    public void GenerateReport() { }
}

public class ReportSaver
{
    public void SaveToFile() { }
}

Giờ đây, Report chỉ chịu trách nhiệm tạo báo cáo, còn ReportSaver chịu trách nhiệm lưu báo cáo.

2. Open/Closed Principle (OCP) – Nguyên tắc đóng/mở

Định nghĩa: Một class nên mở để mở rộng nhưng đóng để chỉnh sửa. Điều này có nghĩa là bạn có thể thêm tính năng mới mà không cần sửa đổi mã gốc.

Ý tưởng chính: Một class sau khi viết xong không nên sửa đổi nữa, mà chỉ nên mở rộng.

Ví dụ thực tế: Điện thoại thông minh có thể mở rộng tính năng bằng cách tải ứng dụng, mà không cần sửa phần cứng.

Ví dụ C# trước khi áp dụng OCP:

public class Rectangle
{
    public double Width { get; set; }
    public double Height { get; set; }
}

public class AreaCalculator
{
    public double CalculateArea(Rectangle rectangle)
    {
        return rectangle.Width * rectangle.Height;
    }
}

Khi muốn hỗ trợ thêm hình tròn, ta phải sửa AreaCalculator, vi phạm OCP.

Sau khi áp dụng OCP:

public interface IShape
{
    double CalculateArea();
}

public class Rectangle : IShape
{
    // Triển khai phương thức tính diện tích
}

public class Circle : IShape
{
    // Triển khai phương thức tính diện tích
}

Giờ đây, khi thêm hình mới chỉ cần tạo một class mới mà không cần sửa mã cũ.

3. Liskov Substitution Principle (LSP) – Nguyên tắc thay thế Liskov

Định nghĩa: Các đối tượng của lớp cha có thể được thay thế bởi các đối tượng của lớp con mà không làm thay đổi tính đúng đắn của chương trình.

Ý tưởng chính: Có thể sử dụng bất kỳ lớp con nào thay thế lớp cha mà không gây lỗi.

Ví dụ thực tế: Điều khiển TV có thể dùng cho nhiều thương hiệu TV khác nhau.

Ví dụ C# trước khi áp dụng LSP:

public class Bird
{
    public virtual void Fly() { /* implementation */ }
}

public class Penguin : Bird
{
    public override void Fly()
    {
        throw new NotImplementedException("Penguins can't fly!");
    }
}

Class Penguin vi phạm LSP vì ném exception khi gọi Fly().

Sau khi áp dụng LSP:

public interface IFlyable
{
    void Fly();
}

public class Bird : IFlyable
{
    public void Fly() { /* implementation */ }
}

public class Penguin
{
    // Không kế thừa IFlyable vì không thể bay
}

Giờ đây, Penguin không bị buộc phải triển khai phương thức Fly(), đảm bảo tuân thủ LSP.

4. Interface Segregation Principle (ISP) – Nguyên tắc phân tách interface

Định nghĩa: Một class không nên bị ép buộc thực hiện các interface mà nó không sử dụng. Điều này giúp interface trở nên nhỏ gọn và chuyên biệt hơn.

Ý tưởng chính: Một class chỉ nên thực hiện những phương thức cần thiết.

Ví dụ thực tế: Khi đăng ký dịch vụ nghe nhạc, bạn chỉ chọn thể loại yêu thích, không phải tất cả thể loại.

Ví dụ C# trước khi áp dụng ISP:

public interface IWorker
{
    void Work();
    void Eat();
}

public class Manager : IWorker
{
    // Triển khai cả Work và Eat
}

public class Robot : IWorker
{
    // Bị buộc phải triển khai Eat() dù không cần
}

Sau khi áp dụng ISP:

public interface IWorkable
{
    void Work();
}

public interface IEatable
{
    void Eat();
}

public class Manager : IWorkable, IEatable
{
    // Triển khai cả Work và Eat
}

public class Robot : IWorkable
{
    // Chỉ triển khai Work
}

Bằng cách chia nhỏ interface, class chỉ cần triển khai phương thức phù hợp.

5. Dependency Inversion Principle (DIP) – Nguyên tắc đảo ngược sự phụ thuộc

Định nghĩa: Các module cấp cao không nên phụ thuộc vào module cấp thấp, cả hai nên phụ thuộc vào abstraction.

Ý tưởng chính: Module cấp cao không nên phụ thuộc vào module cấp thấp, mà cả hai nên phụ thuộc abstraction.

Ví dụ thực tế: Xây dựng tháp LEGO — các viên gạch kết nối qua các khớp nhỏ (abstraction).

Ví dụ C# trước khi áp dụng DIP:

public class LightBulb
{
    public void TurnOn() { /* implementation */ }
    public void TurnOff() { /* implementation */ }
}

public class Switch
{
    private LightBulb bulb;

    public Switch(LightBulb bulb)
    {
        this.bulb = bulb;
    }

    public void Toggle()
    {
        if (bulb.IsOn)
            bulb.TurnOff();
        else
            bulb.TurnOn();
    }
}

Class Switch phụ thuộc trực tiếp vào LightBulb, vi phạm DIP.

Sau khi áp dụng DIP:

public interface ISwitchable
{
    void TurnOn();
    void TurnOff();
}

public class LightBulb : ISwitchable
{
    // Triển khai ISwitchable
}

public class Switch
{
    private ISwitchable device;

    public Switch(ISwitchable device)
    {
        this.device = device;
    }

    public void Toggle()
    {
        if (device.IsOn)
            device.TurnOff();
        else
            device.TurnOn();
    }
}

Giờ đây, Switch không phụ thuộc trực tiếp vào LightBulb, mà vào abstraction ISwitchable, đảm bảo tuân thủ DIP.

Hiểu và áp dụng SOLID giúp anh em lập trình viên .NET viết mã sạch, dễ mở rộng và bảo trì hơn. Các nguyên tắc này bổ trợ lẫn nhau, góp phần xây dựng phần mềm linh hoạt và hiệu quả.

Bạn có góp ý hay bài viết hay ho khác về Solid thì có thể comment bên dưới nhé!

#ntechdevelopers