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.

D3e3f4ac37c02289f5dfed115949fc88?s=128

John Kary

April 02, 2014
Tweet

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
  2. 0 Survey
 the land

  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
  4. TEST

  5. TEST

  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
  7. 1 Masking our PAIN

  8. 1 DRY !== Better

  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  21. class BucketBallDistribution
 {
 private $bucketIds = array();
 private $ballIds =

    array();
 
 private $minBallsPerBucket = 0;
 private $minBucketsPerBall = 0; ! // ... } CLASS
  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
  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
  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
  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
  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
  27. None
  28. 2Single Responsibility Principle

  29. 2 A class should have
 one
 and only one
 reason

    to change
  30. 2(I added the next part)

  31. 2 and when it does change
 you should try to


    preserve
 its public API
  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
  33. Smell! ! Too many public methods make it unclear how

    a class is supposed to be used
  34. Public Methods
 communicate how a class can WILL BE used

  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
  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
  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
  38. Find methods only used internally
 and hide them

  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
  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
  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
  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
  43. Smell! ! When an object's public methods must be invoked


    in a specific order to properly use it
  44. Object Instantiation Rules

  45. An object should be ! ready to use and impossible

    to misuse! ! after it has been instantiated Object Instantiation Rules
  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
  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
  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
  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
  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);
 }
 }
  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);
 }

  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);
 }

  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);
 }

  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);
 }
 }
  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
  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
  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
  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
  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
  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
  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
  62. An object’s configuration should not change after instantiation API Design

    Principles
  63. An object’s configuration should not change after instantiation If you

    need a different configuration create another instance API Design Principles
  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
  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
  66. None
  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
  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
  69. Do not expose getters for values you don’t use yset

    Do not expose getters for configuration API Design Principles
  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
  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
  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
  73. None
  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);
 }
 }
  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);
 }
 }
  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
  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
  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
  79. Class and Methods Principles

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

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

    execution DETERMINISTIC Given the same input, the return value is identical
  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
  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);
 }
 }
  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);
 }
 }
  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);
 } }
  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);
 } }
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  98. None
  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
  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
  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
  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
  103. None
  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 

  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
  106. Questions John Kary Code Slinger, Build Breaker @johnkary http://johnkary.net/talks