DEV Community

Cover image for Why Encapsulate
Rodrigo Vieira
Rodrigo Vieira

Posted on

Why Encapsulate

I'm pretty sure you can find enough material about encapsulation in the context of Object Oriented programming paradigm on the Internet. Thus, my approach here is to expose a simple PHP snippet for those are not used to encapsulation yet.

Let's say you have a wallet object and you want to impose some restrictions on its public interface. I mean, you want to guide how client code should use your wallet object.

Among other attributes and methods, let's keep our object as simple as we can and stay focused on encapsulation. For this reason, let's start with a $balance and a $limit attributes, besides two public methods, withdraw() and deposit(), and a private one, the setLimit() method.

class Wallet
{
    /**
     * Balance of the wallet
     *
     * @var float
     */
    private $balance;

    /**
     * Availabe limit for the wallet
     *
     * @var float
     */
    private $limit;

    /**
     * Creates a Wallet with a initial balance and a certain limit.
     * @param  float  $balance
     * @param  float  $limit
     */
    public function __construct(float $balance, float $limit = 0)
    {
        $this->limit = $this->setLimit($limit);

        if ($balance + $this->limit < 0) {
            throw new Exception("Can't create a wallet with balance under limit.");
        }

        $this->balance = $balance;
    }

    private function setLimit(float $limit)
    {
        return $this->limit = abs($limit);
    }

    /**
     * Increase balance
     * @param  float  $amount
     * @return float
     */
    public function deposit(float $amount)
    {
        return $this->balance += $amount;
    }

    /**
     * Decrease balance
     * @param  float $amount
     * @return float
     */
    public function withdraw(float $amount)
    {
        $amount = abs($amount);

        if ($this->balance + $this->limit - $amount < 0) {
            throw new Exception("Insuficient funds.");
        }

        return $this->balance -= $amount;
    }

    /**
     * A string representation for the wallet's balance
     * @return string
     */
    public function __toString()
    {
        return sprintf("Balance: $ %.2f", $this->balance);
    }
}

First, we don't want to allow client code to create Wallets with no balance, or with a balance under a certain limit. Thus we've defined this initial rules in the constructor method.

Next, we can't let the client code manipulate the balance of a wallet directly. This change must occur through one of the assessor methods, 'deposit()' or 'withdraw()'. For this reason, we've defined the $balance property as private.

We also want to ensure that the balance of a portfolio does not fall below the limit set in the builder. We can define this business rule in the withdraw() method.

Using the Wallet class

With our Wallet class defined, we can easily create a new wallet, specifying its initial balance and limit:

$my_wallet = new Wallet(200, 100);

Changes on $balance property are limited through deposit() and withdraw() methods:

$my_wallet->deposit(1450.25);
$my_wallet->withdraw(500.50);

We are able to see a nice string representation of a Wallet object just printing it, thanks to the __toString() magic method:

print($my_wallet);

Lastly, we are verifying for a limit violation when withdrawing or when creating a new Wallet object:

# Will throw an Exception: "Insuficient funds"
$my_wallet->withdraw(2000.00);

# Will throw an Exception: Can't create a wallet with balance under limit.
$other_wallet = new Wallet(-225.15, 150);

The main goal of encapsulation is to protect entire object data from being accessible to its clients. Our business rule about the limit is hidden from client code. We have blundled the data with the methods that operate on that data.

Oldest comments (0)