Skip to content

Instantly share code, notes, and snippets.

@hopeseekr
Created February 1, 2018 16:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hopeseekr/fc4b6ead84a88b0085e9b0280485b22b to your computer and use it in GitHub Desktop.
Save hopeseekr/fc4b6ead84a88b0085e9b0280485b22b to your computer and use it in GitHub Desktop.
Good Encapsulation Explained

First off, this is a fine piece of work.

Second, there are lessons to be learned that will take you to the next level.

First, whenever you find yourself writing 100% getters for every setter, consider that you're not actually doing encapsulation. One of the few bona fide reasons for this would be at-setting validation, but this app generally does validation after all of the parameters have been set. It's usually better to just make it a "dumb DTO" and use public properties everywhere. Here is my article on this topic: [PHPU] The Public Properties Debate.

Second, this PR is a great example of how so many developers think about Encapsulation in general. Too many of the lower-logic steps are exposed as public methods, compelling the end-developer to construct what is in effect overly complex recipes to do certain actions, recipes that would be much better generalized in the service classes that should provide them.

For instance, consider baking a cookie.

THE BAD EXAMPLE:

interface CookieMaker
{
    public function addMilk($cups);
    public function addEggs($count);
    public function addFlour($cups);
    public function stir($reps);
    public function putOnSheet($numberOfCookies);
}

class Cooker extends Oven
{
    public function bake($material, $minutes)
    {
        return parent::bake($material, $minutes);
    }

    public function makeCookies()
    {
        $cm = new ConcreteCookieMaker();
        $cm->addEggs(2);
        $cm->addFlour(5);
        $cm->stir(5);
        $cm->addMilk(2);
        $cm->stir(10);

        $cookieSheet = $cm->putOnSheet(12);
        $this->bake($cookieSheet, 20);
}

THE GOOD EXAMPLE:

abstract class CookieMaker
{
    abstract protected function addMilk($cups);
    abstract protected function addEggs($count);
    abstract protected function addFlour($cups);
    abstract protected function stir($reps);
    abstract protected function putOnSheet($numberOfCookies);

    public function prepCookieSheet($numberOfCookies)
    {
        if ($numberOfCookies < 10 || $numberOfCookies > 15) {
            throw new NotImplementedException('We have not worked out how much batter is needed for that many cookies.');
        }

        $this->addEggs(2);
        $this->addFlour(5);
        $this->stir(5);
        $this->addMilk(2);
        $this->stir(10);

        $cookieSheet = $cm->putOnSheet($numberOfCookies);
        
        return $cookieSheet;
    }
}

class Cooker extends Oven
{
    public function bake($material, $minutes)
    {
        return parent::bake($material, $minutes);
    }

    public function makeCookies()
    {
        $cm = new ConcreteCookieMaker();
        $cookieSheet = $cm->prepCookieSheet(12);
        $this->bake($cookieSheet, 20);
}

Look at the logic burden taken off of the end-developer in the second example versus the first. Consider also that the overall technical debt is greatly less in the second.

I wrote an article about this almost a decade ago called [PHPU] Encapsulation: Tell; Don't Ask (a pun on the then-very public hysteria around the U.S. military's former "Don't Ask; Don't Tell" sexual preference non-disclosure edict.

This is one of the primary demarcating points between mid-level developers and true senior level gurus.

The ability to always, effectively and near effortlessly design and implement zero-knowledge coding recipes with the end-developer in mind that abstracts down almost all the inane knowledge needed for any given software recipe.

As I point out in another article ([PHPU] On Beautiful Code), think about coding like writing a book. In the first scenario, the oven itself knows way way way more about cookies than it should. Leave that to the cookie maker. Just a like a good [book] script, the second example maintains this fiction quite nicely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment