デザインパターン勉強会⑪Composite

#はじめに
ZOOM勉強会の議事録です。
<a href="https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88Java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80-%E7%B5%90%E5%9F%8E-%E6%B5%A9/dp/4797327030">「増補改訂版Java言語で学ぶデザインパターン入門」</a>を読んで、プログラム作成・パターンの理解を行います。
第11回はCompositeパターンです。

#Compositeパターン
容器と中身を同一視し、再帰的な構造を作るパターンです。
ファイルとディレクトリのような関係です。

- **Component**
    - 中身と容器を同一視するためのクラス
- **Leaf**
    - 中身を表すクラス
- **Composite**
    - 容器を表すクラス
- **Client**
    - Compositeパターンを利用するクラス

##Component
中身と容器を同一視するための抽象クラスです。
ディレクトリエントリを表現します。
ディレクトリエントリは、名前とサイズを持っています。
また、`Add()`という抽象メソッドを持っており、サブクラスに実装を任せています。

```vb:Entry
    Public MustInherit Class Entry

        Protected name As String

        Protected size As Integer

        Public Overridable Function GetSize() As Integer
            Return size
        End Function

        Public Overridable Function GetName() As String
            Return name
        End Function

        Public MustOverride Sub Add(ByVal entry As Entry)

    End Class
```

##Leaf
中身を表すクラスです。
今回はファイルを表現します。
`Entry`クラスを継承しており名前とサイズを持っています。
ファイルには、他のファイルやディレクトリを追加できないので、`Add()`で例外を投げるようにしています。

```vb:File
    Public Class File : Inherits Entry

        Public Sub New(ByVal name As String, ByVal size As Integer)
            Me.name = name
            Me.Size = size
        End Sub

        Public Overrides Sub Add(entry As Entry)
            Throw New NotImplementedException()
        End Sub

        Public Overrides Function ToString() As String
            Return $"Name:{Name}, Size:{Size}"
        End Function
    End Class
```

##Composite
容器を表すクラスです。
今回はディレクトリを表現します。
こちらも`Entry`クラスを継承しており名前とサイズを持っています。
ディレクトリーでは、`Add()`でファイル・ディレクトリをメンバ変数の`children`に追加できるようになっています。
それに伴い、`GetSize()`は子要素のサイズの合計を返すようにしています。

```vb:Directory
    Public Class Directory : Inherits Entry

        Private children As New List(Of Entry)

        Public Sub New(ByVal name As String)
            Me.name = name
            Me.size = 0
        End Sub

        Public Overrides Sub Add(entry As Entry)
            children.Add(entry)
        End Sub

        Public Overrides Function GetSize() As Integer
            Return children.Sum(Function(child) child.GetSize()) + Me.size
        End Function

        Public Overrides Function ToString() As String
            Return $"Name:{name}, Size:{GetSize()}"
        End Function

    End Class
```


##Client
呼び出し側です。
以下のようなディレクトリ構造をコード上で表現しています。
>user/
>　├ documents/
>　│　　　├ txt1
>　│　　　└ txt2
>　├ pictures/
>　│　　　├ png1
>　│　　　└ png2
>　└ musics/
>　 　　　├ wav1
>　 　　　└ wav2

```vb:Main
    Public Sub Main(ByVal args() As String)

        Dim user As New Directory("user")
        Dim documents As New Directory("Documents")
        Dim txt1 As New File("txt1", 100)
        Dim txt2 As New File("txt2", 500)
        documents.Add(txt1)
        documents.Add(txt2)

        Dim pictures As New Directory("Pictures")
        Dim png1 As New File("png1", 1000)
        Dim png2 As New File("png2", 10000)
        pictures.Add(png1)
        pictures.Add(png2)

        Dim musics As New Directory("Musics")
        Dim wav1 As New File("wav1", 50000)
        Dim wav2 As New File("wav2", 80000)
        musics.Add(wav1)
        musics.Add(wav2)

        user.Add(documents)
        user.Add(pictures)
        user.Add(musics)

        Debug.WriteLine(documents.ToString())
        Debug.WriteLine(pictures.ToString())
        Debug.WriteLine(musics.ToString())
        Debug.WriteLine(user.ToString())

    End Sub
```
各ディレクトリのサイズ出力結果はこうなります。
>Name:Documents, Size:600
>Name:Pictures, Size:11000
>Name:Musics, Size:130000
>Name:user, Size:141600

再帰的に`GetSize()`が呼ばれ、ディレクトリー内の子要素がすべて足されたものが出力されます。
以上のように容器と中身を同一視することで、複数のものと単数のものを同じように扱うことができるのがCompositeパターンです。

#まとめ
Compositeパターンを学びました。
節のとき・・・葉のとき・・・という分岐が不要になり、ツリー構造を持つデータを扱うのに向いているやり方でした。