Thế SOLID là gì? SOLID là cứng
Đùa tí Đây là các nguyên lý thiết kế trong OOP, được ghép lại từ các chữ cái đầu của Single Responsibility, Open Close Principle, Liskov Substitution Principle, Interface Segregation và Dependency Inversion.
Hôm nay mình sẽ đi vào tổng quan khái niệm, sau đó lấy ví dụ bằng Python cho các con vợ dễ hiểu nhé =))
Bài viết gốc trên blog cá nhân của mình: https://phamduyhieu.com/solid-trong-oop-va-vi-du-de-hieu-bang-python
1. Single Responsibility
- Mỗi class chỉ nên có 1 trách nhiệm duy nhất
- Về lâu dài, nếu không áp dụng S, class sẽ phình to ra, khó kiểm soát và maintain.
# violate the Single Responsibility PrincipleclassAnimal:def__init__(self, name):
self.name = name
defget_name(self):passdefsave_to_db(self, animal):# save to MySQLpass# comply with Single Responsibility PrincipleclassAnimal:def__init__(self, name):
self.name = name
defget_name(self):passclassAnimalDB:defget_animal(self, a_id):passdefsave_to_db(self, animal):pass
Ví dụ ta có class Animal, trong đó gồm cả method lưu object vào database – save_to_db. Với thiết kế này, khi chương trình thay đổi database, ta phải mò vào sửa class gốc này. Thay vào đó ta tạo thêm 1 class nhỏ dành riêng cho việc lưu trữ database là AnimalDB. Việc này giúp cho việc sửa chữa đơn giản, rõ ràng hơn, ít bug hơn.
2. Open Close Principle
-
Nên mở rộng class thay vì sửa đổi class gốc
-
Khi thêm chức năng, … ta nên mở rộng class cũ (kế thừa, sở hữu) mà tránh việc sửa nó
=> dễ gây lỗi tiềm ẩn khi các module khác đang sử dụng class cũ.
classDiscount:def__init__(self, customer, price):
self.customer = customer
self.price = price
defget_discount(self):return self.price *0.2# when we need to add discount for VIP customers => change Discount classclassDiscount:def__init__(self, customer, price):
self.customer = customer
self.price = price
defgive_discount(self):if self.customer =='fav':return self.price *0.2if self.customer =='vip':return self.price *0.4
Thiết kế trên đã vi phạm nguyên lý OCP, giả dụ mỗi tuần có thêm 1 case khách hàng mới, chúng ta lại phải sửa class Discount, logic hàm give_discount sẽ dài ra vô tận
Thay vào đó, ta nên tạo 1 class mới kế thừa class cũ
classDiscount:def__init__(self, customer, price):
self.customer = customer
self.price = price
defget_discount(self):return self.price *0.2classVIPDiscount(Discount):defget_discount(self):returnsuper().get_discount()*1.2classSuperVIPDiscount(Discount):defget_discount(self):returnsuper().get_discount()*1.5classDiamondDiscount(Discount):defget_discount(self):returnsuper().get_discount()*2
3. Liskov Substitution Principle
- Các 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
- Đảm bảo tính đa hình trong OOP
VD: viết chương trình mô tả các loài chim bay
Có class chimcanhcut cũng là chim nên cho kế thừa class Bird
=> Khi gọi hàm bay của object chim cánh cụt sẽ bị Exception
=> thiết kế này vi phạm nguyên lý LSP
Nôm na khi thiết kế class phải chú ý, tránh bê nguyên các mối quan hệ của các object ngoài đời sống vào code.
A là B không có nghĩa là A nên kế thừa B (nếu class A không thể thay thế được class B)
classAnimal:defleg_count(self):passclassLion(Animal):defleg_count(self):passdefanimal_leg_count(animal: Animal):print(animal.leg_count())
animal = Animal()
lion = Lion()
animal_leg_count(animal)
animal_leg_count(lion)
Với thiết kế bên trên, object lion có thể thay thế object animal từ class Animal mà chương trình vẫn chạy đúng.
4. Interface segregation Principle
• Nên tách interface thành các interface nhỏ hơn phục vụ cho những mục đích cụ thể
# violate Interface segregationclassMyInterface:defconnect_to_db(self):raise NotImplementedError
defwrite(self):raise NotImplementedError
defread(self):raise NotImplementedError
defclose_connect(self):raise NotImplementedError
defshow_info(self):raise NotImplementedError
defupdate_info(self):raise NotImplementedError
# comply with Interface segregationclassDBInterface:defconnect_to_db(self):raise NotImplementedError
defwrite(self):raise NotImplementedError
defread(self):raise NotImplementedError
defclose_connect(self):raise NotImplementedError
classDisplayInterface:defshow_info(self):raise NotImplementedError
defupdate_info(self):raise NotImplementedError
Với Interface MyInterface, các class khi implement MyInterface sẽ phải implement tất cả các method trong nó. Điều này thành ra bất hợp lý, đôi khi gây dư thừa vì 1 class đôi khi không dùng hết tất cả các method. Vì vậy ta nên chia thành các interface nhỏ (DBInterface, DisplayInterface) gồm các method liên quan đến nhau, dễ quản lý, dễ implement hơn.
5. Dependency Inversion Principle
- 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
- Ta có thể thoải mái sửa đổi implement của module cấp thấp mà không làm ảnh hưởng tới module cấp cao
- Trong code thực tế, các module nên liên kết với nhau thông qua interface
classIFood:defbake(self):raise NotImplemented
defeat(self):raise NotImplemented
classPizza(IFood):defbake(self):print("pizza was baked")defeat(self):print("pizza was ate")classBread(IFood):defbake(self):print("bread was baked")defeat(self):print("bread was ate")classProduction:def__init__(self, food: IFood):
self.food = food
defproduce(self):
self.food.bake()defconsume(self):
self.food.eat()if __name__ =='__main__':
pizza = Pizza()
bread = Bread()
p = Production(pizza)
p.produce()
p.consume()
b = Production(bread)
b.produce()
b.consume()
Ở đây ta có các module cấp thấp là Bread và Pizza, module cấp cao là Production. 2 module này giao tiếp với nhau bằng interface IFood, giúp cho chương trình trở lên linh hoạt hơn. Module Production chỉ cần sử dụng các method trong IFood mà không bị ràng buộc hay cần quan tâm object nào sẽ được truyền vào. Ta có thể truyền vào pizza hoặc bread.
Phewww!!! Thế là tôi vừa chém xong tí lý thuyết cũng như code thêm tí Python minh họa cho các con vợ rồi nhé
Thấy hay thì up vote nha các ty
Nay chủ nhật tôi đi dọn nhà đây không vợ nó đá chẻ đôi mặt ra ấy
See yaaaa!!!!
Nguồn: viblo.asia