はじめに
ZOOM勉強会の議事録です。 「増補改訂版Java言語で学ぶデザインパターン入門」を読んで、プログラム作成・パターンの理解を行います。 第8回はAbstract Factoryパターンです。Abstract Factory
抽象的な向上が抽象的な部品を組み合わせて、抽象的な製品をつくります。 役割は以下です。- AbstractProduct 抽象的な製品
- ConcreteProduct 具体的な製品
- AbstractFactory 抽象的な工場
- ConcreteFactory 具体的な工場
AbstractProduct
抽象的な製品です。 今回は4つの抽象的な製品があります。Item
クラスは、キャプション用の文字列をもらって初期化します。
また、MakeHtml()
というHTMLの文字列を返すメソッドを定義しています。
Public MustInherit Class Item
Protected caption As String
Public Sub New(ByVal caption As String)
Me.caption = caption
End Sub
Public MustOverride Function MakeHtml() As String
End Class
Link
クラスは、Item
クラスの機能に追加でURLをもらって初期化するようにしています。
Public MustInherit Class Link : Inherits Item
Protected url As String
Public Sub New(ByVal caption As String, ByVal url As String)
MyBase.New(caption)
Me.url = url
End Sub
End Class
Tray
クラスは、子要素にItemクラスのリストをもつItemクラスになっています。
Public MustInherit Class Tray : Inherits Item
Protected tray As New ArrayList()
Public Sub New(ByVal caption As String)
MyBase.New(caption)
End Sub
Public Sub Add(ByVal item As Item)
tray.Add(item)
End Sub
End Class
Page
クラスは、HTMLのページを表します。
こちらも子要素にItem
クラスのリストを持っています。
また、Output()
メソッドでMakeHtml()
によって生成されるHTMLをファイルに出力する処理を持っています。
Public MustInherit Class Page
Protected title As String
Protected author As String
Protected content As New ArrayList()
Public Sub New(ByVal title As String, ByVal author As String)
Me.title = title
Me.author = author
End Sub
Public Sub Add(ByVal item As Item)
content.Add(item)
End Sub
Public Sub Output()
Try
Dim filename As String = title & ".html"
Using sw As New StreamWriter(filename, False, Encoding.UTF8)
sw.Write(Me.MakeHtml())
End Using
Debug.WriteLine(filename & "を作成しました")
Catch ex As Exception
Debug.WriteLine(ex.StackTrace())
End Try
End Sub
Public MustOverride Function MakeHtml() As String
End Class
ConcreteProduct
具体的な製品です。 今回は`Link`と`Tray`と`Page`の3つを継承した製品を作ります。 List用の部品群とTable用の部品群が用意されています。 Listが箇条書きで、Tableが表形式になるようHtmlを組み立てる部品となっています。今回は、Table用のみを記載します。
TableLink
クラスはurl
とcaption
を指定して、テーブルの要素がリンクになっているものです。
Public Class TableLink : Inherits Link
Public Sub New(ByVal caption As String, ByVal url As String)
MyBase.New(caption, url)
End Sub
Public Overrides Function MakeHtml() As String
Return "<td><a href=""" & url & """>" & caption & "</a></td>" & vbCrLf
End Function
End Class
TableTray
クラスは、1行目にcaption
、2行目に子要素の数だけ列を作成し子要素を羅列します。
Public Class TableTray : Inherits Tray
Public Sub New(ByVal caption As String)
MyBase.New(caption)
End Sub
Public Overrides Function MakeHtml() As String
Dim buffer As New StringBuilder
buffer.Append("<td>")
buffer.Append("<table width=""100%"" border=""1""><tr")
buffer.Append("<td bgcolor=""#cccccc"" align=""center"" colspan=""" & tray.Count & """><b>" & caption & "</b></td>")
buffer.Append("</tr>" & vbCrLf)
buffer.Append("<tr>" & vbCrLf)
For Each item As Item In tray
buffer.Append(item.MakeHtml())
Next
buffer.Append("</tr></table>")
buffer.Append("</td>")
Return buffer.ToString()
End Function
End Class
TablePage
クラスはHtmlのページを作成します。
子要素を表の行に入れていきます。
Public Class TablePage : Inherits Page
Public Sub New(ByVal title As String, ByVal author As String)
MyBase.New(title, author)
End Sub
Public Overrides Function MakeHtml() As String
Dim buffer As New StringBuilder
buffer.Append("<html><head><title>" & title & "</title></head>" & vbCrLf)
buffer.Append("<body>" & vbCrLf)
buffer.Append("<h1>" & title & "</h1>" & vbCrLf)
buffer.Append("<table width=""80%"" border=""3""" & vbCrLf)
For Each item As Item In content
buffer.Append("<tr>" & item.MakeHtml() & "</tr>")
Next
buffer.Append("</table>" & vbCrLf)
buffer.Append("<hr><address>" & author & "</address>")
buffer.Append("</body></html>" & vbCrLf)
Return buffer.ToString()
End Function
End Class
AbstractFactory
抽象的な工場です。 抽象的な部品を生成するための抽象メソッドと工場のインスタンスを生成するための静的メソッドを用意しています。Public MustInherit Class Factory
'工場を生成するためのメソッド
Public Shared Function GetFactory(ByVal classname As String) As Factory
Dim factory As Factory = Nothing
Try
Dim type = System.Type.GetType(classname)
factory = CType(System.Activator.CreateInstance(type), Factory)
Catch ex As Exception
Debug.WriteLine(ex.StackTrace())
End Try
Return factory
End Function
Public MustOverride Function CreateLink(ByVal caption As String, ByVal url As String) As Link
Public MustOverride Function CreateTray(ByVal caption As String) As Tray
Public MustOverride Function CreatePage(ByVal title As String, ByVal author As String) As Page
End Class
GetFactory()
ではクラス名の文字列を受け取り、Factory
型に変換し返しています。
GetFactory()
について、クラス名からFactory
のインスタンスを生成することで、新しいFactory
が追加されたとき生成用の分岐を作らなくて済みます。
数字を受け取って、その数字によって具象Factory
を返すというようなことをしていた場合、新しいFactory
を追加したときには、新たな分岐処理が必要になります。
クラス名が変更したときには修正の必要がありますが、こういった方法もあるのか~という新しい気づきでした。
ConcreteFactory
AbstractFactory役で用意した抽象クラスを実装します。 今回は、Table用・List用といった形で使用する部品群ごとにFactoryを用意します。 Public Class TableFactory : Inherits Factory
Public Overrides Function CreateLink(caption As String, url As String) As Link
Return New TableLink(caption, url)
End Function
Public Overrides Function CreateTray(caption As String) As Tray
Return New TableTray(caption)
End Function
Public Overrides Function CreatePage(title As String, author As String) As Page
Return New TablePage(title, author)
End Function
End Class
Client
AbstractFactoryを利用する側は以下のようになります。 コマンドライン引数に応じて、ConcreteFactory役が選択されます。 ConcreteFactoryによって、作られるHTMLファイルがごっそり入れ替わります。 また、`Main`の中を見ると具象クラスが出てきておらず、抽象的なクラスにのみ依存しています。 Public Sub Main(ByVal args As String())
If args.Length <> 1 Then
Exit Sub
End If
Dim f = Factory.GetFactory(args(0))
Dim asahi = f.CreateLink("朝日新聞", "http://www.asahi.com/")
Dim yomiuri = f.CreateLink("読売新聞", "http://www.yomiuri.co.jp/")
Dim us_yahoo = f.CreateLink("Yahoo!", "http://www.yahoo.com/")
Dim jp_yahoo = f.CreateLink("Yahoo!Japan", "http://www.yahoo.co.jp/")
Dim excite = f.CreateLink("Excite", "http://www.excite.com/")
Dim google = f.CreateLink("Google", "http://www.google.com/")
Dim trayNews = f.CreateTray("新聞")
trayNews.Add(asahi)
trayNews.Add(yomiuri)
Dim trayyahoo = f.CreateTray("Yahoo!")
trayyahoo.Add(us_yahoo)
trayyahoo.Add(jp_yahoo)
Dim traySearch = f.CreateTray("サーチエンジン")
traySearch.Add(trayyahoo)
traySearch.Add(excite)
traySearch.Add(google)
Dim page = f.CreatePage("LinkPage", "tonightoo")
page.Add(trayNews)
page.Add(traySearch)
page.Output()
End Sub
利点
メリット
- 工場を抽象化しているので何の部品が必要か理解しやすい
- 具象クラスをFactoryまとめることができる
- 工場を交換することでプログラムをごっそり入れ替えることができる
デメリット
- 新しい部品を追加するとすべての工場に新たな実装を作る必要がある