Visitorパターンを実装してみる。Compositeについても少し

## はじめに
今回はVisitorパターンを実際に実装しながら学んでいきます。

## Visitorパターンについて
Visitorパターンを使用することで、構造と操作が分離され、新しい操作を追加することが容易になります。  
Visitorパターンの動作は、各具象の訪問者クラスが特定の要素クラスに対して定義された操作を実行することです。これにより、新しい操作を追加する場合、既存の要素クラスには変更を加えずに新しい訪問者クラスを追加することができます。  
といったところで実際に設計＆実装してみます。

## 設計と実装
簡単に設計したものが以下です。
![2023-12-09_21h59_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3131319/5c35f519-6687-745a-d688-b215cd31bc5b.png)

Visitorパターンとしては、このような構えにしました。この設計にすることで、現在は一つしかない操作の具象クラスが増えた際に、簡単に操作を追加することができます。  
とここで、構造側は、Compositeパターンを併用してみました。具体的に説明すると、TaskPackageReportが子要素にTaskPackageReportかTaskReportを持つことができる点です。  
Compositeパターンに関しては、ChatGPTに利点を教えてもらいましたのでそちらを参考に記載します。
>Compositeパターンの利点は次の通りです：
**一貫性**: 単一オブジェクトと複合オブジェクトを同じように扱うことができ、クライアントコードが一貫して記述できます。
**再帰的な構造**: Composite内に他のCompositeを持つことで、再帰的な構造を表現できます。
**柔軟性**: クライアントは個々のオブジェクトと複合オブジェクトを同じように扱うため、新しい要素を容易に追加できます。

ト設計についてはこのあたりにして実装してみます。

```C#
    // Visitorのインターフェース「
    public interface IReportVisitor
    {
        void Visit(TaskPackageReport taskPackageReport);

        void Visit(TaskReport taskReport);
    }

    // Visitorの具象クラス
    public class OutputVisitor : IReportVisitor
    {
        public void Visit(TaskPackageReport taskPackageReport)
        {
            Console.WriteLine($"TaskPackageReport: {taskPackageReport.Name}");

            foreach (var task in taskPackageReport.TaskList)
            {
                task.Accept(this);
            }
        }

        public void Visit(TaskReport taskReport)
        {
            Console.WriteLine($"TaskReport: {taskReport.Name}");
        }
    }

    // Acceptorのインターフェース
    public interface IWBSNodeAcceptor
    {
        void Accept(IReportVisitor visitor);
    }

    // Acceptorの具象クラス
    public class TaskPackageReport : IWBSNodeAcceptor
    {
        public string Name { get; set; }

        public List<IWBSNodeAcceptor> TaskList { get;} = new List<IWBSNodeAcceptor>();

        public void Accept(IReportVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    // Acceptorの具象クラス
    public class TaskReport : IWBSNodeAcceptor
    {
        public string Name { get; set; }

        public void Accept(IReportVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    // メインの実行クラス
    internal class Program
    {
        static void Main(string[] args)
        {
            // タスクレポートを複数作成
            var taskReport1 = new TaskReport { Name = "TaskReport1" };
            var taskReport2 = new TaskReport { Name = "TaskReport2" };
            var taskReport3 = new TaskReport { Name = "TaskReport3" };
            var taskReport4 = new TaskReport { Name = "TaskReport4" };

            // タスクパッケージレポートを複数作成
            var taskPackageReport1 = new TaskPackageReport { Name = "TaskPackageReport1" };  
            var taskPackageReport2 = new TaskPackageReport { Name = "TaskPackageReport2" };

            // ツリー構造の作成
            // タスクパッケージ1にタスクレポート1とタスクレポート2を追加
            taskPackageReport1.TaskList.Add(taskReport1);
            taskPackageReport1.TaskList.Add(taskReport2);

            // タスクパッケージ2にタスクレポート3とタスクレポート4とタスクパッケージ1を追加
            taskPackageReport2.TaskList.Add(taskReport3);
            taskPackageReport2.TaskList.Add(taskReport4);
            taskPackageReport2.TaskList.Add(taskPackageReport1);

            // Visitorを作成
            var outputVisitor = new OutputVisitor();

            // タスクパッケージレポートがVisitorをAccept
            taskPackageReport2.Accept(outputVisitor);

            Console.ReadKey();  
        }
    }
```

上記の設計をそのまま実装してみました。
Compositeを使ったツリー構造にしましたので子要素を順にみていくようなコンソール出力結果が得られるのではないでしょうか？  
実際に実行してみた結果はこちら
![2023-12-09_22h24_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3131319/d44e9715-4891-8d5f-8f89-6dda8d80c3c1.png)

なんとなくツリー構造の感じで出力されていますね。一応思い通りにできました。

## 終わり
今回はVisitorパターンについて実装をしてみました。  
個人的にはCompositeパターンも使ってツリー構造にしてみて遊べたので面白かったです。  
こんな感じでどんどん学習していこうと思います。