1.2. 빌더 (Builder)

1.2.1. 사용 목적

빌더는 복잡한 객체의 일부를 생성하는 인터페이스 입니다.

때때로, 빌더는 그것에 대해 더 명확한 정보(Knowledge)를 가질 때, 해당 인터페이스는 기본 매서드(일명 Adapter)를 가진 추상클래스가 됩니다.

만약 객체들에 복잡한 상속 트리가 있는 경우에, 빌더 또한 복잡한 상속 트리를 가져야 마땅합니다.

Note: 빌더들은 보통 유연한 인터페이스를 가지는데, PHPUnit의 mock builder가 그 예 입니다.

1.2.2. 예시

  • PHPUnit: Mock Builder

1.2.3. UML 다이어그램

Alt Builder UML Diagram

1.2.4. 코드

코드는 또한 GitHub 에서 볼 수 있습니다.

Director.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php

namespace DesignPatterns\Creational\Builder;

/**
 * Director is part of the builder pattern. It knows the interface of the builder
 * and builds a complex object with the help of the builder.
 *
 * You can also inject many builders instead of one to build more complex objects
 */
class Director
{

    /**
     * The director don't know about concrete part
     *
     * @param BuilderInterface $builder
     *
     * @return Parts\Vehicle
     */
    public function build(BuilderInterface $builder)
    {
        $builder->createVehicle();
        $builder->addDoors();
        $builder->addEngine();
        $builder->addWheel();

        return $builder->getVehicle();
    }
}

BuilderInterface.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php

namespace DesignPatterns\Creational\Builder;

/**
 *
 */
interface BuilderInterface
{
    /**
     * @return mixed
     */
    public function createVehicle();

    /**
     * @return mixed
     */
    public function addWheel();

    /**
     * @return mixed
     */
    public function addEngine();

    /**
     * @return mixed
     */
    public function addDoors();

    /**
     * @return mixed
     */
    public function getVehicle();
}

BikeBuilder.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php

namespace DesignPatterns\Creational\Builder;

/**
 * BikeBuilder builds bike
 */
class BikeBuilder implements BuilderInterface
{
    /**
     * @var Parts\Bike
     */
    protected $bike;

    /**
     * {@inheritdoc}
     */
    public function addDoors()
    {
    }

    /**
     * {@inheritdoc}
     */
    public function addEngine()
    {
        $this->bike->setPart('engine', new Parts\Engine());
    }

    /**
     * {@inheritdoc}
     */
    public function addWheel()
    {
        $this->bike->setPart('forwardWheel', new Parts\Wheel());
        $this->bike->setPart('rearWheel', new Parts\Wheel());
    }

    /**
     * {@inheritdoc}
     */
    public function createVehicle()
    {
        $this->bike = new Parts\Bike();
    }

    /**
     * {@inheritdoc}
     */
    public function getVehicle()
    {
        return $this->bike;
    }
}

CarBuilder.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?php

namespace DesignPatterns\Creational\Builder;

/**
 * CarBuilder builds car
 */
class CarBuilder implements BuilderInterface
{
    /**
     * @var Parts\Car
     */
    protected $car;

    /**
     * @return void
     */
    public function addDoors()
    {
        $this->car->setPart('rightdoor', new Parts\Door());
        $this->car->setPart('leftDoor', new Parts\Door());
    }

    /**
     * @return void
     */
    public function addEngine()
    {
        $this->car->setPart('engine', new Parts\Engine());
    }

    /**
     * @return void
     */
    public function addWheel()
    {
        $this->car->setPart('wheelLF', new Parts\Wheel());
        $this->car->setPart('wheelRF', new Parts\Wheel());
        $this->car->setPart('wheelLR', new Parts\Wheel());
        $this->car->setPart('wheelRR', new Parts\Wheel());
    }

    /**
     * @return void
     */
    public function createVehicle()
    {
        $this->car = new Parts\Car();
    }

    /**
     * @return Parts\Car
     */
    public function getVehicle()
    {
        return $this->car;
    }
}

Parts/Vehicle.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

namespace DesignPatterns\Creational\Builder\Parts;

/**
 * VehicleInterface is a contract for a vehicle
 */
abstract class Vehicle
{
    /**
     * @var array
     */
    protected $data;

    /**
     * @param string $key
     * @param mixed  $value
     */
    public function setPart($key, $value)
    {
        $this->data[$key] = $value;
    }
}

Parts/Bike.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php

namespace DesignPatterns\Creational\Builder\Parts;

/**
 * Bike is a bike
 */
class Bike extends Vehicle
{
}

Parts/Car.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php

namespace DesignPatterns\Creational\Builder\Parts;

/**
 * Car is a car
 */
class Car extends Vehicle
{
}

Parts/Engine.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php

namespace DesignPatterns\Creational\Builder\Parts;

/**
 * Class Engine
 */
class Engine
{
}

Parts/Wheel.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php

namespace DesignPatterns\Creational\Builder\Parts;

/**
 * Class Wheel
 */
class Wheel
{
}

Parts/Door.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php

namespace DesignPatterns\Creational\Builder\Parts;

/**
 * Class Door
 */
class Door
{
}

1.2.5. 테스트

Tests/DirectorTest.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

namespace DesignPatterns\Creational\Builder\Tests;

use DesignPatterns\Creational\Builder\Director;
use DesignPatterns\Creational\Builder\CarBuilder;
use DesignPatterns\Creational\Builder\BikeBuilder;
use DesignPatterns\Creational\Builder\BuilderInterface;

/**
 * DirectorTest tests the builder pattern
 */
class DirectorTest extends \PHPUnit_Framework_TestCase
{

    protected $director;

    protected function setUp()
    {
        $this->director = new Director();
    }

    public function getBuilder()
    {
        return array(
            array(new CarBuilder()),
            array(new BikeBuilder())
        );
    }

    /**
     * Here we test the build process. Notice that the client don't know
     * anything about the concrete builder.
     *
     * @dataProvider getBuilder
     */
    public function testBuild(BuilderInterface $builder)
    {
        $newVehicle = $this->director->build($builder);
        $this->assertInstanceOf('DesignPatterns\Creational\Builder\Parts\Vehicle', $newVehicle);
    }
}