Hiểu rõ hơn về mẫu thiết kế 'Chiến lược'


8

Tôi đã quan tâm đến các mẫu thiết kế trong một thời gian và bắt đầu đọc "Các mẫu thiết kế đầu tiên". Tôi bắt đầu với mẫu đầu tiên được gọi là mẫu 'Chiến lược'. Tôi đã trải qua vấn đề được nêu trong các hình ảnh dưới đây và lần đầu tiên cố gắng tự đề xuất một giải pháp để tôi thực sự có thể nắm bắt được tầm quan trọng của mẫu.

Vì vậy, câu hỏi của tôi là tại sao giải pháp của tôi cho vấn đề dưới đây không đủ tốt. Điểm tốt / xấu của giải pháp của tôi so với mô hình là gì? Điều gì làm cho mô hình rõ ràng là giải pháp khả thi duy nhất?

GIẢI PHÁP CỦA TÔI

Lớp phụ huynh: Ducks

<?php
class Duck
{
 public  $swimmable;
 public  $quackable;
 public  $flyable;

 function display()
 {
  echo "A Duck Looks Like This<BR/>";
 }

 function  quack()
 {
  if($this->quackable==1)
  {
   echo("Quack<BR/>");
  }
 }

 function swim()
 {
  if($this->swimmable==1)
  {
   echo("Swim<BR/>");
  }
 }

 function  fly()
 {
  if($this->flyable==1)
  {
   echo("Fly<BR/>");
  }
 }


}
?>

LỚP INHERITING: MallardDuck

<?php
class MallardDuck extends Duck
{
 function MallardDuck()
 {
  $this->quackable = 1;
  $this->swimmable = 1;
 }

 function display()
 {
  echo "A Mallard Duck Looks Like This<BR/>";
 }
}
?>

LỚP INHERITING: WoddenDecoyDuck

<?php
class WoddenDecoyDuck extends Duck
{
 function woddendecoyduck()
 {
  $this->quackable = 0;
  $this->swimmable = 0;
 }

 function display()
 {
  echo "A Wooden Decoy Duck Looks Like This<BR/>";
 }
}

giải pháp của bạn không chính xác nghĩa là gì (hoặc thậm chí là giải pháp cho vấn đề đó) trên cuốn sách
mauris

bạn có thể giúp giải thích rõ hơn
Imran Omar Bukhsh

5
Tôi không chắc chắn rằng bạn có thể quét các trang từ các mẫu Head First Design và chỉ cần đưa chúng lên web.
Ladislav Mrnka

1
Điều này trông giống như một đánh giá mã .
Steven Jeuris

2
@Imran Như đã đề cập tại Amazon, các trang đó là tài liệu có bản quyền. Họ không thể được tự do đăng ở đây. Vui lòng chỉnh sửa câu hỏi để nêu vấn đề bằng từ ngữ của bạn.
Adam Lear

Câu trả lời:


6

Mã của bạn sẽ bị hỏng khi, ví dụ như tiếng vịt kêu với một âm thanh khác. Boolean sẽ chỉ dẫn đến các booleans khác thành các câu lệnh if thực sự có lông.

Các mô hình chiến lược là đơn giản mặc dù. Trong trường hợp này, phương thức quack và đặt nó vào lớp hoặc giao diện riêng của nó. Một giao diện trong php sẽ là một lớp không chứa bất kỳ triển khai nào ngoài các phương thức còn sơ khai (tức là không có gì xảy ra khi bạn gọi chúng).

class Quackable {
    function quack() {}
}

Bằng cách đó, bạn có thể tạo một số triển khai có thể thực hiện được:

class DuckQuack extends Quackable {
    function quack() {
        return "Quack!";
    }
}

class SilentQuack extends Quackable {
    function quack() {
        return ""; 
            // The decoy duck can't quack. It is silent.
    }
}

Và bởi vì chúng tôi đã thực hiện mô hình chiến lược một cách khôn ngoan, chúng tôi có thể thêm nhiều loại "quackery":

class DoubleQuack extends Quackable {
    function quack() {
        return "Quackety-quack!"
    }
}

Điều tương tự có thể được áp dụng cho các phương pháp bay và bơi cho lớp Vịt. Cách bạn triển khai một lớp Duck với quackable sẽ như thế này (giao diện quackable được cung cấp thông qua hàm tạo, đây là cách thức hoạt động của phép tiêm phụ thuộc):

class Duck {
    protected $quackable;

    // The constructor
    function __construct($quackable) {
        $this->quackable = quackable;
    }

    function quack() {
        echo $this->quackable->quack();
    }
}

Việc thực hiện vịt vịt trời và vịt giải mã sẽ đơn giản vì bạn chỉ cần cung cấp loại vịt mà vịt nên làm:

class MallardDuck extends Duck {
    function __construct() {
        parent::__construct(new DuckQuack());
           // we construct the mallard duck with a duck quack
    }
}

class DecoyDuck extends Duck {
    function __construct() {
        parent::__construct(new SilentQuack());
           // we construct the decoy duck with a silent quack
    }
}

Sử dụng tất cả đều đơn giản:

$duck1 = new MallardDuck();
$duck2 = new DecoyDuck();

$duck1.quack(); // echoes out "Quack!"
$duck2.quack(); // echoes out "" (because decoy ducks don't quack)

Tôi hy vọng tất cả điều này có ý nghĩa với bạn.


Mã có thể có lỗi, đã được một thời gian kể từ khi tôi mã hóa một cái gì đó bằng php. :-P
Spoike

Bên cạnh đó, OP cần lật thêm một vài trang nữa để đến với mẫu Chiến lược và nguyên tắc "sáng tác trước khi kế thừa" trong cuốn sách đó. :) Các trang được quét mở đầu với vấn đề thừa kế.
Spoike

thanx cho lời giải thích
Imran Omar Bukhsh

4

Trước hết, vấn đề của bạn không liên quan gì đến mô hình chiến lược. Ý tưởng của mô hình chiến lược là đưa ra trách nhiệm cho một hành vi nhất định vào một lớp khác. Bằng cách đó, bạn có thể cắm các hành vi khác nhau vào một trường hợp trong thời gian chạy.

Bây giờ giải pháp của bạn hoạt động hợp lý tốt cho kịch bản bạn đang tham gia, nhưng kịch bản chỉ là một đoạn trích, khá xa với các vấn đề trong thế giới thực.
Nếu bạn sử dụng kỹ thuật này để giải quyết các dự án lớn, bạn có nguy cơ cuộn lại với 5-10 lớp kế thừa, trong đó mỗi lớp con được ghép với ngày càng nhiều cờ. Mã như vậy là cực kỳ mong manh và dài dòng. Loay hoay với trạng thái bên trong của tất cả các siêu lớp không chính xác là cách OOP xử lý những việc như vậy, bởi vì nó làm mờ đi những lo ngại.

Một giải pháp sử dụng các giao diện sạch hơn đáng kể, mạnh mẽ hơn và do đó sẽ chứng minh được nhiều thứ hơn theo thời gian. Cố gắng không tạo ra một con vịt cơ bản đa năng, mà là làm cho sự trừu tượng rõ ràng về các khía cạnh khác nhau của những con vịt khác nhau. Tôi mới thực hiện một bài viết trên blog về chủ đề này, bạn có thể thấy hữu ích.


1
+1 nhưng tôi phải nói rằng, đây là ví dụ được đưa ra cho Mẫu chiến lược trong cuốn sách, đây là một cuốn sách tuyệt vời. Thật không may, anh ta đã không hiển thị các trang tiếp theo nơi các hành vi bay, quạc v.v ... được thiết kế và sau đó cắm vào vịt (tức là phần áp dụng mô hình chiến lược cho vấn đề), nhưng đó là trong cuốn sách.
Alb

Tôi đã thêm trang tiếp theo của cuốn sách để cho thấy tại sao giải pháp giao diện là hoàn toàn không!
Imran Omar Bukhsh

@ Alb: có khoảng 13 trang
Imran Omar Bukhsh

@Imran: Xin lỗi, nhưng tác giả của cuốn sách hoàn toàn sai. Các giao diện không nói về việc thực hiện. Lớp học có thể được thực hiện thông qua thành phần hoặc ủy quyền (mẫu chiến lược là một trường hợp đặc biệt). Tuy nhiên, đưa ra một phương pháp bay cho một con vịt không thể bay, chỉ vì bạn không thể giải quyết nó bằng cách khác là thiết kế kém. Và nó không có quy mô. Nếu bạn cũng có RuberBallthì sao? Cũng sẽ RuberDuckcó một phương pháp cuộn trống?
back2dos

3

À, câu hỏi hay. Tôi không nghĩ rằng đó là một thực tiễn rất tốt để có các biến thành viên chuyển đổi chức năng. Vấn đề chính của giải pháp này là vì khi bạn trưng ra flyquackcác phương thức trên Ducklớp, có vẻ như bạn đang nói rằng "Tất cả vịt có thể fly/ quack." Điều tồi tệ hơn là, tùy thuộc vào loại thời gian chạy của cá thể vịt mà tôi có, flyhoặc quackcó thể hoặc không thể làm gì. Điều này có thể dẫn đến mã rất khó hiểu.

Trong ví dụ như cuốn sách đưa ra, khi bạn đang mong đợi một con vịt có thể bay, bạn có thể gõ (hoặc kiểm tra, vì bạn đang sử dụng ngôn ngữ động) nếu đó là một Flyablecon vịt.

Nếu mỗi lần tôi có một con vịt, trước khi tôi gọi quacknó, tôi phải quyết định kiểm tra xem biến thành viên có thể bay được được đặt thành đúng hay không sẽ dẫn đến nhiều sự trùng lặp mã.

if($duck->quackable) $duck->quack();

Các Flyablegiao diện giúp thiết lập những kỳ vọng. Có, quackphương thức mặc định của bạn sẽ kiểm tra xem có nên hay không, nhưng tôi, với tư cách là người gọi, không có gì đảm bảo rằng nó thậm chí còn an toàn để gọi quack. Cũng nghĩ về kịch bản RubberDuckymà nên ré lên thay vì quạc. Khi bạn ghi đè quackphương thức bạn phải biết để kiểm tra $quackablebiến thành viên trước khi bạn thực hiện bất kỳ hành động nào.

Một điều khác mà tôi nghĩ làm cho giải pháp này không rõ ràng là vì bạn đang sử dụng một ngôn ngữ động như PHP, nó khiến cho việc đặt kỳ vọng về các kiểu đầu vào trở nên khó khăn. (Không có gì sai với điều này, nhưng một ngôn ngữ được gõ tĩnh có thể giúp bạn dễ hiểu hơn.)

Tôi hy vọng điều này sẽ giúp và có ý nghĩa.


1

Mã của bạn sẽ trở nên lộn xộn một cách nhanh chóng trong trường hợp bạn đã nói 10 loại vịt khác nhau mà tất cả đều có sự kết hợp khác nhau của 3 biến thể khác nhau là bay, quạc và bơi.

Cá nhân, tôi thấy rằng giá trị thực của mẫu chiến lược đã trở nên rõ ràng khi tôi bắt đầu viết các bài kiểm tra đơn vị và sử dụng phép tiêm phụ thuộc. Nếu bạn không sử dụng nó, bạn sẽ gặp nhiều khó khăn trong việc quản lý các phụ thuộc và do đó tạo các đối tượng trong các thử nghiệm của bạn.

Bạn sẽ gặp phải tình huống tạo ra một con vịt trở nên phức tạp, sau đó bạn có thể muốn viết một bài kiểm tra đơn vị cho một số phương pháp đánh hoặc bay, nhưng bạn không thể làm như vậy mà không tạo ra một con vịt trong bài kiểm tra của mình.


1

Có trạng thái boolean để lưu trữ xem vịt có thể quẫy / bay hay không, là một giải pháp rất cụ thể cho vấn đề cụ thể mà bạn gặp phải trong thiết kế lớp học của mình và có thể không áp dụng cho các trường hợp khác khi mô hình chiến lược được chấp thuận.

Tôi nghĩ rằng cách tiếp cận 'quackable' của bạn làm cho mã phức tạp hơn một chút. Đó là một điều nữa mà bạn và người dùng trong lớp của bạn phải nhận thức rõ hơn là cần thiết nếu bạn đang sử dụng một mẫu chiến lược.

Cuối cùng, vì mã mẫu của bạn không hiển thị bất kỳ ví dụ nào về cách bạn sẽ ghi đè các phương thức 'quack' và 'fly', bạn sẽ không nhận thấy lợi ích to lớn của việc sử dụng mẫu chiến lược - cắt giảm sao chép mã. Bạn sẽ làm gì với hàng tá lớp vịt khác nhau mà trong số chúng có 3 hành vi 'bay' khác nhau? Sử dụng phương pháp của bạn, có lẽ bạn sẽ thấy mình cắt và dán, và sau đó có thể trích xuất mã được sao chép vào các lớp 'người trợ giúp' tĩnh. Mô hình chiến lược sạch sẽ hơn nhiều .


1

Một bản demo trực quan đẹp luôn đáng giá ngàn lời nói.

John Lindquist đang làm một công việc tuyệt vời để ghi lại các screencasts về các mẫu Thiết kế khác nhau. Bạn có thể tìm thấy 50 xu của anh ấy về mẫu đặc biệt này (và nhiều hơn nữa) trên blog của anh ấy

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.