Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Refactoring: Less Code, Less Problems (Part 1)

Refactoring: Less Code, Less Problems (Part 1)

Video of this talk: https://www.youtube.com/watch?v=PjHZFTbg6gg

John talks about guidelines and code smells for improving object API design. He takes an existing class and demonstrates refactoring it to a leaner more focused version of its previous self. Most importantly, what questions to ask yourself as you're writing new classes.

Pass 1 (and a future Part 2) will approach refactoring a class from the outside as opposed to the inside. We discuss topics like breaking apart bigger classes to make smaller classes that do one thing, programming without getters and setters, consistent entry/return points, and guidelines for object instantiation. This talk focuses less on in-function refactoring and more on API-level refactoring.

John Kary

April 02, 2014
Tweet

More Decks by John Kary

Other Decks in Technology

Transcript

  1. Less Code, Less Problems
    Pass 1(of 2)
    April 2, 2014

    Kansas City PHP User Group
    Slides: http://johnkary.net/talks
    John Kary
    Code Slinger, Build Breaker
    @johnkary

    View Slide

  2. 0
    Survey

    the land

    View Slide

  3. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();


    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function setBucketIds($bucketIds){...}


    public function setBallIds($ballIds){...}


    public function setMinBallsPerBucket($count){...}


    public function setMinBucketsPerBall($count){...}


    public function run(){...}


    public function getResults(){...}


    public function getResultsByBall(){...}


    public function getMinBucketsPerBall(){...}


    public function getMinBallsPerBucket(){...}


    public function getBallPoolSize(){...}


    public function getStatusMessage(){...}


    public function createBallIdPool(){...}


    public function checkBallIdForBucketId($ballId, $bucketId){...}

    }
    CLASS

    View Slide

  4. TEST

    View Slide

  5. TEST

    View Slide

  6. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $bucketIds;

    private $ballIds;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->bucketIds = array('a');

    $this->ballIds = array('200');


    $results = $this->runDistribution();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }


    // ...


    private function runDistribution() {

    $this->dist->setBucketIds($this->bucketIds);

    $this->dist->setBallIds($this->ballIds);

    $this->dist->setMinBallsPerBucket($this->minBallsPerBucket);

    $this->dist->setMinBucketsPerBall($this->minBucketsPerBall);


    $this->dist->run();


    return $this->dist->getResults();

    }

    }
    TEST

    View Slide

  7. 1
    Masking our
    PAIN

    View Slide

  8. 1
    DRY !== Better

    View Slide

  9. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $bucketIds;

    private $ballIds;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->bucketIds = array('a');

    $this->ballIds = array('200');


    $results = $this->runDistribution();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }


    // ...


    private function runDistribution() {

    $this->dist->setBucketIds($this->bucketIds);

    $this->dist->setBallIds($this->ballIds);

    $this->dist->setMinBallsPerBucket($this->minBallsPerBucket);

    $this->dist->setMinBucketsPerBall($this->minBucketsPerBall);


    $this->dist->run();


    return $this->dist->getResults();

    }

    }
    TEST

    View Slide

  10. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $bucketIds;

    private $ballIds;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->bucketIds = array('a');

    $this->ballIds = array('200');


    $this->dist->setBucketIds($this->bucketIds);

    $this->dist->setBallIds($this->ballIds);

    $this->dist->setMinBallsPerBucket($this->minBallsPerBucket);

    $this->dist->setMinBucketsPerBall($this->minBucketsPerBall);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  11. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $bucketIds;

    private $ballIds;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->bucketIds = array('a');

    $this->ballIds = array('200');


    $this->dist->setBucketIds($this->bucketIds);

    $this->dist->setBallIds($this->ballIds);

    $this->dist->setMinBallsPerBucket($this->minBallsPerBucket);

    $this->dist->setMinBucketsPerBall($this->minBucketsPerBall);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  12. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $bucketIds;

    private $ballIds;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->bucketIds = array('a');

    $this->ballIds = array('200');


    $this->dist->setBucketIds($this->bucketIds);

    $this->dist->setBallIds($this->ballIds);

    $this->dist->setMinBallsPerBucket($this->minBallsPerBucket);

    $this->dist->setMinBucketsPerBall($this->minBucketsPerBall);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  13. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $bucketIds;

    private $ballIds;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->bucketIds = array('a');

    $this->ballIds = array('200');


    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->setMinBallsPerBucket($this->minBallsPerBucket);

    $this->dist->setMinBucketsPerBall($this->minBucketsPerBall);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  14. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $bucketIds;

    private $ballIds;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->bucketIds = array('a');

    $this->ballIds = array('200');


    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->setMinBallsPerBucket($this->minBallsPerBucket);

    $this->dist->setMinBucketsPerBall($this->minBucketsPerBall);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  15. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $bucketIds;

    private $ballIds;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->setMinBallsPerBucket($this->minBallsPerBucket);

    $this->dist->setMinBucketsPerBall($this->minBucketsPerBall);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  16. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->setMinBallsPerBucket($this->minBallsPerBucket);

    $this->dist->setMinBucketsPerBall($this->minBucketsPerBall);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  17. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->setMinBallsPerBucket($this->minBallsPerBucket);

    $this->dist->setMinBucketsPerBall($this->minBucketsPerBall);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  18. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->setMinBallsPerBucket($this->minBallsPerBucket);

    $this->dist->setMinBucketsPerBall($this->minBucketsPerBall);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  19. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->setMinBallsPerBucket(0);

    $this->dist->setMinBucketsPerBall(0);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  20. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->setMinBallsPerBucket(0);

    $this->dist->setMinBucketsPerBall(0);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  21. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();


    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;
    !
    // ...
    }
    CLASS

    View Slide

  22. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->setMinBallsPerBucket(0);

    $this->dist->setMinBucketsPerBall(0);
    !
    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  23. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  24. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  25. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));

    $this->dist->run();


    $results = $this->dist->getResults();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  26. use KCPHPUG\BucketBallDistribution;


    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /** @var BucketBallDistribution */

    private $dist;


    protected function setUp() {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeOneBucketToOneApp() {

    $this->dist->setBucketIds(array('a'));

    $this->dist->setBallIds(array('200'));


    $results = $this->dist->run();

    $expected = array(

    'a' => array('200'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  27. View Slide

  28. 2Single
    Responsibility
    Principle

    View Slide

  29. 2
    A class should have

    one

    and only one

    reason to change

    View Slide

  30. 2(I added the next part)

    View Slide

  31. 2
    and when it does change

    you should try to

    preserve

    its public API

    View Slide

  32. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();


    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function setBucketIds($bucketIds){...}


    public function setBallIds($ballIds){...}


    public function setMinBallsPerBucket($count){...}


    public function setMinBucketsPerBall($count){...}


    public function run(){...}


    public function getResults(){...}


    public function getResultsByBall(){...}


    public function getMinBucketsPerBall(){...}


    public function getMinBallsPerBucket(){...}


    public function getBallPoolSize(){...}


    public function getStatusMessage(){...}


    public function createBallIdPool(){...}


    public function checkBallIdForBucketId($ballId, $bucketId){...}

    }
    CLASS

    View Slide

  33. Smell!
    !
    Too many public methods
    make it unclear
    how a class is supposed to be used

    View Slide

  34. Public Methods

    communicate how a class can WILL BE used

    View Slide

  35. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();


    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function setBucketIds($bucketIds){...}


    public function setBallIds($ballIds){...}


    public function setMinBallsPerBucket($count){...}


    public function setMinBucketsPerBall($count){...}


    public function run(){...}


    public function getResults(){...}


    public function getResultsByBall(){...}


    public function getMinBucketsPerBall(){...}


    public function getMinBallsPerBucket(){...}


    public function getBallPoolSize(){...}


    public function getStatusMessage(){...}


    public function createBallIdPool(){...}


    public function checkBallIdForBucketId($ballId, $bucketId){...}

    }
    CLASS

    View Slide

  36. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Inform runtime about various state

    public function createBallIdPool()

    public function getBallPoolSize()

    public function checkBallIdForBucketId($ballId, $bucketId)


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  37. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Inform runtime about various state

    public function createBallIdPool()

    public function getBallPoolSize()

    public function checkBallIdForBucketId($ballId, $bucketId)


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  38. Find methods only used internally

    and hide them

    View Slide

  39. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Inform runtime about various state

    public function createBallIdPool()

    public function getBallPoolSize()

    public function checkBallIdForBucketId($ballId, $bucketId)


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  40. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Inform runtime about various state

    public function createBallIdPool()

    public function getBallPoolSize()

    public function checkBallIdForBucketId($ballId, $bucketId)


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  41. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Inform runtime about various state

    private function createBallIdPool()

    private function getBallPoolSize()

    private function checkBallIdForBucketId($ballId, $bucketId)


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  42. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  43. Smell!
    !
    When an object's public methods must be invoked

    in a specific order to properly use it

    View Slide

  44. Object Instantiation Rules

    View Slide

  45. An object should be
    !
    ready to use and impossible to misuse!
    !
    after it has been instantiated
    Object Instantiation Rules

    View Slide

  46. An object should be
    !
    ready to use and impossible to misuse!
    !
    after it has been instantiated
    Object Instantiation Rules
    Enforce required values in the constructor

    View Slide

  47. class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function setUp()

    {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array('201','202','203','204','205','206','207','208'
    $this->dist->setMinBallsPerBucket(1);

    $this->dist->setMinBucketsPerBall(3);


    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  48. class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function setUp()

    {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array('201','202','203','204','205','206','207','208'
    $this->dist->setMinBallsPerBucket(1);

    $this->dist->setMinBucketsPerBall(3);


    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  49. class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function setUp()

    {

    $this->dist = new BucketBallDistribution();

    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array('201','202','203','204','205','206','207','208'
    $this->dist->setMinBallsPerBucket(1);

    $this->dist->setMinBucketsPerBall(3);


    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }

    }
    TEST

    View Slide

  50. TEST
    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function build($minBucketsPerBall = 0, $minBallsPerBucket = 0)

    {

    $this->dist = new BucketBallDistribution($minBucketsPerBall, $minBallsPerBuck
    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array(‘201','202','203','204','205','206','207','208'
    $this->dist->setMinBallsPerBucket(1);

    $this->dist->setMinBucketsPerBall(3);


    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }

    }

    View Slide

  51. TEST
    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function build($minBucketsPerBall = 0, $minBallsPerBucket = 0)

    {

    $this->dist = new BucketBallDistribution($minBucketsPerBall, $minBallsPerBuck
    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->build(3, 1);


    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array(‘201','202','203','204','205','206','207','208'
    $this->dist->setMinBallsPerBucket(1);

    $this->dist->setMinBucketsPerBall(3);


    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }


    View Slide

  52. TEST
    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function build($minBucketsPerBall = 0, $minBallsPerBucket = 0)

    {

    $this->dist = new BucketBallDistribution($minBucketsPerBall, $minBallsPerBuck
    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->build(3, 1);


    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array(‘201','202','203','204','205','206','207','208'
    $this->dist->setMinBallsPerBucket(1);

    $this->dist->setMinBucketsPerBall(3);


    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }


    View Slide

  53. TEST
    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function build($minBucketsPerBall = 0, $minBallsPerBucket = 0)

    {

    $this->dist = new BucketBallDistribution($minBucketsPerBall, $minBallsPerBuck
    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->build(3, 1);


    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array(‘201','202','203','204','205','206','207','208'
    $this->dist->setMinBallsPerBucket(1);

    $this->dist->setMinBucketsPerBall(3);


    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }


    View Slide

  54. TEST
    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function build($minBucketsPerBall = 0, $minBallsPerBucket = 0)

    {

    $this->dist = new BucketBallDistribution($minBucketsPerBall, $minBallsPerBuck
    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->build(3, 1);


    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array(‘201','202','203','204','205','206','207','208'

    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }

    }

    View Slide

  55. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  56. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  57. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }
    !
    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()

    CLASS

    View Slide

  58. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }
    !
    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()

    CLASS

    View Slide

  59. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }
    !
    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()

    CLASS

    View Slide

  60. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }
    !
    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()

    CLASS

    View Slide

  61. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }
    !
    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()

    CLASS

    View Slide

  62. An object’s configuration
    should not change after instantiation
    API Design Principles

    View Slide

  63. An object’s configuration
    should not change after instantiation
    If you need a different configuration
    create another instance
    API Design Principles

    View Slide

  64. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }
    !
    // Set Configuration

    public function setMinBucketsPerBall($count)

    public function setMinBallsPerBucket($count)


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()

    CLASS

    View Slide

  65. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }
    !
    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()
    !
    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  66. View Slide

  67. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }
    !
    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  68. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }
    !
    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  69. Do not expose getters for values you don’t use yset
    Do not expose getters for configuration
    API Design Principles

    View Slide

  70. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }


    // Get Configuration (but based on runtime data?)

    public function getMinBallsPerBucket()

    public function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  71. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }


    // Get Configuration (but based on runtime data?)

    private function getMinBallsPerBucket()

    private function getMinBucketsPerBall()


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  72. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    // Set Configuration

    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0)
    $this->minBucketsPerBall = $minBucketsPerBall;
    $this->minBallsPerBucket = $minBallsPerBucket;

    }


    // Set runtime data

    public function setBucketIds($bucketIds)

    public function setBallIds($ballIds)


    // Main runtime algorithm

    public function run()


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage()

    }

    CLASS

    View Slide

  73. View Slide

  74. TEST
    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function build($minBucketsPerBall = 0, $minBallsPerBucket = 0)

    {

    $this->dist = new BucketBallDistribution($minBucketsPerBall, $minBallsPerBuck
    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->build(3, 1);


    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array(‘201','202','203','204','205','206','207','208'

    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }

    }

    View Slide

  75. TEST
    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function build($minBucketsPerBall = 0, $minBallsPerBucket = 0)

    {

    $this->dist = new BucketBallDistribution($minBucketsPerBall, $minBallsPerBuck
    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->build(3, 1);


    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array(‘201','202','203','204','205','206','207','208'

    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }

    }

    View Slide

  76. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    public function setBucketIds(array $bucketIds) {

    $this->bucketIds = $bucketIds;

    }
    !
    public function setBallIds(array $ballIds) {

    $this->ballIds = $ballIds;

    }


    public function run() {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($this->bucketIds, $this->ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  77. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    public function setBucketIds(array $bucketIds) {

    $this->bucketIds = $bucketIds;

    }
    !
    public function setBallIds(array $ballIds) {

    $this->ballIds = $ballIds;

    }


    public function run() {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($this->bucketIds, $this->ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  78. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    public function setBucketIds(array $bucketIds) {

    $this->bucketIds = $bucketIds;

    }
    !
    public function setBallIds(array $ballIds) {

    $this->ballIds = $ballIds;

    }


    public function run() {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($this->bucketIds, $this->ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  79. Class and Methods Principles

    View Slide

  80. Class and Methods Principles
    STATELESS
    Do not store data during execution

    View Slide

  81. Class and Methods Principles
    STATELESS
    Do not store data during execution
    DETERMINISTIC
    Given the same input, the return value is identical

    View Slide

  82. Class and Methods Principles
    STATELESS
    Do not store data during execution
    IDEMPOTENT
    Given the same input, the state of the system is identical
    when the method completes
    DETERMINISTIC
    Given the same input, the return value is identical

    View Slide

  83. TEST
    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function build($minBucketsPerBall = 0, $minBallsPerBucket = 0)

    {

    $this->dist = new BucketBallDistribution($minBucketsPerBall, $minBallsPerBuck
    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->build(3, 1);


    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array(‘201','202','203','204','205','206','207','208'

    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }

    }

    View Slide

  84. TEST
    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function build($minBucketsPerBall = 0, $minBallsPerBucket = 0)

    {

    $this->dist = new BucketBallDistribution($minBucketsPerBall, $minBallsPerBuck
    }


    public function testDistributeManyBucketToManyAppsLimit3()

    {

    $this->build(3, 1);


    $this->dist->setBucketIds(array('a','b','c','d'));

    $this->dist->setBallIds(array(‘201','202','203','204','205','206','207','208'

    $results = $this->dist->run();

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }

    }

    View Slide

  85. TEST
    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function build($minBucketsPerBall = 0, $minBallsPerBucket = 0)

    {

    $this->dist = new BucketBallDistribution($minBucketsPerBall, $minBallsPerBuck
    }


    public function testDistributeManyBucketToManyAppsLimit3()
    {

    $this->build(3, 1);


    $bucketIds = array('a', 'b', 'c', 'd');

    $ballIds = array('201', '202', '203', '204', '205', '206', '207', '208', '209

    $results = $this->dist->run($bucketIds, $ballIds);

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }
    }

    View Slide

  86. TEST
    class BucketBallDistributionTest extends \PHPUnit_Framework_TestCase

    {

    /**

    * @var BucketBallDistribution

    */

    private $dist;


    protected function build($minBucketsPerBall = 0, $minBallsPerBucket = 0)

    {

    $this->dist = new BucketBallDistribution($minBucketsPerBall, $minBallsPerBuck
    }


    public function testDistributeManyBucketToManyAppsLimit3()
    {

    $this->build(3, 1);


    $bucketIds = array('a', 'b', 'c', 'd');

    $ballIds = array('201', '202', '203', '204', '205', '206', '207', '208', '209

    $results = $this->dist->run($bucketIds, $ballIds);

    $expected = array(

    'a' => array('201','205','209','203','207','204','208'),

    'b' => array('202','206','210','204','208','201','205','209'),

    'c' => array('203','207','201','205','209','202','206','210'),

    'd' => array('204','208','202','206','210','203','207'),

    );


    $this->assertEquals($expected, $results);

    }
    }

    View Slide

  87. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    public function setBucketIds(array $bucketIds) {

    $this->bucketIds = $bucketIds;

    }
    !
    public function setBallIds(array $ballIds) {

    $this->ballIds = $ballIds;

    }


    public function run() {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($this->bucketIds, $this->ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  88. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    public function setBucketIds(array $bucketIds) {

    $this->bucketIds = $bucketIds;

    }
    !
    public function setBallIds(array $ballIds) {

    $this->ballIds = $ballIds;

    }


    public function run() {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($this->bucketIds, $this->ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  89. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    public function setBucketIds(array $bucketIds) {

    $this->bucketIds = $bucketIds;

    }
    !
    public function setBallIds(array $ballIds) {

    $this->ballIds = $ballIds;

    }


    public function run(array $bucketIds, array $ballIds) {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($this->bucketIds, $this->ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  90. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    public function setBucketIds(array $bucketIds) {

    $this->bucketIds = $bucketIds;

    }
    !
    public function setBallIds(array $ballIds) {

    $this->ballIds = $ballIds;

    }


    public function run(array $bucketIds, array $ballIds) {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($this->bucketIds, $this->ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  91. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    public function setBucketIds(array $bucketIds) {

    $this->bucketIds = $bucketIds;

    }
    !
    public function setBallIds(array $ballIds) {

    $this->ballIds = $ballIds;

    }


    public function run(array $bucketIds, array $ballIds) {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($bucketIds, $ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  92. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    public function setBucketIds(array $bucketIds) {

    $this->bucketIds = $bucketIds;

    }
    !
    public function setBallIds(array $ballIds) {

    $this->ballIds = $ballIds;

    }


    public function run(array $bucketIds, array $ballIds) {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($bucketIds, $ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  93. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;
    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    !
    !
    !
    !
    !
    !


    public function run(array $bucketIds, array $ballIds) {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($bucketIds, $ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  94. class BucketBallDistribution

    {

    private $minBallsPerBucket;

    private $minBucketsPerBall;
    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    !
    !
    !
    !
    !
    !


    public function run(array $bucketIds, array $ballIds) {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($bucketIds, $ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  95. class BucketBallDistribution

    {

    private $minBallsPerBucket;

    private $minBucketsPerBall;
    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    !
    !
    !
    !
    !
    !


    public function run(array $bucketIds, array $ballIds) {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($bucketIds, $ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  96. class BucketBallDistribution

    {

    private $minBallsPerBucket;

    private $minBucketsPerBall;
    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    !
    !
    !
    !
    !
    !


    public function run(array $bucketIds, array $ballIds) {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($bucketIds, $ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  97. class BucketBallDistribution

    {

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){}
    !
    !
    !
    !
    !
    !
    !


    public function run(array $bucketIds, array $ballIds) {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($bucketIds, $ballIds);
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  98. View Slide

  99. class BucketBallDistribution

    {

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){

    // Main runtime algorithm

    public function run(array $bucketIds, array $ballIds)


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage(array $bucketIds, array $ballIds)

    }

    CLASS

    View Slide

  100. class BucketBallDistribution

    {

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){

    // Main runtime algorithm

    public function run(array $bucketIds, array $ballIds)


    // Get results after runtime

    public function getResults()

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage(array $bucketIds, array $ballIds)

    }

    CLASS

    View Slide

  101. class BucketBallDistribution

    {

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();


    public function getResults() {

    return $this->results;

    }
    !
    public function run() {

    $this->results = array();

    $this->resultsByBall = array();


    $ballPool = $this->createBallIdPool($this->bucketIds, $this->ballI
    while ($ballPool ...) {

    // ...

    $this->results[$bucketId][] = $ballId;

    // ...

    }


    return $this->results;

    }
    }
    CLASS

    View Slide

  102. class BucketBallDistribution

    {

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall = 0, $minBallsPerBucket = 0){

    // Main runtime algorithm

    public function run(array $bucketIds, array $ballIds)


    // Get results after runtime

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage(array $bucketIds, array $ballIds)

    }

    CLASS

    View Slide

  103. View Slide

  104. ass BucketBallDistribution

    private $bucketIds = array();

    private $ballIds = array();

    private $minBallsPerBucket = 0;

    private $minBucketsPerBall = 0;

    private $ballPool = array();

    private $results = array();

    private $resultsByBall = array();

    public function setBucketIds($bucketIds){...}

    public function setBallIds($ballIds){...}

    public function setMinBallsPerBucket($count){...}

    public function setMinBucketsPerBall($count){...}

    public function run(){...}

    public function getResults(){...}

    public function getResultsByBall(){...}

    public function getMinBucketsPerBall(){...}

    public function getMinBallsPerBucket(){...}

    public function getBallPoolSize(){...}

    public function getStatusMessage(){...}

    public function createBallIdPool(){...}

    public function checkBallIdForBucketId($ballId, $bucketId){...}

    class BucketBallDistribution

    {

    private $minBallsPerBucket;

    private $minBucketsPerBall;

    private $results = array();

    private $resultsByBall = array();


    public function __construct($minBucketsPerBall

    // Main runtime algorithm

    public function run(array $bucketIds, array $ba

    // Get results after runtime

    public function getResultsByBall()


    // Text describing results

    public function getStatusMessage(array $bucketI

    View Slide

  105. Further Learning
    Refactoring: Improving the Design of Existing Code

    Martin Fowler, 2000
    http://refactoring.com
    Less: The Path to Better Design

    Sandi Metz (GoRuCo 2011)
    http://vimeo.com/26330100
    Therapeutic Refactoring

    Katrina Owen (2012)
    http://www.youtube.com/watch?v=J4dlF0kcThQ

    View Slide

  106. Questions
    John Kary
    Code Slinger, Build Breaker
    @johnkary
    http://johnkary.net/talks

    View Slide