はじめに
ZOOM勉強会の議事録です。 第7回はBuilderパターンです。Builderパターン
オブジェクトの生成における呼び出すメソッドの順序と具体的な生成メソッドの分離をします。 分離することにより、オブジェクト生成の順序を使い回すことができます。 特に、コンストラクタの引数が多いときに有効です。ビルダーパターンの役割は以下です。
- Builder 一つのBuilderに依存しないようにするインタフェース、建築材料を持っている
- ConcreteBuilder インスタンス生成時に使用するメソッドの具体的な実装
- Director 設計図のようにインスタンス生成時の順序を知っている、材料を組み上げる役割
- Client ビルダーパターンを利用してオブジェクトを生成するコード
今回はRefactoring.GuruのJavaコードを参考にさせていただきました。
リファクタリング前
車を表すクラスと、そのマニュアルを表すクラスをインスタンス化しています。 Public Sub Main()
'スポーツカーとそのマニュアルを生成
Dim sportsCar As New Car(CarType.SPORTS_CAR, 1, New Engine(3.0, 0), Transmission.SEMI_AUTOMATIC, New TripComputer(), New GPSNavigator())
Dim sportsCarManual As New Manual(CarType.SPORTS_CAR, 1, New Engine(3.0, 0), Transmission.SEMI_AUTOMATIC, New TripComputer(), New GPSNavigator())
'シティーカーとそのマニュアルを生成
Dim cityCar As New Car(CarType.CITY_CAR, 2, New Engine(1.2, 0), Transmission.AUTOMATIC, New TripComputer(), New GPSNavigator())
Dim cityCarManual As New Manual(CarType.CITY_CAR, 2, New Engine(1.2, 0), Transmission.AUTOMATIC, New TripComputer(), New GPSNavigator())
'SUVとそのマニュアルを生成
Dim suvCar As New Car(CarType.SUV, 4, New Engine(2.5, 0), Transmission.MANUAL, Nothing, New GPSNavigator())
Dim suvCarManual As New Manual(CarType.SUV, 4, New Engine(2.5, 0), Transmission.MANUAL, Nothing, New GPSNavigator())
End Sub
多くのパラメータがあり、車の種類に応じて、コンストラクタの引数が異なり、間違える可能性が高いです。
また、ある車とそのマニュアルでコンストラクタに渡す引数が同じため、共通のパラメータを渡せるようにしたいです。
Builder
インスタンスを生成するメソッドを定めたインタフェースです。 コンストラクタに必要なパラメータを受け取るメソッドのみを定義しています。Builder
Public Interface Builder
Sub SetCarType(ByVal carType As CarType)
Sub SetSeats(ByVal seats As Integer)
Sub SetEngine(ByVal engine As Engine)
Sub SetTransmission(ByVal transmission As Transmission)
Sub SetTripComputer(ByVal tripComputer As TripComputer)
Sub SetGPSNavigator(ByVal gpsNavigator As GPSNavigator)
End Interface
ConcreteBuilder
`Builder`インタフェースを実装した具象クラスです。 それぞれ車用とマニュアル用です。CarBuilder
Public Class CarBuilder : Implements Builder
Private type As CarType
Private seats As Integer
Private engine As Engine
Private transmission As Transmission
Private tripComputer As TripComputer
Private gpsNavigator As GPSNavigator
Public Sub SetCarType(carType As CarType) Implements Builder.SetCarType
Me.type = carType
End Sub
Public Sub SetSeats(seats As Integer) Implements Builder.SetSeats
Me.seats = seats
End Sub
Public Sub SetEngine(engine As Engine) Implements Builder.SetEngine
Me.engine = engine
End Sub
Public Sub SetTransmission(transmission As Transmission) Implements Builder.SetTransmission
Me.transmission = transmission
End Sub
Public Sub SetTripComputer(tripComputer As TripComputer) Implements Builder.SetTripComputer
Me.tripComputer = tripComputer
End Sub
Public Sub SetGPSNavigator(gpsNavigator As GPSNavigator) Implements Builder.SetGPSNavigator
Me.gpsNavigator = gpsNavigator
End Sub
'車のインスタンスを生成して返す
Public Function GetResult() As Car
Return New Car(type, seats, engine, transmission, tripComputer, gpsNavigator)
End Function
End Class
CarManualBuilder
Public Class CarManualBuilder : Implements Builder
Private type As CarType
Private seats As Integer
Private engine As Engine
Private transmission As Transmission
Private tripComputer As TripComputer
Private gpsNavigator As GPSNavigator
Public Sub SetCarType(carType As CarType) Implements Builder.SetCarType
Me.type = carType
End Sub
Public Sub SetSeats(seats As Integer) Implements Builder.SetSeats
Me.seats = seats
End Sub
Public Sub SetEngine(engine As Engine) Implements Builder.SetEngine
Me.engine = engine
End Sub
Public Sub SetTransmission(transmission As Transmission) Implements Builder.SetTransmission
Me.transmission = transmission
End Sub
Public Sub SetTripComputer(tripComputer As TripComputer) Implements Builder.SetTripComputer
Me.tripComputer = tripComputer
End Sub
Public Sub SetGPSNavigator(gpsNavigator As GPSNavigator) Implements Builder.SetGPSNavigator
Me.gpsNavigator = gpsNavigator
End Sub
'マニュアルのインスタンスを生成して返す
Public Function GetResult() As Manual
Return New Manual(type, seats, engine, transmission, tripComputer, gpsNavigator)
End Function
End Class
Builder役を実装しています。
コンストラクタに必要なパラメータをフィールドに持ち、Builder役で定義されていたメソッドで各パラメータをセットします。
GetResult()
でフィールドのパラメータを使用して、対象のクラスをインスタンス化して返します。
Director
渡されたBuilder役に、コンストラクタのパラメータをセットしています。 作りたい車に応じて、Builder役に与えるパラメータを変えています。Director
Public Class Director
'スポーツカーのパラメータ設定
Public Sub ConstructSportsCar(ByVal builder As Builder)
builder.SetCarType(CarType.SPORTS_CAR)
builder.SetSeats(1)
builder.SetEngine(New Engine(3.0, 0))
builder.SetTransmission(Transmission.SEMI_AUTOMATIC)
builder.SetTripComputer(New TripComputer())
builder.SetGPSNavigator(New GPSNavigator())
End Sub
'シティーカーのパラメータ設定
Public Sub ConstructCityCar(ByVal builder As Builder)
builder.SetCarType(CarType.CITY_CAR)
builder.SetSeats(2)
builder.SetEngine(New Engine(1.2, 0))
builder.SetTransmission(Transmission.AUTOMATIC)
builder.SetTripComputer(New TripComputer())
builder.SetGPSNavigator(New GPSNavigator())
End Sub
'SUVのパラメータ設定
Public Sub ConstructSUV(ByVal builder As Builder)
builder.SetCarType(CarType.SUV)
builder.SetSeats(4)
builder.SetEngine(New Engine(2.5, 0))
builder.SetTransmission(Transmission.MANUAL)
builder.SetGPSNavigator(New GPSNavigator())
End Sub
End Class
Client
DirectorとBuliderを用意して、DirectorにBuilderを渡しコンストラクタパラメータを設定します。
このように複雑なコンストラクタがなくなり、生成の手順を使い回すことができます。
Client
Public Sub Main()
Dim director As New Director()
'スポーツカーのインスタンス作成
Dim builder As New CarBuilder()
director.ConstructSportsCar(builder)
Dim car As Car = builder.GetResult()
'スポーツカーのマニュアルのインスタンス生成
Dim manualBuilder As New CarManualBuilder()
director.ConstructSportsCar(manualBuilder)
Dim carManual = manualBuilder.GetResult()
End Sub
利点
- 生成処理を使い回すことができる(今回の場合、車とマニュアル)
- 生成するためのメソッドと生成時の呼び出し順序を分けて管理できる