Sử dụng mẫu chiến lược và mẫu lệnh


121

Cả hai mẫu thiết kế đều đóng gói một thuật toán và tách các chi tiết triển khai khỏi các lớp gọi của chúng. Sự khác biệt duy nhất mà tôi có thể nhận ra là mẫu Chiến lược lấy tham số để thực thi, trong khi mẫu Lệnh thì không.

Đối với tôi, có vẻ như mẫu lệnh yêu cầu tất cả thông tin để thực thi phải có sẵn khi nó được tạo và nó có thể trì hoãn việc gọi của nó (có thể là một phần của tập lệnh).

Những xác định nào hướng dẫn bạn nên sử dụng mẫu này hay mẫu khác?

Câu trả lời:


94

Tôi đang đưa vào một bảng phân cấp đóng gói của một số mẫu thiết kế GoF để giúp giải thích sự khác biệt giữa hai mẫu này. Hy vọng rằng nó minh họa tốt hơn những gì mỗi gói gọn lại để giải thích của tôi có ý nghĩa hơn.

Trước hết, hệ thống phân cấp liệt kê phạm vi áp dụng một mẫu nhất định hoặc mẫu thích hợp để sử dụng để đóng gói một số mức độ chi tiết, tùy thuộc vào việc bạn bắt đầu từ phía nào của bảng.

bảng phân cấp mô hình thiết kế

Như bạn có thể thấy từ bảng, đối tượng Mẫu chiến lược ẩn chi tiết về việc triển khai thuật toán, vì vậy việc sử dụng đối tượng chiến lược khác sẽ thực hiện cùng một chức năng nhưng theo một cách khác. Mỗi đối tượng chiến lược có thể được tối ưu hóa cho một yếu tố cụ thể hoặc hoạt động trên một số tham số khác; và, thông qua việc sử dụng giao diện chung, ngữ cảnh có thể hoạt động an toàn với một trong hai.

Command Pattern đóng gói một mức độ chi tiết nhỏ hơn nhiều so với một thuật toán. Nó mã hóa các chi tiết cần thiết để gửi một thông điệp đến một đối tượng: bộ nhận, bộ chọn và các đối số. Lợi ích của việc phản đối một phần nhỏ như vậy của quá trình thực thi là các thông báo như vậy có thể được gọi ra dọc theo các thời điểm hoặc địa điểm khác nhau theo cách chung mà không cần phải mã hóa chi tiết của nó. Nó cho phép các thông báo được gọi một hoặc nhiều lần, hoặc được chuyển đến các phần khác nhau của hệ thống hoặc nhiều hệ thống mà không yêu cầu biết chi tiết của một lệnh gọi cụ thể trước khi thực thi.

Như là điển hình cho các mẫu thiết kế, chúng không yêu cầu tất cả các triển khai phải giống nhau đến từng chi tiết để mang tên mẫu. Các chi tiết có thể khác nhau về cách triển khai và dữ liệu nào được mã hóa trong đối tượng so với dưới dạng các đối số của phương thức.


Vì vậy, nếu tôi có một hệ thống lọc kết quả bằng "đường ống bộ lọc" và sử dụng các đại biểu làm bộ lọc (trong đó mỗi thuật toán của bộ lọc sẽ được đóng gói trong một hàm) thì đó có được coi là một mẫu lệnh không? Trong trường hợp này, tôi thấy đại biểu cho chức năng bộ lọc cung cấp một hợp đồng các loại cho những gì mỗi bộ lọc phải tuân thủ về đầu vào và đầu ra.
KTF

4
@KTF, không. Mẫu lệnh sử dụng một đối tượng có hầu hết (nếu không phải tất cả) thông tin cần thiết (ví dụ: bộ nhận, bộ chọn, các đối số) để gọi phương thức của đối tượng. Đó là một mẫu đơn giản có thể được sử dụng trong các mẫu thiết kế khác như Chuỗi trách nhiệm, Bộ sưu tập và mẫu Đường ống mà bạn mô tả. "Hợp đồng các loại" được cung cấp bởi các đại biểu của bạn là một mẫu khác, Giao diện.
Huperniketes

50

Chiến lược đóng gói các thuật toán. Các lệnh tách người gửi khỏi người nhận một yêu cầu, chúng biến một yêu cầu thành một đối tượng.

Nếu đó là một thuật toán, một cái gì đó sẽ được thực hiện như thế nào, hãy sử dụng Chiến lược. Nếu bạn cần tách lời gọi của một phương thức khỏi việc thực thi nó, hãy sử dụng Lệnh. Các lệnh thường được sử dụng khi bạn xếp hàng các thư để sử dụng sau này, như một nhiệm vụ hoặc một giao dịch.


điều đó có ý nghĩa en.wikipedia.org/wiki/Command_Pattern client và invoker có mối quan hệ ràng buộc, nhưng đồng thời, họ không biết về nhau!
Kalpesh Soni

26

Trả lời một câu hỏi rất cũ. (có ai thấy câu trả lời cuối cùng thay vì được bình chọn nhiều nhất không?)

Đó là một sự nhầm lẫn hợp lệ để có vì những điểm tương đồng. Cả hai mẫu Chiến lược và Lệnh đều sử dụng tính năng đóng gói . Nhưng điều đó không làm cho chúng giống nhau.

Sự khác biệt chính là hiểu những gì được đóng gói. Nguyên tắc OO, cả hai mẫu đều phụ thuộc vào, là Đóng gói những gì khác nhau .

Trong trường hợp chiến lược, những gì thay đổi là thuật toán . Ví dụ: một đối tượng chiến lược biết cách xuất ra tệp XML, trong khi đối tượng khác xuất ra, chẳng hạn như JSON. Các thuật toán khác nhau được giữ lại ( đóng gói ) trong các lớp khác nhau. Nó đơn giản như vậy.

Trong trường hợp lệnh, những gì thay đổi là chính yêu cầu . Yêu cầu có thể đến từ File Menu > Deletehoặc Right Click > Context Menu > DeletehoặcJust Delete Button pressed . Cả ba trường hợp đều có thể tạo ra 3 đối tượng lệnh cùng kiểu. Các đối tượng lệnh này chỉ đại diện cho 3 yêu cầu xóa; không phải thuật toán xóa. Vì các yêu cầu bây giờ là một loạt các đối tượng, chúng tôi có thể quản lý chúng một cách dễ dàng. Đột nhiên, việc cung cấp chức năng như hoàn tác hoặc làm lại trở nên tầm thường.

Không quan trọng cách lệnh thực hiện logic được yêu cầu. Khi gọi thực thi (), nó có thể thực hiện một thuật toán để kích hoạt xóa hoặc thậm chí có thể ủy quyền nó cho các đối tượng khác, thậm chí có thể ủy quyền cho một chiến lược. Nó chỉ là chi tiết triển khai của mẫu lệnh. Đây là lý do tại sao nó được đặt tên là lệnh mặc dù nó không phải là một cách lịch sự để yêu cầu : -)

Đối chiếu nó với chiến lược; mẫu này chỉ liên quan đến logic thực tế được thực thi. Nếu chúng ta làm điều đó, nó sẽ giúp đạt được các kết hợp hành vi khác nhau với tập hợp các lớp tối thiểu, do đó ngăn chặn sự bùng nổ lớp.

Tôi nghĩ, Command giúp chúng ta mở rộng hiểu biết về tính đóng gói trong khi Strategy cung cấp cách sử dụng tự nhiên về tính đóng gói và tính đa hình.


15

Theo cách mà tôi xem xét nó là bạn có nhiều cách để thực hiện cùng một việc, mỗi cách trong số đó là một chiến lược và một cái gì đó trong thời gian chạy sẽ xác định chiến lược nào được thực thi.

Có thể trước tiên hãy thử StrategyOne, nếu kết quả không đủ tốt, hãy thử StrategyTwo ...

Các lệnh liên kết với những thứ riêng biệt cần phải xảy ra như TryToWalkAcrossTheRoomCommand. Lệnh này sẽ được kích hoạt bất cứ khi nào một đối tượng nào đó cố gắng đi ngang qua phòng, nhưng bên trong nó, nó có thể thử StrategyOne và StrategyTwo để cố gắng đi ngang qua phòng.

dấu


2
RE: "nhiều cách làm cùng một việc" - Điều đó dường như mâu thuẫn với một số ví dụ phổ biến về Chiến lược. Cụ thể là những cái mà có các lớp thực hiện làm cộng, trừ, nhân, vv Có thể đó không phải là những ví dụ tốt?
Joshua Davis,

1
@JoshuaDavis tất cả các "tổ hợp con" này là trường hợp đặc biệt của một chiến lược: phép toán số học . chúng có các đối số chung (2 toán hạng) và kết quả là một giá trị. khá nhiều việc làm giống nhau (là hộp đen) theo cách riêng của chúng, tùy thuộc vào việc triển khai. vì vậy tôi thấy không có xung đột ở đây, nhưng hoàn toàn ngược lại: ví dụ tốt đẹp =)
Forest_mole

7

Tôi có thể sai theo quan điểm của mình, nhưng tôi coi lệnh như một hàm để thực thi hoặc phản ứng. Cần có ít nhất hai người chơi: người yêu cầu hành động và người thực hiện hành động. GUI là ví dụ điển hình cho mẫu lệnh:

  • Tất cả các nút trên thanh công cụ của ứng dụng được liên kết với một số hành động.
  • Nút là người thực thi trong trường hợp này.
  • Hành động là lệnh trong trường hợp này.

Lệnh này thường bị ràng buộc đối với một số phạm vi hoặc lĩnh vực kinh doanh, nhưng không cần thiết: bạn có thể có các lệnh phát hành hóa đơn, khởi động tên lửa hoặc xóa tệp triển khai cùng một giao diện (ví dụ: một execute()phương pháp) trong một ứng dụng. Thông thường, các lệnh là tự chứa, vì vậy chúng không cần bất kỳ thứ gì từ người thực thi để xử lý tác vụ mà chúng dự định (tất cả thông tin cần thiết được cung cấp tại thời điểm xây dựng), đôi khi các lệnh có ngữ cảnh nhạy cảm và có thể phát hiện ra ngữ cảnh này. ( Lệnh Backspace nên biết vị trí dấu mũ trong văn bản để loại bỏ chính xác ký tự trước đó; Rollback Lệnh sẽ phát hiện ra giao dịch hiện tại để khôi phục; ...).

Các chiến lược là một chút khác nhau: nó là ràng buộc hơn đối với một số khu vực. Chiến lược có thể xác định quy tắc để định dạng ngày (trong UTC? Ngôn ngữ cụ thể?) (Chiến lược "định dạng ngày") hoặc để tính hình vuông cho một hình hình học (chiến lược "máy tính hình vuông"). Chiến lược theo nghĩa này là các đối tượng trọng lượng bay, lấy một thứ gì đó làm đầu vào ("ngày tháng", "con số", ...) và đưa ra một số quyết định trên cơ sở của nó. Có lẽ không phải là tốt nhất, nhưng ví dụ điển hình về chiến lược là một chiến lược được kết nối với javax.xml.transform.Sourcegiao diện: tùy thuộc vào đối tượng được truyền là DOMSourcehay SAXSourcehoặc StreamSourcechiến lược (= XSLT biến trong trường hợp này) sẽ áp dụng các quy tắc khác nhau để xử lý nó. Việc thực hiện có thể là một mô hình đơn giản switchhoặc liên quan đến Chuỗi trách nhiệm .

Nhưng thực sự có điểm chung giữa hai mẫu này: các lệnh và chiến lược đóng gói các thuật toán trong cùng một vùng ngữ nghĩa.


1
Tôi coi lệnh như một hàm gọi lại hoặc phản ứng. Nên có ít nhất hai người chơi: một người yêu cầu hành động và một người thực hiện ... - Tôi hiểu bạn đang muốn nói gì, nhưng tôi tránh sử dụng từ 'gọi lại', bởi vì từ này thường là 'callback' ngụ ý một lệnh gọi không đồng bộ và bạn không cần phải thực hiện các lệnh gọi không đồng bộ để mẫu lệnh trở nên hữu ích. Trường hợp điển hình: Microsoft Word. Thanh công cụ nút nhấp chuột và ép phím tắt không gọi lệnh không đồng bộ, nhưng chúng ta có thể đánh giá cao như thế nào để mô hình lệnh sẽ hữu ích trong trường hợp này
Jim G.

Tôi đồng ý, mặc dù như Jim đã nói, tôi sẽ chỉnh sửa để loại bỏ tham chiếu đến gọi lại.
JARC

Cảm ơn, tôi đã thực hiện một số tiện ích mở rộng. Hãy cho tôi biết, nếu bạn đồng ý / không đồng ý.
dma_k

5

Chỉ huy:

Thành phần cơ bản:

  1. Lệnh khai báo một giao diện cho các lệnh trừu tượng nhưexecute()
  2. Người nhận biết cách thực hiện một lệnh cụ thể
  3. Invoker nắm giữ ConcreteCommand , phải được thực thi
  4. Máy khách tạo ConcreteCommand và chỉ định Người nhận
  5. ConcreteCommand định nghĩa ràng buộc giữa Command Receiver

Quy trình làm việc:

Client gọi Invoker => Invoker gọi ConcreteCommand => ConcreteCommand gọi phương thức Receiver , phương thức này thực thi phương thức Command trừu tượng .

Ưu điểm : Khách hàng không bị ảnh hưởng bởi các thay đổi trong Lệnh và Bộ thu. Invoker cung cấp khớp nối lỏng lẻo giữa Máy khách và Người nhận. Bạn có thể chạy nhiều lệnh với cùng một Invoker.

Mẫu lệnh cho phép bạn thực hiện một lệnh trên các Bộ nhận khác nhaubằng cách sử dụng cùng một Invoker . Invoker không biết về loại Receiver

Để hiểu rõ hơn về các khái niệm, hãy xem bài viết JournalDev này của Pankaj Kumarbài viết dzone của James Sugrue ngoài liên kết Wikipedia.

Bạn có thể sử dụng Command pattern để

  1. Tách kẻ gọi và người nhận lệnh

  2. Triển khai cơ chế gọi lại

  3. Triển khai chức năng hoàn tác và làm lại

  4. Duy trì lịch sử các lệnh

java.lang.Threadlà một trong những cách triển khai tốt của Command pattern. Bạn có thể coi Thread như invoker & lớp triển khai Runnable dưới dạng ConcreteCommonad / Receiverrun()phương thức là Command .

Bạn có thể đọc phiên bản Undo / Redo của mẫu lệnh tại bài viết của Theodore Norvell

Chiến lược:

Mô hình chiến lược rất đơn giản để hiểu. Sử dụng mẫu này khi

Bạn có nhiều cách triển khai cho một thuật toán và việc triển khai thuật toán có thể thay đổi tại thời điểm chạy tùy thuộc vào các điều kiện cụ thể .

Lấy ví dụ về thành phần Giá vé của hệ thống đặt vé máy bay

Các hãng hàng không muốn cung cấp giá vé khác nhau trong các khoảng thời gian khác nhau - Tháng cao điểm và tháng thấp điểm. Trong những ngày cao điểm du lịch, nên kích cầu bằng cách giảm giá hấp dẫn.

Những điểm chính của mô hình Chiến lược :

  1. Đó là một khuôn mẫu hành vi
  2. Nó dựa trên sự ủy quyền
  3. Nó thay đổi ruột của đối tượng bằng cách sửa đổi hành vi của phương thức
  4. Nó được sử dụng để chuyển đổi giữa các nhóm thuật toán
  5. Nó thay đổi hành vi của đối tượng tại thời điểm chạy

Các bài viết liên quan với các ví dụ về mã:

Sử dụng mẫu thiết kế lệnh

Ví dụ về mô hình chiến lược trong thế giới thực


0

Đối với tôi, sự khác biệt là một trong những ý định. Cách triển khai của cả hai mẫu khá giống nhau, nhưng có các mục đích khác nhau:

  • Đối với một chiến lược, các thành phần sử dụng các đối tượng biết những gì đối tượng không (và sẽ sử dụng nó để thực hiện một phần công việc riêng của mình), nhưng nó không quan tâm như thế nào nó có phải nó.

  • Đối với một lệnh, các thành phần sử dụng các đối tượng biết không phải những gì Bộ chỉ huy không cũng không cách nó có phải nó - nó chỉ biết làm thế nào để gọi nó. Nhiệm vụ của người gọi chỉ là chạy lệnh - quá trình xử lý do Lệnh thực hiện không phải là một phần công việc cốt lõi của người gọi.

Đây là sự khác biệt - đối tượng sử dụng thành phần có thực sự biết hoặc quan tâm đến thành phần làm gì không? Hầu hết thời gian, điều này có thể được xác định dựa trên việc liệu đối tượng mẫu có trả về giá trị cho người gọi của nó hay không. Nếu kẻ xâm lược quan tâm đến những gì đối tượng mẫu làm thì nó có thể sẽ muốn nó trả lại một cái gì đó và nó sẽ là một Chiến lược. Nếu nó không quan tâm đến bất kỳ giá trị trả về nào thì nó có thể là Lệnh (lưu ý, một cái gì đó giống như Java Callable vẫn là Lệnh vì mặc dù nó trả về một giá trị, nhưng người gọi không quan tâm đến giá trị - nó chỉ chuyển nó trở lại đối với bất cứ thứ gì ban đầu được cung cấp cho Lệnh).

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.