デザインパターン勉強会　第22回：Commandパターン


# はじめに
本エントリーは某社内で実施するデザインパターン勉強会向けの資料となります。
本エントリーで書籍[「Java言語で学ぶデザインパターン入門」](http://www.hyuki.com/dp/)をベースに学習を進めますが、サンプルコードはC#に置き換えて解説します。

第1回：[Iteratorパターン](http://qiita.com/Nuits/items/800f080d44b52e1cae18)
第2回：[Adapterパターン](http://qiita.com/Nanakusajp/items/747d1913b1e8bea8d340)
第3回：[Template Methodパターン](http://qiita.com/_kyam/items/9b5722e6a9635af6e18b)
第4回：[Factory Methodパターン](http://qiita.com/ayayo/items/3eece04bbc4b7504179a)
第5回：[Singletonパターン](http://qiita.com/Keenag/items/cae53d55cb953562fee4)
第6回：[Prototypeパターン](http://qiita.com/aconit96/items/9235d30a07f508886c7e)
第7回：[Builderパターン](https://qiita.com/Uesk/items/8f12be304671e9905795)
第8回：[Abstract Factoryパターン](https://qiita.com/buru/items/bb3930dda1bdd2b5f70b)
第9回：[Bridgeパターン](https://qiita.com/mitakaosamu/items/cff9a39953ae03092f43)
第10回：[Strategyパターン](https://qiita.com/a1146234/items/fdecc374b7225d99ab07)　
第11回：[Compositeパターン](https://qiita.com/Nanakusajp/items/32ed648eaadaa9910159)
第12回：[Decoratorパターン](https://qiita.com/_kyam/items/3072a70c1f31f690e00c)
第13回：[Visitorパターン](https://qiita.com/ayayo/items/cf63041d047ea5b676c3)
第14回：[Chain of Responsibilityパターン](https://qiita.com/Keenag/items/6ef9cd9671aca38833d8)
第15回：[Facadeパターン](https://qiita.com/aconit96/items/60c924dbc69a059e5746#_reference-9d763076e525fb232407)
第16回：[Mediatorパターン](https://qiita.com/Uesk/items/5c3f2b2efd1cb4bb17ab#_reference-6f6607cd67571b932942)
第17回：[Observerパターン](https://qiita.com/buru/items/cc5c4cd32953f62c78c5)
第18回：[Mementoパターン](https://qiita.com/mitakaosamu/items/31d881ae14b28919e910#_reference-d009419cef2d0741bdc1)
第19回：[Stateパターン](https://qiita.com/a1146234/items/bba88081b0f84d70da1f)
第20回：[Flyweightパターン](https://qiita.com/dai329/items/dfbe829f02c064ebdaac)
第21回：[Proxyパターン](https://qiita.com/24be_yuuki/items/e088c67547b2fddaf5ac)

---

# Commandパターンとは

　Commandは「命令」という意味で、「この仕事を行いなさい」という「命令」をクラスに表したデザインパターンです。
命令を1つの「もの」として表現することにより、命令の集まりから履歴管理や複数の命令をまとめて新しい命令を作成することが可能になります。
　また、Commandは「イベント駆動型プログラミング」で使われるEventと呼ばれる場合もあります。GUIに関わるプログラミングではEventがよく登場しており、マウスのクリック、キーの操作といったイベントが起きた先に、そのイベントを「もの」にしておき、発生した順に処理していくという形で使われています。

![Command.png](https://qiita-image-store.s3.amazonaws.com/0/236542/a04bea40-46f7-4e8e-5e91-da7a1d590a81.png)

|名前|役割|
|:--|:--|
|Command|命令の役。命令のインターフェース(API)を定義する|
|ConcreateCommand|具体的命令の役。Commandのインターフェースを実際に実装する|
|Receiver|受信者の役。命令の受け取り手となる|
|Client|依頼者の役。具体的命令を生成し、命令の受け取り手を割り当てる|
|Invoker|起動者の役。Commandで定義されているインターフェースを呼び出し、命令の実行を開始する|

---

# サンプルプログラムのクラス図
Commandパターンを用いた、お絵かきプログラムを作成します。

![CommandSample.png](https://qiita-image-store.s3.amazonaws.com/0/236542/25cd1d69-1c3f-4a94-8906-15121196679e.png)

---

# 各クラスの役割
|名前|役割|
|:--|:--|
|ICommand|「命令」を表現するインターフェース|
|MacroCommand|「複数の命令をまとめた命令」を表現するクラス|
|DrawCommand|「点の描画命令」を表現するクラス|
|IDrawable|「描画対象」を表現するインターフェース|
|DrawCanvas|「描画対象」を実装したクラス|
|MainForm|動作確認用クラス|

---

## ICommandインターフェース
ICommandインターフェースは、System.Windows.Input内に存在します。
今回はExecuteメソッドのみの実装を行いたいため、インターフェースを作成します。

```c#:ICommand.cs
public interface ICommand
{
    void Execute();
}
```
---

## MacroCommandクラス
```c#:MacroCommand.cs
public class MacroCommand : ICommand
{
    private Stack<ICommand> commands = new Stack<ICommand>();

    // 実行
    public void Execute()
    {
        if (commands.Count > 0)
        {
            IEnumerator enumerator = commands.GetEnumerator();
            while (enumerator.MoveNext())
            {
                ICommand obj =(ICommand)enumerator.Current;
                obj.Execute();                
            }
        }
    }

    // 追加
    public virtual void Apppend(ICommand addCommand)
    {
        if (addCommand != this) commands.Push(addCommand);
    }

    // 削除
    public void Undo()
    {
        if (commands.Count > 0) commands.Pop();
    }

    // 全件削除
    public void Clear() => commands.Clear();
}
```

---

## DrawCommandクラス
```c#:DrawCommand.cs
class DrawCommand : ICommand
{
    //描画対象
    protected IDrawable drawable;

    //描画位置
    private Point point;

    //コンストラクタ
    public DrawCommand(IDrawable drawable, Point point)
    {
        this.drawable = drawable;
        this.point = point;
    }

    //実行
    public void Execute()
    {
        drawable.Draw(point.X, point.Y);
    }
}
```
---

## IDrawableインターフェース
```c#:IDrawable.cs
interface IDrawable
{
    void Draw(int x, int y);
}
```
---

## DrawCanvasクラス
```c#:DrawCanvas.cs
public partial class DrawCanvas : UserControl, IDrawable
{
    public DrawCanvas()
    {
        InitializeComponent();
    }

    //描画色
    private Color color = Color.Black;
    //半径
    private int radius = 6;
    //履歴
    private MacroCommand history = null;

    //履歴セット（本来はコンストラクタ、今回はメソッドを用意）
    public void SetHistory(MacroCommand history) => this.history = history;

    //描画
    public void Draw(int x, int y)
    {
        using (Graphics g = CreateGraphics())
        using (Brush brush = new SolidBrush(color))
        {
            Rectangle rectangle = new Rectangle(x, y, radius, radius);
            g.FillEllipse(brush, rectangle);
        }
    }

    //再描画
    private void DrawCanvas_Paint(object sender, PaintEventArgs e)
    {
        if (history != null)
        {
            history.Execute();
        }
    }
}
```
---

## MainFormクラス
```c#:MainForm.cs
public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
        drawCanvas1.SetHistory(history);
    }

    //描画履歴
    private MacroCommand history = new MacroCommand();

    bool drag = false;

    //マウスが動いている時
    private void drawCanvas1_MouseMove(object sender, MouseEventArgs e)
    {
        if (drag)
        {
            ICommand command = new DrawCommand(drawCanvas1, e.Location);
            history.Apppend(command);
            command.Execute();
        }
    }

    //Clearボタン押下
    private void ClearButton_Click(object sender, EventArgs e)
    {
        history.Clear();
        drawCanvas1.Invalidate();
    }

    //Undoボタン押下
    private void Undo_Click(object sender, EventArgs e)
    {
        history.Undo();
        drawCanvas1.Invalidate();
    }

    private void drawCanvas1_MouseDown(object sender, MouseEventArgs e)
    {
        drag = true;
    }

    private void drawCanvas1_MouseUp(object sender, MouseEventArgs e)
    {
        drag = false;
    }
}
```

---

#プログラム実行結果

![command.PNG](https://qiita-image-store.s3.amazonaws.com/0/236542/92ad9b3b-a608-fc9e-7d54-6a964fd5471e.png)

---
#関連するデザインパターン

今回のサンプルプログラムでは使用していないが、関連しているパターンがある。

・Compositeパターン
マクロコマンド（具体的命令役）を実現するために、使用される場合がある。

・Mementoパターン
Commandの履歴を保持するために、使用される場合がある。

・Prototypeパターン
発生した命令を複製するために、使用される場合がある。

---

# 考察
　「命令」をオブジェクトとして表現するCommandパターンを用いることで、命令の履歴管理、再実行、複製などを行うことができるようになります。これまでに学習したCompositeパターン、Mementoパターン、Prototypeパターンを組み合わせて使用することで、命令というオブジェクトを管理しやすくなる場合があります。