Your code sucks, let's fix it (CakeFest2012)
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

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

on

  • 6,606 views

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, ...

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.

Statistics

Views

Total Views
6,606
Views on SlideShare
6,362
Embed Views
244

Actions

Likes
16
Downloads
184
Comments
2

15 Embeds 244

http://192.168.1.5 148
https://twitter.com 23
http://www.amkesolution.com 21
http://nativekc.stanleydevelopment.com 15
http://www.techgig.com 9
http://test1.peoplebrowsr.com 9
http://blog.jetbrains.com 5
http://www.php-talks.com 3
http://kred.com 3
https://si0.twimg.com 2
https://twimg0-a.akamaihd.net 2
http://www.twylah.com 1
http://www.conferize.com 1
http://www.scoop.it 1
http://twitter.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

  • 1. Your code sucks, let’s !x it! Object Calisthenics and Code readability Rafael Dohms @rdohms
  • 2. Rafael Dohms photo credit: Eli White @rdohms Evangelist, Speaker and Contributor.Developer at WEBclusive.Enabler at AmsterdamPHP.
  • 3. What’s the talk about?• Why does my code suck?• How can we fix it?
  • 4. Why does mycode suck?
  • 5. Is it Readable?Why does mycode suck?
  • 6. Is it Readable?Why does mycode suck? Is it Testable?
  • 7. Is it Maintainable? Is it Readable? Why does my code suck? Is it Testable?
  • 8. Is it Maintainable? Is it Readable? Why does my code suck? Is it Reusable? Is it Testable?
  • 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 allnews WHERE id=$id-$count")){ while($i=0;$i<6;$i++){ if($small==$best[i])$best[i]=mysql_query("SELECT id FROMallnews WHERE id=$id-$count");}}}} while($i=0;$i<6;$i++) echo <a href="news-page.php?id=.$best[i]."><div class="box .mysql_query("SELECTtype 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("SELECTimage1 FROM allnews WHERE id=$best[i]").);"></div></div></a>; mysql_close($list);}?>
  • 10. <?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 FROMallnews WHERE id=$id-$count");}}}} while($i=0;$i<6;$i++) echo <a href="news-page.php?id=.$best[i]."><div class="box .mysql_query("SELECTtype 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("SELECTimage1 FROM allnews WHERE id=$best[i]").);"></div></div></a>; mysql_close($list);}?>
  • 11. How do we fix it?
  • 12. 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
  • 14. 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
  • 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 BayObject Calisthenics
  • 16. “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 BayObject Calisthenics Important: PHP != JAVA Adaptations will be done
  • 17. Object Calisthenics + Readability Tips
  • 18. “You need to write code that minimizes the time it wouldObject Calisthenics take someone else to understand it—even if that someone else is you.” + -- Dustin Boswell and Trevor Foucher Readability Tips
  • 19. “You need to write code that minimizes the time it wouldObject 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. Disclaimer:“These are guidelines, not rules”
  • 21. OC #1“Only one indentation level per method”
  • 22. 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;}
  • 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; 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;}
  • 25. 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;}
  • 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; $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; 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;}
  • 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 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;}
  • 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($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;}
  • 31. 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);}
  • 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;}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){ $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);}
  • 39. function validateProductList($products) iteration faster{ $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);}
  • 40. function validateProductList($products) iteration faster{ $invalidProducts = array_filter($products, isInvalidProduct); return (count($invalidProducts) === 0);} reusable methodfunction isInvalidProduct($rawProduct){ $requiredFields = array(price, name, description, type); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) > 0);}
  • 41. function validateProductList($products) iteration faster{ $invalidProducts = array_filter($products, isInvalidProduct); return (count($invalidProducts) === 0);} reusable methodfunction isInvalidProduct($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) iteration faster{ $invalidProducts = array_filter($products, isInvalidProduct); return (count($invalidProducts) === 0);} readable return: zero invalid products reusable methodfunction isInvalidProduct($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 pluralfunction validateProductList($products) iteration faster{ $invalidProducts = array_filter($products, isInvalidProduct); return (count($invalidProducts) === 0);} readable return: zero invalid products reusable methodfunction isInvalidProduct($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• Cohesiveness• Methods does only one thing• 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• Makes code clearer (single path)
  • 60. Ad ap OC #3 te d “Wrap all primitivetypes and string, if it has 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 te d OC #4“Only one -> per line, if not getter or fluent”
  • 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• 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?
  • 86. Why?Its repeated many times, and i’m lazy.
  • 87. Why?Its repeated many times, and i’m lazy. Underlying Problem! You need to transfer those operations into a separate class.
  • 88. Why?function processResponseHeadersAndDefineOutput($response) { ... }
  • 89. Why?function processResponseHeadersAndDefineOutput($response) { ... } This method name is too long to type, and i’m lazy.
  • 90. Why? 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• Indicates underlying problems
  • 98. Ad ap te d OC #6“Keep your classes small”
  • 99. 100 lines per class15 classes per package
  • 100. Increased to include docblocks 100 lines per class15 classes per package
  • 101. Increased to include docblocks 15-20 lines per method 100 lines per class15 classes per package
  • 102. Increased to include docblocks 15-20 lines per method 100 lines per class15 classes per package read this as namespace or folder
  • 103. Key Benefits• Single Responsibility• Objective methods• Slimmer namespaces• Less clunky folders
  • 104. Ad ap OC #7 te d “Limit the number ofinstance variables in a class (max: 2 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 not 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(); }
  • 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(); }
  • 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 voidif ($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 voidif ($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 voidif ($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 ex: docBlox 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. @rdohms http://doh.msrafael @doh.ms http://slides.doh.ms Questions? https://joind.in/6749 Got bad code? https://!xthatcode.com
  • 132. The ThoughtWorks Anthology http://goo.gl/OcSNx The Art of Readable Code http://goo.gl/unrijDISCLAIMER: This talk re-uses some of the examples used by Guilherme Blanco in hisoriginal Object Calisthenic talk. These principles were studied and applied by us while weworked together in previous jobs. The result taught us all a lesson we really want to spreadto other developers.