CÁC NGUYÊN TẮC TRONG DESIGN PATTERN
What is Design Pattern? Việc thiết kế chương trình hoặc phần mềm là một vấn đề thường xuyên phải thực hiện đối với các kỹ sư phần mềm. Tuy nhiên, không phải lúc nào việc này cũng được thực hiện một cách hoàn hảo, đôi khi vẫn sẽ xảy ra sai sót, rủi ro vì sẽ rất dễ bỏ qua các yêu cầu hay những giải pháp dù chúng được lặp lại nhiều lần. Điều này khiến công việc thiết kế phần mềm trở nên phức tạp và khó kiểm soát. Đòi hỏi một kỹ sư phần mềm phải nắm bắt và hiểu hết được toàn bộ các giải pháp cho mọi vấn đề khi thiết kế. Việc này gần như rất khó thực hiện vì công nghệ phần mềm luôn thay đổi, với mỗi phần mềm lại có những yêu cầu khác nhau đòi hỏi việc đưa ra giải pháp cũng khác nhau, hơn nữa lại có quá nhiều giải pháp cho một vấn đề để các kỹ sư lựa chọn, mà không phải kỹ sư nào cũng nắm bắt hết được tất cả các giải pháp. Tuy nhiên, vào năm 1995, bộ tứ GoF (Gang of Four) gồm Erich Gamma, Richard Helm, Ralph Johnson, và John Vlissides đã nỗ lực đưa ra cách xây dựng phần mềm hiệu quả hơn, với quyển sách “Design Patterns: Elements of Reusable Object Oriented Software”. Quyển sách này đã tổng hợp và giới thiệu được hầu như toàn bộ các kỹ thuật phần mềm trên thế giới thành khái niệm về các design patterns. Các design patterns có thể coi là một giải pháp đóng gói cho một vấn đề thiết kế chung. Nó giúp các kỹ sư phần mềm giới hạn được các giải pháp thiết kế phần mềm thành các ý tưởng và mô hình đóng gói mà không phụ thuộc vào bất cứ công nghệ nào, khiến việc thiết kế phần mềm dễ dàng kiểm soát hơn. Ngoài ra, việc đưa ra khái niệm design patterns còn giúp cho việc trao đổi ý tưởng và giải pháp thiết kế giữa các kỹ sư cũng dễ dàng hơn vì mọi người đều có chung ý niệm về các giải pháp theo pattern. Patterns for Patterns Nguyên tắc của 1 pattern được tóm gọn trong 5 điểm chính sau:- “Separate out the things that change from those that stay the same.”
- “Program to an interface, not an implementation”
- “Prefer composition over inheritance”
- “Delegate, delegate, delegate.”
- “You ain’t gonna need it”
#thiết kế không tốt
class Car
def drive
end
end
class Motor
def drive
end
end
class Plane
def fly
end
end
#thiết kế tốt
class Vehicle
def travel
end
end
class Car < Vehicle
end
class Motor < Vehicle
end
class Plane < Vehicle
end
“Prefer composition over inheritance”
Nếu đã lập trình OPP, chắc hẳn ai cũng thấy được sức mạnh của Kế thừa Inheritance. Nó thực sự rất tuyệt vời, và đôi khi là giải pháp cho hầu hết các vấn đề trong lập trình. Tuy nhiên không nên lúc nào cũng sử dụng Inheritance, vì nó sẽ gây cho chúng ta nhiều rắc rối khi thiết kế.
Chẳng hạn, với ví dụ ở trên, ta đã có class Car kế thừa class Vehicle, giờ ta muốn thêm phương thức mới dành cho loại có động cơ, thì ở đây ta có thể tách riêng 2 loại Vehicle gồm loại có động cơ engine và không có động cơ not_engine. Để làm việc này với Inheritance, ta phải tạo thêm 2 class VehicleEngine và class VehicleNotEngine, đồng thời sửa lại và giới hạn Vehicle chỉ gồm các phương thức method cơ bản chung nhất giữa loại có động cơ và không động cơ. Việc này gây ra xáo trộn và sẽ ảnh hưởng tới các method được các SubClass kế thừa từ trước, nếu không cẩn thận có thể sẽ gây ra lỗi.
class Vehicle
def start_engine
end
def stop_engine
end
end
class Car < Vehicle
end
class Bike < Vehicle
end
#tách 2 class
class Vehicle
def start_engine
end
def stop_engine
end
end
class Bike < VehicleNoEngine
end
class Car < VehicleEngine
end
class VehicleEngine < Vehicle
def start_engine
end
def stop_engine
end
end
class VehicleNoEngine < Vehicle
end
Vậy làm sao để giải quyết yêu cầu này mà không dùng tới Inheritance, câu trả lời ở đây là hãy sử dụng Composition. Composition là cách chúng ta gom tất cả các phương thức cần tạo vào một đối tượng riêng được cài đặt bởi một class mới, không phải SubClass, sau đó thực hiện tham chiếu các đối tượng của ta tới các đối tượng mới tạo.
Cụ thể, thay vì tạo 2 class VehicleEngine và class VehicleNotEngine, ta chỉ tạo một class mới chứa tất cả các phương thức liên quan tới động cơ đặt tên là Engine. Và class Car sẽ gọi các phương thức của động cơ bằng cách thông qua đối tượng của class Engine.
class Engine
def start
end
def stop
end
end
class Car < Vehicle
def initialize
@engine = Engine.new
end
def sunday_drive
@engine.start
@engine.stop
end
end
Có thể thấy ngay, giải pháp này thực sự hiệu quả và đem lại nhiều lợi ích:
- Engine code được tách riêng khỏi class Vehicle, vì thế nó có thể tái sử dụng một cách độc lập
- Hơn nữa, tách Engine cũng giúp đơn giản hóa class Vehicle.
- Tăng tính đóng gói
- Dễ dàng mở rộng thành nhiều loại Engine như hình
class Car < Vehicle
def initialize
@engine = Engine.new
end
def sunday_drive
start_engine
stop_engine
end
def start_engine
@engine.start
end
def stop_engine
@engine.start
end
end
“You ain’t gonna need it”
Trong thiết kế, việc có được một hệ thống được thiết kế tốt hoàn toàn là không có, các thiết kế luôn cần sự mềm dẻo, linh hoạt khi cần thay đổi yêu cầu và sửa lỗi. Vì vậy, các kỹ sư phần mềm luôn tìm cách thiết kế thêm các tính năng mở rộng, dự trù có thể sẽ phát triển sau này mà không có trong yêu cầu ở thời điểm hiện tại. Việc này rất tốt tuy nhiên nó có thực sự cần thiết không?
Nguyên tắc này khuyên bạn chỉ nên tập trung vào những điều mà bạn cần ngay bây giờ, những vấn đề không chắc chắn là cần sử dụng ngay thì không nên thực hiện mà hãy chờ đến lúc bạn thực sự cần nó. Vì những vấn đề mở rộng đó chưa chắc sau này đã được sử dụng. Thay vì mất thời gian nghĩ tới nó, hãy tập trung vào những yêu cầu bạn chắc chắn cần ngay bây giờ cho hệ thống.
14 Design Patterns phổ biến
- Template Method
- Strategy
- Observer
- Composite
- Iterator
- Command
- Adapter
- Proxy
- Decorator
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Interpreter
- Internal Domain-Specific Language (DSL)
- Meta-programming
- Convention Not Configuration
Chương trình học
Các bài học
Bài học trước Bài học tiếp theo
Chương trình học
Bao gồm Module, Chương, Bài học, Bài tập, Kiểm tra...Chương trình học
Bài học trước Bài học tiếp theo