Nguyên Lý SOLID trong C#

1. Định nghĩa SOLID là nguyên tắc thiết kế, phát triển phần mềm. Mục đích giúp cho lập trình viên viết code tốt hơn, dễ đọc, dễ bảo trì. SOLID được ghép lại từ 5 chữ viết tắt đầu tiên của 5 nguyên tắc này: S is single responsibility principle (SRP) O stands for open

1. Định nghĩa

  • SOLID là nguyên tắc thiết kế, phát triển phần mềm. Mục đích giúp cho lập trình viên viết code tốt hơn, dễ đọc, dễ bảo trì.
    image.png

  • SOLID được ghép lại từ 5 chữ viết tắt đầu tiên của 5 nguyên tắc này:

    • S is single responsibility principle (SRP)
    • O stands for open closed principle (OCP)
    • L Liskov substitution principle (LSP)
    • I interface segregation principle (ISP)
    • D Dependency injection principle (DIP)

2.1 . Single responsibility principle (SRP)

  • Một class chỉ nên giữ 1 trách nhiệm duy nhất (Chỉ có thể sửa đổi class với 1 lý do duy nhất)

  • Mục đích là để dễ đọc, dễ quản lý. Thử tưởng tượng bạn có một class làm quá nhiều việc, khi đó bạn nhìn vào sẽ rất rối và việc sửa đổi cũng tốn nhiều công sức => thiết kế tồi nhét tất cả mọi thứ vào một class.

  • Ví dụ thực tế nếu bạn thiết kế một dụng cụ vừa là búa vừa là cờ lê thì rất khó sử dụng => thiết kế tồi. Tốt hơn là nên chia ra riêng. Một dụng cụ búa, một dụng cụ là cờ lê.

image.png

publicclassEmployee{publicint Employee_Id {get;set;}publicstring Employee_Name {get;set;}/// <summary>/// This method used to insert into employee table/// </summary>/// <param name="em">Employee object</param>/// <returns>Successfully inserted or not</returns>publicboolInsertIntoEmployeeTable(Employee em){// Insert into employee table.returntrue;}/// <summary>/// Method to generate report/// </summary>/// <param name="em"></param>publicvoidGenerateReport(Employee em){// Report generation with employee data using crystal report.}}
  • Phân tích: Class ‘Employee’ có 2 trách nhiệm, một là trách nhiệm thao tác với cơ sở dữ liệu và cái kia là tạo ra báo cáo. Lớp Employee không nên đảm nhận việc tạo ra báo cáo vì giả sử đến một ngày khách hàng yêu cầu phải tạo ra báo cáo trong Excel hoặc bất cứ định dạng nào khác, class này lại phải thay đổi cho phù hợp => Điều này là không tốt.

  • Giải pháp: Chúng ta nên viết sang một class khác cho việc tạo báo cáo, vậy khi có bất cứ sự thay đổi nào với việc tạo báo cáo, sẽ không ảnh hưởng đến class Employee.

publicclassReportGeneration{/// <summary>/// Method to generate report/// </summary>/// <param name="em"></param>publicvoidGenerateReport(Employee em){// Report reneration with employee data.}}

2.2 Open closed principle (OCP)

  • Nguyên lý thứ hai, tương ứng với chữ O trong SOLID. Nội dung nguyên lý:

Có thể mở rộng 1 class, nhưng không được sửa đổi bên trong class đó

publicclassReportGeneration{/// <summary>/// Report type/// </summary>publicstring ReportType {get;set;}/// <summary>/// Method to generate report/// </summary>/// <param name="em"></param>publicvoidGenerateReport(Employee em){if(ReportType =="CRS"){// Report generation with employee data in Crystal Report.}if(ReportType =="PDF"){// Report generation with employee data in PDF.}}}
  • Phân tích: Vấn đề xảy ra khi bạn muốn thêm nhiều loại export như xls, word, xlsx,… lúc này bạn phải sửa class cũ ReportGeneration và thêm vào nhiều if bên trong phương thức GenerateReport.
    => Việc chỉnh sửa class này không được khuyến khích, ta sẽ tìm giải pháp khác để mở rộng class ban đầu.

  • Giải pháp: Ta sẽ sử dụng một interface để trừu tượng hóa chức năng tạo report. Khi có thêm loại report ta chỉ cần tạo class cụ thể và sử dụng interface này.

publicinterfaceIReportGeneration{publicvoidGenerateReport(Employee em);}/// <summary>/// Class to generate Crystal report/// </summary>publicclassCrystalReportGeneraion:IReportGeneration{publicvoidGenerateReport(Employee em){// Generate crystal report.}}/// <summary>/// Class to generate PDF report/// </summary>publicclassPDFReportGeneraion:IReportGeneration{publicvoidGenerateReport(Employee em){// Generate PDF report.}}

2.3 Liskov substitution principle (LSP)

  • Class con không nên phá vỡ các định nghĩa và hành vi của class cha.

  • Ví dụ: Employee là lớp cha của Developer và BotDeveloper. Hai class này kết thừa từ Employee.

publicabstractclassEmployee{publicstring Empcode {get;set;}publicstring FristName {get;set;}publicstring LastName {get;set;}publicvirtualstringGetFullname(){return$"{FirstName}{LastName}";}}publicclassDeveloper:Employee{publicoverridestringGetFullname(){return$"Developer - {FirstName}{LastName}";}}publicclassBotDeveloper:Employee{publicoverridestringGetFullname(){thrownewNotImplementedException();}}List<Employee> employeeList =newList<Employee>();
employeeList.Add(newDeveloper());
employeeList.Add(newBotDeveloper());foreach(Employee e in employeeList){
    e.GetFullname();}
  • Vấn đề: class nhân viên máy móc đã phá vỡ các định nghĩa và hành vi của class cha là class nhân viên – một nhân viên là con người.

  • Giải pháp: BotDeveloper không được kế thừa Employee và nên được tách riêng ra 1 class mới.

2.4 Interface segregation principle (ISP)

  • Thay vì dùng 1 interface lớn, ta nên tách thành nhiều interface nhỏ, với các mục đích khác nhau.
publicinterfaceIExportReport{voidExportExcelReport();voidExportPdfReport();}
  • Vấn đề: có những nhân viên chỉ cần chức năng export excel, có nhân viên cần cả 2 chức năng. Lúc này nếu gom chung lại thì ta phải implement lại tất cả phương thức.
  • Giải pháp: Tách nhỏ mỗi loại export ra thành các interface nhỏ.
publicinterfaceIExportPdfFile{voidExportPdfReport();}publicinterfaceIExportExcelReport{voidExportExcelReport();}

2. 5 Dependency inversion principle (DIP)

  • Không nên viết code gắn chặt với nhau bởi vì sẽ là cơn ác mộng cho việc bảo trì khi ứn dụng trở lên lớn dần. Nếu một class phụ thuộc một class khác, bạn sẽ cần phải thay đổi class đó nếu một trong những class phụ thuộc phải thay đổi. Chúng ta nên cố gắng viết các class ít phụ thuộc nhất có thể.

image.png

  • Giảm sự phụ thuộc bằng cách inject (tiêm) các class phụ thuộc vào class dùng class đó.

Tham khảo:

Nguồn: viblo.asia

Bài viết liên quan

Thay đổi Package Name của Android Studio dể dàng với plugin APR

Nếu bạn đang gặp khó khăn hoặc bế tắc trong việc thay đổi package name trong And

Lỗi không Update Meta_Value Khi thay thế hình ảnh cũ bằng hình ảnh mới trong WordPress

Mã dưới đây hoạt động tốt có 1 lỗi không update được postmeta ” meta_key=

Bài 1 – React Native DevOps các khái niệm và các cài đặt căn bản

Hướng dẫn setup jenkins agent để bắt đầu build mobile bằng jenkins cho devloper an t

Chuyển đổi từ monolith sang microservices qua ví dụ

1. Why microservices? Microservices là kiến trúc hệ thống phần mềm hướng dịch vụ,