設計模式系列-命令模式


        新的一年的春天到啦,又是一輪跳槽離職的高峰期,面對新的一年的開始大家都希望各個方面都在更上一層樓,難免會出現跳槽、離職創業等等現象,最近項目組有人離職啦,當然人走了活還是要有人干的嘛,如何合理的利用有限的人力資源把多余的活分配出去呢?這就是項目經理們考慮的事情啦!不過一般還是會出現一個開發人員同時兼有幾個項目的現象啦~!OK,這個就是今天的場景,我們來用代碼模擬一下吧!

        1.場景模擬

        首先我們考慮,當項目多,人手不夠時,我們需要一個人去同時兼有好幾個項目的工作,可能有開發也可能有運維!

        那么第一步我們先設計一個項目類,分別代表需要參與的項目,代碼如下:

    ///   <summary>
    
///  Web項目類
    
///   </summary>
     public  class WebProject
    {
         ///   <summary>
        
///  項目名稱
        
///   </summary>
         public  string ProjectName {  getset; }
    }

         接下來第二步,我們設計一個開發者類表示具體的開發人員,並且開發人員具有開發項目和運維項目的操作,代碼如下:

    ///   <summary>
    
///  開發者
    
///   </summary>
     public  class Developer
    {
         ///   <summary>
        
///  開發項目
        
///   </summary>
        
///   <param name="project"> 項目 </param>
         public  void Coding(WebProject project)
        {
            Console.WriteLine( " 開發{0}項目 ",project.ProjectName);
        }

         ///   <summary>
        
///  運維項目
        
///   </summary>
        
///   <param name="project"> 項目 </param>
         public  void UpdateRoutine(WebProject project)
        {
            Console.WriteLine( " 維護{0}項目 ", project.ProjectName);
        }
    }

        主函數調用方法如下:

    static  void Main( string[] args)
    {
             // 表示一個具體的項目名字叫:項目A
            WebProject wpA =  new WebProject() { ProjectName =  " 項目A " };
             // 表示一個具體的項目名字叫:項目B
            WebProject wpB =  new WebProject() { ProjectName =  " 項目B " };

             // 實例化一個開發者,程序員
            Developer dp =  new Developer();

             // 命令他開發項目A
            dp.Coding(wpA);
             // 命令他開發項目B
            dp.UpdateRoutine(wpB);
    }

        這么簡單的程序運行結果我就不貼出來啦,我們來看看上邊的代碼,我們把主函數的調用當做客戶,我們發現如果客戶直接和開發者形成這種緊耦合的狀態是非常不好的,比如說,客戶會提出很多業務上的要求,而開發者並不能很准確的估算出開發成本與時間,所以我們需要一個項目經理來與客戶溝通,並且將客戶提出的不合理需求過濾掉,在給開發人員發出一個命令將項目交給開發人員!也就是說需要將“命令發起者”與“命令實現者”分離!

        2.命令模式

         那么我們來看看實現“命令發起者”與“命令實現者”分離的設計模式,命令模式:

         命令模式:將一個請求封裝為一個對象,從而可用不同的的請求對客戶進行參數化,隊請求排隊或者記錄請求日志,以及支持可撤銷的操作。

         命令模式結構圖如下:

     

       ①接口ICommand:用來聲明抽象一個命令操作的接口。

       ②ConcreteCommand:具體的命令實現,用來表示命令的類型例如上邊場景中的開發項目和維護項目可分別實現為:開發項目命令,維護項目命令等。

       ③Receiver:具體命令的實現者,也就是說命令的執行者,相對於上邊場景中的開發者。

       ④Invoker:命令的指揮者,用來設置命令,並且通知執行命令,相對於上邊場景中的項目經理。

       了解完命令模式的概念后,我們用命令模式來實現上邊的場景:

        首先是命令接口ICommand的實現,代碼如下:

     public  interface ICommand
    {
         void Excute(WebProject project);
    }

        接下來就是我們接受命令並且執行的實現者啦,也就是我們場景中的開發者,代碼如下:

     ///   <summary>
    
///  開發者
    
///   </summary>
     public  class Developer
    {
         ///   <summary>
        
///  開發項目
        
///   </summary>
        
///   <param name="project"> 項目 </param>
         public  void Coding(WebProject project)
        {
            Console.WriteLine( " 開發{0}項目 ", project.ProjectName);
        }

         ///   <summary>
        
///  運維項目
        
///   </summary>
        
///   <param name="project"> 項目 </param>
         public  void UpdateRoutine(WebProject project)
        {
            Console.WriteLine( " 維護{0}項目 ", project.ProjectName);
        }
    }

       現在有了命令的實現者,就應該有具體的命令啦,上邊那場景中的命令分別有,開發命令與維護命令,具體代碼如下:

   ///   <summary>
    
///  開發項目命令
    
///   </summary>
     public  class CodingCommand : ICommand
    {
        Developer dp {  getset; }
         public WebProject project {  getset; } // 所對應的項目

         public  void Excute()
        {
            dp.Coding(project);
        }
    }

     ///   <summary>
    
///  維護項目命令
    
///   </summary>
     public  class UpdateRoutineCommand : ICommand
    {
        Developer dp {  getset; }
         public WebProject project {  getset; } // 所對應的項目

         public  void Excute()
        {
            dp.UpdateRoutine(project);
        }
    }

       命令與實現者都就緒了,指揮調度者就該出現了,也就是項目經理,項目經理用來負責設置命令調度命令執行,代碼如下:

     ///   <summary>
    
///  項目經理類
    
///   </summary>
     public  class ProjectManager
    {
        List<ICommand> cmdList =  new List<ICommand>();

         ///   <summary>
        
///  設置命令
        
///   </summary>
        
///   <param name="cmd"></param>
         public  void SetCommand(ICommand cmd)
        {
            cmdList.Add(cmd);
        }

         ///   <summary>
        
///  發起命令
        
///   </summary>
         public  void ExcuteCommand()
        {
            cmdList.ForEach(p => p.Excute());
        }
    }

       代碼寫好后,我們來模擬一下調用吧,現在項目經理需要分配給開發者兩個任務,一個是開發項目A,另一個是維護項目B!主函數調用如下:

            // 表示一個具體的項目名字叫:項目A
            WebProject wpA =  new WebProject() { ProjectName =  " 項目A " };
             // 表示一個具體的項目名字叫:項目B
            WebProject wpB =  new WebProject() { ProjectName =  " 項目B " };
             // 開發者
            Developer developer=  new Developer();

             // 開發者所需要接收的命令
            ICommand codeCmd =  new CodingCommand() { project = wpA, dp = developer };
            ICommand UpdateCmd =  new UpdateRoutineCommand() { project = wpB, dp = developer };

             // 項目經理
            ProjectManager pm =  new ProjectManager();
             // 設置命令
            pm.SetCommand(codeCmd);
            pm.SetCommand(UpdateCmd);
             // 發起命令讓開發者去完成
            pm.ExcuteCommand();
}

       那么一個簡單的命令模式就完成了。

      3.更加靈活的分配任務

       接下來我們在增加一些要求:

       ①項目經理每次發起的命令都需要記錄下來,年底好根據開發者的工作量評估獎金。

       ②項目經理可以撤銷發起的命令,例如:撤銷維護項目B的命令,讓開發者專心做項目A的開發工作。

       回顧下命令模式的定義,上邊兩條需求就等同於定義中的“隊請求排隊或者記錄請求日志,以及支持可撤銷的操作

       那么我們給需求中加入一個方法用來撤銷命令,因為項目經理類中已經有一個集合來記錄命令的總數了,所以已經實現了請求記錄的功能啦,我們只需加入撤銷功能就好了。

       首先,給ICommand接口加入一個Cancel方法,用來表示撤銷操作,因為命令撤銷了需要通知實現者做一些撤銷的工作,代碼如下:

     public  interface ICommand
    {
         void Excute();
         void Cancel();
    }

      接下來既然有取消了,那么開發者受到取消命令后要執行一些關於取消的代碼,所以開發者類加入一個方法StopCoding,表示停止項目的工作,代碼如下:

   ///   <summary>
    
///  開發者
    
///   </summary>
     public  class Developer
    {
         ///   <summary>
        
///  開發項目
        
///   </summary>
        
///   <param name="project"> 項目 </param>
         public  void Coding(WebProject project)
        {
            Console.WriteLine( " 開發{0}項目 ", project.ProjectName);
        }

         ///   <summary>
        
///  運維項目
        
///   </summary>
        
///   <param name="project"> 項目 </param>
         public  void UpdateRoutine(WebProject project)
        {
            Console.WriteLine( " 維護{0}項目 ", project.ProjectName);
        }

         ///   <summary>
        
///  停止項目的工作
        
///   </summary>
        
///   <param name="project"></param>
         public  void StopCoding(WebProject project)
        {
            Console.WriteLine( " 停止{0}項目 ",project.ProjectName);
        }
    }

      修改實際的命令類實現撤銷命令的方法,並調用開發者類的StopCoding方法做撤銷命令的工作,代碼如下:

///   <summary>
    
///  開發項目命令
    
///   </summary>
     public  class CodingCommand : ICommand
    {
         public Developer dp {  getset; }
         public WebProject project {  getset; } // 所對應的項目

         public  void Excute()
        {
            dp.Coding(project);
        }

         ///   <summary>
        
///  撤銷操作
        
///   </summary>
         public  void Cancel()
        {
            dp.StopCoding(project);
        }
    }

     ///   <summary>
    
///  維護項目命令
    
///   </summary>
     public  class UpdateRoutineCommand : ICommand
    {
         public Developer dp {  getset; }
         public WebProject project {  getset; } // 所對應的項目

         public  void Excute()
        {
            dp.UpdateRoutine(project);
        }

         ///   <summary>
        
///  撤銷操作
        
///   </summary>
         public  void Cancel()
        {
            dp.StopCoding(project);
        }
    }

       接下來就是命令的指揮者啦,項目經理首先設置一個撤銷命令,並且將執撤銷命令,在執行撤銷命令的時候首先要將命令從集合中移除,因為該開發者已經不參與該撤銷項目的工作了,所以不記錄考核依據中,在調用命令中的撤銷方法讓開發者做具體的撤銷工作。代碼如下:

///   <summary>
    
///  項目經理類
    
///   </summary>
     public  class ProjectManager
    {
        List<ICommand> cmdList =  new List<ICommand>();

         ///   <summary>
        
///  設置命令
        
///   </summary>
        
///   <param name="cmd"></param>
         public  void SetCommand(ICommand cmd)
        {
            cmdList.Add(cmd);
        }

         ///   <summary>
        
///  發起命令
        
///   </summary>
         public  void ExcuteCommand()
        {
            cmdList.ForEach(p => p.Excute());
        }

         ///   <summary>
        
///  返回記錄行數
        
///   </summary>
        
///   <returns></returns>
         public  int GetCommandCount()
        {
             return cmdList.Count;
        }

         ///   <summary>
        
///  撤銷命令
        
///   </summary>
        
///   <param name="cmd"></param>
         public  void CancelExectueCommand(ICommand cmd)
        {
            cmd.Cancel();
            cmdList.Remove(cmd);
        }
    }

     可見對功能的封裝和“請求與實現分離”,可以讓我們很容易的進行撤銷與記錄的工作,可以再命令執行前作一些必須要的操作。主函數調用如下:

        static  void Main( string[] args)
        {
             // 表示一個具體的項目名字叫:項目A
            WebProject wpA =  new WebProject() { ProjectName =  " 項目A " };
             // 表示一個具體的項目名字叫:項目B
            WebProject wpB =  new WebProject() { ProjectName =  " 項目B " };
             // 開發者
            Developer developer=  new Developer();

             // 開發者所需要接收的命令
            ICommand codeCmd =  new CodingCommand() { project = wpA, dp = developer };
            ICommand UpdateCmd =  new UpdateRoutineCommand() { project = wpB, dp = developer };

             // 項目經理
            ProjectManager pm =  new ProjectManager();
             // 設置命令
            pm.SetCommand(codeCmd);
            pm.SetCommand(UpdateCmd);
             // 發起命令讓開發者去完成
            pm.ExcuteCommand();

            pm.CancelExectueCommand(UpdateCmd); // 撤銷UpdateCmd命令
         }

       命令模式總結:

       ①它能很容易的維護所有命令的集合。

       ②它可以很方便的實現撤銷和恢復命令。

       ③可以很方便的將每個執行記錄日志。

       ④最重要的就是將發起者與實現者分離。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM