SlideShare a Scribd company logo
1 of 132
Download to read offline
Your code sucks,
   let’s !x it!
   Object Calisthenics and Code readability




            Rafael Dohms
Rafael Dohms




                           photo credit: Eli White
        @rdohms


 Evangelist, Speaker and
      Contributor.

Developer at WEBclusive.

Enabler at AmsterdamPHP.
Why does my
code suck?
Is it Readable?


Why does my
code suck?
Is it Readable?


Why does my
code suck?
        Is it Testable?
Is it Maintainable?   Is it Readable?


           Why does my
           code suck?
                      Is it Testable?
Is it Maintainable?   Is it Readable?


           Why does my
           code suck?
  Is it Reusable?     Is it Testable?
<?php                                                 Does it look like this?
$list=mysql_connect("******","*******","*****");
if(!$list)echo 'Cannot login.';
else{
    mysql_select_db("******", $list);
    $best=array("0","0","0","0","0","0");
    $id=mysql_num_rows(mysql_query("SELECT * FROM allnews"));
    $count=0;
    for($i=0;$i<6;$i++){
        while(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")!="he")$count+
+;
        $best[$i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");}
    $id=$id-$count;
    $maxdate=mktime(0,0,0,date('m'),date('d')-7,date('Y'));
    while(mysql_query("SELECT date FROM allnews WHERE id=$id-$count")>=$maxdate){
        if(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")=="he"){
            $small=$best[0];
            while($i=0;$i<6;$i++){
                if(mysql_query("SELECT score FROM allnews WHERE id=
$small)"<mysql_query("SELECT score FROM allnews WHERE id=$best[i+1]"))
                         $small=$best[i+1];}
                    if(mysql_query("SELECT score FROM allnews WHERE id=
$small")<mysql_query("SELECT score FROM allnews WHERE id=$id-$count")){
                         while($i=0;$i<6;$i++){
                             if($small==$best[i])$best[i]=mysql_query("SELECT id FROM
allnews WHERE id=$id-$count");}}}}
    while($i=0;$i<6;$i++)
    echo '<a href="news-page.php?id='.$best[i].'"><div class="box '.mysql_query("SELECT
type FROM allnews WHERE id=$best[i]").'">'.mysql_query("SELECT title FROM allnews WHERE id=
$best[i]").'<div class="img" style="background-image:url(images/'.mysql_query("SELECT
image1 FROM allnews WHERE id=$best[i]").');"></div></div></a>';
    mysql_close($list);
}
?>
<?php                                                 Does it look like this?
$list=mysql_connect("******","*******","*****");
if(!$list)echo 'Cannot login.';
else{
    mysql_select_db("******", $list);
    $best=array("0","0","0","0","0","0");
    $id=mysql_num_rows(mysql_query("SELECT * FROM allnews"));
    $count=0;
    for($i=0;$i<6;$i++){
        while(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")!="he")$count+
+;
        $best[$i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");}
    $id=$id-$count;
    $maxdate=mktime(0,0,0,date('m'),date('d')-7,date('Y'));
    while(mysql_query("SELECT date FROM allnews WHERE id=$id-$count")>=$maxdate){
        if(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")=="he"){
            $small=$best[0];
            while($i=0;$i<6;$i++){
                if(mysql_query("SELECT score FROM allnews WHERE id=
$small)"<mysql_query("SELECT score FROM allnews WHERE id=$best[i+1]"))
                         $small=$best[i+1];}
                    if(mysql_query("SELECT score FROM allnews WHERE id=
$small")<mysql_query("SELECT score FROM Rebecca WHERE id=$id-$count")){
                                      If allnews Black was a developer
                         while($i=0;$i<6;$i++){
                             if($small==$best[i])$best[i]=mysql_query("SELECT id FROM
allnews WHERE id=$id-$count");}}}}
    while($i=0;$i<6;$i++)
    echo '<a href="news-page.php?id='.$best[i].'"><div class="box '.mysql_query("SELECT
type FROM allnews WHERE id=$best[i]").'">'.mysql_query("SELECT title FROM allnews WHERE id=
$best[i]").'<div class="img" style="background-image:url(images/'.mysql_query("SELECT
image1 FROM allnews WHERE id=$best[i]").');"></div></div></a>';
    mysql_close($list);
}
?>
How do we fix it?
Object Calisthenics
cal·is·then·ics - noun - /ˌkaləsˈTHeniks/
     Calisthenics are a form of dynamic
      exercise consisting of a variety of
    simple, often rhythmical, movements,
    generally using minimal equipment or
                  apparatus.



Object Calisthenics
cal·is·then·ics - noun - /ˌkaləsˈTHeniks/
     Calisthenics are a form of dynamic
      exercise consisting of a variety of
    simple, often rhythmical, movements,
    generally using minimal equipment or
                  apparatus.



Object Calisthenics
                             A variety of simple, often
                          rhythmical, exercises to achieve
                           better OO and code quality
“So here’s an exercise that can help you to internalize
      principles of good object-oriented design and actually
                                       use them in real life.”

                                                -- Jeff Bay




Object Calisthenics
“So here’s an exercise that can help you to internalize
      principles of good object-oriented design and actually
                                       use them in real life.”

                                                -- Jeff Bay




Object Calisthenics
                            Important:

                         PHP != JAVA

                    Adaptations will be done
Object Calisthenics
         +
 Readability Tips
“You need to write code that minimizes the time it would


Object Calisthenics
          take someone else to understand it—even if that
                                     someone else is you.”



         +
          -- Dustin Boswell and Trevor Foucher




 Readability Tips
“You need to write code that minimizes the time it would


Object Calisthenics
          take someone else to understand it—even if that
                                     someone else is you.”



         +
          -- Dustin Boswell and Trevor Foucher




 Readability Tips
                                I’m a tip
Disclaimer:
“These are guidelines, not rules”
OC #1
“Only one indentation
  level per method”
function validateProducts($products) {

    // Check to make sure that our valid fields are in there
    $requiredFields = array(
        'price',
        'name',
        'description',
        'type',
    );

    $valid = true;

    foreach ($products as $rawProduct) {

        $fields = array_keys($rawProduct);

        foreach ($requiredFields as $requiredField) {
            if (!in_array($requiredField, $fields)) {
                $valid = false;
            }
        }
    }

    return $valid;
}
function validateProducts($products) {

        // Check to make sure that our valid fields are in there
        $requiredFields = array(
            'price',
            'name',
            'description',
            'type',
        );

        $valid = true;

    0   foreach ($products as $rawProduct) {

            1   $fields = array_keys($rawProduct);

                foreach ($requiredFields as $requiredField) {
                  2 if (!in_array($requiredField, $fields)) {
                      3 $valid = false;
                    }
                }
        }

        return $valid;
}
function validateProducts($products) {

        // Check to make sure that our valid fields are in there
        $requiredFields = array(
            'price',
            'name',
            'description',
            'type',
        );

        $valid = true;

    0   foreach ($products as $rawProduct) {

            1   $fields = array_keys($rawProduct);

                foreach ($requiredFields as $requiredField) {
                  2 if (!in_array($requiredField, $fields)) {
                      3 $valid = false;
                    }
                }
        }

        return $valid;
}
function validateProducts($products) {

    // Check to make sure that our valid fields are in there
    $requiredFields = array(
        'price',
        'name',
        'description',
        'type',
    );

    $valid = true;

    foreach ($products as $rawProduct) {
        $validationResult = validateSingleProduct($rawProduct, $requiredFields);

        if ( ! $validationResult){
            $valid = false;
        }
    }

    return $valid;
}

function validateSingleProduct($product, $requiredFields)
{
    $valid = true;

    $fields = array_keys($rawProduct);

    foreach ($requiredFields as $requiredField) {
        if (!in_array($requiredField, $fields)) {
            $valid = false;
        }
    }
    return $valid;
}
function validateProducts($products) {

    // Check to make sure that our valid fields are in there
    $requiredFields = array(
        'price',
        'name',
        'description',
        'type',
    );

    $valid = true;

        whitespace
    foreach ($products as $rawProduct) {
        $validationResult = validateSingleProduct($rawProduct, $requiredFields);

        if ( ! $validationResult){
            $valid = false;
        }
    }

    return $valid;
}

function validateSingleProduct($product, $requiredFields)
{
    $valid = true;

    $fields = array_keys($rawProduct);

    foreach ($requiredFields as $requiredField) {
        if (!in_array($requiredField, $fields)) {
            $valid = false;
        }
    }
    return $valid;
}
function validateProducts($products) {

    // Check to make sure that our valid fields are in there
    $requiredFields = array(
        'price',
        'name',
        'description',
        'type',
    );

    $valid = true;

        whitespace
    foreach ($products as $rawProduct) {
        $validationResult = validateSingleProduct($rawProduct, $requiredFields);

        if ( ! $validationResult){
            $valid = false;
        }
    }

    return $valid;
}

function validateSingleProduct($product, $requiredFields)
{
    $valid = true;         duplicated logic
    $fields = array_keys($rawProduct);

    foreach ($requiredFields as $requiredField) {
        if (!in_array($requiredField, $fields)) {
            $valid = false;
        }
    }
    return $valid;
}
function validateProducts($products) {

      // Check to make sure that our valid fields are in there
      $requiredFields = array(
          'price',
          'name',
          'description',
          'type',
      );

      $valid = true;

    0 foreach ($products as
          whitespace
          $validationResult
                                $rawProduct) {
                                = validateSingleProduct($rawProduct, $requiredFields);

          1   if ( ! $validationResult){

              }
               2  $valid = false;

      }

      return $valid;
}

function validateSingleProduct($product, $requiredFields)
{
    $valid = true;               duplicated logic
      $fields = array_keys($rawProduct);

    0 foreach(!in_array($requiredField, $fields))
              ($requiredFields as $requiredField)     {

        1 if                                          {

          } 2
              $valid = false;

      }
      return $valid;
}
function validateProducts($products) {

      // Check to make sure that our valid fields are in there
      $requiredFields = array(
          'price',
          'name',
          'description',
          'type',
      );

      $valid = true;

    0 foreach ($products as
          $validationResult
                                $rawProduct) {
                                = validateSingleProduct($rawProduct, $requiredFields);

          1   if ( ! $validationResult){

              }
               2  $valid = false;

      }

      return $valid;
}

function validateSingleProduct($product, $requiredFields)
{
    $valid = true;

      $fields = array_keys($rawProduct);

    0 foreach(!in_array($requiredField, $fields))
              ($requiredFields as $requiredField)     {

        1 if                                          {

          } 2
              $valid = false;

      }
      return $valid;
}
function validateProducts($products) {

      // Check to make sure that our valid fields are in there
      $requiredFields = array(
          'price',
          'name',
          'description',
          'type',
      );

      $valid = true;

    0 foreach ($products as
          $validationResult
                                $rawProduct) {
                                = validateSingleProduct($rawProduct, $requiredFields);

          1   if ( ! $validationResult){

              }
               2  $valid = false;

      }

      return $valid;
}

function validateSingleProduct($product, $requiredFields)
{
    $valid = true;

      $fields = array_keys($rawProduct);

    0 foreach(!in_array($requiredField, $fields))
              ($requiredFields as $requiredField)     {

        1 if                                          {

          } 2
              $valid = false;

      }
      return $valid;
}
function validateProducts($storeData) {

    $requiredFields = array('price','name','description','type');

    foreach ($storeData['products'] as $rawProduct) {
        if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
    }

    return true;
}

function validateSingleProduct($product, $requiredFields)
{
    $fields = array_keys($rawProduct);
    $missingFields = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}
function validateProducts($storeData) {

    $requiredFields = array('price','name','description','type'); cheating!
                                                            I see
    foreach ($storeData['products'] as $rawProduct) {
        if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
    }

    return true;
}

function validateSingleProduct($product, $requiredFields)
{
    $fields = array_keys($rawProduct);
    $missingFields = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}
function validateProducts($storeData) {

    $requiredFields = array('price','name','description','type'); cheating!
                                                            I see
    foreach ($storeData['products'] as $rawProduct) {
        if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
    }

    return true;    Single line IF, simple operations
}

function validateSingleProduct($product, $requiredFields)
{
    $fields = array_keys($rawProduct);
    $missingFields = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}
function validateProducts($storeData) {

    $requiredFields = array('price','name','description','type'); cheating!
                                                            I see
    foreach ($storeData['products'] as $rawProduct) {
        if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
    }

    return true;    Single line IF, simple operations
}

function validateSingleProduct($product, $requiredFields)
{
    $fields = array_keys($rawProduct);
    $missingFields = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}
function validateProducts($storeData) {

    $requiredFields = array('price','name','description','type');

    foreach ($storeData['products'] as $rawProduct) {
        if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
    }

    return true;    Single line IF, simple operations               return early
}

function validateSingleProduct($product, $requiredFields)
{
    $fields = array_keys($rawProduct);
    $missingFields = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}
function validateProducts($storeData) {

    $requiredFields = array('price','name','description','type');

    foreach ($storeData['products'] as $rawProduct) {
        if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
    }

    return true;    Single line IF, simple operations               return early
}

function validateSingleProduct($product, $requiredFields)
{
    $fields = array_keys($rawProduct);
    $missingFields = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}                        C (native) functions are
                             faster then PHP
function validateProducts($storeData) {

    $requiredFields = array('price','name','description','type');

    foreach ($storeData['products'] as $rawProduct) {
        if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
    }

    return true;
}

function validateSingleProduct($product, $requiredFields)
{
    $fields = array_keys($rawProduct);
    $missingFields = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}
function validateProducts($storeData) {

    $requiredFields = array('price','name','description','type');

    foreach ($storeData['products'] as $rawProduct) {
        if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
    }

    return true;
}

function validateSingleProduct($product, $requiredFields)
{
    $fields = array_keys($rawProduct);
    $missingFields = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}
function validateProductList($products)
{
    $validProducts = array_filter($products, 'isValidProduct');

    return (count($products) == count($validProducts));
}




function isValidProduct($rawProduct)
{
    $requiredFields = array('price', 'name', 'description', 'type');

    $fields          = array_keys($rawProduct);
    $missingFields   = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}
function validateProductList($products)      faster iteration
{
    $validProducts = array_filter($products, 'isValidProduct');

    return (count($products) == count($validProducts));
}




function isValidProduct($rawProduct)
{
    $requiredFields = array('price', 'name', 'description', 'type');

    $fields          = array_keys($rawProduct);
    $missingFields   = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}
function validateProductList($products)      faster iteration
{
    $validProducts = array_filter($products, 'isValidProduct');

    return (count($products) == count($validProducts));
}


          reusable method
function isValidProduct($rawProduct)
{
    $requiredFields = array('price', 'name', 'description', 'type');

    $fields          = array_keys($rawProduct);
    $missingFields   = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}
function validateProductList($products)      faster iteration
{
    $validProducts = array_filter($products, 'isValidProduct');

    return (count($products) == count($validProducts));
}


          reusable method
function isValidProduct($rawProduct)
{
    $requiredFields = array('price', 'name', 'description', 'type');

    $fields          = array_keys($rawProduct);
    $missingFields   = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}

                          method name matches “true” result
function validateProductList($products)      faster iteration
{
    $validProducts = array_filter($products, 'isValidProduct');

    return (count($products) == count($validProducts));
}

                                  assertable return: expected/returned
          reusable method
function isValidProduct($rawProduct)
{
    $requiredFields = array('price', 'name', 'description', 'type');

    $fields          = array_keys($rawProduct);
    $missingFields   = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}

                          method name matches “true” result
List is more readable the plural

function validateProductList($products)      faster iteration
{
    $validProducts = array_filter($products, 'isValidProduct');

    return (count($products) == count($validProducts));
}

                                       assertable return: expected/returned
           reusable method
function isValidProduct($rawProduct)
{
    $requiredFields = array('price', 'name', 'description', 'type');

    $fields          = array_keys($rawProduct);
    $missingFields   = array_diff($requiredFields, $fields);

    return (count($missingFields) == 0);
}

                           method name matches “true” result
Key Benefits


• Single Responsibility Principle (S in SOLID)

• Increases re-use
OC #2
“Do not use the ‘else’
     keyword”
public function createPost($request)
{
    $entity = new Post();

    $form = new MyForm($entity);
    $form->bind($request);

    if ($form->isValid()){

        $repository = $this->getRepository('MyBundle:Post');
        if (!$repository->exists($entity) ) {
            $repository->save($entity);
            return $this->redirect('create_ok');
        } else {
            $error = "Post Title already exists";
            return array('form' => $form, 'error' => $error);
        }

    } else {
        $error = "Invalid fields";
        return array('form' => $form, 'error' => $error);
    }

}
public function createPost($request)
{
    $entity = new Post();

    $form = new MyForm($entity);
    $form->bind($request);

    if ($form->isValid()){

        $repository = $this->getRepository('MyBundle:Post');
        if (!$repository->exists($entity) ) {
            $repository->save($entity);
            return $this->redirect('create_ok');
        } else {
            $error = "Post Title already exists";
            return array('form' => $form, 'error' => $error);
        }

    } else {
        $error = "Invalid fields";
        return array('form' => $form, 'error' => $error);
    }

}
public function createPost($request)
{
    $entity = new Post();

    $form = new MyForm($entity);
    $form->bind($request);

    if ($form->isValid()){

        $repository = $this->getRepository('MyBundle:Post');
        if (!$repository->exists($entity) ) {
            $repository->save($entity);
            return $this->redirect('create_ok');
        } else {
            $error = "Post Title already exists";
            return array('form' => $form, 'error' => $error);
        }

    } else {
        $error = "Invalid fields";
        return array('form' => $form, 'error' => $error);
    }

}
public function createPost($request)
{
    $entity = new Post();

    $form = new MyForm($entity);
    $form->bind($request);

    if ($form->isValid()){

        $repository = $this->getRepository('MyBundle:Post');
        if (!$repository->exists($entity) ) {
            $repository->save($entity);
            return $this->redirect('create_ok');
        } else {
            $error = "Post Title already exists";
            return array('form' => $form, 'error' => $error);
        }

    } else {
        $error = "Invalid fields";
        return array('form' => $form, 'error' => $error);
    }

}
public function createPost($request)
{
    $entity = new Post();

    $form = new MyForm($entity);
    $form->bind($request);

    if ($form->isValid()){

        $repository = $this->getRepository('MyBundle:Post');
        if (!$repository->exists($entity) ) {
            $repository->save($entity);
            return $this->redirect('create_ok');
        } else {
            $error = "Post Title already exists";
            return array('form' => $form, 'error' => $error);
        }

    } else {
        $error = "Invalid fields";
        return array('form' => $form, 'error' => $error);
    }

}
public function createPost($request)
{
    $entity = new Post();

    $form = new MyForm($entity);
    $form->bind($request);

    if ($form->isValid()){

        $repository = $this->getRepository('MyBundle:Post');
        if (!$repository->exists($entity) ) {
            $repository->save($entity);
            return $this->redirect('create_ok');
        } else {
            $error = "Post Title already exists";
            return array('form' => $form, 'error' => $error);
        }

    } else {
        $error = "Invalid fields";
        return array('form' => $form, 'error' => $error);
    }

}
public function createPost($request)
{
    $entity = new Post();

    $form = new MyForm($entity);
    $form->bind($request);

    if ($form->isValid()){

        $repository = $this->getRepository('MyBundle:Post');
        if (!$repository->exists($entity) ) {
            $repository->save($entity);
            return $this->redirect('create_ok');
        } else {
            $error = "Post Title already exists";
            return array('form' => $form, 'error' => $error);
        }

    } else {
        $error = "Invalid fields";
        return array('form' => $form, 'error' => $error);
    }

}
public function createPost($request)
{
    $entity = new Post();

    $form = new MyForm($entity);
    $form->bind($request);

    if ($form->isValid()){

        $repository = $this->getRepository('MyBundle:Post');
        if (!$repository->exists($entity) ) {
             $repository->save($entity);
         intermediate variable
             return $this->redirect('create_ok');
        } else {
             $error = "Post Title already exists";
             return array('form' => $form, 'error' => $error);
        }
    intermediate variable
    } else {
        $error = "Invalid fields";
        return array('form' => $form, 'error' => $error);
    }

}
public function createPost($request)
{
    $entity     = new Post();
    $repository = $this->getRepository('MyBundle:Post');

    $form = new MyForm($entity);
    $form->bind($request);

    if ( ! $form->isValid()){
        return array('form' => $form, 'error' => 'Invalid fields');
    }

    if ($repository->exists($entity)){
        return array('form' => $form, 'error' => 'Duplicate post title');
    }

    $repository->save($entity);

    return $this->redirect('create_ok');

}
public function createPost($request)
{
    $entity     = new Post();
    $repository = $this->getRepository('MyBundle:Post');

    $form = new MyForm($entity);
    $form->bind($request);

    if ( ! $form->isValid()){
        return array('form' => $form, 'error' => 'Invalid fields');
    }

    if ($repository->exists($entity)){
        return array('form' => $form, 'error' => 'Duplicate post title');
    }

    $repository->save($entity);

    return $this->redirect('create_ok');

}
public function createPost($request)
{
    $entity     = new Post();
    $repository = $this->getRepository('MyBundle:Post');

    $form = new MyForm($entity);
    $form->bind($request);
                                           removed intermediates
    if ( ! $form->isValid()){
        return array('form' => $form, 'error' => 'Invalid fields');
    }

    if ($repository->exists($entity)){
        return array('form' => $form, 'error' => 'Duplicate post title');
    }

    $repository->save($entity);

    return $this->redirect('create_ok');

}
public function createPost($request)
{
    $entity     = new Post();
    $repository = $this->getRepository('MyBundle:Post');

      $form = new MyForm($entity);
      $form->bind($request);
                                             removed intermediates
      if ( ! $form->isValid()){
          return array('form' => $form, 'error' => 'Invalid fields');
      }
    early return
      if ($repository->exists($entity)){
          return array('form' => $form, 'error' => 'Duplicate post title');
      }

      $repository->save($entity);

      return $this->redirect('create_ok');

}
Separate code
                                                              into blocks.
public function createPost($request)
{                                                             Its like using
    $entity     = new Post();
    $repository = $this->getRepository('MyBundle:Post');     Paragraphs.
      $form = new MyForm($entity);
      $form->bind($request);
                                             removed intermediates
      if ( ! $form->isValid()){
          return array('form' => $form, 'error' => 'Invalid fields');
      }
    early return
      if ($repository->exists($entity)){
          return array('form' => $form, 'error' => 'Duplicate post title');
      }

      $repository->save($entity);

      return $this->redirect('create_ok');

}
Key Benefits

• Helps avoid code duplication
• Easier to read (single path)
• Reduces cyclomatic complexity
Ad
                      ap
        OC #3




                       te
                           d
“Wrap primitive types
and strings, if there is
     behaviour”
class UIComponent
{
//...
    public function repaint($animate = true){
            //...
    }
}

//...
$component->repaint(false);
class UIComponent
{
//...
    public function repaint($animate = true){
            //...
    }
}

//...
$component->repaint(false);
class UIComponent
{
//...
    public function repaint($animate = true){
            //...
    }
}

//...
$component->repaint(false);


                  unclear operation
class UIComponent
{
    //...
    public function repaint( Animate $animate ){
            //...
    }
}

class Animate
{
    public $animate;

    public function __construct( $animate = true ) {
        $this->animate = $animate;}

}

//...
$component->repaint( new Animate(false) );
class UIComponent
{
    //...
    public function repaint( Animate $animate ){
            //...
    }
}

class Animate
                  This can now encapsulate all
{
                  animation related operations
    public $animate;

    public function __construct( $animate = true ) {
        $this->animate = $animate;}

}

//...
$component->repaint( new Animate(false) );
Key Benefits


• Type Hinting

• Encapsulation of operations
Ad
                     ap
       OC #4




                         te
                         d
“Only one -> per line,
unless its getter or a
 fluent interface”
Source: CodeIgniter




$this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this-
>CI->uri->slash_segment(2, 'both');




$this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');
Source: CodeIgniter




                         properties are harder to mock
$this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this-
>CI->uri->slash_segment(2, 'both');




$this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');
Source: CodeIgniter




                         properties are harder to mock
$this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this-
>CI->uri->slash_segment(2, 'both');


                                              no whitespace
$this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');
Source: CodeIgniter




                         properties are harder to mock
$this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this-
>CI->uri->slash_segment(2, 'both');


                                              no whitespace
$this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');




                   - Underlying encapsulation problem
                   - Hard to debug and test
                   - Hard to read and understand
Source: CodeIgniter




                         properties are harder to mock
$this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this-
>CI->uri->slash_segment(2, 'both');


                                              no whitespace
$this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');



                            move everything to uri object

       $this->getCI()->getUriBuilder()->getBaseUri(‘leading’);



                   - Underlying encapsulation problem
                   - Hard to debug and test
                   - Hard to read and understand
Source: Zend Framework App




$filterChain->addFilter(new Zend_Filter_Alpha())
            ->addFilter(new Zend_Filter_StringToLower());




                                                            Source: Symfony 2 Docs.
Source: Zend Framework App




    fluent interface
$filterChain->addFilter(new Zend_Filter_Alpha())
            ->addFilter(new Zend_Filter_StringToLower());




                                                            Source: Symfony 2 Docs.
Source: Zend Framework App




    fluent interface
$filterChain->addFilter(new Zend_Filter_Alpha())
            ->addFilter(new Zend_Filter_StringToLower());


      operator alignment




                                                            Source: Symfony 2 Docs.
Source: Zend Framework App




    fluent interface
$filterChain->addFilter(new Zend_Filter_Alpha())
            ->addFilter(new Zend_Filter_StringToLower());


      operator alignment




  $user = $this->get('security.context')->getToken()->getUser();




                                                            Source: Symfony 2 Docs.
Source: Zend Framework App




    fluent interface
$filterChain->addFilter(new Zend_Filter_Alpha())
            ->addFilter(new Zend_Filter_StringToLower());


      operator alignment




                                     only getters (no operations)

  $user = $this->get('security.context')->getToken()->getUser();




                                                            Source: Symfony 2 Docs.
Source: Zend Framework App




    fluent interface
$filterChain->addFilter(new Zend_Filter_Alpha())
            ->addFilter(new Zend_Filter_StringToLower());


      operator alignment




                                     only getters (no operations)

  $user = $this->get('security.context')->getToken()->getUser();




                                                            Source: Symfony 2 Docs.
Source: Zend Framework App




    fluent interface
$filterChain->addFilter(new Zend_Filter_Alpha())
            ->addFilter(new Zend_Filter_StringToLower());


      operator alignment




                                     only getters (no operations)

  $user = $this->get('security.context')->getToken()->getUser();


               where did my
             autocomplete go?

                                                            Source: Symfony 2 Docs.
Source: Zend Framework App




    fluent interface
$filterChain->addFilter(new Zend_Filter_Alpha())
            ->addFilter(new Zend_Filter_StringToLower());


      operator alignment




                                     only getters (no operations)

  $user = $this->get('security.context')->getToken()->getUser();


               where did my                  return null?
             autocomplete go?

                                                            Source: Symfony 2 Docs.
Key Benefits

• Readability
• Easier Mocking (Testing)

• Easier to Debug
• Demeter’s Law
OC #5
“Do not Abbreviate”
if($sx >= $sy) {
    if ($sx > $strSysMatImgW) {
        $ny = $strSysMatImgW * $sy / $sx;
        $nx = $strSysMatImgW;
    }

    if ($ny > $strSysMatImgH) {
        $nx = $strSysMatImgH * $sx / $sy;
        $ny = $strSysMatImgH;
    }

} else {

    if ($sy > $strSysMatImgH) {
        $nx = $strSysMatImgH * $sx / $sy;
        $ny = $strSysMatImgH;
    }

    if($nx > $strSysMatImgW) {
        $ny = $strSysMatImgW * $sy / $sx;
        $nx = $strSysMatImgW;
    }
}
?
if($sx >= $sy) {                      ?
    if ($sx > $strSysMatImgW) {
        $ny = $strSysMatImgW * $sy / $sx;
        $nx = $strSysMatImgW;
    }
                                ?
    if ($ny > $strSysMatImgH) {
        $nx = $strSysMatImgH * $sx / $sy;
        $ny = $strSysMatImgH;
    }

} else {

         ?
    if ($sy > $strSysMatImgH) {
        $nx = $strSysMatImgH * $sx / $sy;
        $ny = $strSysMatImgH;
    }

         ?
    if($nx > $strSysMatImgW) {
        $ny = $strSysMatImgW * $sy / $sx;
        $nx = $strSysMatImgW;
    }
}
Why do you abbreviate?
Why do you abbreviate?


 Its repeated many times,
        and i’m lazy.
Why do you abbreviate?


 Its repeated many times,
        and i’m lazy.
                   Underlying Problem!

   You need to transfer those operations into a separate class.
Why do you abbreviate?

function processResponseHeadersAndDefineOutput($response) { ... }
Why do you abbreviate?

function processResponseHeadersAndDefineOutput($response) { ... }




      This method name is too long to type,
                 and i’m lazy.
Why do you abbreviate?
                          more then one
                          responsibility?

function processResponseHeadersAndDefineOutput($response) { ... }




      This method name is too long to type,
                 and i’m lazy.
function getPage($data) { ... }




function startProcess() { ... }




$tr->process(“site.login”);
get from where?
function getPage($data) { ... }




function startProcess() { ... }




$tr->process(“site.login”);
get from where?
                                  Use clearer names:
function getPage($data) { ... }   fetchPage()
                                  downloadPage()




function startProcess() { ... }




$tr->process(“site.login”);
get from where?
                                  Use clearer names:
function getPage($data) { ... }   fetchPage()
                                  downloadPage()




                                  Use a thesaurus:
function startProcess() { ... }
                                  fork, create, begin, open




$tr->process(“site.login”);
get from where?
                                  Use clearer names:
function getPage($data) { ... }   fetchPage()
                                  downloadPage()




                                  Use a thesaurus:
function startProcess() { ... }
                                  fork, create, begin, open


      Table row?

$tr->process(“site.login”);
get from where?
                                  Use clearer names:
function getPage($data) { ... }   fetchPage()
                                  downloadPage()




                                  Use a thesaurus:
function startProcess() { ... }
                                  fork, create, begin, open


      Table row?
                                  Easy understanding, complete scope:
$tr->process(“site.login”);
                                  $translatorService
Key Benefits


• Clearer communication and maintainability

• Indicates underlying problems
Ad
                 ap
                     te
                     d
     OC #6
“Keep your classes
     small”
200 lines per class
 10 methods per class
15 classes per package
Increased to include
    docblocks



  200 lines per class
 10 methods per class
15 classes per package
Increased to include
    docblocks          15-20 lines per method



  200 lines per class
 10 methods per class
15 classes per package
Increased to include
    docblocks             15-20 lines per method



  200 lines per class
 10 methods per class
15 classes per package
                           read this as
                       namespace or folder
Key Benefits

• Single Responsibility
• Objective and clear methods

• Slimmer namespaces
• Avoids clunky folders
Ad
                     ap
         OC #7




                      te
                          d
 “Limit the number of
instance variables in a
    class (max: 5)”
class MyRegistrationService
{

    protected   $userService;
    protected   $passwordService;
    protected   $logger;
    protected   $translator;
    protected   $entityManager;
    protected   $imageCropper;

    // ...
}
class MyRegistrationService
{

    protected   $userService;
    protected   $passwordService;
    protected   $logger;
    protected   $translator;
    protected   $entityManager;
    protected   $imageCropper;

    // ...
}




       Limit: 5
class MyRegistrationService
                     {

                         protected   $userService;
                         protected   $passwordService;
                         protected   $logger;
All DB interaction       protected   $translator;
   should be in          protected   $entityManager;
    userService          protected   $imageCropper;    Use and event based
                                                      system and move this
                         // ...                             to listener
                     }




                            Limit: 5
Key Benefits


• Shorter dependency list

• Easier Mocking for unit test
OC #8
“Use first class
 collections”
Doctrine:
      ArrayCollection


$collection->getIterator();

$collection->filter(...);

$collection->append(...);

$collection->map(...);
Key Benefits


• Implements collection operations

• Uses SPL interfaces
Dr
                    op
                    pe
                     d
      OC #9
“Do use accessors
 (getter/setter)”
/**
  * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE.
  */
class DoctrineTestsModelsCMSCmsUserProxy
    extends DoctrineTestsModelsCMSCmsUser
    implements DoctrineORMProxyProxy
{

    public function getId()
    {
        $this->__load();
        return parent::getId();
    }

    public function getStatus()
    {
        $this->__load();
        return parent::getStatus();
    }




                                      Property get/set syntax RFC may change the game.
/**
  * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE.
  */
class DoctrineTestsModelsCMSCmsUserProxy
    extends DoctrineTestsModelsCMSCmsUser
    implements DoctrineORMProxyProxy
{

    public function getId()
    {
        $this->__load();          Example: Doctrine uses getters to
        return parent::getId();     inject lazy loading operations
    }

    public function getStatus()
    {
        $this->__load();
        return parent::getStatus();
    }




                                      Property get/set syntax RFC may change the game.
Key Benefits


• Injector operations

• Encapsulation of transformations
Cr
                   ea
                    te
                        d!
   OC #10 (bonus!)
“Document your code!”
//check to see if the section above set the $overall_pref variable to void
if ($overall_pref == 'void')




// implode the revised array of selections in group three into a string
// variable so that it can be transferred to the database at the end of the
// page
$groupthree = implode($groupthree_array, "nr");
really?

//check to see if the section above set the $overall_pref variable to void
if ($overall_pref == 'void')




// implode the revised array of selections in group three into a string
// variable so that it can be transferred to the database at the end of the
// page
$groupthree = implode($groupthree_array, "nr");
really?

//check to see if the section above set the $overall_pref variable to void
if ($overall_pref == 'void')




// implode the revised array of selections in group three into a string
// variable so that it can be transferred to the database at the end of the
// page
$groupthree = implode($groupthree_array, "nr");


         Documenting because i’m doing it wrong in an anusual way
$priority = isset($event['priority']) ? $event['priority'] : 0;

if (!isset($event['event'])) {
    throw new InvalidArgumentException(...));
}

if (!isset($event['method'])) {
    $event['method'] = 'on'.preg_replace(array(
      '/(?<=b)[a-z]/ie',
      '/[^a-z0-9]/i'
    ), array('strtoupper("0")', ''), $event['event']);
}

$definition->addMethodCall(
   'addListenerService',
   array($event['event'],
   array($listenerId,
   $event['method']),
   $priority
));




                                                                  Source: Symfony2
$priority = isset($event['priority']) ? $event['priority'] : 0;

if (!isset($event['event'])) {
    throw new InvalidArgumentException(...));
}

if (!isset($event['method'])) {
    $event['method'] = 'on'.preg_replace(array(
      '/(?<=b)[a-z]/ie',
      '/[^a-z0-9]/i'
    ), array('strtoupper("0")', ''), $event['event']);
}

$definition->addMethodCall(
   'addListenerService',
   array($event['event'],
   array($listenerId,               What does this do?
   $event['method']),
   $priority
));




                                                                  Source: Symfony2
$priority = isset($event['priority']) ? $event['priority'] : 0;
                            Add a simple comment:
if   (!isset($event['event'])) {
      throw new //Strips special chars and camel cases to onXxx
                 InvalidArgumentException(...));
}

if (!isset($event['method'])) {
    $event['method'] = 'on'.preg_replace(array(
      '/(?<=b)[a-z]/ie',
      '/[^a-z0-9]/i'
    ), array('strtoupper("0")', ''), $event['event']);
}

$definition->addMethodCall(
   'addListenerService',
   array($event['event'],
   array($listenerId,                   What does this do?
   $event['method']),
   $priority
));




                                                                  Source: Symfony2
$priority = isset($event['priority']) ? $event['priority'] : 0;
                            Add a simple comment:
if   (!isset($event['event'])) {
      throw new //Strips special chars and camel cases to onXxx
                 InvalidArgumentException(...));
}

if (!isset($event['method'])) {
    $event['method'] = 'on'.preg_replace(array(
                                                    Don’t explain bad
      '/(?<=b)[a-z]/ie',                               code, fix it!
      '/[^a-z0-9]/i'
    ), array('strtoupper("0")', ''), $event['event']);
}

$definition->addMethodCall(
   'addListenerService',
   array($event['event'],
   array($listenerId,                   What does this do?
   $event['method']),
   $priority
));




                                                                  Source: Symfony2
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @todo implement caching for better performance
* @param mixed $element The element to search for.
* @return boolean TRUE if the collection contains the element, or FALSE.
*/
function contains($element);
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @todo implement caching for better performance
* @param mixed $element The element to search for.
* @return boolean TRUE if the collection contains the element, or FALSE.
*/
function contains($element);


   mark todo items so the
    changes don’t get lost
A note on cost of
       running function
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @todo implement caching for better performance
* @param mixed $element The element to search for.
* @return boolean TRUE if the collection contains the element, or FALSE.
*/
function contains($element);


   mark todo items so the
    changes don’t get lost
Do a mind dump,
                                                       then clean it up.
      A note on cost of
       running function
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @todo implement caching for better performance
* @param mixed $element The element to search for.
* @return boolean TRUE if the collection contains the element, or FALSE.
*/
function contains($element);


   mark todo items so the
    changes don’t get lost
Do a mind dump,
                                                       then clean it up.
      A note on cost of
       running function
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @todo implement caching for better performance
* @param mixed $element The element to search for.
* @return boolean TRUE if the collection contains the element, or FALSE.
*/
function contains($element);
                                                  Generate API docs
                                                 with phpDocumentor
   mark todo items so the
    changes don’t get lost
Key Benefits

• Automatic API documentation
• Transmission of “line of thought”
• Avoids confusion
Recap
•   #1 - Only one indentation level per method.

•   #2 - Do not use the ‘else’ keyword.

•   #3 - Wrap primitive types and string, if it has behavior.

•   #4 - Only one -> per line, if not getter or fluent.

•   #5 - Do not Abbreviate.

•   #6 - Keep your classes small

•   #7 - Limit the number of instance variables in a class (max: 5)

•   #8 - Use first class collections

•   #9 - Use accessors (getter/setter)

•   #10 - Document your code!
Questions?




                          http://fixthatcode.com


@rdohms

http://doh.ms                       http://joind.in/7404
http://slides.doh.ms
Recommended Links:


                    The ThoughtWorks Anthology
                    http://goo.gl/OcSNx

                    The Art of Readable Code
                    http://goo.gl/unrij


DISCLAIMER: This talk re-uses some of the examples used by Guilherme Blanco in his
original Object Calisthenic talk. These principles were studied and applied by us while we
worked together in previous jobs. The result taught us all a lesson we really want to spread
to other developers.

More Related Content

What's hot

PHP Functions & Arrays
PHP Functions & ArraysPHP Functions & Arrays
PHP Functions & ArraysHenry Osborne
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksNate Abele
 
Design Patterns in PHP5
Design Patterns in PHP5 Design Patterns in PHP5
Design Patterns in PHP5 Wildan Maulana
 
Chapter 8- Advanced Views and URLconfs
Chapter 8- Advanced Views and URLconfsChapter 8- Advanced Views and URLconfs
Chapter 8- Advanced Views and URLconfsVincent Chien
 
Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium AppsNate Abele
 
Typed Properties and more: What's coming in PHP 7.4?
Typed Properties and more: What's coming in PHP 7.4?Typed Properties and more: What's coming in PHP 7.4?
Typed Properties and more: What's coming in PHP 7.4?Nikita Popov
 
The State of Lithium
The State of LithiumThe State of Lithium
The State of LithiumNate Abele
 
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionLithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionNate Abele
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsBastian Feder
 
PHP Unit 4 arrays
PHP Unit 4 arraysPHP Unit 4 arrays
PHP Unit 4 arraysKumar
 
What's new in PHP 8.0?
What's new in PHP 8.0?What's new in PHP 8.0?
What's new in PHP 8.0?Nikita Popov
 
Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)David de Boer
 
PHP Performance Trivia
PHP Performance TriviaPHP Performance Trivia
PHP Performance TriviaNikita Popov
 
Static Optimization of PHP bytecode (PHPSC 2017)
Static Optimization of PHP bytecode (PHPSC 2017)Static Optimization of PHP bytecode (PHPSC 2017)
Static Optimization of PHP bytecode (PHPSC 2017)Nikita Popov
 
Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"Fwdays
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsRoss Tuck
 

What's hot (20)

PHP Functions & Arrays
PHP Functions & ArraysPHP Functions & Arrays
PHP Functions & Arrays
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate Frameworks
 
Design Patterns in PHP5
Design Patterns in PHP5 Design Patterns in PHP5
Design Patterns in PHP5
 
Chapter 8- Advanced Views and URLconfs
Chapter 8- Advanced Views and URLconfsChapter 8- Advanced Views and URLconfs
Chapter 8- Advanced Views and URLconfs
 
Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium Apps
 
Typed Properties and more: What's coming in PHP 7.4?
Typed Properties and more: What's coming in PHP 7.4?Typed Properties and more: What's coming in PHP 7.4?
Typed Properties and more: What's coming in PHP 7.4?
 
The State of Lithium
The State of LithiumThe State of Lithium
The State of Lithium
 
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionLithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
 
PHP Unit 4 arrays
PHP Unit 4 arraysPHP Unit 4 arrays
PHP Unit 4 arrays
 
Ch8(oop)
Ch8(oop)Ch8(oop)
Ch8(oop)
 
What's new in PHP 8.0?
What's new in PHP 8.0?What's new in PHP 8.0?
What's new in PHP 8.0?
 
Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)
 
PHP Performance Trivia
PHP Performance TriviaPHP Performance Trivia
PHP Performance Trivia
 
Static Optimization of PHP bytecode (PHPSC 2017)
Static Optimization of PHP bytecode (PHPSC 2017)Static Optimization of PHP bytecode (PHPSC 2017)
Static Optimization of PHP bytecode (PHPSC 2017)
 
Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"
 
PHP 8.1: Enums
PHP 8.1: EnumsPHP 8.1: Enums
PHP 8.1: Enums
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and Hobgoblins
 
Arrays in PHP
Arrays in PHPArrays in PHP
Arrays in PHP
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 

Viewers also liked

How Not to Build a WordPress Plugin
How Not to Build a WordPress PluginHow Not to Build a WordPress Plugin
How Not to Build a WordPress PluginWill Norris
 
Introduction to Twig
Introduction to TwigIntroduction to Twig
Introduction to Twigmarkstory
 
Owasp top 10
Owasp top 10Owasp top 10
Owasp top 10markstory
 
Composer The Right Way - 010PHP
Composer The Right Way - 010PHPComposer The Right Way - 010PHP
Composer The Right Way - 010PHPRafael Dohms
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConRafael Dohms
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsGuilherme Blanco
 
ORM dont kill your DB, developers do
ORM dont kill your DB, developers doORM dont kill your DB, developers do
ORM dont kill your DB, developers doGuilherme Blanco
 
PHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object CalisthenicsPHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object CalisthenicsGuilherme Blanco
 
Be Your Own Angel Investor - A Revenue Model for Bootstrapping
Be Your Own Angel Investor - A Revenue Model for BootstrappingBe Your Own Angel Investor - A Revenue Model for Bootstrapping
Be Your Own Angel Investor - A Revenue Model for BootstrappingAmy Hoy
 
Genscape Photos
Genscape Photos Genscape Photos
Genscape Photos mhislop
 

Viewers also liked (13)

How Not to Build a WordPress Plugin
How Not to Build a WordPress PluginHow Not to Build a WordPress Plugin
How Not to Build a WordPress Plugin
 
Introduction to Twig
Introduction to TwigIntroduction to Twig
Introduction to Twig
 
PHP WTF
PHP WTFPHP WTF
PHP WTF
 
Owasp top 10
Owasp top 10Owasp top 10
Owasp top 10
 
Composer The Right Way - 010PHP
Composer The Right Way - 010PHPComposer The Right Way - 010PHP
Composer The Right Way - 010PHP
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnCon
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object Calisthenics
 
ORM dont kill your DB, developers do
ORM dont kill your DB, developers doORM dont kill your DB, developers do
ORM dont kill your DB, developers do
 
Career- Keppel
Career- KeppelCareer- Keppel
Career- Keppel
 
PHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object CalisthenicsPHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object Calisthenics
 
Be Your Own Angel Investor - A Revenue Model for Bootstrapping
Be Your Own Angel Investor - A Revenue Model for BootstrappingBe Your Own Angel Investor - A Revenue Model for Bootstrapping
Be Your Own Angel Investor - A Revenue Model for Bootstrapping
 
Genscape Photos
Genscape Photos Genscape Photos
Genscape Photos
 
reveal.js 3.0.0
reveal.js 3.0.0reveal.js 3.0.0
reveal.js 3.0.0
 

Similar to Object Calisthenics and Code Readability

Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)Rafael Dohms
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix itRafael Dohms
 
You code sucks, let's fix it
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix itRafael Dohms
 
Your code sucks, let's fix it! - php|tek13
Your code sucks, let's fix it! - php|tek13Your code sucks, let's fix it! - php|tek13
Your code sucks, let's fix it! - php|tek13Rafael Dohms
 
Propel sfugmd
Propel sfugmdPropel sfugmd
Propel sfugmdiKlaus
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersKacper Gunia
 
Why Hacking WordPress Search Isn't Some Big Scary Thing
Why Hacking WordPress Search Isn't Some Big Scary ThingWhy Hacking WordPress Search Isn't Some Big Scary Thing
Why Hacking WordPress Search Isn't Some Big Scary ThingChris Reynolds
 
Open Source Search: An Analysis
Open Source Search: An AnalysisOpen Source Search: An Analysis
Open Source Search: An AnalysisJustin Finkelstein
 
jQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and PerformancejQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and PerformanceJonas De Smet
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needKacper Gunia
 
Object Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPObject Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPChad Gray
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutesBarang CK
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 MinutesAzim Kurt
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11Michelangelo van Dam
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxMichelangelo van Dam
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Coding for Scale and Sanity
Coding for Scale and SanityCoding for Scale and Sanity
Coding for Scale and SanityJimKellerES
 
Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB jhchabran
 
How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014Guillaume POTIER
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency InjectionRifat Nabi
 

Similar to Object Calisthenics and Code Readability (20)

Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix it
 
You code sucks, let's fix it
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix it
 
Your code sucks, let's fix it! - php|tek13
Your code sucks, let's fix it! - php|tek13Your code sucks, let's fix it! - php|tek13
Your code sucks, let's fix it! - php|tek13
 
Propel sfugmd
Propel sfugmdPropel sfugmd
Propel sfugmd
 
PHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4DevelopersPHPSpec - the only Design Tool you need - 4Developers
PHPSpec - the only Design Tool you need - 4Developers
 
Why Hacking WordPress Search Isn't Some Big Scary Thing
Why Hacking WordPress Search Isn't Some Big Scary ThingWhy Hacking WordPress Search Isn't Some Big Scary Thing
Why Hacking WordPress Search Isn't Some Big Scary Thing
 
Open Source Search: An Analysis
Open Source Search: An AnalysisOpen Source Search: An Analysis
Open Source Search: An Analysis
 
jQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and PerformancejQuery: Tips, tricks and hints for better development and Performance
jQuery: Tips, tricks and hints for better development and Performance
 
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you needDutch PHP Conference - PHPSpec 2 - The only Design Tool you need
Dutch PHP Conference - PHPSpec 2 - The only Design Tool you need
 
Object Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPObject Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHP
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Coding for Scale and Sanity
Coding for Scale and SanityCoding for Scale and Sanity
Coding for Scale and Sanity
 
Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB
 
How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
 

More from Rafael Dohms

The Individual Contributor Path - DPC2024
The Individual Contributor Path - DPC2024The Individual Contributor Path - DPC2024
The Individual Contributor Path - DPC2024Rafael Dohms
 
Application Metrics - IPC2023
Application Metrics - IPC2023Application Metrics - IPC2023
Application Metrics - IPC2023Rafael Dohms
 
How'd we get here? A guide to Architectural Decision Records
How'd we get here? A guide to Architectural Decision RecordsHow'd we get here? A guide to Architectural Decision Records
How'd we get here? A guide to Architectural Decision RecordsRafael Dohms
 
Architectural Decision Records - PHPConfBR
Architectural Decision Records - PHPConfBRArchitectural Decision Records - PHPConfBR
Architectural Decision Records - PHPConfBRRafael Dohms
 
Application Metrics (with Prometheus examples)
Application Metrics (with Prometheus examples)Application Metrics (with Prometheus examples)
Application Metrics (with Prometheus examples)Rafael Dohms
 
Application metrics - Confoo 2019
Application metrics - Confoo 2019Application metrics - Confoo 2019
Application metrics - Confoo 2019Rafael Dohms
 
Writing code you won’t hate tomorrow - PHPCE18
Writing code you won’t hate tomorrow - PHPCE18Writing code you won’t hate tomorrow - PHPCE18
Writing code you won’t hate tomorrow - PHPCE18Rafael Dohms
 
Application Metrics (with Prometheus examples) #PHPDD18
Application Metrics (with Prometheus examples) #PHPDD18Application Metrics (with Prometheus examples) #PHPDD18
Application Metrics (with Prometheus examples) #PHPDD18Rafael Dohms
 
Application metrics with Prometheus - DPC18
Application metrics with Prometheus - DPC18Application metrics with Prometheus - DPC18
Application metrics with Prometheus - DPC18Rafael Dohms
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonfRafael Dohms
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...Rafael Dohms
 
Writing Code That Lasts - #Magento2Seminar, Utrecht
Writing Code That Lasts - #Magento2Seminar, UtrechtWriting Code That Lasts - #Magento2Seminar, Utrecht
Writing Code That Lasts - #Magento2Seminar, UtrechtRafael Dohms
 
Composer the Right Way - PHPSRB16
Composer the Right Way - PHPSRB16Composer the Right Way - PHPSRB16
Composer the Right Way - PHPSRB16Rafael Dohms
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16Rafael Dohms
 
Composer the Right Way - MM16NL
Composer the Right Way - MM16NLComposer the Right Way - MM16NL
Composer the Right Way - MM16NLRafael Dohms
 
Composer The Right Way - PHPUGMRN
Composer The Right Way - PHPUGMRNComposer The Right Way - PHPUGMRN
Composer The Right Way - PHPUGMRNRafael Dohms
 
Composer the Right Way - PHPBNL16
Composer the Right Way - PHPBNL16Composer the Right Way - PHPBNL16
Composer the Right Way - PHPBNL16Rafael Dohms
 
“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.Rafael Dohms
 
A Journey into your Lizard Brain - PHP Conference Brasil 2015
A Journey into your Lizard Brain - PHP Conference Brasil 2015A Journey into your Lizard Brain - PHP Conference Brasil 2015
A Journey into your Lizard Brain - PHP Conference Brasil 2015Rafael Dohms
 
“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.Rafael Dohms
 

More from Rafael Dohms (20)

The Individual Contributor Path - DPC2024
The Individual Contributor Path - DPC2024The Individual Contributor Path - DPC2024
The Individual Contributor Path - DPC2024
 
Application Metrics - IPC2023
Application Metrics - IPC2023Application Metrics - IPC2023
Application Metrics - IPC2023
 
How'd we get here? A guide to Architectural Decision Records
How'd we get here? A guide to Architectural Decision RecordsHow'd we get here? A guide to Architectural Decision Records
How'd we get here? A guide to Architectural Decision Records
 
Architectural Decision Records - PHPConfBR
Architectural Decision Records - PHPConfBRArchitectural Decision Records - PHPConfBR
Architectural Decision Records - PHPConfBR
 
Application Metrics (with Prometheus examples)
Application Metrics (with Prometheus examples)Application Metrics (with Prometheus examples)
Application Metrics (with Prometheus examples)
 
Application metrics - Confoo 2019
Application metrics - Confoo 2019Application metrics - Confoo 2019
Application metrics - Confoo 2019
 
Writing code you won’t hate tomorrow - PHPCE18
Writing code you won’t hate tomorrow - PHPCE18Writing code you won’t hate tomorrow - PHPCE18
Writing code you won’t hate tomorrow - PHPCE18
 
Application Metrics (with Prometheus examples) #PHPDD18
Application Metrics (with Prometheus examples) #PHPDD18Application Metrics (with Prometheus examples) #PHPDD18
Application Metrics (with Prometheus examples) #PHPDD18
 
Application metrics with Prometheus - DPC18
Application metrics with Prometheus - DPC18Application metrics with Prometheus - DPC18
Application metrics with Prometheus - DPC18
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...
 
Writing Code That Lasts - #Magento2Seminar, Utrecht
Writing Code That Lasts - #Magento2Seminar, UtrechtWriting Code That Lasts - #Magento2Seminar, Utrecht
Writing Code That Lasts - #Magento2Seminar, Utrecht
 
Composer the Right Way - PHPSRB16
Composer the Right Way - PHPSRB16Composer the Right Way - PHPSRB16
Composer the Right Way - PHPSRB16
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16
 
Composer the Right Way - MM16NL
Composer the Right Way - MM16NLComposer the Right Way - MM16NL
Composer the Right Way - MM16NL
 
Composer The Right Way - PHPUGMRN
Composer The Right Way - PHPUGMRNComposer The Right Way - PHPUGMRN
Composer The Right Way - PHPUGMRN
 
Composer the Right Way - PHPBNL16
Composer the Right Way - PHPBNL16Composer the Right Way - PHPBNL16
Composer the Right Way - PHPBNL16
 
“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.
 
A Journey into your Lizard Brain - PHP Conference Brasil 2015
A Journey into your Lizard Brain - PHP Conference Brasil 2015A Journey into your Lizard Brain - PHP Conference Brasil 2015
A Journey into your Lizard Brain - PHP Conference Brasil 2015
 
“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.
 

Recently uploaded

Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfIngrid Airi González
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesBernd Ruecker
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Alkin Tezuysal
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsRavi Sanghani
 
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...itnewsafrica
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integrationmarketing932765
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkPixlogix Infotech
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfNeo4j
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 

Recently uploaded (20)

Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdf
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architectures
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and Insights
 
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App Framework
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 

Object Calisthenics and Code Readability

  • 1. Your code sucks, let’s !x it! Object Calisthenics and Code readability Rafael Dohms
  • 2. Rafael Dohms photo credit: Eli White @rdohms Evangelist, Speaker and Contributor. Developer at WEBclusive. Enabler at AmsterdamPHP.
  • 4. Is it Readable? Why does my code suck?
  • 5. Is it Readable? Why does my code suck? Is it Testable?
  • 6. Is it Maintainable? Is it Readable? Why does my code suck? Is it Testable?
  • 7. Is it Maintainable? Is it Readable? Why does my code suck? Is it Reusable? Is it Testable?
  • 8. <?php Does it look like this? $list=mysql_connect("******","*******","*****"); if(!$list)echo 'Cannot login.'; else{ mysql_select_db("******", $list); $best=array("0","0","0","0","0","0"); $id=mysql_num_rows(mysql_query("SELECT * FROM allnews")); $count=0; for($i=0;$i<6;$i++){ while(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")!="he")$count+ +; $best[$i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");} $id=$id-$count; $maxdate=mktime(0,0,0,date('m'),date('d')-7,date('Y')); while(mysql_query("SELECT date FROM allnews WHERE id=$id-$count")>=$maxdate){ if(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")=="he"){ $small=$best[0]; while($i=0;$i<6;$i++){ if(mysql_query("SELECT score FROM allnews WHERE id= $small)"<mysql_query("SELECT score FROM allnews WHERE id=$best[i+1]")) $small=$best[i+1];} if(mysql_query("SELECT score FROM allnews WHERE id= $small")<mysql_query("SELECT score FROM allnews WHERE id=$id-$count")){ while($i=0;$i<6;$i++){ if($small==$best[i])$best[i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");}}}} while($i=0;$i<6;$i++) echo '<a href="news-page.php?id='.$best[i].'"><div class="box '.mysql_query("SELECT type FROM allnews WHERE id=$best[i]").'">'.mysql_query("SELECT title FROM allnews WHERE id= $best[i]").'<div class="img" style="background-image:url(images/'.mysql_query("SELECT image1 FROM allnews WHERE id=$best[i]").');"></div></div></a>'; mysql_close($list); } ?>
  • 9. <?php Does it look like this? $list=mysql_connect("******","*******","*****"); if(!$list)echo 'Cannot login.'; else{ mysql_select_db("******", $list); $best=array("0","0","0","0","0","0"); $id=mysql_num_rows(mysql_query("SELECT * FROM allnews")); $count=0; for($i=0;$i<6;$i++){ while(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")!="he")$count+ +; $best[$i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");} $id=$id-$count; $maxdate=mktime(0,0,0,date('m'),date('d')-7,date('Y')); while(mysql_query("SELECT date FROM allnews WHERE id=$id-$count")>=$maxdate){ if(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")=="he"){ $small=$best[0]; while($i=0;$i<6;$i++){ if(mysql_query("SELECT score FROM allnews WHERE id= $small)"<mysql_query("SELECT score FROM allnews WHERE id=$best[i+1]")) $small=$best[i+1];} if(mysql_query("SELECT score FROM allnews WHERE id= $small")<mysql_query("SELECT score FROM Rebecca WHERE id=$id-$count")){ If allnews Black was a developer while($i=0;$i<6;$i++){ if($small==$best[i])$best[i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");}}}} while($i=0;$i<6;$i++) echo '<a href="news-page.php?id='.$best[i].'"><div class="box '.mysql_query("SELECT type FROM allnews WHERE id=$best[i]").'">'.mysql_query("SELECT title FROM allnews WHERE id= $best[i]").'<div class="img" style="background-image:url(images/'.mysql_query("SELECT image1 FROM allnews WHERE id=$best[i]").');"></div></div></a>'; mysql_close($list); } ?>
  • 10. How do we fix it?
  • 12. cal·is·then·ics - noun - /ˌkaləsˈTHeniks/ Calisthenics are a form of dynamic exercise consisting of a variety of simple, often rhythmical, movements, generally using minimal equipment or apparatus. Object Calisthenics
  • 13. cal·is·then·ics - noun - /ˌkaləsˈTHeniks/ Calisthenics are a form of dynamic exercise consisting of a variety of simple, often rhythmical, movements, generally using minimal equipment or apparatus. Object Calisthenics A variety of simple, often rhythmical, exercises to achieve better OO and code quality
  • 14. “So here’s an exercise that can help you to internalize principles of good object-oriented design and actually use them in real life.” -- Jeff Bay Object Calisthenics
  • 15. “So here’s an exercise that can help you to internalize principles of good object-oriented design and actually use them in real life.” -- Jeff Bay Object Calisthenics Important: PHP != JAVA Adaptations will be done
  • 16. Object Calisthenics + Readability Tips
  • 17. “You need to write code that minimizes the time it would Object Calisthenics take someone else to understand it—even if that someone else is you.” + -- Dustin Boswell and Trevor Foucher Readability Tips
  • 18. “You need to write code that minimizes the time it would Object Calisthenics take someone else to understand it—even if that someone else is you.” + -- Dustin Boswell and Trevor Foucher Readability Tips I’m a tip
  • 20. OC #1 “Only one indentation level per method”
  • 21. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; foreach ($products as $rawProduct) { $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } } return $valid; }
  • 22. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; 0 foreach ($products as $rawProduct) { 1 $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { 2 if (!in_array($requiredField, $fields)) { 3 $valid = false; } } } return $valid; }
  • 23. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; 0 foreach ($products as $rawProduct) { 1 $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { 2 if (!in_array($requiredField, $fields)) { 3 $valid = false; } } } return $valid; }
  • 24. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; foreach ($products as $rawProduct) { $validationResult = validateSingleProduct($rawProduct, $requiredFields); if ( ! $validationResult){ $valid = false; } } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } return $valid; }
  • 25. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; whitespace foreach ($products as $rawProduct) { $validationResult = validateSingleProduct($rawProduct, $requiredFields); if ( ! $validationResult){ $valid = false; } } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } return $valid; }
  • 26. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; whitespace foreach ($products as $rawProduct) { $validationResult = validateSingleProduct($rawProduct, $requiredFields); if ( ! $validationResult){ $valid = false; } } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; duplicated logic $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } return $valid; }
  • 27. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; 0 foreach ($products as whitespace $validationResult $rawProduct) { = validateSingleProduct($rawProduct, $requiredFields); 1 if ( ! $validationResult){ } 2 $valid = false; } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; duplicated logic $fields = array_keys($rawProduct); 0 foreach(!in_array($requiredField, $fields)) ($requiredFields as $requiredField) { 1 if { } 2 $valid = false; } return $valid; }
  • 28. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; 0 foreach ($products as $validationResult $rawProduct) { = validateSingleProduct($rawProduct, $requiredFields); 1 if ( ! $validationResult){ } 2 $valid = false; } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; $fields = array_keys($rawProduct); 0 foreach(!in_array($requiredField, $fields)) ($requiredFields as $requiredField) { 1 if { } 2 $valid = false; } return $valid; }
  • 29. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; 0 foreach ($products as $validationResult $rawProduct) { = validateSingleProduct($rawProduct, $requiredFields); 1 if ( ! $validationResult){ } 2 $valid = false; } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; $fields = array_keys($rawProduct); 0 foreach(!in_array($requiredField, $fields)) ($requiredFields as $requiredField) { 1 if { } 2 $valid = false; } return $valid; }
  • 30. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 31. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); cheating! I see foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 32. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); cheating! I see foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; Single line IF, simple operations } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 33. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); cheating! I see foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; Single line IF, simple operations } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 34. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; Single line IF, simple operations return early } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 35. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; Single line IF, simple operations return early } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } C (native) functions are faster then PHP
  • 36. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 37. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 38. function validateProductList($products) { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 39. function validateProductList($products) faster iteration { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 40. function validateProductList($products) faster iteration { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } reusable method function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 41. function validateProductList($products) faster iteration { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } reusable method function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } method name matches “true” result
  • 42. function validateProductList($products) faster iteration { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } assertable return: expected/returned reusable method function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } method name matches “true” result
  • 43. List is more readable the plural function validateProductList($products) faster iteration { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } assertable return: expected/returned reusable method function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } method name matches “true” result
  • 44. Key Benefits • Single Responsibility Principle (S in SOLID) • Increases re-use
  • 45. OC #2 “Do not use the ‘else’ keyword”
  • 46. public function createPost($request) { $entity = new Post(); $form = new MyForm($entity); $form->bind($request); if ($form->isValid()){ $repository = $this->getRepository('MyBundle:Post'); if (!$repository->exists($entity) ) { $repository->save($entity); return $this->redirect('create_ok'); } else { $error = "Post Title already exists"; return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields"; return array('form' => $form, 'error' => $error); } }
  • 47. public function createPost($request) { $entity = new Post(); $form = new MyForm($entity); $form->bind($request); if ($form->isValid()){ $repository = $this->getRepository('MyBundle:Post'); if (!$repository->exists($entity) ) { $repository->save($entity); return $this->redirect('create_ok'); } else { $error = "Post Title already exists"; return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields"; return array('form' => $form, 'error' => $error); } }
  • 48. public function createPost($request) { $entity = new Post(); $form = new MyForm($entity); $form->bind($request); if ($form->isValid()){ $repository = $this->getRepository('MyBundle:Post'); if (!$repository->exists($entity) ) { $repository->save($entity); return $this->redirect('create_ok'); } else { $error = "Post Title already exists"; return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields"; return array('form' => $form, 'error' => $error); } }
  • 49. public function createPost($request) { $entity = new Post(); $form = new MyForm($entity); $form->bind($request); if ($form->isValid()){ $repository = $this->getRepository('MyBundle:Post'); if (!$repository->exists($entity) ) { $repository->save($entity); return $this->redirect('create_ok'); } else { $error = "Post Title already exists"; return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields"; return array('form' => $form, 'error' => $error); } }
  • 50. public function createPost($request) { $entity = new Post(); $form = new MyForm($entity); $form->bind($request); if ($form->isValid()){ $repository = $this->getRepository('MyBundle:Post'); if (!$repository->exists($entity) ) { $repository->save($entity); return $this->redirect('create_ok'); } else { $error = "Post Title already exists"; return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields"; return array('form' => $form, 'error' => $error); } }
  • 51. public function createPost($request) { $entity = new Post(); $form = new MyForm($entity); $form->bind($request); if ($form->isValid()){ $repository = $this->getRepository('MyBundle:Post'); if (!$repository->exists($entity) ) { $repository->save($entity); return $this->redirect('create_ok'); } else { $error = "Post Title already exists"; return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields"; return array('form' => $form, 'error' => $error); } }
  • 52. public function createPost($request) { $entity = new Post(); $form = new MyForm($entity); $form->bind($request); if ($form->isValid()){ $repository = $this->getRepository('MyBundle:Post'); if (!$repository->exists($entity) ) { $repository->save($entity); return $this->redirect('create_ok'); } else { $error = "Post Title already exists"; return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields"; return array('form' => $form, 'error' => $error); } }
  • 53. public function createPost($request) { $entity = new Post(); $form = new MyForm($entity); $form->bind($request); if ($form->isValid()){ $repository = $this->getRepository('MyBundle:Post'); if (!$repository->exists($entity) ) { $repository->save($entity); intermediate variable return $this->redirect('create_ok'); } else { $error = "Post Title already exists"; return array('form' => $form, 'error' => $error); } intermediate variable } else { $error = "Invalid fields"; return array('form' => $form, 'error' => $error); } }
  • 54. public function createPost($request) { $entity = new Post(); $repository = $this->getRepository('MyBundle:Post'); $form = new MyForm($entity); $form->bind($request); if ( ! $form->isValid()){ return array('form' => $form, 'error' => 'Invalid fields'); } if ($repository->exists($entity)){ return array('form' => $form, 'error' => 'Duplicate post title'); } $repository->save($entity); return $this->redirect('create_ok'); }
  • 55. public function createPost($request) { $entity = new Post(); $repository = $this->getRepository('MyBundle:Post'); $form = new MyForm($entity); $form->bind($request); if ( ! $form->isValid()){ return array('form' => $form, 'error' => 'Invalid fields'); } if ($repository->exists($entity)){ return array('form' => $form, 'error' => 'Duplicate post title'); } $repository->save($entity); return $this->redirect('create_ok'); }
  • 56. public function createPost($request) { $entity = new Post(); $repository = $this->getRepository('MyBundle:Post'); $form = new MyForm($entity); $form->bind($request); removed intermediates if ( ! $form->isValid()){ return array('form' => $form, 'error' => 'Invalid fields'); } if ($repository->exists($entity)){ return array('form' => $form, 'error' => 'Duplicate post title'); } $repository->save($entity); return $this->redirect('create_ok'); }
  • 57. public function createPost($request) { $entity = new Post(); $repository = $this->getRepository('MyBundle:Post'); $form = new MyForm($entity); $form->bind($request); removed intermediates if ( ! $form->isValid()){ return array('form' => $form, 'error' => 'Invalid fields'); } early return if ($repository->exists($entity)){ return array('form' => $form, 'error' => 'Duplicate post title'); } $repository->save($entity); return $this->redirect('create_ok'); }
  • 58. Separate code into blocks. public function createPost($request) { Its like using $entity = new Post(); $repository = $this->getRepository('MyBundle:Post'); Paragraphs. $form = new MyForm($entity); $form->bind($request); removed intermediates if ( ! $form->isValid()){ return array('form' => $form, 'error' => 'Invalid fields'); } early return if ($repository->exists($entity)){ return array('form' => $form, 'error' => 'Duplicate post title'); } $repository->save($entity); return $this->redirect('create_ok'); }
  • 59. Key Benefits • Helps avoid code duplication • Easier to read (single path) • Reduces cyclomatic complexity
  • 60. Ad ap OC #3 te d “Wrap primitive types and strings, if there is behaviour”
  • 61. class UIComponent { //... public function repaint($animate = true){ //... } } //... $component->repaint(false);
  • 62. class UIComponent { //... public function repaint($animate = true){ //... } } //... $component->repaint(false);
  • 63. class UIComponent { //... public function repaint($animate = true){ //... } } //... $component->repaint(false); unclear operation
  • 64. class UIComponent { //... public function repaint( Animate $animate ){ //... } } class Animate { public $animate; public function __construct( $animate = true ) { $this->animate = $animate;} } //... $component->repaint( new Animate(false) );
  • 65. class UIComponent { //... public function repaint( Animate $animate ){ //... } } class Animate This can now encapsulate all { animation related operations public $animate; public function __construct( $animate = true ) { $this->animate = $animate;} } //... $component->repaint( new Animate(false) );
  • 66. Key Benefits • Type Hinting • Encapsulation of operations
  • 67. Ad ap OC #4 te d “Only one -> per line, unless its getter or a fluent interface”
  • 68. Source: CodeIgniter $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this- >CI->uri->slash_segment(2, 'both'); $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');
  • 69. Source: CodeIgniter properties are harder to mock $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this- >CI->uri->slash_segment(2, 'both'); $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');
  • 70. Source: CodeIgniter properties are harder to mock $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this- >CI->uri->slash_segment(2, 'both'); no whitespace $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');
  • 71. Source: CodeIgniter properties are harder to mock $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this- >CI->uri->slash_segment(2, 'both'); no whitespace $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading'); - Underlying encapsulation problem - Hard to debug and test - Hard to read and understand
  • 72. Source: CodeIgniter properties are harder to mock $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this- >CI->uri->slash_segment(2, 'both'); no whitespace $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading'); move everything to uri object $this->getCI()->getUriBuilder()->getBaseUri(‘leading’); - Underlying encapsulation problem - Hard to debug and test - Hard to read and understand
  • 73. Source: Zend Framework App $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); Source: Symfony 2 Docs.
  • 74. Source: Zend Framework App fluent interface $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); Source: Symfony 2 Docs.
  • 75. Source: Zend Framework App fluent interface $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment Source: Symfony 2 Docs.
  • 76. Source: Zend Framework App fluent interface $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment $user = $this->get('security.context')->getToken()->getUser(); Source: Symfony 2 Docs.
  • 77. Source: Zend Framework App fluent interface $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment only getters (no operations) $user = $this->get('security.context')->getToken()->getUser(); Source: Symfony 2 Docs.
  • 78. Source: Zend Framework App fluent interface $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment only getters (no operations) $user = $this->get('security.context')->getToken()->getUser(); Source: Symfony 2 Docs.
  • 79. Source: Zend Framework App fluent interface $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment only getters (no operations) $user = $this->get('security.context')->getToken()->getUser(); where did my autocomplete go? Source: Symfony 2 Docs.
  • 80. Source: Zend Framework App fluent interface $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment only getters (no operations) $user = $this->get('security.context')->getToken()->getUser(); where did my return null? autocomplete go? Source: Symfony 2 Docs.
  • 81. Key Benefits • Readability • Easier Mocking (Testing) • Easier to Debug • Demeter’s Law
  • 82. OC #5 “Do not Abbreviate”
  • 83. if($sx >= $sy) { if ($sx > $strSysMatImgW) { $ny = $strSysMatImgW * $sy / $sx; $nx = $strSysMatImgW; } if ($ny > $strSysMatImgH) { $nx = $strSysMatImgH * $sx / $sy; $ny = $strSysMatImgH; } } else { if ($sy > $strSysMatImgH) { $nx = $strSysMatImgH * $sx / $sy; $ny = $strSysMatImgH; } if($nx > $strSysMatImgW) { $ny = $strSysMatImgW * $sy / $sx; $nx = $strSysMatImgW; } }
  • 84. ? if($sx >= $sy) { ? if ($sx > $strSysMatImgW) { $ny = $strSysMatImgW * $sy / $sx; $nx = $strSysMatImgW; } ? if ($ny > $strSysMatImgH) { $nx = $strSysMatImgH * $sx / $sy; $ny = $strSysMatImgH; } } else { ? if ($sy > $strSysMatImgH) { $nx = $strSysMatImgH * $sx / $sy; $ny = $strSysMatImgH; } ? if($nx > $strSysMatImgW) { $ny = $strSysMatImgW * $sy / $sx; $nx = $strSysMatImgW; } }
  • 85. Why do you abbreviate?
  • 86. Why do you abbreviate? Its repeated many times, and i’m lazy.
  • 87. Why do you abbreviate? Its repeated many times, and i’m lazy. Underlying Problem! You need to transfer those operations into a separate class.
  • 88. Why do you abbreviate? function processResponseHeadersAndDefineOutput($response) { ... }
  • 89. Why do you abbreviate? function processResponseHeadersAndDefineOutput($response) { ... } This method name is too long to type, and i’m lazy.
  • 90. Why do you abbreviate? more then one responsibility? function processResponseHeadersAndDefineOutput($response) { ... } This method name is too long to type, and i’m lazy.
  • 91. function getPage($data) { ... } function startProcess() { ... } $tr->process(“site.login”);
  • 92. get from where? function getPage($data) { ... } function startProcess() { ... } $tr->process(“site.login”);
  • 93. get from where? Use clearer names: function getPage($data) { ... } fetchPage() downloadPage() function startProcess() { ... } $tr->process(“site.login”);
  • 94. get from where? Use clearer names: function getPage($data) { ... } fetchPage() downloadPage() Use a thesaurus: function startProcess() { ... } fork, create, begin, open $tr->process(“site.login”);
  • 95. get from where? Use clearer names: function getPage($data) { ... } fetchPage() downloadPage() Use a thesaurus: function startProcess() { ... } fork, create, begin, open Table row? $tr->process(“site.login”);
  • 96. get from where? Use clearer names: function getPage($data) { ... } fetchPage() downloadPage() Use a thesaurus: function startProcess() { ... } fork, create, begin, open Table row? Easy understanding, complete scope: $tr->process(“site.login”); $translatorService
  • 97. Key Benefits • Clearer communication and maintainability • Indicates underlying problems
  • 98. Ad ap te d OC #6 “Keep your classes small”
  • 99. 200 lines per class 10 methods per class 15 classes per package
  • 100. Increased to include docblocks 200 lines per class 10 methods per class 15 classes per package
  • 101. Increased to include docblocks 15-20 lines per method 200 lines per class 10 methods per class 15 classes per package
  • 102. Increased to include docblocks 15-20 lines per method 200 lines per class 10 methods per class 15 classes per package read this as namespace or folder
  • 103. Key Benefits • Single Responsibility • Objective and clear methods • Slimmer namespaces • Avoids clunky folders
  • 104. Ad ap OC #7 te d “Limit the number of instance variables in a class (max: 5)”
  • 105. class MyRegistrationService { protected $userService; protected $passwordService; protected $logger; protected $translator; protected $entityManager; protected $imageCropper; // ... }
  • 106. class MyRegistrationService { protected $userService; protected $passwordService; protected $logger; protected $translator; protected $entityManager; protected $imageCropper; // ... } Limit: 5
  • 107. class MyRegistrationService { protected $userService; protected $passwordService; protected $logger; All DB interaction protected $translator; should be in protected $entityManager; userService protected $imageCropper; Use and event based system and move this // ... to listener } Limit: 5
  • 108. Key Benefits • Shorter dependency list • Easier Mocking for unit test
  • 109. OC #8 “Use first class collections”
  • 110. Doctrine: ArrayCollection $collection->getIterator(); $collection->filter(...); $collection->append(...); $collection->map(...);
  • 111. Key Benefits • Implements collection operations • Uses SPL interfaces
  • 112. Dr op pe d OC #9 “Do use accessors (getter/setter)”
  • 113. /** * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. */ class DoctrineTestsModelsCMSCmsUserProxy extends DoctrineTestsModelsCMSCmsUser implements DoctrineORMProxyProxy { public function getId() { $this->__load(); return parent::getId(); } public function getStatus() { $this->__load(); return parent::getStatus(); } Property get/set syntax RFC may change the game.
  • 114. /** * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. */ class DoctrineTestsModelsCMSCmsUserProxy extends DoctrineTestsModelsCMSCmsUser implements DoctrineORMProxyProxy { public function getId() { $this->__load(); Example: Doctrine uses getters to return parent::getId(); inject lazy loading operations } public function getStatus() { $this->__load(); return parent::getStatus(); } Property get/set syntax RFC may change the game.
  • 115. Key Benefits • Injector operations • Encapsulation of transformations
  • 116. Cr ea te d! OC #10 (bonus!) “Document your code!”
  • 117. //check to see if the section above set the $overall_pref variable to void if ($overall_pref == 'void') // implode the revised array of selections in group three into a string // variable so that it can be transferred to the database at the end of the // page $groupthree = implode($groupthree_array, "nr");
  • 118. really? //check to see if the section above set the $overall_pref variable to void if ($overall_pref == 'void') // implode the revised array of selections in group three into a string // variable so that it can be transferred to the database at the end of the // page $groupthree = implode($groupthree_array, "nr");
  • 119. really? //check to see if the section above set the $overall_pref variable to void if ($overall_pref == 'void') // implode the revised array of selections in group three into a string // variable so that it can be transferred to the database at the end of the // page $groupthree = implode($groupthree_array, "nr"); Documenting because i’m doing it wrong in an anusual way
  • 120. $priority = isset($event['priority']) ? $event['priority'] : 0; if (!isset($event['event'])) { throw new InvalidArgumentException(...)); } if (!isset($event['method'])) { $event['method'] = 'on'.preg_replace(array( '/(?<=b)[a-z]/ie', '/[^a-z0-9]/i' ), array('strtoupper("0")', ''), $event['event']); } $definition->addMethodCall( 'addListenerService', array($event['event'], array($listenerId, $event['method']), $priority )); Source: Symfony2
  • 121. $priority = isset($event['priority']) ? $event['priority'] : 0; if (!isset($event['event'])) { throw new InvalidArgumentException(...)); } if (!isset($event['method'])) { $event['method'] = 'on'.preg_replace(array( '/(?<=b)[a-z]/ie', '/[^a-z0-9]/i' ), array('strtoupper("0")', ''), $event['event']); } $definition->addMethodCall( 'addListenerService', array($event['event'], array($listenerId, What does this do? $event['method']), $priority )); Source: Symfony2
  • 122. $priority = isset($event['priority']) ? $event['priority'] : 0; Add a simple comment: if (!isset($event['event'])) { throw new //Strips special chars and camel cases to onXxx InvalidArgumentException(...)); } if (!isset($event['method'])) { $event['method'] = 'on'.preg_replace(array( '/(?<=b)[a-z]/ie', '/[^a-z0-9]/i' ), array('strtoupper("0")', ''), $event['event']); } $definition->addMethodCall( 'addListenerService', array($event['event'], array($listenerId, What does this do? $event['method']), $priority )); Source: Symfony2
  • 123. $priority = isset($event['priority']) ? $event['priority'] : 0; Add a simple comment: if (!isset($event['event'])) { throw new //Strips special chars and camel cases to onXxx InvalidArgumentException(...)); } if (!isset($event['method'])) { $event['method'] = 'on'.preg_replace(array( Don’t explain bad '/(?<=b)[a-z]/ie', code, fix it! '/[^a-z0-9]/i' ), array('strtoupper("0")', ''), $event['event']); } $definition->addMethodCall( 'addListenerService', array($event['event'], array($listenerId, What does this do? $event['method']), $priority )); Source: Symfony2
  • 124. /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @todo implement caching for better performance * @param mixed $element The element to search for. * @return boolean TRUE if the collection contains the element, or FALSE. */ function contains($element);
  • 125. /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @todo implement caching for better performance * @param mixed $element The element to search for. * @return boolean TRUE if the collection contains the element, or FALSE. */ function contains($element); mark todo items so the changes don’t get lost
  • 126. A note on cost of running function /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @todo implement caching for better performance * @param mixed $element The element to search for. * @return boolean TRUE if the collection contains the element, or FALSE. */ function contains($element); mark todo items so the changes don’t get lost
  • 127. Do a mind dump, then clean it up. A note on cost of running function /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @todo implement caching for better performance * @param mixed $element The element to search for. * @return boolean TRUE if the collection contains the element, or FALSE. */ function contains($element); mark todo items so the changes don’t get lost
  • 128. Do a mind dump, then clean it up. A note on cost of running function /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @todo implement caching for better performance * @param mixed $element The element to search for. * @return boolean TRUE if the collection contains the element, or FALSE. */ function contains($element); Generate API docs with phpDocumentor mark todo items so the changes don’t get lost
  • 129. Key Benefits • Automatic API documentation • Transmission of “line of thought” • Avoids confusion
  • 130. Recap • #1 - Only one indentation level per method. • #2 - Do not use the ‘else’ keyword. • #3 - Wrap primitive types and string, if it has behavior. • #4 - Only one -> per line, if not getter or fluent. • #5 - Do not Abbreviate. • #6 - Keep your classes small • #7 - Limit the number of instance variables in a class (max: 5) • #8 - Use first class collections • #9 - Use accessors (getter/setter) • #10 - Document your code!
  • 131. Questions? http://fixthatcode.com @rdohms http://doh.ms http://joind.in/7404 http://slides.doh.ms
  • 132. Recommended Links: The ThoughtWorks Anthology http://goo.gl/OcSNx The Art of Readable Code http://goo.gl/unrij DISCLAIMER: This talk re-uses some of the examples used by Guilherme Blanco in his original Object Calisthenic talk. These principles were studied and applied by us while we worked together in previous jobs. The result taught us all a lesson we really want to spread to other developers.