1st CI&T Lightning Talks: Writing better code with Object Calisthenics

999 views

Published on

"We’ve all seen poorly written code that’s hard to understand, test, and maintain. Object-oriented programming promised to save us from our old procedural code, allowing us to write software incrementally, reusing as we go along. But sometimes it seems like we’re just chasing down the same old complex, coupled designs in Java that we had in C.

Good object-oriented design is hard to learn. Transitioning from procedural development to
object-oriented design requires a major shift in thinking that is more difficult than it seems.
Many developers assume they’re doing a good job with OO design, when in reality they’re
unconsciously stuck in old habits that are hard to break. It doesn’t help that many examples and best practices (even Sun’s code in the JDK) encourage poor OO design in the name of
performance or simple weight of history." -- Jeff Bay

Here are some key rules which are going to improve your OO designing skills and make you a better programmer. Some are harder to implement but will open your mind for new ways of coding. Happy coding! :)

Published in: Technology
  • Be the first to comment

1st CI&T Lightning Talks: Writing better code with Object Calisthenics

  1. 1. Writing better code with Object Calisthenics Lucas Arruda lucas@ciandt.com github.com/larruda Adapted from Jeff Bay’s paper from “The ThoughtWorks Anthology” and Guilherme Blanco’s presentation.
  2. 2. Calis...what?!
  3. 3. 7 code qualities cohesion loose coupling no redundancy encapsulation testability readability focus
  4. 4. 9 rules of thumb
  5. 5. 1. One level of indentation per method class Board { ... String board() { StringBuffer buf = new StringBuffer(); for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) buf.append(data[i][j]); buf.append(“n”); } return buf.toString(); } }
  6. 6. 1. One level of indentation per method class Board { ... String Board() { StringBuffer buf = new StringBuffer(); collectRows(buf); return buf.toString(); } void collectRows(StringBuffer buf) { for (int i = 0; i < 10; i++) collectRow(buf, i); } void collectRow(StringBuffer buf, int row) { for (int i = 0; i < 10; i++) Buf.append(data[row][i]); buf.append(“n”); } }
  7. 7. 1. One level of indentation per method public function validateForm($filters='', $validators='', $options='') { $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim()); if ($input->hasInvalid() || $input->hasMissing()) { foreach ($input->getMessages() as $field => $messageList) { foreach ($messageList as $message) { if (strpos($message, "empty")) { throw new Tss_FormException( "The field {$field} cannot be empty!", 3, 'javascript:history.back();' ); } else { throw new Tss_FormException( "{$message}", 3, 'javascript:history.back();' ); } } } } return $input; }
  8. 8. 1. One level of indentation per method public function validateForm($filters='', $validators='', $options='') { $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim()); if ( ! ($input->hasInvalid() || $input->hasMissing())) { return $input; } foreach ($input->getMessages() as $field => $messageList) { foreach ($messageList as $message) { if (strpos($message, "empty")) { throw new Tss_FormException( "The field {$field} cannot be empty!", 3, 'javascript:history.back();' ); } else { throw new Tss_FormException( "{$message}", 3, 'javascript:history.back();' ); } } } }
  9. 9. 1. One level of indentation per method public function validateForm($filters='', $validators='', ... { $data = $_POST; $input = new Zend_Filter_Input ($filters, $validators, $data); $input->setDefaultEscapeFilter( new Zend_Filter_StringTrim ()); if ( ! ($input->hasInvalid() || $input->hasMissing())) { return $input; } foreach ($input->getMessages() as $field => $messageList) { foreach ($messageList as $message) { $errorMessage = (strpos($message, "empty") === false) ? "The field {$field} cannot be empty!" : "{$message}"; throw new Tss_FormException ( $errorMessage, 3, 'javascript:history.back();' ); } } }
  10. 10. 1. One level of indentation per method public function validateForm($filters='', $validators='', ... { $data = $_POST; $input = new Zend_Filter_Input ($filters, $validators, $data); $input->setDefaultEscapeFilter( new Zend_Filter_StringTrim ()); if ( ! ($input->hasInvalid() || $input->hasMissing())) { return $input; } foreach ($input->getMessages() as $field => $messageList) { $messageKey = key($message); $message = $message[$messageKey]; $errorMessage = (strpos($message, "empty") === false) ? "The field {$field} cannot be empty!" : "{$message}"; throw new Tss_FormException ( $errorMessage, 3, 'javascript:history.back();' ); } }
  11. 11. 2. Don’t use the ELSE keyword if (status == DONE) { doSomething(); } else { … }
  12. 12. YOU MUST BE JOKING!!!
  13. 13. 2. Don’t use the ELSE keyword class Board { ... String board(StringBuffer buf) { if (buf.length()) { return buf.toString(); } else { collectRows(buf); return buf.toString(); } } ... }
  14. 14. 2. Don’t use the ELSE keyword class Board { ... String board(StringBuffer buf) { if (!buf.length()) { collectRows(buf); } return buf.toString(); } ... }
  15. 15. 2. Don’t use the ELSE keyword function login() { $login = $this->input->post('email', true); $password = $this->input->post('password', true); $reference = $this->input->post('reference', true); if ($this->clients_model->login($login, $password)) { redirect( $reference); } else { $this->session->set_flashdata( 'error' , 'User or password invalid.' ); $this->session->set_flashdata( 'reference', $reference); redirect( 'clients'); } }
  16. 16. 2. Don’t use the ELSE keyword function login() { $login = $this->input->post('email', true); $password = $this->input->post('password', true); $reference = $this->input->post('reference', true); if (!$this->clients_model->login($login, $password)) { $this->session->set_flashdata( 'error' , 'User or password invalid.' ); $this->session->set_flashdata( 'reference', $reference); $reference = 'clients'; } redirect($reference); }
  17. 17. 3. Wrap all primitives and Strings
  18. 18. If the primitive type has a behavior it should be encapsulated
  19. 19. 3. Wrap all primitives and Strings class UIComponent { // ... public function repaint($animate = true) { // … } // ... $component->repaint(false); }
  20. 20. 3. Wrap all primitives and Strings class UIComponent { // ... public function repaint(Animate $animate) { // ... } } class Animate { public $animate; public function __construct($animate = true) { $this->animate = $animate; } } // ... $component->repaint(new Animate(false));
  21. 21. 4. First class collections Any class that contains a collection/array should not contain any other member variables Java Collections follow this rule
  22. 22. 4. First class collections Transversable Countable Iterator Filtering Mapping Combining
  23. 23. 5. One dot/arrow per line Sign of misplaced responsibilities The Law of Demeter “Only talk to your friends”
  24. 24. 5. One dot/arrow per line class Board { ... class Piece { ... String representation; } class Location { ... Piece current; } String boardRepresentation () { StringBuffer buf = new StringBuffer(); for (Location l: squares()) buf .append(l.current.representation .substring(0, 1)); return buf.toString(); } }
  25. 25. 5. One dot/arrow per line class Board { ... class Piece { ... private String representation; String character() { return representation.substring(0, 1); } void addTo(StringBuffer buf) { buf.append(character()); } } class Location { ... private Piece current; void addTo(StringBuffer buf) { current.addTo(buf); } } String boardRepresentation() { StringBuffer buf = new StringBuffer(); for (Location l: squares()) l.addTo(buf); return buf.toString(); } }
  26. 26. 6. Don’t abbreviate Are you writing the same name repeatedly? method being used multiple times sign of code duplication Is the method name too long? class with multiple responsibilities missing additional class redundancy
  27. 27. 7. Keep all entities small Java No class over 50 lines No packages over 10 files PHP No class over 100 lines No packages over 15 files Packages, like classes, should be cohesive and have a purpose
  28. 28. 8. No classes with more than two/five instance variables Improve cohesion Split on more entities
  29. 29. 9. No getters/setters/properties Improve encapsulation “Tell, don’t ask”
  30. 30. Q&A ciandt.com http://www.cs.helsinki.fi/u/luontola/tdd-2009/ext/ObjectCalisthenics.pdf http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php https://www.youtube.com/watch?v=ftBUHrxfOcc
  31. 31. THANKS FOR BEING HERE!
  32. 32. ciandt.com lunascar@gmail.com @lunascarruda google.com/+LucasArruda fb.com/lucasnarruda linkedin.com/in/larruda github.com/larruda coderbits.com/larruda drupal.org/user/1009514

×