デザインパターン勉強会⑦Builder

<h1>はじめに</h1>
ZOOM勉強会の議事録です。
第7回はBuilderパターンです。

<h1>Builderパターン</h1>
オブジェクトの生成における呼び出すメソッドの順序と具体的な生成メソッドの分離をします。
分離することにより、オブジェクト生成の順序を使い回すことができます。
特に、コンストラクタの引数が多いときに有効です。

ビルダーパターンの役割は以下です。
<ul><li>Builder</li>一つのBuilderに依存しないようにするインタフェース、建築材料を持っている<li>ConcreteBuilder</li>インスタンス生成時に使用するメソッドの具体的な実装<li>Director</li>設計図のようにインスタンス生成時の順序を知っている、材料を組み上げる役割<li>Client</li>ビルダーパターンを利用してオブジェクトを生成するコード</ul>

今回は<a href="https://refactoring.guru/design-patterns/builder/java/example">Refactoring.Guru</a>のJavaコードを参考にさせていただきました。


<h2>リファクタリング前</h2>
車を表すクラスと、そのマニュアルを表すクラスをインスタンス化しています。

```vb:
    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
```
多くのパラメータがあり、車の種類に応じて、コンストラクタの引数が異なり、間違える可能性が高いです。
また、ある車とそのマニュアルでコンストラクタに渡す引数が同じため、共通のパラメータを渡せるようにしたいです。


<h2>Builder</h2>
インスタンスを生成するメソッドを定めたインタフェースです。
コンストラクタに必要なパラメータを受け取るメソッドのみを定義しています。

```vb: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
```




<h2>ConcreteBuilder</h2>
`Builder`インタフェースを実装した具象クラスです。
それぞれ車用とマニュアル用です。

```vb: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
```

```vb: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()`でフィールドのパラメータを使用して、対象のクラスをインスタンス化して返します。

<h2>Director</h2>
渡されたBuilder役に、コンストラクタのパラメータをセットしています。
作りたい車に応じて、Builder役に与えるパラメータを変えています。

```vb: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
```

<h2>Client</h2>

DirectorとBuliderを用意して、DirectorにBuilderを渡しコンストラクタパラメータを設定します。
このように複雑なコンストラクタがなくなり、生成の手順を使い回すことができます。


```vb: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
```

<h2>利点</h2>
<ul><li>生成処理を使い回すことができる（今回の場合、車とマニュアル）</li><li>生成するためのメソッドと生成時の呼び出し順序を分けて管理できる</li></ul>

<h2>まとめ</h2>
今回はBuilderパターンについて学びました。
多数のパラメータを持つコンストラクタが出てきたときや複雑な生成方法があるときに、リファクタリングとしてBuilderパターンを採用するのが良いと思いました。