3.2. Bridge

3.2.1. Intent

According to the Gang of Four, the Bridge pattern is a way to “decouple an abstraction from its implementation so that the two can vary independently” (Design Patterns: Elements of Reusable Object-Oriented Software, 2013, p. 151).

3.2.2. When to use it?

The Bridge pattern is similar to the Adapter pattern but its underlying motivation is significantly different. The Adapter pattern will create an interface for unrelated classes to work together (these classes aren’t supposed to be aware of each other) while the Bridge pattern should be thought as soon as possible during the design level, before having knowledge of future involved classes, to separate the interface from implementation!

3.2.3. Diagram

Created using PhpStorm and yFiles.

Class diagram of the Bridge pattern

3.2.4. Implementation

AbstractFighter.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
<?php

namespace Phpatterns\Structural\Bridge;

abstract class AbstractFighter
{
    /** @var string */
    protected $name;

    /** @var float */
    protected $life = 100.0;

    /** @var float */
    protected $force = 50.0;

    /** @var WeaponInterface */
    protected $weapon;

    /**
     * @param string $name
     * @param WeaponInterface $weapon
     */
    public function __construct($name, WeaponInterface $weapon)
    {
        $this->name = $name;
        $this->weapon = $weapon;
    }

    /**
     * Perform an attack to the opponent with a weapon
     * @param AbstractFighter $opponent
     * @return string
     */
    public function attack(AbstractFighter $opponent)
    {
        return $this->weapon->attack($opponent);
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Move the fighter
     * @return string
     */
    abstract public function move();
}

God.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
<?php

namespace Phpatterns\Structural\Bridge\Fighter;

use Phpatterns\Structural\Bridge;

class God extends Bridge\AbstractFighter
{
    /**
     * @param string $name
     * @param Bridge\WeaponInterface $weapon
     */
    public function __construct($name, Bridge\WeaponInterface $weapon)
    {
        parent::__construct($name, $weapon);
        $this->force = 150.0;
        $this->life = 200.0;
    }

    /**
     * Move the fighter
     * @return string
     */
    public function move()
    {
        return 'Moving by teleportation...';
    }
}

Human.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
<?php

namespace Phpatterns\Structural\Bridge\Fighter;

use Phpatterns\Structural\Bridge;

class Human extends Bridge\AbstractFighter
{
    /**
     * @param string $name
     * @param Bridge\WeaponInterface $weapon
     */
    public function __construct($name, Bridge\WeaponInterface $weapon)
    {
        parent::__construct($name, $weapon);
        $this->force = 100.0;
    }

    /**
     * Move the fighter
     * @return string
     */
    public function move()
    {
        return 'Running...';
    }
}

WeaponInterface.php

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

namespace Phpatterns\Structural\Bridge;

interface WeaponInterface
{
    /**
     * Perform an attack to the opponent
     * @param AbstractFighter $opponent
     * @return string
     */
    public function attack(AbstractFighter $opponent);
}

Gun.php

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

namespace Phpatterns\Structural\Bridge\Weapon;

use Phpatterns\Structural\Bridge;

class Gun implements Bridge\WeaponInterface
{
    /**
     * Perform an attack to the opponent
     * @param Bridge\AbstractFighter $opponent
     * @return string
     */
    public function attack(Bridge\AbstractFighter $opponent)
    {
        return sprintf(
            "Attacking %s with a gun!",
            $opponent->getName()
        );
    }
}

Knife.php

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

namespace Phpatterns\Structural\Bridge\Weapon;

use Phpatterns\Structural\Bridge;

class Knife implements Bridge\WeaponInterface
{
    /**
     * Perform an attack to the opponent
     * @param Bridge\AbstractFighter $opponent
     * @return string
     */
    public function attack(Bridge\AbstractFighter $opponent)
    {
        return sprintf(
            "Attacking %s with a knife!",
            $opponent->getName()
        );
    }
}

3.2.5. Tests

BridgeTest.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
<?php

namespace Test\Phpatterns\Structural\Bridge;

use Phpatterns\Structural\Bridge;
use Phpatterns\Structural\Bridge\Fighter;
use Phpatterns\Structural\Bridge\Weapon;

class BridgeTest extends \PHPUnit_Framework_TestCase
{
    /** @var Bridge\AbstractFighter */
    private $human;

    /** @var Bridge\AbstractFighter */
    private $god;

    protected function setUp()
    {
        $this->human = new Fighter\Human(
            'Jet Li',
            new Weapon\Knife()
        );

        $this->god = new Fighter\God(
            'Super Gun Man',
            new Weapon\Gun()
        );
    }

    public function testHumanAttack()
    {
        $this->assertSame('Attacking Super Gun Man with a knife!', $this->human->attack($this->god));
    }

    public function testGodAttack()
    {
        $this->assertSame('Attacking Jet Li with a gun!', $this->god->attack($this->human));
    }

    public function testHumanMove()
    {
        $this->assertSame('Running...', $this->human->move());
    }

    public function testGodMove()
    {
        $this->assertSame('Moving by teleportation...', $this->god->move());
    }
}