Your code sucks, let's fix it (CakeFest2012)

132
Your code sucks, let’s !x it! Object Calisthenics and Code readability Rafael Dohms @rdohms

description

How do you measure the quality of your code? Performance and testing are just one aspect of code, in order to meet deadlines and make maintenance quicker you also need your code to be readable, decoupled and generally easier to comprehend and work with. This talk will go over tips and exercises to help you identify trouble areas, refactor them and train you to write better code in future projects. Come make your code look and function better.

Transcript of Your code sucks, let's fix it (CakeFest2012)

Page 1: Your code sucks, let's fix it (CakeFest2012)

Your code sucks, let’s !x it!

Object Calisthenics and Code readability

Rafael Dohms@rdohms

Page 2: Your code sucks, let's fix it (CakeFest2012)

phot

o cr

edit:

Eli W

hite

Evangelist, Speaker and Contributor.

Developer at WEBclusive.

Enabler at AmsterdamPHP.

Rafael Dohms@rdohms

Page 3: Your code sucks, let's fix it (CakeFest2012)

What’s the talk about?

• Why does my code suck?

• How can we fix it?

Page 4: Your code sucks, let's fix it (CakeFest2012)

Why does mycode suck?

Page 5: Your code sucks, let's fix it (CakeFest2012)

Why does mycode suck?

Is it Readable?

Page 6: Your code sucks, let's fix it (CakeFest2012)

Why does mycode suck?

Is it Readable?

Is it Testable?

Page 7: Your code sucks, let's fix it (CakeFest2012)

Why does mycode suck?

Is it Readable?

Is it Testable?

Is it Maintainable?

Page 8: Your code sucks, let's fix it (CakeFest2012)

Why does mycode suck?

Is it Readable?

Is it Testable?

Is it Maintainable?

Is it Reusable?

Page 9: Your code sucks, let's fix it (CakeFest2012)

Does it look like this?<?php $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); } ?>

Page 10: Your code sucks, let's fix it (CakeFest2012)

Does it look like this?<?php $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); } ?>

If Rebecca Black was a developer

Page 11: Your code sucks, let's fix it (CakeFest2012)

How do we fix it?

Page 12: Your code sucks, let's fix it (CakeFest2012)

Object Calisthenics

Page 13: Your code sucks, let's fix it (CakeFest2012)

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.

Page 14: Your code sucks, let's fix it (CakeFest2012)

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.

A variety of simple, often rhythmical, exercises to achieve better OO and code quality

Page 17: Your code sucks, let's fix it (CakeFest2012)

Object Calisthenics+

Readability Tips

Page 20: Your code sucks, let's fix it (CakeFest2012)

Disclaimer:“These are guidelines, not rules”

Page 21: Your code sucks, let's fix it (CakeFest2012)

OC #1“Only one indentation

level per method”

Page 22: Your code sucks, let's fix it (CakeFest2012)

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

Page 23: Your code sucks, let's fix it (CakeFest2012)

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

01

23

Page 24: Your code sucks, let's fix it (CakeFest2012)

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

01

23

Page 25: Your code sucks, let's fix it (CakeFest2012)

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

Page 26: Your code sucks, let's fix it (CakeFest2012)

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

whitespace

Page 27: Your code sucks, let's fix it (CakeFest2012)

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

whitespace

duplicated logic

Page 28: Your code sucks, let's fix it (CakeFest2012)

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

whitespace

duplicated logic

01

2

01 2

Page 29: Your code sucks, let's fix it (CakeFest2012)

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

01

2

01 2

Page 30: Your code sucks, let's fix it (CakeFest2012)

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

01

2

01 2

Page 31: Your code sucks, let's fix it (CakeFest2012)

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

Page 32: Your code sucks, let's fix it (CakeFest2012)

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

I see cheating!

Page 33: Your code sucks, let's fix it (CakeFest2012)

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

I see cheating!

Single line IF, simple operations

Page 34: Your code sucks, let's fix it (CakeFest2012)

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

return earlySingle line IF, simple operations

Page 35: Your code sucks, let's fix it (CakeFest2012)

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

return earlySingle line IF, simple operations

C (native) functions are faster then PHP

Page 36: Your code sucks, let's fix it (CakeFest2012)

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

Page 37: Your code sucks, let's fix it (CakeFest2012)

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

Page 38: Your code sucks, let's fix it (CakeFest2012)

function validateProductList($products) { $invalidProducts = array_filter($products, 'isInvalidProduct'); return (count($invalidProducts) === 0); }

function isInvalidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) > 0); }

Page 39: Your code sucks, let's fix it (CakeFest2012)

function validateProductList($products) { $invalidProducts = array_filter($products, 'isInvalidProduct'); return (count($invalidProducts) === 0); }

function isInvalidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) > 0); }

faster iteration

Page 40: Your code sucks, let's fix it (CakeFest2012)

function validateProductList($products) { $invalidProducts = array_filter($products, 'isInvalidProduct'); return (count($invalidProducts) === 0); }

function isInvalidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) > 0); }

faster iteration

reusable method

Page 41: Your code sucks, let's fix it (CakeFest2012)

function validateProductList($products) { $invalidProducts = array_filter($products, 'isInvalidProduct'); return (count($invalidProducts) === 0); }

function isInvalidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) > 0); }

faster iteration

reusable method

method name matches “true” result

Page 42: Your code sucks, let's fix it (CakeFest2012)

function validateProductList($products) { $invalidProducts = array_filter($products, 'isInvalidProduct'); return (count($invalidProducts) === 0); }

function isInvalidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) > 0); }

faster iteration

reusable method

method name matches “true” result

readable return: zero invalid products

Page 43: Your code sucks, let's fix it (CakeFest2012)

function validateProductList($products) { $invalidProducts = array_filter($products, 'isInvalidProduct'); return (count($invalidProducts) === 0); }

function isInvalidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) > 0); }

faster iteration

reusable method

method name matches “true” result

readable return: zero invalid products

List is more readable the plural

Page 44: Your code sucks, let's fix it (CakeFest2012)

Key Benefits

• Cohesiveness

• Methods does only one thing

• Increases re-use

Page 45: Your code sucks, let's fix it (CakeFest2012)

OC #2“Do not use the ‘else’

keyword”

Page 46: Your code sucks, let's fix it (CakeFest2012)

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

Page 47: Your code sucks, let's fix it (CakeFest2012)

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

Page 48: Your code sucks, let's fix it (CakeFest2012)

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

Page 49: Your code sucks, let's fix it (CakeFest2012)

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

Page 50: Your code sucks, let's fix it (CakeFest2012)

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

Page 51: Your code sucks, let's fix it (CakeFest2012)

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

Page 52: Your code sucks, let's fix it (CakeFest2012)

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

Page 53: Your code sucks, let's fix it (CakeFest2012)

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

intermediate variable

intermediate variable

Page 54: Your code sucks, let's fix it (CakeFest2012)

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

Page 55: Your code sucks, let's fix it (CakeFest2012)

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

Page 56: Your code sucks, let's fix it (CakeFest2012)

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

removed intermediates

Page 57: Your code sucks, let's fix it (CakeFest2012)

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

removed intermediates

early return

Page 58: Your code sucks, let's fix it (CakeFest2012)

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

Separate code into blocks.

Its like using Paragraphs.

removed intermediates

early return

Page 59: Your code sucks, let's fix it (CakeFest2012)

Key Benefits

• Helps avoid code duplication

• Makes code clearer (single path)

Page 60: Your code sucks, let's fix it (CakeFest2012)

OC #3“Wrap all primitive

types and string, if it has behaviour”

Ada p t e d

Page 61: Your code sucks, let's fix it (CakeFest2012)

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

Page 62: Your code sucks, let's fix it (CakeFest2012)

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

Page 63: Your code sucks, let's fix it (CakeFest2012)

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

unclear operation

Page 64: Your code sucks, let's fix it (CakeFest2012)

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

Page 65: Your code sucks, let's fix it (CakeFest2012)

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

This can now encapsulate all animation related operations

Page 66: Your code sucks, let's fix it (CakeFest2012)

Key Benefits

• Type Hinting

• Encapsulation of operations

Page 67: Your code sucks, let's fix it (CakeFest2012)

OC #4“Only one -> per line, if

not getter or fluent”

Ada p t e d

Page 68: Your code sucks, let's fix it (CakeFest2012)

$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

Page 69: Your code sucks, let's fix it (CakeFest2012)

$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');

properties are harder to mock

Source: CodeIgniter

Page 70: Your code sucks, let's fix it (CakeFest2012)

$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');

properties are harder to mock

no whitespace

Source: CodeIgniter

Page 71: Your code sucks, let's fix it (CakeFest2012)

- Underlying encapsulation problem- Hard to debug and test- Hard to read and understand

$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');

properties are harder to mock

no whitespace

Source: CodeIgniter

Page 72: Your code sucks, let's fix it (CakeFest2012)

- Underlying encapsulation problem- Hard to debug and test- Hard to read and understand

$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');

properties are harder to mock

no whitespace

move everything to uri object

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

Source: CodeIgniter

Page 73: Your code sucks, let's fix it (CakeFest2012)

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

Source: Zend Framework App

Source: Symfony 2 Docs.

Page 74: Your code sucks, let's fix it (CakeFest2012)

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

fluent interface

Source: Zend Framework App

Source: Symfony 2 Docs.

Page 75: Your code sucks, let's fix it (CakeFest2012)

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

operator alignment

fluent interface

Source: Zend Framework App

Source: Symfony 2 Docs.

Page 76: Your code sucks, let's fix it (CakeFest2012)

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

operator alignment

fluent interface

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

Source: Zend Framework App

Source: Symfony 2 Docs.

Page 77: Your code sucks, let's fix it (CakeFest2012)

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

operator alignment

fluent interface

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

only getters (no operations)

Source: Zend Framework App

Source: Symfony 2 Docs.

Page 78: Your code sucks, let's fix it (CakeFest2012)

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

operator alignment

fluent interface

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

only getters (no operations)

Source: Zend Framework App

Source: Symfony 2 Docs.

Page 79: Your code sucks, let's fix it (CakeFest2012)

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

operator alignment

fluent interface

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

only getters (no operations)

where did my autocomplete go?

Source: Zend Framework App

Source: Symfony 2 Docs.

Page 80: Your code sucks, let's fix it (CakeFest2012)

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

operator alignment

fluent interface

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

only getters (no operations)

return null?where did my autocomplete go?

Source: Zend Framework App

Source: Symfony 2 Docs.

Page 81: Your code sucks, let's fix it (CakeFest2012)

Key Benefits

• Readability

• Easier Mocking

• Easier to Debug

• Demeter’s Law

Page 82: Your code sucks, let's fix it (CakeFest2012)

OC #5“Do not Abbreviate”

Page 83: Your code sucks, let's fix it (CakeFest2012)

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

Page 84: Your code sucks, let's fix it (CakeFest2012)

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

??

?

?

?

Page 85: Your code sucks, let's fix it (CakeFest2012)

Why?

Page 86: Your code sucks, let's fix it (CakeFest2012)

Why?

Its repeated many times, and i’m lazy.

Page 87: Your code sucks, let's fix it (CakeFest2012)

Why?

Its repeated many times, and i’m lazy.

Underlying Problem!

You need to transfer those operations into a separate class.

Page 88: Your code sucks, let's fix it (CakeFest2012)

Why?

function processResponseHeadersAndDefineOutput($response) { ... }

Page 89: Your code sucks, let's fix it (CakeFest2012)

Why?

This method name is too long to type, and i’m lazy.

function processResponseHeadersAndDefineOutput($response) { ... }

Page 90: Your code sucks, let's fix it (CakeFest2012)

Why?

This method name is too long to type, and i’m lazy.

function processResponseHeadersAndDefineOutput($response) { ... }

more then one responsibility?

Page 91: Your code sucks, let's fix it (CakeFest2012)

function getPage($data) { ... }

function startProcess() { ... }

$tr->process(“site.login”);

Page 92: Your code sucks, let's fix it (CakeFest2012)

function getPage($data) { ... }

get from where?

function startProcess() { ... }

$tr->process(“site.login”);

Page 93: Your code sucks, let's fix it (CakeFest2012)

Use clearer names:fetchPage()downloadPage()

function getPage($data) { ... }

get from where?

function startProcess() { ... }

$tr->process(“site.login”);

Page 94: Your code sucks, let's fix it (CakeFest2012)

Use clearer names:fetchPage()downloadPage()

function getPage($data) { ... }

get from where?

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

$tr->process(“site.login”);

Page 95: Your code sucks, let's fix it (CakeFest2012)

Use clearer names:fetchPage()downloadPage()

function getPage($data) { ... }

get from where?

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

$tr->process(“site.login”);

Table row?

Page 96: Your code sucks, let's fix it (CakeFest2012)

Use clearer names:fetchPage()downloadPage()

function getPage($data) { ... }

get from where?

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

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

Table row?

Page 97: Your code sucks, let's fix it (CakeFest2012)

Key Benefits

• Clearer communication

• Indicates underlying problems

Page 98: Your code sucks, let's fix it (CakeFest2012)

OC #6“Keep your classes

small”

Ada p t e d

Page 99: Your code sucks, let's fix it (CakeFest2012)

100 lines per class15 classes per package

Page 100: Your code sucks, let's fix it (CakeFest2012)

100 lines per class15 classes per package

Increased to include docblocks

Page 101: Your code sucks, let's fix it (CakeFest2012)

100 lines per class15 classes per package

15-20 lines per methodIncreased to include

docblocks

Page 102: Your code sucks, let's fix it (CakeFest2012)

100 lines per class15 classes per package

15-20 lines per methodIncreased to include

docblocks

read this as namespace or folder

Page 103: Your code sucks, let's fix it (CakeFest2012)

Key Benefits

• Single Responsibility

• Objective methods

• Slimmer namespaces

• Less clunky folders

Page 104: Your code sucks, let's fix it (CakeFest2012)

OC #7“Limit the number of

instance variables in a class (max: 2 5)”

Ada p t e d

Page 105: Your code sucks, let's fix it (CakeFest2012)

class MyRegistrationService { protected $userService; protected $passwordService; protected $logger; protected $translator; protected $entityManager; protected $imageCropper; // ... }

Page 106: Your code sucks, let's fix it (CakeFest2012)

class MyRegistrationService { protected $userService; protected $passwordService; protected $logger; protected $translator; protected $entityManager; protected $imageCropper; // ... }

Limit: 5

Page 107: Your code sucks, let's fix it (CakeFest2012)

class MyRegistrationService { protected $userService; protected $passwordService; protected $logger; protected $translator; protected $entityManager; protected $imageCropper; // ... }

Limit: 5

Use and event based system and move this

to listener

All DB interaction should be in userService

Page 108: Your code sucks, let's fix it (CakeFest2012)

Key Benefits

• Shorter dependency list

• Easier Mocking for unit test

Page 109: Your code sucks, let's fix it (CakeFest2012)

OC #8“Use first class

collections”

Page 110: Your code sucks, let's fix it (CakeFest2012)

$collection->getIterator(); $collection->filter(...);

$collection->append(...);

$collection->map(...);

Doctrine: ArrayCollection

Page 111: Your code sucks, let's fix it (CakeFest2012)

Key Benefits

• Implements collection operations

• Uses SPL interfaces

Page 112: Your code sucks, let's fix it (CakeFest2012)

OC #9“Do not use accessors

(getter/setter)”

Dr o p p e d

Page 113: Your code sucks, let's fix it (CakeFest2012)

/** * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. */ class DoctrineTestsModelsCMSCmsUserProxy

extends \Doctrine\Tests\Models\CMS\CmsUser implements \Doctrine\ORM\Proxy\Proxy

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

Page 114: Your code sucks, let's fix it (CakeFest2012)

/** * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. */ class DoctrineTestsModelsCMSCmsUserProxy

extends \Doctrine\Tests\Models\CMS\CmsUser implements \Doctrine\ORM\Proxy\Proxy

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

Example: Doctrine uses getters to inject lazy loading operations

Page 115: Your code sucks, let's fix it (CakeFest2012)

Key Benefits

• Injector operations

• Encapsulation of transformations

Page 116: Your code sucks, let's fix it (CakeFest2012)

OC #10 (bonus!)“Document your code!”

C r e a t e d !

Page 117: Your code sucks, let's fix it (CakeFest2012)

//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, "\n\r");

Page 118: Your code sucks, let's fix it (CakeFest2012)

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

really?

// 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, "\n\r");

Page 119: Your code sucks, let's fix it (CakeFest2012)

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

really?

// 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, "\n\r");

Documenting because i’m doing it wrong in an anusual way

Page 120: Your code sucks, let's fix it (CakeFest2012)

$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

Page 121: Your code sucks, let's fix it (CakeFest2012)

$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

));

What does this do?

Source: Symfony2

Page 122: Your code sucks, let's fix it (CakeFest2012)

$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

));

What does this do?

Add a simple comment://Strips special chars and camel cases to onXxx

Source: Symfony2

Page 123: Your code sucks, let's fix it (CakeFest2012)

$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

));

What does this do?

Add a simple comment://Strips special chars and camel cases to onXxx

Source: Symfony2

Don’t explain bad code, fix it!

Page 124: Your code sucks, let's fix it (CakeFest2012)

/** * 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);

Page 125: Your code sucks, let's fix it (CakeFest2012)

/** * 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

Page 126: Your code sucks, let's fix it (CakeFest2012)

/** * 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

Page 127: Your code sucks, let's fix it (CakeFest2012)

/** * 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

Do a mind dump, then clean it up.

Page 128: Your code sucks, let's fix it (CakeFest2012)

/** * 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

Do a mind dump, then clean it up.

Generate API docsex: docBlox

Page 129: Your code sucks, let's fix it (CakeFest2012)

Key Benefits

• Automatic API documentation

• Transmission of “line of thought”

• Avoids confusion

Page 130: Your code sucks, let's fix it (CakeFest2012)

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!

Page 131: Your code sucks, let's fix it (CakeFest2012)

Questions?

@rdohmshttp://slides.doh.ms

http://doh.msrafael @doh.ms

https://joind.in/6749

https://!xthatcode.comGot bad code?