Thứ Hai, 24 tháng 9, 2012

Command Pattern

Tiếp tục với các mẫu Behavioral Pattern, Ở phần trước ta đã đi sơ qua về mẫu Chain of Responsibility và lần này chúng ta đến với một mẫu cũng tương tự... đó là Command Pattern


Command Pattern

Command Pattern là mẫu cho phép tất cả những Request đến object được lưu trữ trong chính object đó dưới dạng 1 object command. Có nghĩa là nếu bạn chuyển 1 Request (đóng gói dưới dạng 1 command object) tới 1 chức năng nào đó của object thì command object của Request đó sẽ được giữ lại bên trong object chính.

Khái niệm Command Object giống như một class trung gian được tạo ra chuyên để lưu trữ các câu lệnhtrạng thái của object tại một thời điểm nào đó. Hay có thể hiểu nôm na là đóng gói Request thành 1 class.

Tại sao ta cần đến việc lưu trữ những Object Command (hay lưu log các Request)???
Chắc hẳn bạn đã từng dùng NotePad hay bất cứ 1 text editor nào như winword ... Mỗi khi bạn muốn Undo hay Redo một hành động ... Và văn bản chuyển về các trạng thái phía trước hay sau đó ... Đó là ứng dụng của Command Object.

Command Object gồm có 3 phần chính là:
  • Invoker : Đây sẽ là nơi lưu trữ và phát sinh Request (dưới dạng Command Object).
  • Command : Là đối tượng lưu giữ RequestTrạng thái của đối tượng tại 1 thời điểm
  • Receiver : Là đối tượng thực hiện lệnh trên mỗi Request.
Sau đây là sơ đồ UML của mẫu Command  Pattern


Vấn đề: Đối tượng văn bản cần 1 cách nào đó để thêm hoặc lưu trữ những hành động undo hay redo
 
Document Object có thiết lập như sau
 //receiver  
 class Document  
 {  
   private ArrayList _textArray = new ArrayList();  
   public void Write(string text)  
   {  
      _textArray.Add(text);  
   }  
   public void Erase(string text)  
   {  
      _textArray.Remove(text);  
   }  
   public void Erase(int textLevel)  
   {  
      _textArray.RemoveAt(textLevel);  
   }  
   public string ReadDocument()  
   {  
      System.Text.StringBuilder sb = new System.Text.StringBuilder();  
      foreach(string text in _textArray)  
        sb.Append(text);  
      return sb.ToString();  
   }  
 }  

Giải pháp: Sử dụng 1 đối tượng Command như 1 Request để lưu trữ các Text và cho phép command xử lý yêu cầu Undo và Redo.

Ta xây dựng 1 chương trình dựa trên UML sau :


  • Trong đó đối tượng DocumentInstance sẽ là đối tượng người dùng thao tác trực tiếp và cũng là Invoker.
  •  Trong đối tượng DocumentInstance sẽ có 1 Array các CommandObject (với thể hiện là DocumentEditCommand) để lưu trữ trạng thái của văn bản.
  • Đối tượng DocumentEditCommand là thể hiện trực tiếp của lớp Command được sử dụng nó đóng vai trò là lớp ConcreteCommand trên mô hình UML chuẩn (hình đầu tiên)
  • Trong Document EditCommand sẽ có các Receiver là Document để thể hiện sự thay đổi trong từng giai đoạn Request.

Cài đặt:

 //base command  
 abstract class Command  
 {  
   abstract public void Redo();  
   abstract public void Redo();  
 }  

 //concrete implementation  
 class DocumentEditCommand : Command  
 {  
      private Document _editableDoc;  
      private string _text;  
      public DocumentEditCommand(Document doc, string text)  
      {  
           _editableDoc = doc;  
           _text = text;  
           _editableDoc.Write(_text);  
      }  
      override public void Redo()  
      {  
           _editableDoc.Write(_text);  
      }  
      override public void Undo()  
      {  
           _editableDoc.Erase(_text);  
      }  
 }  


 //invoker  
 class DocumentInvoker  
 {  
      private ArrayList _commands = new ArrayList();  
      private Document _doc = new Document();  
      public void Redo(int level)  
      {  
           Console.WriteLine("---- Redo {0} level", level);  
           ((Command) _commands[level]).Redo();  
      }  
      public void Undo(int level)  
      {  
           Console.WriteLine("---- Undo {0} level", level);  
           ((Command) _commands[level]).Undo();  
      }  
      public void Write(string text)  
      {  
           DocumentEditCommand cmd = new DocumentEditCommand(_doc, text);  
           _command.Add(cmd);  
      }  
      public string Read()  
      {  
           return _doc.ReadDocument();  
      }  
 }  

Việc cài đặt xong ... ta tiến hành chạy và kiểm tra kế quả có đúng với mong muốn hay không


 DocumentInvoker instance = new DocumentInvoker();  
 instance.Write("This is the original text.");  
   
 //---------Result--------------  
 // This is the original text. --first write  
   
 instance.Write(" Here is some other text.");  
   
 //---------Result---------------  
 // This is the original text. Here is some other text. --second write  
   
 instance.Undo(1);  
   
 //----------Result---------------  
 // ---- Undo 1 level  
 // This is the original text.  
   
 instance.Redo(1);  
   
 //----------Result----------------  
 // ---- Redo 1 level  
 // This is the original text. Here is some other text.  
   
 instance.Write(" And a little more text.");  
 instance.Undo(2);  
 instance.Redo(2);  
 instance.Undo(1);  
   
 //----------Result-----------------  
 // This is the original text. Here is some other text. And a little more text  
 // ----Undo 2 level  
 // This is the original text. Here is some other text.  
 // ----Redo 2 level  
 // This is the original text. Here is some other text. And a little more text.  
 // ---- Undo 1 level  
 // This is the original text. And a little more text.  
   


Kết luận : Mẫu Command giúp chúng ta giải quyết vấn đề xử lý các sự kiện 1 cách tuần tự lần lượt và có thể lưu log... Nó khá giống với mẫu Chain of Responsibility ở chỗ đều thực hiện các công việc một cách tuần tự nhưng cải tiến hơn ở cách quản lý tập trung theo 1 list và có thể quay lại về được bất kì bước nào ở phía trước.










Không có nhận xét nào:

Đăng nhận xét