Object Calisthenics Applied to PHP

  • 9,093 views
Uploaded on

Presentation made at GTA meetup in 2012-02-07. …

Presentation made at GTA meetup in 2012-02-07.

Object Calisthenics is a set of exercise rules to reach better code, maintainable, testable and readable.

More in: Technology , Business
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • @devnull3 Sorry, but when I published this talk, PDF and PPT version were all wrong, leading to what I consider non-comprehensible format.
    That's why I uploaded the original format I created (.key).
    Are you sure you want to
    Your message goes here
  • @DominicWatson Hi! During my talk explanation, I detail between slide 16 and 17 that you throw an exception. This means that having a foreach for messageList is irrelevant, and you could grab a single message right away.
    Maybe I shouldn't have updated the variable inside of initial foreach, but the idea was to reuse the same message variable.

    Hope you liked the presentation! =)
    Are you sure you want to
    Your message goes here
  • Slide 17 changes variable $messageList to $message which confused me. Isn't it still a list of messages rather than a singular one? I'm guessing it's a mistake :P
    Are you sure you want to
    Your message goes here
  • pdf please. dot key files are apple only :(
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
9,093
On Slideshare
0
From Embeds
0
Number of Embeds
7

Actions

Shares
Downloads
124
Comments
4
Likes
30

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

Transcript

  • 1. Object Calisthenics Applied to PHP
  • 2. Object CalisthenicsThe speaker @guilhermeblanco http://github.com/guilhermeblanco
  • 3. Object CalisthenicsAgenda‣ Motivation‣ Rules‣ Application
  • 4. Object CalisthenicsErm... WTH is Object Calisthenics?‣ Object Calisthenics
  • 5. Object CalisthenicsErm... WTH is Object Calisthenics?‣ Object Calisthenics Term derived from greek, “exercise”, under the context of gymnastics.
  • 6. Object CalisthenicsErm... WTH is Object Calisthenics?‣ Jeff Bay in The ThoughtWorks Anthology [1] coined the term Object Calisthenics in computers, as a group of exercises to Object Oriented programming.[1] The ThoughtWorks Anthology: Essays on Software Technology and Innovation
  • 7. Object CalisthenicsMotivation‣ Readable Code‣ Comprehensible‣ Testable‣ Maintainable Learning about good code practices at Object CaIisthenics talk of @guilhermeblanco on @gtaphp
  • 8. Object CalisthenicsRules‣ Nine (9) rules “very” simple...
  • 9. Object CalisthenicsRule 1: Only one indentation level per method‣ Only one indentation level per method
  • 10. Object CalisthenicsRule 1: Only one indentation level per methodpublic function validateForm($filters=, $validators=, $options=){ $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options); $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;}
  • 11. Object CalisthenicsRule 1: Only one indentation level per methodpublic function validateForm($filters=, $validators=, $options=){ $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim()); 0 if ($input->hasInvalid() || $input->hasMissing()) { 1 foreach ($input->getMessages() as $field => $messageList) { 2 foreach(strpos($message, "empty")) { ($messageList as $message) { 3 if throw new Tss_FormException( 4 "The field {$field} cannot be empty!", 3, javascript:history.back(); ); } else { throw new Tss_FormException( "{$message}", 3, javascript:history.back(); ); } } } } return $input;}
  • 12. Object CalisthenicsRule 1: Only one indentation level per methodpublic function validateForm($filters=, $validators=, $options=){ $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim()); 0 if ($input->hasInvalid() || $input->hasMissing()) { 1 foreach ($input->getMessages() as $field => $messageList) { 2 foreach(strpos($message, "empty")) { ($messageList as $message) { 3 if throw new Tss_FormException( 4 "The field {$field} cannot be empty!", 3, javascript:history.back(); ); } else { throw new Tss_FormException( "{$message}", 3, javascript:history.back(); ); } } } } return $input;}
  • 13. Object CalisthenicsRule 1: Only one indentation level per methodpublic function validateForm($filters = array(), $validators = array(), $options = null){ $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options); $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(); ); } } }}
  • 14. Object CalisthenicsRule 1: Only one indentation level per methodpublic function validateForm($filters = array(), $validators = array(), $options = null){ $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim()); if ( ! ($input->hasInvalid() || $input->hasMissing())) { return $input; }0 foreach ($input->getMessages() as $field => $messageList) { 1 foreach(strpos($message, "empty")) { ($messageList as $message) { 2 if throw new Tss_FormException( 3 "The field {$field} cannot be empty!", 3, javascript:history.back(); ); } else { throw new Tss_FormException( "{$message}", 3, javascript:history.back(); ); } } }}
  • 15. Object CalisthenicsRule 1: Only one indentation level per methodpublic function validateForm($filters = array(), $validators = array(), $options = null){ $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options); $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(); ); } }}
  • 16. Object CalisthenicsRule 1: Only one indentation level per methodpublic function validateForm($filters = array(), $validators = array(), $options = null){ $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim()); if ( ! ($input->hasInvalid() || $input->hasMissing())) { return $input; }0 foreach ($input->getMessages() as $field => $messageList) { 1 foreach ($messageList as $message) { "empty") === false) 2 $errorMessage = (strpos($message,be empty!" ? "The field {$field} cannot : "{$message}"; throw new Tss_FormException( $errorMessage, 3, javascript:history.back(); ); } }}
  • 17. Object CalisthenicsRule 1: Only one indentation level per methodpublic function validatePost($filters = array(), $validators = array(), $options = null){ $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim()); if ( ! ($input->hasInvalid() || $input->hasMissing())) { return $input; } foreach ($input->getMessages() as $field => $message) { $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(); ); }}
  • 18. Object CalisthenicsRule 1: Only one indentation level per methodpublic function validatePost($filters = array(), $validators = array(), $options = null){ $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim()); if ( ! ($input->hasInvalid() || $input->hasMissing())) { return $input; } foreach ($input->getMessages() as $field => $message) { $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(); ); }}
  • 19. Object CalisthenicsRule 2: Do not use the “else” keyword‣ Never use the “else” keyword
  • 20. Object CalisthenicsRule 2: Do not use the “else” keywordfunction 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); }}
  • 21. Object CalisthenicsRule 2: Do not use the “else” keywordfunction 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); }}
  • 22. Object CalisthenicsRule 2: Do not use the “else” keywordfunction 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); }}
  • 23. Object CalisthenicsRule 2: Do not use the “else” keywordfunction 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);}
  • 24. Object CalisthenicsRule 3: Wrap primitive types and strings‣ Wrap all primitive types and strings
  • 25. Object CalisthenicsRule 3: Wrap primitive types and strings‣ This rule cannot be completely ported to PHP, because the language does not perform well with an entirely Object Oriented code with a huge amount of instances
  • 26. Object CalisthenicsRule 3: Wrap primitive types and strings‣ But... if the variable of primitive type has a behavior, it must be encapsulated
  • 27. Object CalisthenicsRule 3: Wrap primitive types and stringsclass UIComponent{! // ...! public function repaint($animate = true) { // ... }}// ...$component->repaint(false);
  • 28. Object CalisthenicsRule 3: Wrap primitive types and stringsclass UIComponent{! // ...! public function repaint($animate = true) { // ... }}// ...$component->repaint(false);
  • 29. Object CalisthenicsRule 3: Wrap primitive types and stringsclass UIComponent{! // ...! public function repaint(Animate $animate) { // ... }}class Animate{ public $animate; public function __construct($animate = true) { $this->animate = $animate; }}// ...$component->repaint(new Animate(false));
  • 30. Object CalisthenicsRule 4: Only one dot per line‣ Only one dot (arrow for PHP) per line
  • 31. Object CalisthenicsRule 4: Only one dot per line‣ Not applicable to PHP...
  • 32. Object CalisthenicsRule 4: Only one dot per line‣ ...but multiple nested calls... ‣ tend to expose an encapsulation problem ‣ increase difficulty to debug and exception handling ‣ do not represent an atomic action
  • 33. Object CalisthenicsRule 4: Only one dot per line‣ We could adapt to the language, contemplating...
  • 34. Object CalisthenicsRule 4: Only one dot per line‣ A chain of different objects, but only if the execution only includes getters and setters $user->getLocationPoint()->getCountry()->getName();
  • 35. Object CalisthenicsRule 4: Only one dot per line‣ A chain of a unique object, through the usage of a fluent interface $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower());
  • 36. Object CalisthenicsRule 5: Do not abbreviate‣ Do not abbreviate
  • 37. Object CalisthenicsRule 5: Do not abbreviate
  • 38. Object CalisthenicsRule 5: Do not abbreviate‣ Think about it... why do you want to abbreviate?
  • 39. Object CalisthenicsRule 5: Do not abbreviate‣ Think about it... why do you want to abbreviate? ‣ Write the same name repeatedly?
  • 40. Object CalisthenicsRule 5: Do not abbreviate‣ Think about it... why do you want to abbreviate? ‣ Write the same name repeatedly? ‣ Then your method is reused multiple times, signalling a code duplication.
  • 41. Object CalisthenicsRule 5: Do not abbreviate‣ Think about it... why do you want to abbreviate? ‣ Write the same name repeatedly? ‣ Then your method is reused multiple times, signalling a code duplication. ‣ Method name too long?
  • 42. Object CalisthenicsRule 5: Do not abbreviate‣ Think about it... why do you want to abbreviate? ‣ Write the same name repeatedly? ‣ Then your method is reused multiple times, signalling a code duplication. ‣ Method name too long? ‣ Maybe your class has multiple responsibilities or it is missing a helper class (bad architecture).
  • 43. Object CalisthenicsRule 5: Do not abbreviate‣ Think about it... why do you want to abbreviate? ‣ Write the same name repeatedly? ‣ Then your method is reused multiple times, signalling a code duplication. ‣ Method name too long? ‣ Maybe your class has multiple responsibilities or it is missing a helper class (bad architecture).
  • 44. Object CalisthenicsRule 6: Keep your entities small‣ Keep your entities small
  • 45. Object CalisthenicsRule 6: Keep your entities small‣ Original rule: 50 lines per class
  • 46. Object CalisthenicsRule 6: Keep your entities small‣ Adapted to PHP: 100 lines per class and no more than 15 classes per package.‣ The change is necessary because of the lack of rule for documentation, which can easily occupy up to 50% of the lines of a class.
  • 47. Object CalisthenicsRule 7: Do not create classes with more than 2 instance variables‣ Do not create classes with more than 2 instance variables
  • 48. Object CalisthenicsRule 7: Do not create classes with more than 2 instance variables‣ Objective: ‣ Low cohesion ‣ Better encapsulation
  • 49. Object CalisthenicsRule 7: Do not create classes with more than 2 instance variables‣ The original rule points to 2 instance variables‣ To PHP, the suggestion is no more than 5 variables
  • 50. Object CalisthenicsRule 8: Use first class collections‣ Use first class collections
  • 51. Object CalisthenicsRule 8: Use first class collections‣ The rule is simple: Any class that contains a collection (array to PHP), cannot contain any other properties
  • 52. Object CalisthenicsRule 8: Use first class collections‣ Objectives: ‣ Specific behaviors have a good place to stay ‣ Filtering, combining, mapping, ...
  • 53. Object CalisthenicsRule 8: Use first class collections‣ DoctrineCommonCollectionsArrayCollection ‣ Countable ‣ IteratorAggregate (inherits Traversable) ‣ ArrayAccess
  • 54. Object CalisthenicsRule 9: Do not create getter/setter methods to properties‣ Do not create getter/setter methods to properties
  • 55. Object CalisthenicsRule 9: Do not create getter/setter methods to properties‣ Non-applicable to PHP due to language’s nature
  • 56. Object CalisthenicsRule 9: Do not create getter/setter methods to properties/** * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. */class ApplicationCoreDomainUserModelUserProxy! extends ApplicationCoreDomainUserModelUser! implements DoctrineORMProxyProxy{ // ... public function getId() { $this->__load(); return parent::getId(); } public function setId($id) { $this->__load(); return parent::setId($id); } // ...}
  • 57. Object Calisthenics“Rule 10”: Document your code!‣ Document your code!!!!!!!!!!!!!!!!!
  • 58. Object CalisthenicsThat’s all folks!‣ Questions? @guilhermeblanco http://github.com/guilhermeblanco