PHP - Object Calisthenics
I am Giorgio Cefaro
I work as Lead Software Engineer in Hootsuite
You can find me at @giorrrgio
Hello!
“
Our code sucks!
let’s fix it!
Calisthenics
/ˌkalɪsˈθɛnɪks/
kalòs (καλός) good
sthénos (σθένος) strength
Calisthenics are
not rules
Code
class SpreadSheet
{
private $headers;
private $rows;
public function printCSV(string $endLine, string $separator): string {
$sheet = $this->headers . $endLine;
foreach ($this->rows as $row) {
foreach ($row as $cell) {
$sheet .= $cell->print() . $separator;
}
$sheet .= $endLine;
}
return $sheet;
}
}
Only one level of
indentation per line
1
Code
class SpreadSheet {
private $headers;
private $rows;
public function printCSV(string $endLine, string $separator): string {
return $this->headers . $endLine . $this->printRows();
}
private function printRows(string $endLine, string $separator): string {
$printed = '';
foreach ($this->rows as $row) {
$printed .= $this->printRow($row, $separator) . $endLine;
}
return $printed;
}
private function printRow(array $row, string $separator): string {
$printed = '';
foreach ($row as $cell) {
$printed .= $cell->print() . $separator;
}
return $printed;
}
}
Code
class SpreadSheet
{
public function isMultitab(): bool {
if (count($this->tabs) > 1) {
return true;
} else {
return false;
}
}
}
Don’t use the ELSE
keyword
2
Code
class SpreadSheet
{
public function isMultitab(): bool {
if (count($this->tabs) > 1) {
return true;
}
return false;
}
}
Code
class Task
{
/** @var int Task priority. */
private $priority;
public function isHighPriority(): bool {
return $this->priority > 9;
}
}
Wrap all primitives
and strings
3
Code
class Task
{
/** @var Priority Task priority. */
private $priority;
public function isHighPriority(): bool {
return $this->priority->isHigh();
}
}
class Priority
{
const PRIORITY_HIGH = 9;
/** @var int priority value. */
private $value;
public function isHigh(): bool {
return $this->value > self::PRIORITY_HIGH;
}
}
Code
class SpreadSheet
{
private $headers;
private $title;
private $rows;
public function __construct(
string $headers,
string $title,
array $rows) {
//[...]
}
public function addRow(Row $row) {
$this->rows[] = $row;
}
}
First class
collections
4
Code
class SpreadSheet
{
private $headers;
private $title;
private $rows;
public function __construct(string $headers, string $title, RowCollection $rows) {
//[...]
}
public function addRow(Row $row) {
$this->rows->add($row);
}
}
class RowCollection implements ArrayAccess
{
private $rows;
public function __construct(array $rows) {
$this->rows = $rows;
}
//[...]
}
Code
class SpreadSheet
{
private $user;
public function userCanUseSpreadsheet() {
return $this->user->getSettings()->canUseSpreadsheet();
}
}
One arrow per line5
● Each unit should have only limited knowledge
about other units: only units "closely" related
to the current unit.
● Each unit should only talk to its friends; don't
talk to strangers.
● Only talk to your immediate friends.
Law of Demeter
Code
class SpreadSheet
{
private $user;
public function userCanUseSpreadsheet() {
return $this->user->canUseSpreadsheet();
}
}
class User
{
private $settings;
public function canUseSpreadsheet() {
return $this->settings->canUseSpreadsheet();
}
}
Don’t abbreviate6
Keep all entities
small
7
No classes with more than
two instance variables
8
“
Decomposing objects from a set of
attributes into a hierarchy of
collaborating objects, leads much
more directly to an effective object
model
https://github.com/TheLadders/object-calisthenics#rule-8-no-classes-with-more-than-two-instance-variables
Code
class User
{
private $firstname;
private $lastname;
public function getFirstname() {
return $this->firstname;
}
public function setFirstname($firstname) {
return $this->firstname = $firstname;
}
public function getLastname() {
return $this->lastname;
}
public function setLastname($lastname) {
return $this->lastname = $lastname;
}
}
No Getters
No Setters
No Properties
9
Code
class User
{
private $firstname;
private $lastname;
private function __constructor(string $firstname, string $lastname) {
$this->firstname = $firstname;
$this->lastname = $lastname;
}
public static function create(string $firstname, string $lastname): self {
return new self($firstname, $lastname);
}
public function toArray(): array {
return [
'firstname' => $firstname,
'lastname' => $lastname
];
}
}
Any questions ?
You can find me at
◉ @giorrrgio
Thanks!

PHP object calisthenics

  • 1.
    PHP - ObjectCalisthenics
  • 2.
    I am GiorgioCefaro I work as Lead Software Engineer in Hootsuite You can find me at @giorrrgio Hello!
  • 3.
  • 4.
  • 5.
  • 6.
    Code class SpreadSheet { private $headers; private$rows; public function printCSV(string $endLine, string $separator): string { $sheet = $this->headers . $endLine; foreach ($this->rows as $row) { foreach ($row as $cell) { $sheet .= $cell->print() . $separator; } $sheet .= $endLine; } return $sheet; } }
  • 7.
    Only one levelof indentation per line 1
  • 8.
    Code class SpreadSheet { private$headers; private $rows; public function printCSV(string $endLine, string $separator): string { return $this->headers . $endLine . $this->printRows(); } private function printRows(string $endLine, string $separator): string { $printed = ''; foreach ($this->rows as $row) { $printed .= $this->printRow($row, $separator) . $endLine; } return $printed; } private function printRow(array $row, string $separator): string { $printed = ''; foreach ($row as $cell) { $printed .= $cell->print() . $separator; } return $printed; } }
  • 9.
    Code class SpreadSheet { public functionisMultitab(): bool { if (count($this->tabs) > 1) { return true; } else { return false; } } }
  • 10.
    Don’t use theELSE keyword 2
  • 11.
    Code class SpreadSheet { public functionisMultitab(): bool { if (count($this->tabs) > 1) { return true; } return false; } }
  • 12.
    Code class Task { /** @varint Task priority. */ private $priority; public function isHighPriority(): bool { return $this->priority > 9; } }
  • 13.
  • 14.
    Code class Task { /** @varPriority Task priority. */ private $priority; public function isHighPriority(): bool { return $this->priority->isHigh(); } } class Priority { const PRIORITY_HIGH = 9; /** @var int priority value. */ private $value; public function isHigh(): bool { return $this->value > self::PRIORITY_HIGH; } }
  • 15.
    Code class SpreadSheet { private $headers; private$title; private $rows; public function __construct( string $headers, string $title, array $rows) { //[...] } public function addRow(Row $row) { $this->rows[] = $row; } }
  • 16.
  • 17.
    Code class SpreadSheet { private $headers; private$title; private $rows; public function __construct(string $headers, string $title, RowCollection $rows) { //[...] } public function addRow(Row $row) { $this->rows->add($row); } } class RowCollection implements ArrayAccess { private $rows; public function __construct(array $rows) { $this->rows = $rows; } //[...] }
  • 18.
    Code class SpreadSheet { private $user; publicfunction userCanUseSpreadsheet() { return $this->user->getSettings()->canUseSpreadsheet(); } }
  • 19.
  • 20.
    ● Each unitshould have only limited knowledge about other units: only units "closely" related to the current unit. ● Each unit should only talk to its friends; don't talk to strangers. ● Only talk to your immediate friends. Law of Demeter
  • 21.
    Code class SpreadSheet { private $user; publicfunction userCanUseSpreadsheet() { return $this->user->canUseSpreadsheet(); } } class User { private $settings; public function canUseSpreadsheet() { return $this->settings->canUseSpreadsheet(); } }
  • 22.
  • 23.
  • 24.
    No classes withmore than two instance variables 8
  • 25.
    “ Decomposing objects froma set of attributes into a hierarchy of collaborating objects, leads much more directly to an effective object model https://github.com/TheLadders/object-calisthenics#rule-8-no-classes-with-more-than-two-instance-variables
  • 26.
    Code class User { private $firstname; private$lastname; public function getFirstname() { return $this->firstname; } public function setFirstname($firstname) { return $this->firstname = $firstname; } public function getLastname() { return $this->lastname; } public function setLastname($lastname) { return $this->lastname = $lastname; } }
  • 27.
  • 28.
    Code class User { private $firstname; private$lastname; private function __constructor(string $firstname, string $lastname) { $this->firstname = $firstname; $this->lastname = $lastname; } public static function create(string $firstname, string $lastname): self { return new self($firstname, $lastname); } public function toArray(): array { return [ 'firstname' => $firstname, 'lastname' => $lastname ]; } }
  • 29.
    Any questions ? Youcan find me at ◉ @giorrrgio Thanks!