2009-07-20 63 views
36

Khi tôi đã có một cuộc thảo luận về thiết kế, liên quan đến mẫu lệnh. Đồng đẳng của tôi tuyên bố rằng một đối tượng lệnh không được trả lại trạng thái (thành công, không thành công và tại sao) sau khi phương thức .execute() được gọi. Lý do là bạn không nên quan tâm nếu lệnh được thực hiện hay không, bởi vì lệnh phải chứa không có trạng thái. Tuy nhiên, bạn phải kiểm tra sau lệnh gọi nếu lệnh có hiệu ứng mong đợi. Một điểm khác mà ông cho rằng trên Gang of Four, mẫu lệnh không trình bày trường hợp này (trạng thái trả về).mẫu lệnh trả về trạng thái

Tôi đã xác nhận điểm ngược lại. GoF không trình bày trường hợp này, nhưng một mô hình có thể được mô hình hóa theo nhu cầu của bạn. Nếu một lệnh không thành công, trình khách gọi phải nhận được bằng chứng về trạng thái và cuối cùng triển khai một phản ứng thích hợp. Bằng cách buộc khách hàng phải kiểm tra xem hành động đạt được thành công có dễ xảy ra lỗi và tạo mã trùng lặp hay không. Hơn nữa, có những trường hợp lệnh tạo ra một kết quả (ví dụ: một lệnh thêm một dòng vào một cốt truyện, bằng cách nào đó sẽ có id dòng để trả về máy khách) và giả vờ có các lệnh không có trạng thái có nghĩa là bạn phải "cá ra" định danh đối tượng mới từ mô hình dữ liệu. Cuối cùng, chúng tôi đã đạt được thỏa hiệp bằng cách không trả lại trạng thái nhưng vẫn giữ id của đối tượng mới được tạo trong đối tượng lệnh và ứng dụng hoạt động khá tốt, nhưng giờ đây tôi cũng tò mò muốn biết ý kiến ​​của bạn.

Trả lời

11

Có hai câu hỏi trong câu hỏi với nhiều câu trả lời :) Câu hỏi đầu tiên là cần một lệnh quay trở lại trạng thái lỗi?

Không có câu trả lời rõ ràng cho mỗi chương trình mỗi khi bạn áp dụng mẫu bạn phải suy nghĩ về nó một lần nữa.

Một trong những điều bạn cần phải suy nghĩ về là:

  • Tôi có thêm khớp nối hơn với nhiều lệnh và các khách hàng chỉ một số trường hợp lỗi cụ thể?

Trong trường hợp xấu nhất, bạn có nhiều lệnh không quan tâm đến lỗi nhưng một hoặc hai lệnh thực hiện điều quan trọng để máy khách biết nếu nó hoạt động. Bây giờ bạn thêm các ngoại lệ đã kiểm tra vào Giao diện và vì vậy mọi máy khách và mọi Lệnh đều bị ràng buộc để xử lý lỗi và được ghép nối với Ngoại lệ. Nếu bạn có một khách hàng chỉ giao dịch với các lệnh không ném các ngoại lệ, bạn có một khoản phí lớn trong mã của mình.

Đây là điều bạn không muốn có. Vì vậy, bạn có thể di chuyển các lệnh cần xử lý lỗi trong cấu trúc lệnh vì chúng dường như khác với các lệnh khác hoặc nếu ngôn ngữ của bạn cho phép bạn có thể thêm ngoại lệ thời gian chạy chỉ được xử lý bởi khách hàng các lệnh cần phải ném chúng.

Điểm cực khác là mọi lệnh có thể thất bại và máy khách có cách xử lý nhất quán các lỗi này có nghĩa là các lỗi không phụ thuộc vào lệnh cụ thể. Khách hàng không phải biết loại lệnh nào thất bại, nó có thể xử lý mọi lỗi theo cùng một cách. Bây giờ bạn có thể có giao diện của lệnh trả về trạng thái lỗi và máy khách có thể xử lý các lỗi. Nhưng đối phó với các lỗi không nên phụ thuộc vào loại lệnh cho khách hàng.

Câu hỏi thứ hai là: Liệu lệnh có trạng thái không?

Có kiến ​​trúc nơi lệnh cần trạng thái và một số nơi không cần trạng thái.

Một số khả năng để quyết định này:

  • Nếu bạn muốn có một undo đối với lệnh của bạn các lệnh cần phải có một nhà nước.
  • Nếu các lệnh chỉ được sử dụng để ẩn một hàm hoạt động trên một bộ thông số nhỏ và kết quả chỉ phụ thuộc vào lệnh giống như mẫu trạng thái, không cần trạng thái và bạn có thể sử dụng cùng một đối tượng hơn và hơn.

  • Nếu bạn sử dụng lệnh để giao tiếp giữa các chuỗi và bạn muốn chuyển dữ liệu từ chuỗi này sang chuỗi khác, lệnh cần trạng thái.

  • ... Nếu có điều gì đó bạn cho là nằm trong danh sách này, hãy để lại nhận xét.
4

này chắc chắn là gây tranh cãi, nhưng nó cho thấy rõ ràng hai kiểu suy nghĩ:

  • Kiểm tra nếu có điều gì không quan trọng, và sau đó tiến hành phù hợp
  • Vẫn tiến hành và đối phó với nó nếu có điều gì xấu xảy ra

Tôi không nghĩ một cách nào tốt hơn cách khác. Ví dụ trong Java, tốt nhất là không lạm dụng xử lý ngoại lệ của bạn và để chăm sóc bất kỳ vấn đề nào có thể xảy ra trước khi bạn chỉ cần ném tay (và ngoại lệ) trong không khí. Với Python, bạn nên tiếp tục và cố gắng làm bất cứ điều gì, bất kể mã trạng thái, và để cho bất kỳ ngoại lệ nào chỉ được xử lý theo đó.

Điều đó thực sự tùy thuộc vào việc bạn có muốn mẫu lệnh trả lại trạng thái hay không.

25

Tôi không có mẫu thiết kế: Các phần tử của phần mềm hướng đối tượng có thể sử dụng lại trước mắt, nhưng tôi chắc chắn các tác giả thậm chí còn nói rằng các mẫu thiết kế mà chúng trình bày là một mô hình có thể được sửa đổi để phù hợp với một tình huống cụ thể.

Câu hỏi này cắt giảm cốt lõi của mẫu thiết kế - mẫu. Nó không phải là cái gì đó phải được thực hiện bởi-the-book. Bạn đã xác định một trường hợp mà một sửa đổi hợp lý cho mẫu như được trình bày trong cuốn sách sẽ giúp ứng dụng, và điều đó hoàn toàn ổn, đặc biệt khi bạn cân nhắc các lợi ích và chi phí.

+0

Câu trả lời của tôi sẽ khá giống với điều này nếu Thomas không trả lời trước. Câu trả lời tốt. Một mẫu là một hướng dẫn, không phải là một quy tắc cứng nhắc và nhanh chóng. – Odd

4

Có thể vấn đề ở đây là lệnh sẽ được thực thi bởi một số lớp thi hành mà sẽ không có kiến ​​thức trực tiếp về lệnh thực sự thực hiện.

Nếu chúng ta đang nói về việc thêm kiểu trả về cho phương thức thực thi, có tiềm năng để hiển thị các kiểu trả về cụ thể cho người thi hành. Điều này có nghĩa là bạn đang mở một cánh cửa đến một tình huống mà các lệnh khác nhau có thể có các giá trị trả về khác nhau. Nếu người thực thi phải xử lý những điều này thì nó sẽ trở nên chặt chẽ hơn với việc triển khai lệnh.Tuy nhiên, tôi thường đưa ra các lệnh - cho phép chúng được cấu hình với các giá trị làm việc của khách hàng khi xây dựng, và sau đó cung cấp các getters để cho phép khách hàng trích xuất các kết quả thực thi lệnh khi hoàn thành. Trong trường hợp này, tôi có thể không theo đúng mẫu lệnh - nhưng thiết kế hoạt động tốt - và trừ khi có một mã xác định có mùi về điều này - đây thực sự là một vấn đề?

Lưu ý: Điều đó nói rằng, tôi muốn biết suy nghĩ về lý do tại sao điều này có thể là mùi mã.

+1

Có một lý do khác để cho lệnh một trạng thái. Khi bạn muốn hoàn tác chúng, chúng phải biết cách hành động. Mặc dù đây là một vấn đề trong chính nó, đầy rắc rối, khi bạn hoàn tác việc tạo ra một dòng, lệnh phải nhớ id được tạo ra. Đầy mìn khi tôi lặp lại, vì bạn không được bảo đảm có id đó vẫn còn đó (có nghĩa là bạn vẫn có thể có đối tượng, nhưng id của nó đã thay đổi). –

+1

Tôi nghĩ đoạn thứ hai, ở trên, là mấu chốt của vấn đề ở đây. Ý định ban đầu của mẫu này là có một số đối tượng thực hiện các lệnh nhưng không có kiến ​​thức về những gì chúng thực sự làm. Câu hỏi đặt ra là: người thực hiện có yêu cầu kiến ​​thức về một số trạng thái lệnh không cụ thể (chẳng hạn như pass, fail etc) không? Nếu có, hãy thêm kiểu trả về, nếu không, thì không. – Steg

+0

Tôi đồng ý với việc sử dụng teabot để thêm loại trả lại nếu bạn cần thứ gì đó từ lệnh stateful. – JamesC

0

Trong phần mềm CAD/CAM của tôi, các Hội đồng chứa các Lệnh tham chiếu đến các hội đồng chứa các giao diện và phân cấp đối tượng chứa các phần tử giao diện người dùng khác nhau của phần mềm của tôi. Nó tương tự như Passive View

Các lệnh có thể thao tác giao diện người dùng thông qua Giao diện chế độ xem và tự báo cáo bất kỳ lỗi nào.

Về cơ bản nó đi

Các hình thức thực hiện IFormInterfaces và đăng ký chính nó với xem màn hình trong EXE

ScreenObjects thực hiện IScreenView và đăng ký với chính lắp ráp màn hình là cũng như các lệnh lấy từ các hội lệnh

lệnh Các hội đồng tham chiếu Hội đồng ScreenView và Mô hình

Hội đồng ScreenView ít hơn một bộ sưu tập Giao diện và giữ Xem triển khai ứng dụng.

7

Tôi sẽ tham khảo "Mẫu thiết kế đầu tiên". Các ví dụ họ sử dụng cho các mẫu lệnh là:

  1. kịch bản diner (khách hàng tạo ra trật tự, các nhân viên chờ đợi gọi nó bằng hollering tại nhân viên nhà bếp, và các nhân viên nhà bếp nhận thứ tự)
  2. điều khiển từ xa kịch bản điều khiển (người nhấp vào một nút, điều khiển từ xa gọi lệnh và điện thoại nhận nó)

Rõ ràng trong trường hợp đầu tiên, một số loại nhà nước được sản xuất bởi người nhận: "đây là grub", hoặc "chúng tôi 'ra khỏi bánh mì lúa mạch đen'. Trong một nhà hàng ưa thích, bạn có thể làm điều này thông qua xử lý ngoại lệ ở cấp cao hơn (maitre d 'đến bàn và xin lỗi, cung cấp thay thế và bổ sung món tráng miệng của bạn), và nhân viên chờ đợi không cần phải làm gì ngoài việc gọi đúng lệnh. Nhưng tại một quán ăn, có lẽ đầu bếp đi trước và thay thế bánh mì nâu - nhân viên chờ đợi (và khách hàng) cần phải có thể xử lý mà không nhìn chằm chằm vào quầy tự hỏi "cá ngừ của tôi trên lúa mạch đen ở đâu?" Điều này không được giải quyết trực tiếp bởi cuốn sách, nhưng tôi nghĩ nó rõ ràng là một trường hợp hợp lệ.

Nhưng trong trường hợp thứ hai, người gọi là cố ý làm ngu ngốc. Nó sẽ không báo lỗi cho bạn nếu có điều gì đó sai, nó sẽ không có tác dụng gì cả. Tất cả các thông minh đều ở trong máy khách để xác định xem lệnh của nó có thành công một cách kịp thời hay không ("crap, tôi quên cắm nó vào"), hoặc trong máy thu để tìm ra việc cần làm ("phát đĩa CD: đóng khay CD Đầu tiên").

Tôi không phải là chuyên gia, nhưng tôi sẽ nói rằng trạng thái trở lại với người gọi là hoàn toàn ổn cho một số ứng dụng.

3

Như đã nói trong câu hỏi của bạn:

Nếu một lệnh không thành công, các cách gọi khách hàng phải nhận được một bằng chứng về tình trạng này, và cuối cùng triển khai một phản ứng thích hợp.

Trong trường hợp đó, tôi ném ngoại lệ thời gian chạy làm trạng thái, chứa thông tin cần thiết về nó. Bạn có thể thử nó.

regards,

+0

+1 cho điều đó. Không bao giờ nhìn thấy thực hiện mẫu lệnh với trạng thái trả lại –

1

Một thỏa hiệp khác là phơi bày thuộc tính "Xử lý ngoại lệ" trên lệnh cụ thể có thể không thành công. Bằng cách này, người tạo ra lệnh có thể xử lý ngoại lệ và bạn không thêm phí trên máy cho khách hàng của mình. Điều này rất hữu ích khi hầu hết các lệnh của bạn không nên thất bại.

4

Thảo luận rất hay. Tôi đã có câu hỏi triết học này hàng giờ đồng hồ và tôi đã đến một giải pháp thỏa mãn nỗi ám ảnh của tôi. (Lý do tôi thích công cụ này là nó kết hợp logic cụ thể và trừu tượng - Booleans + thiết kế.)

Tôi đã xem xét ngắn gọn sử dụng Ngoại lệ để trả lại kết quả. Tôi đã từ bỏ ý tưởng đó bởi vì trong nhiều trường hợp, nó sẽ loại bỏ sự tách rời, trái tim của mô hình, như một số bạn đã lưu ý. Ngoài ra, kết quả thường không phải là Ngoại lệ, mà là một giá trị trả về chuẩn. Tôi có thể bị loét.

Cuối cùng, tôi đã viết một máy khách tự tạo ra một máy thu với chính nó, giữ tất cả logic trong bộ thu nơi nó thuộc về. Máy khách chỉ gọi lệnh execute() và tiếp tục. Người nhận có thể gọi các phương thức công khai trên máy khách. Không có gì để trở về.

Dưới đây là một số mã mẫu. Tôi đã không viết lớp lệnh vì tôi nghĩ bạn sẽ có ý tưởng mà không có nó. Phương thức execute() của nó gọi phương thức run() của người nhận.

Client:

Class ClientType{ 

    CommandType m_Command; 
    ReceiverType m_Receiver; 
    boolean m_bResult; 

    ClientType(){ 

     m_Receiver = new ReceiverType(this); 
     m_Command = new CommandType(m_Receiver); 
    } 

    public void run(){ 
      ... 
     m_Command.execute(); 
    } 


    /* Decoupled from both the 
    * command and the receiver. 
    * It's just a public function that 
    * can be called from anywhere./
    public setResult(boolean bResult){ 
     m_bResult = bResult; 
    } 
} 

Người nhận:

Class ReceiverType{ 

    ClientType m_Client; 
    boolean m_bResult; 

    ReceiverType(ClientType client){ 
     m_Client = client; 
    } 

    public void run(){ 
      ... 
     m_Client.setResult(m_bResult);  
    } 
} 

Thoạt nhìn, nó có thể xuất hiện mà tôi đã vi phạm các yêu cầu tách. Nhưng hãy xem xét rằng khách hàng không biết gì về việc triển khai của người nhận. Thực tế là người nhận biết gọi các phương thức công khai trên máy khách là giá vé tiêu chuẩn. Người nhận luôn biết phải làm gì với các đối tượng tham số của họ. Không có phụ thuộc. Thực tế là hàm tạo của người nhận có tham số ClientType không liên quan. Nó cũng có thể là bất kỳ đối tượng nào.

Tôi biết rằng đây là một chủ đề cũ, nhưng hy vọng rằng một số bạn sẽ kêu gọi lại. Cảm thấy tự do để phá vỡ trái tim của tôi nếu bạn nhìn thấy một lỗ hổng. Đó là những gì chúng tôi làm.

+2

Nó không phải là một thiết kế tồi. Điều duy nhất mà tôi không thực sự thích là việc thiết lập kết quả không phải là một phần của lệnh. Nó là một phần của khách hàng, và trong thực tế là gần để đăng ký một chức năng gọi lại. Điều này có thể, trong một số trường hợp, khó theo dõi lệnh nào thực sự được gọi là hàm setResult và lịch sử thực thi và thiết lập kết quả, nhưng nó có thể hoạt động khá tốt trong một số trường hợp nhất định. –

Các vấn đề liên quan