3.5. Facade

3.5.1. Intent

According to the Gang of Four, the Facade pattern is a way to “provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use” (Design Patterns: Elements of Reusable Object-Oriented Software, 2013, p. 185).

3.5.2. When to use it?

The Facade pattern should be used when you face a complex subsystem and you only want to expose a simplified interface to the clients (the trick is to hide internal complexity by providing a simple default view).

Clients needing more flexibility are still able to access subsystem classes and customize the behavior of the features.

Another use of the Facade pattern is decoupling source code between clients and concrete classes (implementations of an abstraction).

3.5.3. Diagram

Created using PhpStorm and yFiles.

Class diagram of the Facade pattern

3.5.4. Implementation

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

namespace Phpatterns\Structural\Facade;

class ATMFacade
{
    /** @var Bank */
    private $bank;

    /** @var Customer */
    private $customer;

    /**
     * @param Bank $bank
     * @param Customer $customer
     */
    public function __construct(Bank $bank, Customer $customer)
    {
        $this->bank = $bank;
        $this->customer = $customer;
    }

    /**
     * Withdrawing money
     * @param float $amount
     * @throws \Exception
     */
    public function withdraw($amount)
    {
        $this->bank->validateCard($this->customer->getCreditCard());
        $this->bank->checkAccountBalance($this->customer, $amount);
        $this->bank->withdraw($this->customer, $amount);
    }

    /**
     * @return float
     */
    public function getBalance()
    {
        return $this->bank->getBalance($this->customer);
    }

    /*
     * ...
     *
     * Other methods (examples: deposit money, transfer money, etc.)
     *
     * ...
     */
}

Bank.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
59
60
61
62
63
64
65
66
67
68
<?php

namespace Phpatterns\Structural\Facade;

class Bank
{
    /** @var Account[] */
    private $accounts;

    /**
     * @param array $accounts
     */
    public function __construct(array $accounts)
    {
        $this->accounts = $accounts;
    }

    /**
     * Validating a credit card
     * @param CreditCard $card
     * @throws \Exception
     */
    public function validateCard(CreditCard $card)
    {
        /**
         * ...
         * Executing control mechanisms, etc.
         * Throw \Exception on error
         * ...
         */
    }

    /**
     * @param Customer $customer
     * @param float $amount
     * @throws \Exception
     */
    public function checkAccountBalance(Customer $customer, $amount)
    {
        /**
         * ...
         * Executing control mechanisms, etc.
         * Throw \Exception on error
         * ...
         */
    }

    /**
     * Withdrawing money from an account
     * @param Customer $customer
     * @param float $amount
     */
    public function withdraw(Customer $customer, $amount)
    {
        $account = $this->accounts[$customer->getUid()];
        $account->setBalance($account->getBalance() - $amount);
        $this->accounts[$customer->getUid()] = $account;
    }

    /**
     * @param Customer $customer
     * @return float
     */
    public function getBalance(Customer $customer)
    {
        return $this->accounts[$customer->getUid()]->getBalance();
    }
}

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

namespace Phpatterns\Structural\Facade;

class Account
{
    /** @var float */
    private $balance;

    /**
     * @param float $balance
     */
    public function __construct($balance)
    {
        $this->balance = $balance;
    }

    /**
     * @return float
     */
    public function getBalance()
    {
        return $this->balance;
    }

    /**
     * @param float $balance
     */
    public function setBalance($balance)
    {
        $this->balance = $balance;
    }
}

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

namespace Phpatterns\Structural\Facade;

class Customer
{
    /** @var string */
    private $uid;

    /** @var string */
    private $firstName;

    /** @var string */
    private $lastName;

    /** @var CreditCard */
    private $creditCard;

    /**
     * @param string $firstName
     * @param string $lastName
     * @param CreditCard $creditCard
     */
    public function __construct($firstName, $lastName, CreditCard $creditCard)
    {
        $this->uid = uniqid(); //just an example...
        $this->firstName = $firstName;
        $this->lastName = $lastName;
        $this->creditCard = $creditCard;
    }

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

    /**
     * @return CreditCard
     */
    public function getCreditCard()
    {
        return $this->creditCard;
    }
}

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

namespace Phpatterns\Structural\Facade;

class CreditCard
{
    /** @var string */
    private $number;

    /** @var string */
    private $pin;

    /** @var \DateTime */
    private $expirationDate;

    /**
     * @param string $number
     * @param string $pin
     * @param \DateTime $expirationDate
     */
    public function __construct($number, $pin, \DateTime $expirationDate)
    {
        $this->number = $number;
        $this->pin = $pin;
        $this->expirationDate = $expirationDate;
    }

    /**
     * @return \DateTime
     */
    public function getExpirationDate()
    {
        return $this->expirationDate;
    }
}

3.5.5. Tests

FacadeTest.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 Test\Phpatterns\Structural\Facade;

use Phpatterns\Structural\Facade;

class FacadeTest extends \PHPUnit_Framework_TestCase
{
    /** @var Facade\Customer */
    private $customer;

    protected function setUp()
    {
        $this->customer = new Facade\Customer(
            'Vincent',
            'Belmehel',
            new Facade\CreditCard(
                '123456789',
                '0000',
                new \DateTime('2025-01-01')
            )
        );
    }

    public function testWithdrawingMoneyFromFacade()
    {
        $bank = $this->getMock(
            Facade\Bank::class,
            ['validateCard', 'checkAccountBalance'],
            [
                [$this->customer->getUid() => new Facade\Account(10000)]
            ]
        );

        $bank
            ->expects($this->once())
            ->method('validateCard');

        $bank
            ->expects($this->once())
            ->method('checkAccountBalance');

        $atmFacade = new Facade\ATMFacade($bank, $this->customer);
        $atmFacade->withdraw(5000);

        $this->assertSame(5000, $atmFacade->getBalance());
    }

    /**
     * Still possible to access to the underlying classes and methods
     */
    public function testWithdrawingMoney()
    {
        $bank = new Facade\Bank([$this->customer->getUid() => new Facade\Account(8000)]);
        $bank->withdraw($this->customer, 6000);
        $this->assertSame(2000, $bank->getBalance($this->customer));
    }
}