#はじめに
ZOOM勉強会の議事録です。
「増補改訂版Java言語で学ぶデザインパターン入門」を読んで、プログラム作成・パターンの理解を行います。
第11回はCompositeパターンです。
#Compositeパターン
容器と中身を同一視し、再帰的な構造を作るパターンです。
ファイルとディレクトリのような関係です。
-
Component
- 中身と容器を同一視するためのクラス
-
Leaf
- 中身を表すクラス
-
Composite
- 容器を表すクラス
-
Client
- Compositeパターンを利用するクラス
##Component
中身と容器を同一視するための抽象クラスです。
ディレクトリエントリを表現します。
ディレクトリエントリは、名前とサイズを持っています。
また、Add()
という抽象メソッドを持っており、サブクラスに実装を任せています。
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()
で例外を投げるようにしています。
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()
は子要素のサイズの合計を返すようにしています。
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
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パターンを学びました。
節のとき・・・葉のとき・・・という分岐が不要になり、ツリー構造を持つデータを扱うのに向いているやり方でした。