[Design Pattern] Phần 1: Strategy Pattern
[Design Pattern] Phần 1: Strategy
Pattern
Đây
là phần đầu tiên trong loạt 10 phần về Design Pattern. Bài viết này hy vọng các
bạn sẽ có hiểu biết về một số mẫu thiết kế, cũng như ứng dụng được vào các vấn
đề các bạn gặp phải.
Trước khi đi vào phần đầu
tiên. Mình sẽ giải thích sơ bộ về Design Pattern.
Design
Pattern là gì?
Đó là một mô hình thiết kế
để giải quyết một vấn đề thực tiễn nào đó, và vấn đề này xảy ra khá thường
xuyên. Design Pattern = vấn đề + cách giải quyết vấn đề
Giả
dụ như bạn gặp vấn đề: Khi càng mở rộng các lớp mới, thì việc
chỉnh sửa bảo trì ngày càng cực hơn. Bạn cần một giải pháp thiết kế nào đó để giảm
thiểu việc chỉnh sửa này. Bạn nhận ra rằng có nhiều người cũng gặp vấn đề giống
bạn, và cũng đã xử lý được khi sử dụng giải pháp chung nào đó. Và học Design
Pattern chính là học những khuôn mẫu, những giải pháp chung ấy để áp
dụng giải quyết vấn đề của mình một cách nhanh chóng và hiệu quả.
Sau
khi giải thích xong. Chúng ta cùng bắt đầu đi tìm hiểu về mẫu thiết kế đầu
tiên: Strategy Pattern
Trước tiên ta sẽ đi tìm
hiểu vấn đề
Giả sử bạn là chủ của một
công ty xe. Bạn nhận một hợp đồng làm các xe như xe đạp, xe máy, xe hơi. Bạn tạo
bản thiết kế như sau:
Tuyệt! Bây giờ bạn cần
thêm phương thức go() để thể hiện sự di chuyển của các lớp con. Bạn viết như
sau
Bây giờ chúng ta hãy thử
xem các lớp con thực hiện thế nào
Ok mọi thứ đều ổn.
Bây giờ bạn mở rộng công
ty, và muốn đặt thêm hàng về máy bay trực thăng, máy bay chở khách, cũng như máy
bay phản lực. Bạn thiết kế lại như sau
Hmm. Việc thêm vào các lớp
con lạ thế này sẽ thay đổi khá nhiều phương thức go(). Ta phải chỉnh sửa phương
thức go() tại Plane, Helicopter cũng như Jet.
Đơn giản mà. Chỉ cần
override lại phương thức là xong.
Đầu tiên chúng ta thêm
vào 3 lớp con mới
Sau đó tại từng lớp con
chúng ta override lại phương thức của nó
Bơ phệt! Code chạy khá tốt.
Vấn đề không thể nhìn thấy
ở mức độ đơn giản như thế này được. Ở mức độ phức tạp hơn, khi mà có tới hàng
trăm hàng nghìn các lớp con, lúc đó vấn đề mới xuất hiện.
Bạn biết rằng mỗi lớp con
có thể sẽ là một thuật toán go() khác (Có thể có nhiều lớp con cùng thuật toán
go() chung, như ví dụ Plane và Helicopter). Nếu sự thay đổi của thuật toán go()
diễn ra khá thường xuyên, ta sẽ phải kiểm soát vị trí chứa thuật và thay đổi
thuật toán, với tần suất công việc liên tục. Với các thuật toán khác nhau nằm ở
các lớp rải rác như thế này thì sự kiểm soát nó cực kỳ khó khăn. Giống như việc
bạn trông những đứa trẻ ở những ngôi nhà khác nhau vậy. Nếu một đứa trẻ có
thông báo về vấn đề gì đó, bạn phải chạy lại ngôi nhà đó và chỉnh sửa. Việc của
bạn là gom những đứa trẻ này lại một chỗ để khi xảy ra vấn đề, bạn chỉ việc tới
đúng chỗ đó giải quyết vấn đề là ổn.
Cũng giống như các thuật
toán go(), ta phải làm sao để tách ra khỏi lớp con, và gom nó lại một chỗ.
Thêm một vấn đề nữa là sự
linh động thuật toán. Bạn thấy rằng máy bay thì phải chạy trên đường trước, rồi
mới bay, rồi mới chạy trên đường. Bạn muốn thuật toán go() thay đổi từ “I’m
driving” sang “I’m flying” rồi sang “I’m driving”.
Với Strategy Pattern,
ngoài việc kiểm soát các thuật toán tốt hơn, bạn còn có thể thay đổi thuật toán
tùy thích.
Cốt lõi của Strategy Pattern là gì? Đó chính là việc thay thế mối quan hệ “is-a” bằng quan hệ “has-a”.
Thay vì việc bạn kế thừa phương thức, bạn sẽ tạo một biến chứa đối tượng là một
thuật toán trên. Với mỗi đối tượng là một thuật toán, bạn hoàn toàn tách biệt
thuật toán ra khỏi quan hệ “is-a” để linh động sử dụng, đồng thời có thể gom
các thuật toán vào một nơi để dễ dàng kiểm soát.
(Strategy = chiến lược =
thuật toán = phương thức chính xử lý một vấn đề nào đó)
Vậy chúng ta sẽ dựa vào
tư tưởng này thiết kế lại nào
Ta tạo một biến lưu trữ một
đối tượng là cái mà chứa thuật toán của chúng ta
Tại FlyingAlgorithm ta
xây dựng thuật toán go()
Về cơ bản, ta đã chuyển
được mối quan hệ “is-a” sang “has-a” rồi
Tại lớp Helicopter, ta có
thể sử dụng lại thuật toán này mà không cần phải viết lại
Với các lớp con còn lại
Car, Bicycle, Motorcycle, Jet, ta cũng xây dựng tương tự.
3 lớp con trên sẽ xài
chung một thuật toán
Jet sẽ sử dụng thuật toán
khác
Ta có thể gom các đối tượng
này vào 1 package để dễ kiểm soát
Ta da. Về cơ bản ta đã
tách biệt được các thuật toán ra khỏi các lớp con rồi.
Tuy nhiên ta vẫn nên tạo
cho thuật toán có sự thay đổi. Ví dụ như tại lớp Plane khi nãy
Ta muốn nó có thể chuyển
thuật toán DrivingAlgorithm() sang FlyingAlgorithm() rồi sang DrivingAlgorithm() như
hồi nãy ta đã nói. Mình sẽ lấy ví dụ về sự thay đổi này
Lỗi là đương nhiên rồi
haha, vì mình chưa thiết kế, mà chỉ mới đưa ra ý tưởng cho sự thay đổi này.
Bạn biết rằng mỗi lớp con
có một thuộc tính thể hiện thuật toán nó sử dụng. Để có thể linh động thay đổi
thuật toán, ta áp dụng tính đa hình trong OOP vào đây. (Nhắc nhẹ về tính đa
hình: Là khả năng thay đổi nhiều cách thể hiện của một đối tượng)
Ta có thể sử dụng Abstract
hoặc Interface để tạo tính đa hình. Trong trường hợp này mình sử dụng Interface
vì các class chỉ thể hiện các phương thức. Cụ thể như sau.
Tại các lớp ta implements
lại GoAlgorithm và thực hiện phương thức (ở đây ta đã thực hiện rồi)
Đây là lớp Plane trước
khi thay đổi
Để tạo tính đa hình, ta
thay đổi biến fly với kiểu khai báo là GoAlgorithm, đồng thời ta nên đổi tên
fly thành algorithm để hiểu rằng đây không phải thuật toán cố định
Ta cũng đồng thời thêm hàm
set để thiết lập lại thuật toán vì algorithm là biến private.
Ta quay lại hàm test nào
*Có
một chút sự thay đổi là mình khai báo plane với kiểu ban đầu là Plane để dễ sử
dụng biến algorithm thay vì phải ép kiểu từng cái một (Các bạn có thể tạo biến
algorithm từ lớp cha, rồi chỉnh sửa hợp lý để biến plane có tính đa hình luôn
nhé)
Hãy chạy thử nào
Wow, bơ phệt!! Bạn đã đi
đúng hướng rồi đấy.
Các class con còn lại bạn
tự thay đổi cho phù hợp nhé
Và như thế chúng ta đã hiểu
hơn về mẫu chiến lược (Strategy Pattern) rồi đấy. Khi làm việc với nhau bạn chỉ
cần nhắc đến mẫu này người ta sẽ hiểu ý của bạn muốn nói mà không cần phải giải
thích gì nhiều!
Nội dung bài viết thuộc về Lê Công Diễn. Có sự tham khảo từ cuốn
sách Design Pattens for Dummies.
Người viết: Lê Công Diễn
Mang đi nhớ ghi nguồn
Tuyệt vời <3 hóng các mô hình tiếp theo, thế này thì cần gì đi học nữa, ờ nhà đọc blog cho lành :3
Trả lờiXóaCám ơn bạn đã ủng hộ! <3 Hôm nay sẽ có bài mới luôn
XóaKhá dễ hiểu với một đứa khó tiếp thu như em ạ !
Trả lờiXóa