- vừa được xem lúc

Tổng quan về nguyên tắc Solid

0 0 2

Người đăng: Huy Trần

Theo Viblo Asia

Giới thiệu về SOLID

image.png

SOLID là từ viết tắt của năm nguyên tắc thiết kế hướng đối tượng (Object-Oriented Design - OOD) do Robert C. Martin (còn được gọi là Uncle Bob) đề xuất.

Lưu ý: Mặc dù các nguyên tắc này có thể áp dụng cho nhiều ngôn ngữ lập trình khác nhau, nhưng các ví dụ trong bài viết này sẽ sử dụng PHP.

Những nguyên tắc này giúp lập trình viên phát triển phần mềm dễ bảo trì và mở rộng khi dự án phát triển. Áp dụng chúng có thể giúp tránh các code smells, cải thiện khả năng tái cấu trúc và phát triển phần mềm theo hướng Agile hoặc Adaptive.

SOLID bao gồm:

S - Nguyên tắc đơn trách nhiệm (Single-responsibility Principle - SRP)

O - Nguyên tắc mở-đóng (Open-closed Principle - OCP)

L - Nguyên tắc thay thế Liskov (Liskov Substitution Principle - LSP)

I - Nguyên tắc phân tách giao diện (Interface Segregation Principle - ISP)

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

Nguyên tắc đơn trách nhiệm (SRP)

Nguyên tắc SRP phát biểu rằng:Một lớp chỉ nên có một lý do để thay đổi, nghĩa là một lớp chỉ nên có một trách nhiệm duy nhất.

Ví dụ, giả sử chúng ta có một ứng dụng tính tổng diện tích của một tập hợp các hình như hình tròn và hình vuông. Trước tiên, chúng ta tạo các lớp hình học và định nghĩa constructor để thiết lập các tham số cần thiết.

class Square
{ public $length; public function __construct($length) { $this->length = $length; }
} class Circle
{ public $radius; public function __construct($radius) { $this->radius = $radius; }
}

Sau đó, tạo lớp AreaCalculator để tính tổng diện tích các hình.

class AreaCalculator
{ protected $shapes; public function __construct($shapes = []) { $this->shapes = $shapes; } public function sum() { $area = []; foreach ($this->shapes as $shape) { if ($shape instanceof Square) { $area[] = pow($shape->length, 2); } elseif ($shape instanceof Circle) { $area[] = pi() * pow($shape->radius, 2); } } return array_sum($area); }
}

Khi cần xuất kết quả, chúng ta sử dụng phương thức output():

public function output()
{ return "Tổng diện tích các hình: " . $this->sum();
}

Vấn đề ở đây là AreaCalculator vừa tính toán diện tích vừa đảm nhiệm việc xuất kết quả. Nếu sau này cần xuất kết quả ở định dạng khác (ví dụ JSON), chúng ta phải chỉnh sửa lớp này, vi phạm nguyên tắc SRP.

Giải pháp: Tạo một lớp riêng để xử lý đầu ra:

class SumCalculatorOutputter
{ protected $calculator; public function __construct(AreaCalculator $calculator) { $this->calculator = $calculator; } public function JSON() { return json_encode(['sum' => $this->calculator->sum()]); } public function HTML() { return "Tổng diện tích các hình: " . $this->calculator->sum(); }
}

Bây giờ, việc tính toán và hiển thị đã được tách biệt, tuân thủ nguyên tắc SRP.

Nguyên tắc mở-đóng (OCP)

OCP phát biểu rằng: Một lớp nên được mở rộng mà không cần sửa đổi mã nguồn ban đầu.

Tiếp tục với AreaCalculator, giả sử chúng ta muốn hỗ trợ thêm các hình khác như tam giác, lục giác...

Nếu cứ thêm các khối if-else, mã sẽ ngày càng phức tạp, vi phạm OCP.

Giải pháp: Chúng ta đưa logic tính diện tích vào từng lớp hình và sử dụng giao diện ShapeInterface:

interface ShapeInterface
{ public function area();
} class Square implements ShapeInterface
{ public $length; public function __construct($length) { $this->length = $length; } public function area() { return pow($this->length, 2); }
} class Circle implements ShapeInterface
{ public $radius; public function __construct($radius) { $this->radius = $radius; } public function area() { return pi() * pow($this->radius, 2); }
}

Bây giờ, AreaCalculator có thể gọi area() mà không cần quan tâm đến từng loại hình:

public function sum()
{ $area = []; foreach ($this->shapes as $shape) { if ($shape instanceof ShapeInterface) { $area[] = $shape->area(); } } return array_sum($area);
}

Điều này đảm bảo rằng chúng ta có thể thêm hình mới mà không cần chỉnh sửa AreaCalculator, tuân thủ OCP.

Nguyên tắc thay thế Liskov (LSP)

LSP phát biểu rằng: Một lớp con phải có thể thay thế lớp cha của nó mà không làm thay đổi tính đúng đắn của chương trình.

Ví dụ, giả sử chúng ta mở rộng AreaCalculator thành VolumeCalculator:

class VolumeCalculator extends AreaCalculator
{ public function sum() { return $summedData; // Giá trị đơn thay vì mảng }
}

Điều này giúp VolumeCalculator có thể thay thế AreaCalculator mà không gây lỗi khi xuất kết quả, tuân thủ LSP.

Nguyên tắc phân tách giao diện (ISP)

ISP phát biểu rằng: Một giao diện không nên ép các lớp triển khai những phương thức mà chúng không sử dụng.

Giả sử chúng ta có một giao diện ShapeInterface với phương thức volume(), nhưng hình vuông không có thể tích.

Giải pháp:Tách giao diện:

interface ShapeInterface
{ public function area();
} interface ThreeDimensionalShapeInterface
{ public function volume();
}

Bây giờ, hình khối ba chiều như Cuboid sẽ triển khai ThreeDimensionalShapeInterface, còn hình phẳng chỉ cần ShapeInterface, tuân thủ ISP.

Nguyên tắc đảo ngược sự phụ thuộc (DIP)

DIP phát biểu rằng:Các module cấp cao không nên phụ thuộc vào các module cấp thấp. Cả hai nên phụ thuộc vào abstraction.

Ví dụ, PasswordReminder phụ thuộc vào MySQLConnection:

class MySQLConnection
{ public function connect() { return 'Database connection'; }
} class PasswordReminder
{ private $dbConnection; public function __construct(MySQLConnection $dbConnection) { $this->dbConnection = $dbConnection; }
}

Nếu thay đổi database, chúng ta phải chỉnh sửa PasswordReminder, vi phạm DIP.

Giải pháp: Tạo interface DBConnectionInterface:

interface DBConnectionInterface
{ public function connect();
} Và sử dụng interface trong PasswordReminder: class PasswordReminder
{ private $dbConnection; public function __construct(DBConnectionInterface $dbConnection) { $this->dbConnection = $dbConnection; }
}

Bây giờ, PasswordReminder không bị ràng buộc vào một loại database cụ thể, tuân thủ DIP.

Kết luận

Bằng cách phân tích từng chữ cái một trong SOLID, chúng ta đã đi sâu vào ý nghĩa của từng nguyên tắc cũng như những cách áp dụng phù hợp được minh họa qua ví dụ dễ hiểu.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Nguyên tắc SOLID trong lập trình hướng đối tượng (OOP) - thực hành cùng ngôn ngữ Python

SOLID là gì. SOLID giúp thiết kế chương trình hướng đối tượng linh động, dễ hiểu và dễ duy trì. . .

0 0 27

- vừa được xem lúc

Principle trong Programing, Các nguyên tắc lập trình cơ bản

Mã của bạn phải rõ ràng và dễ bảo trì.Thật dễ dàng để viết mã nhưng Thật khó để viết mã tốt.

0 0 30

- vừa được xem lúc

OOP và nguyên tắc SOLID, KISS, YAGNI & DRY

Ngày nay, việc phát triển một phần mềm đòi hỏi nhiều sự nổ lực và cố gắng của nhiều người. Phần mềm càng ngày càng trở nên phức tạp nên việc phát triển phần mềm theo team nhiều người là một điều cần t

0 0 27

- vừa được xem lúc

SOLID là gì? Nguyên tắc lập trình SOLID và cách áp dụng chúng

1. Giới thiệu. SOLID là một tập hợp của năm nguyên tắc lập trình quan trọng, được Robert C. Martin, còn được biết đến với tên Uncle Bob, đề xuất.

0 0 22

- vừa được xem lúc

SOLID qua các ví dụ Python

Khi lập trình phần mềm, tuân thủ các quy tắc phát triển (nhất là các best-practise) là rất quan trọng. Bài này mình sẽ sử dụng các ví dụ Python để các bạn có thể hiểu bộ nguyên tắc SOLID trong phát tr

0 0 20

- vừa được xem lúc

SOLID - nguyên lý bạn nên biết để trở thành dev có tâm, có tầm

Nguyên tắc SOLID trong lập trình là một tập hợp các nguyên tắc thiết kế phần mềm. Các nguyên tắc này giúp tạo ra mã nguồn linh hoạt, dễ bảo trì và mở rộng về sau.

0 0 14