SOLID là nguyên tắc thiết kế, phát triển phần mềm giúp code dễ đọc, code tốt, code dễ bảo trì, ượ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): Một class chỉ nên giữ 1 trách nhiệm duy nhất
  • O stands for open closed principle (OCP): Có thể thoải mái mở rộng 1 class, nhưng không được sửa đổi bên trong class đó
  • L Liskov substitution principle (LSP): trong 1 chương trình, các object của class con có thể thay thế class cha mà không làm thay đổi tính đúng đắn của chương trình
  • I 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 nhiều mục đích cụ thể
  • D Dependency injection principle (DIP): các module cấp cao không nên phụ thuộc vào các module cấp thấp, cả 2 nên phụ thuộc vào abstract interface(abtraction) không nên phụ thuộc vào chi tiết mà ngược lại( các class giao tiếp với nhau qua interface, không phải qua implement)

Example:

Single responsibility principle

using System;
using System.Collections.Generic;
using System.Linq;
namespace Hondanho {
// entity: đai diện cho các field database
public class Student {
public int StudentID { get; set; }
public string StudentName { get; set; }
public int Age { get; set; }
public List<Student> GetAllStudent() {
return new List<Student> {
new Student() { StudentID = 1, StudentName = "Ali", Age = 18 },
};
}
}
// repository: giao tiếp với data layer, lấy data từ database
public interface IStudentRepository {
List<Student> GetAllStudent();
Student GetStudent(int id);
}
public class StudentRepository : IStudentRepository {
public Student _student { get; set; }
public StudentRepository(Student student) {
_student = student;
}
public List<Student> GetAllStudent() => _student.GetAllStudent();
public Student GetStudent(int id) => _student.GetAllStudent().FirstOrDefault(x => x.StudentID == id);
}
// service: sử lý nghiệp vụ logic phức tạp tại đây
public interface IServiceStudent {
List<Student> GetAllStudent();
Student GetStudent(int id);
}
public class ServiceStudent : IServiceStudent {
private IStudentRepository _studentRepository;
public ServiceStudent(IStudentRepository studentRepository) { // đưa các class StudentRepository vào hàm khởi tạo
_studentRepository = studentRepository;
}
public List<Student> GetAllStudent() => _studentRepository.GetAllStudent();
public Student GetStudent(int id) => _studentRepository.GetStudent(id);
}
public class Program {
public static void Main(string[] args) {
var student = new Student();
IStudentRepository repositoryStudent = new StudentRepository(student); // inject student vào StudentRepository
IServiceStudent studentService = new ServiceStudent(repositoryStudent); // inject StudentRepository vào ServiceStudent
var listStudent = studentService.GetAllStudent();
listStudent.ForEach(std => Console.WriteLine("Student Name: " + std.StudentName));
}
}
}
/* - trong ví dụ này, class StudentRepository dùng để lấy dữ liệu từ database
* - class ServiceStudent dùng để xử lý logic lấy data của student
* - trong trường hợp cần báo cáo về tình hình học tập, thì lại làm 1 class Report chuyên dùng để report, khi có bất cứ thay đổi nào từ class report cũng ko ảnh hướng tới student
* - Tóm lại: nên tách các công việc sử lý ra theo từng chứ năng riêng biệt, mục đích không để ảnh hưởng tới code trước đó: ko phải test các code trước đó, gọn gàng dễ hiểu, ..
*/

Stands for open closed principle

using System.Composition;
using System.Threading.Tasks;
namespace Hondanho {
public class ReportStudent {
public string typeExport { get; set; }
public void ExportStudent(string type) {
if(type == "csv") {
// doing csv
} else if(type == "pdf") {
// doing pdf
}
}
}
}
/*
trong trường hợp thêm 1 cái type là html thì phải sửa hàm ExportStudent, vấn đề là ở đó, không nên sửa code cũ khi chức năng mở rộng,
thay vào đó tạo thành 1 class khác xem ví dụ dưới đây
*/
namespace Hondanho {
public interface IGenerateRerport {
public Task Export();
}
public class ExportCsv : IGenerateRerport {
public async Task Export() {
// doing export csv
}
}
public class ExportPdf : IGenerateRerport {
public async Task Export() {
// doing export pdf
}
}
public class ReportStudent {
private IGenerateRerport _generateRerport { get; set; }
public ReportStudent(IGenerateRerport generateRerport) {
}
public void ExportStudent() {
_generateRerport.Export();
}
}
public class Program {
public static void Main(string[] args) {
var reportStudentPdf = new ReportStudent(new ExportPdf());
reportStudentPdf.ExportStudent();
var reportStudentCsv = new ReportStudent(new ExportPdf());
reportStudentCsv.ExportStudent();
}
}
}
view raw OpenClose.cs hosted with ❤ by GitHub

Liskov substitution principle

using System;
using System.Composition;
using System.Threading.Tasks;
namespace Hondanho {
public class ReportStudent {
public string typeExport { get; set; }
public virtual void ExportStudent(string type) {
Console.WriteLine("class cha");
}
}
public class ReportStudentSummaryA : ReportStudent {
public override void ExportStudent(string type) {
Console.WriteLine("class Con A");
}
}
public class ReportStudentSummaryB : ReportStudent {
public override void ExportStudent(string type) {
Console.WriteLine("class Con B");
}
}
}
/* vấn đề ở đây là 2 class đều ghi đè 1 method của thg cha, nên ko kiểm soát được code, dễ phát sinh bug
nên tách ReportStudentSummaryB thành 1 class mới
*/
view raw gistfile1.txt hosted with ❤ by GitHub

Interface segregation principle

Nên chia interface lớn thành các interface nhỏ khác nhau, việc gom quá nhiều method thì sẽ khó implement(các class kế thừa phải implement tất cả các method của interface đó)

Dependency inversion principle

Xem tại đây