Thứ Ba, 25 tháng 12, 2018

Strategy Pattern

Nếu ai đó hỏi tui, pattern nào là dễ nhất! tui bợp ngay câu trả lời là : Strategy Pattern. Nếu người ta lại bắt tui miêu tả strategy pattern trong 1 câu ngắn gọn thì tui độp ngay cho 1 câu trả lời: đừng suy nghĩ theo kiểu kế thừa, mà hãy suy nghĩ theo kiểu hiện thực. Vậy, định nghĩa ( 1 cách máy móc và khó hiểu) của strategy pattern là gì?
Strategy pattern: định nghĩa một danh sách các thuật toán và đóng gói chúng để giúp chúng có thể thay đổi được. Pattern loại này giúp cho các thuật toán được định nghĩa sẳn có thể được thay đổi bởi người sử dụng chúng.
Oài, quăng ba cái định nghĩa đó đi. Đọc vào chỉ rối não chứ chả hiểu gì đâu. Nên theo chân mình, để mình dẫn bạn vào thế giới của những ví dụ.
 Giả sử, bạn có 2 con vịt: 1 con là vịt nhà, 1 con là vịt rừng. Cả 2 con này đều kêu "quạc quạc", đều có thể bay, và đều có thể nhìn thấy được. Và ta có bản thiết kế sau:

Đúng như những gì ta được học về kế thừa rồi đúng ko? Nhưng sẽ chẳng có gì to tát nếu bỗng nhưng trên trời rơi xuống thêm 1 con vịt cao su. Con vịt cao su này thì lại không biết bay, vậy là ta phải thiết kế lại.

Nhìn có vẻ ổn rồi nhể? Nhưng chưa đâu anh bạn, khách hàng kêu là: "ê chú, tui mới đặt thêm 2 loại vịt nữa. 1 con là vịt trời, 1 con là vịt núi, 2 con này nó cũng bay được bình thường, nhưng mà nó bay khác với con vịt nhà với vịt rừng chú ạ! chú coi coi sao sửa lại dùm tui gấp nhá!". Và lúc đó bạn kiểu: "ơ, goắt đờ hợi? Lại sửa à?". Vừa mới mở bản thiết kế lên để chuẩn bị chèn vào 2 con vịt chó đẻ thì ông khách hàng lại hớn hở chạy vào:
- Ê chú ơi, bên kia người ta lại sắp giao thêm vịt xi măng nữa chú ạ. Vịt xi măng thì không biết bay và cũng chả biết kêu nhá.
- Cút!!!!
Đấy, bạn thấy đấy. Lúc đầu kế thừa có vẻ là hay. Nhưng về lâu về dài thì nó lại có vấn đề. Khi gặp nhiều thay đổi thì cái cây kế thừa của bạn càng ngày càng mở rộng. Và khi bạn mở rộng quá dài thì nhận ra là 2 con vịt ở 2 nhánh cách xa nhau lại có 1 đặc điểm chung và lúc này bạn phải ngậm ngùi để cho code của bạn bị lặp lại. Và kết quả là code của bạn nhìn như cứt ấy. (Nói thẳng ra là vậy! =)) )
Vậy để giải quyết vấn đề này thì ta sẽ dùng Strategy pattern.
Ban đầu, ta có 1 con vịt. 1 con vịt thì biết bay, và nó biết kêu. Okie. Ta sẽ tạo 1 con vịt như thế, NHƯNG! ta sẽ thiết kế hành động bay và kêu này là những interface. Nghĩa là sao? nghĩa là con vịt của chúng ta có hành động bay và kêu đấy, nhưng nó chỉ là interface mà thôi, nó không có làm được gì nêu như ta chưa định nghĩa cho nó.

Tiếp đến, ta bắt đầu định nghĩa cho những interface này. Ta có con vịt nhà và vịt rừng bay kiểu nhẹ nhàng. Còn con vịt trời và vịt núi bay kiểu mạnh mẽ. Con vịt cao su và vịt xi măng thì không biết bay. Tức là ta có 3 hiện thực cho interface bay.

Tương tự với tiếng kêu của vịt cũng thế. Ta có 2 hiện thực là kêu và không kêu.

Và bây giờ để tạo ra con vịt nhà thì ta chỉ cần truyền vào cho đối tượng "DUCK" 2 hiện thực là "Smooth fly" và "Normal quack". Muốn tạo vịt xi măng thì truyền vào "DUCK" 2 hiện thực là "No fly" , "No quack". Và đây chính là strategy pattern.
 Bây giờ thử code vài dòng xem nó như thế nào xem nào?
 Đầu tiên ta tạo 2 cái interface bay và kêu.
public interface IFly {
    public void fly();
}

public interface IQuack {
    public void quack();
}
 Sau đó ta hiện thực những interface này. Đầu tiên là những lớp bay.
public class NoFly implements IFly{
    public void fly(){
         // khong lam gi ca
    }
}

public class SmoothFly implements IFly{
    public void fly(){
         Systems.out.println("bay nhe nhang!");
    }
}

public class StrongFly implements IFly{
    public void fly(){
         Systems.out.println("bay manh me!");
    }
}
 Sau đó là những lớp kêu.
public class NoQuack implements IQuack{
    public void quack(){
         // khong lam gi ca
    }
}

public class NormalQuack implements IQuack{
    public void quack(){
         Systems.out.println("Quac quac!");
    }
}
 Và giờ ta có thể tạo đối tượng vịt dựa trên 2 interface IFly và IQuack
public class Duck {
    private IFly flyBeha;
    private IQuack quackBeha;
    public Duck (IFly flyBeha, IQuack quackBeha){
        this.flyBeha = flyBeha;
        this.quackBeha = quackBeha;    
    }
    public void quack(){
         this.quackBeha.quack();
    }

    public void fly(){
         this.flyBeha.fly();
    }
}
 Đối tượng đã có hết rồi, h muốn tạo con vịt nào là có con vịt nấy.
Duck CityDuck = new Duck(new SmoothFly(), new NormalQuack());
Duck RubberDuck = new Duck(new NoFly(), new NormalQuack());
Duck CementDuck = new Duck(new NoFly(), new NoQuack());
 Strategy pattern giúp chúng ta thiết kế 1 cách tùy biến hơn, linh động hơn và giúp ta làm chủ hơn việc khởi tạo đối tượng. Với phương pháp này thì cái cây kế thừa của chung ta sẽ bị dẹp qua 1 bên và không còn phải lo lằng nhiều nếu như có thêm 1 con vịt chó chết nào được thêm vào nữa. Giả dụ chúng ta có thêm con vịt điên chẳng hạn, nó không biết kêu mà lại biết bơi. Thì cũng chỉ cần truyền vào đối tượng "DUCK" những hiện thực tương ứng thôi. Và từ đây thì cũng có thể thấy được là, tính kế thừa cũng chả có quá đỉnh cao như chúng ta vẫn được học, quan trọng là kế thừa lúc nào mới là hiệu quả.
Share:

0 nhận xét:

Đăng nhận xét

Được tạo bởi Blogger.