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

SOLID trong OOP và ví dụ dễ hiểu bằng Python

0 0 27

Người đăng: Tony Pham

Theo Viblo Asia

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 SegregationDependency 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é =))

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 Principle class Animal: def __init__(self, name): self.name = name def get_name(self): pass def save_to_db(self, animal): # save to MySQL pass # comply with Single Responsibility Principle class Animal: def __init__(self, name): self.name = name def get_name(self): pass class AnimalDB: def get_animal(self, a_id): pass def save_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ũ.

class Discount: def __init__(self, customer, price): self.customer = customer self.price = price def get_discount(self): return self.price * 0.2 # when we need to add discount for VIP customers => change Discount class class Discount: def __init__(self, customer, price): self.customer = customer self.price = price def give_discount(self): if self.customer == 'fav': return self.price * 0.2 if 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ũ ???

class Discount: def __init__(self, customer, price): self.customer = customer self.price = price def get_discount(self): return self.price * 0.2 class VIPDiscount(Discount): def get_discount(self): return super().get_discount() * 1.2 class SuperVIPDiscount(Discount): def get_discount(self): return super().get_discount() * 1.5 class DiamondDiscount(Discount): def get_discount(self): return super().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. AB 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)

class Animal: def leg_count(self): pass class Lion(Animal): def leg_count(self): pass def animal_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 segregation
class MyInterface: def connect_to_db(self): raise NotImplementedError def write(self): raise NotImplementedError def read(self): raise NotImplementedError def close_connect(self): raise NotImplementedError def show_info(self): raise NotImplementedError def update_info(self): raise NotImplementedError # comply with Interface segregation
class DBInterface: def connect_to_db(self): raise NotImplementedError def write(self): raise NotImplementedError def read(self): raise NotImplementedError def close_connect(self): raise NotImplementedError class DisplayInterface: def show_info(self): raise NotImplementedError def update_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
class IFood: def bake(self): raise NotImplemented def eat(self): raise NotImplemented class Pizza(IFood): def bake(self): print("pizza was baked") def eat(self): print("pizza was ate") class Bread(IFood): def bake(self): print("bread was baked") def eat(self): print("bread was ate") class Production: def __init__(self, food: IFood): self.food = food def produce(self): self.food.bake() def consume(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à BreadPizza, 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!!!!

Bình luận

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

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

Lập Trình Hướng Đối Tượng trong Python

Chào các bạn Trong bài này, bạn sẽ tìm hiểu về Lập trình hướng đối tượng (OOP) bằng Python và khái niệm cơ bản của nó và một số các ví dụ. Các bạn cùng tìm hiểu trong bài viết của mình nhé.

0 0 35

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

Lớp trong Python

. Hôm ni, mình học tiếp về bạn “Lớp(class) trong python”, bài blog tiếp theo nằm trong series “Khám phá Đại Bản Doanh Python”(nội dung trong bài series này từ chủ yếu mình lấy từ python.org rồi viết lại hoặc dịch lại theo ngôn ngữ của mình).

0 0 17

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

Tìm hiểu về thư viện Numpy trong Python(Phần 3)

Trong bài viết trước tôi đã giới thiệu cho bạn về NumPy, tìm hiểu về Mảng trong NumPy. Trong bài viết này chúng ta sẽ tiếp tục tìm hiểu về các kiểu dữ liệu khác trong NumPy.

0 0 128

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

Hướng dẫn cài đặt Anaconda trên Ubuntu

Anaconda là một nền tảng mã nguồn mở về Data Science và Machine Learning trên Python thông dụng nhất hiện nay, Anaconda có vai trò đơn giản hóa việc triển khai và quản lí các gói cài đặt khi làm việc với Python. Anaconda được cài đặt dễ dàng trên 3 nền tảng hệ điều hành thông dụng hiện nay là Ubuntu

0 0 31

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

Tùy chỉnh Exceptions trong Python

Chào các bạn trong bài viết này, mình sẽ giới thiệu với các bạn về cách tùy chỉnh các Exceptions trong Python.Mình sẽ giải thích cho các bạn hiểu và cách xử dụng chúng.

0 0 24

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

Không gian tên(namspace) và phạm vi(scope) trong Python

. Khi mình ngồi học và dịch bài "Class trong Python" cho sê-ri "Khám Phá Đại Bản Doanh Python", mình đã đụng hai bạn này, và các bạn thật là trừu tượng và khó gặm. Thế là mình tìm kiếm và viết bài này để hiểu rõ hơn về hai bạn ấy, hi vọng bạn đọc thêm để hiểu về Python nhé.

0 0 34