Deep Simplicity - Rule 4

The next rule in Deep Simplicity is based on the Law of Demeter ("talk only to your friends") but is phrased more colloquially as Use only one -> per line. In PHP it should actually be "Don't use more than two -> per line" since we have to use $this-> in order to access local properties. The main reason we want to try to follow this rule is because if we don't, we are breaking a basic principal of OOP - encapsulation. By reaching into the child of a child object, we are essentially saying that the child object is a middle-man that does not provide us with the functionality we need. It can also be an indication that there is a missing object in our Domain. By treating code that looks like this $a->b->c->d() as a warning flag, you can be on the look out for objects and classes that may be missing. Let's take a look at some code and then we can see if we can make it better.

class User
{   
    protected $account;

    public function getBalance()
    {
        $balance = 0;

        $numTransactions = count($this->account->transactions);
        for ($i=0; $i < count($this->account->transactions); $i++) { 
            if ($this->account->transactions[$i]->isDeposit()) {
                $balance += $this->account->transactions[$i]->amount();
            } else {
                $balance -= $this->account->transactions[$i]->amount();
            }
        }
        return $balance;
    }
}

As you can see, we are reaching far into the account's dirty bits to put together the balance for the User. To me, the code felt bad as soon as I wrote it. To be completely honest, it's actually the for loop that is bugging me. Other than this example, it has probably been over a year since I used the for keyword. Lemme just fix it up a bit before we continue.

class User
{   
    protected $account;

    public function getBalance()
    {
        $balance = 0;

        foreach ($this->account->transactions as $transaction) { 
            if ($transaction->isDeposit()) {
                $balance += $transaction->amount();
            } else {
                $balance -= $transaction->amount();
            }
        }
        return $balance;
    }
}

Although this code is much improved over the previous version, we are still reaching into transactions to get their amount. This is bad. To be completely honest, the User object should have no knowledge of the concept of a transaction at all. When you go to the bank machine you choose to withdraw $100 from your checking account. My bank machines don't offer the "Add withdrawal transaction to your checking account's list of transactions" option, does yours?. After you get your cash monies, you are presented with a receipt, on which is printed your checking account balance, not a sum of your transactions. So, let's write it like that:

class User
{
    protected $account;

    public function getBalance()
    {
        return $this->account->getBalance();
    }
}

I know right? Have you noticed how many times in this series when we are done we end up with methods that are less than five lines long? Although the User class API is essentially the same, we have improved the Account class API by adding the getBalance method to it. This method has a strong cohesive name and responsibility. This means that other developers are far more likely to use it and the line $this->account->transactions[$i]->amount() should be rarely seen in the Domain if at all.

Next part is #5: Do not abbreviate.

blogroll

social