visit
Say it only once
final class SocialNetwork {
function postStatus(string $newStatus) {
if (!$user->isLogged()) {
throw new Exception('User is not logged');
}
///...
}
function uploadProfilePicture(Picture $newPicture) {
if (!$user->isLogged()) {
throw new Exception('User is not logged');
}
///...
}
function sendMessage(User $recipient, Message $messageSend) {
if (!$user->isLogged()) {
throw new Exception('User is not logged');
}
///...
}
}
<?
final class SocialNetwork {
function postStatus(string $newStatus) {
$this->assertUserIsLogged();
///...
}
function uploadProfilePicture(Picture $newPicture) {
$this->assertUserIsLogged();
///...
}
function sendMessage(User $recipient, Message $messageSend) {
$this->assertUserIsLogged();
///...
}
function assertUserIsLogged() {
if (!$this->user->isLogged()) {
throw new Exception('User is not logged');
//This is just a simplification to show the code smell
//Operations should be defined as objects with preconditions etc.
}
}
}
Photo by
Duplication is the primary enemy of a well-designed system.
Robert Martin
Let's break Demeter's Law.
public class Client {
Address address;
public ZipCode zipCode() {
return address.zipCode();
}
}
public class Address {
private ZipCode zipCode;
public ZipCode zipCode() {
return new ZipCode('CA90210');
}
}
public class Application {
ZipCode zipCode = client.zipCode();
}
public class Client {
Address address;
//client now has to expose its address
public address() {
return address;
}
}
public class Address {
private ZipCode zipCode;
public ZipCode zipCode() {
return new ZipCode('CA90210');
}
}
public class Application {
ZipCode zipCode = client.address().zipCode();
}
Code Smell 08 - Long Chains Of Collaborations
Photo by
Whenever I have to think to understand what the code is doing, I ask myself if I can refactor the code to make that understanding more immediately apparent.
Martin Fowler
Getting things is widespread and safe. But it is a very bad practice.
<?php
final class Window {
public $width;
public $height;
public $children;
public function getWidth() {
return $this->width;
}
public function getArea() {
return $this->width * $this->height;
}
public function getChildren() {
return $this->children;
}
}
<?php
final class Window {
private $width;
private $height;
private $children;
public function width() {
return $this->width;
}
public function area() {
return $this->height * $this->width;
}
public function addChildren($aChild) {
//do not expose internal attributes
return $this->children[] = $aChild;
}
}
Getters coincide in certain scenarios with a true responsibility. It will be reasonable for a window to return its color and it may accidentally store it as color. so a color() method returning the attribute color might be a good solution.
getColor() breaks since it is implementative and has no real counterpart on our .
Most linters can warn us if they detect anemic models with getters and setters.Code Smell 64 - Inappropriate Intimacy
The value of a prototype is in the education it gives you, not in the code itself.
Alan Cooper
This handy operator is a trouble maker.
TL;DR: Don't mix booleans with non-booleans.
The One and Only Software Design Principle
!true // returns false
!false // returns true
isActive = true
!isActive // returns false
age = 54
!age // returns false
array = []
!array // returns false
obj = new Object;
!obj // returns false
!!true // returns true
!!false // returns false
!!isActive // returns true
!!age // returns true
!!array // returns true
!!obj // returns true
!true // returns false
!false // returns true
isActive = true
!isActive // returns false
age = 54
!age // should return type mismatch (or 54 factorial!)
array = []
!array // should return type mismatch
obj = new Object;
!obj // should return type mismatch (what is an obejct negated in a real domain?)
!!true // returns true - it is idempotent
!!false // returns false - it is idempotent
!!isActive // returns true - it is idempotent
!!age // nonsense
!!array // nonsense
!!obj // nonsense
We should detect ! !! usages in non-boolean objects and warn our programmers.
Languages like JavaScript divide their whole universe into true or false values. This decision hides errors when dealing with non booleans.
We should be very strict and keep booleans (and their behavior), far away from non booleans.Code Smell 06 - Too Clever Programmer
Photo by
It is easier to write an incorrect program than understand a correct one.
Alan J Perlis
TL;DR: Do not create anemic objects. Much less with automatic tools.
AnemicClassCreator::create(
'Employee',
[
new AutoGeneratedField('id', '$validators->getIntegerValidator()'),
new AutoGeneratedField('name', '$validators->getStringValidator()'),
new AutoGeneratedField('currentlyWorking', '$validators->getBooleanValidator()')
]);
class Employee extends AutoGeneratedObjectEmployee {
}
//We have magic setters and getters
//getId() , setId(), getName()
//Validation is not implicit
//Class are loaded via an autoloader
$john = new Employee;
$john->setId(1);
$john->setName('John');
$john->setCurrentlyWorking(true);
$john->getName(); //returns 'John'
final class Employee {
private $name;
private $workingStatus;
public function __construct(string $name, WorkingStatus $workingStatus) {
//..
}
public function name(): string {
return $this->name;
//This is not a getter. It is Employee's responsibility to tell us her/his name
}
}
//We have no magic setters or getters
//all methods are real and can be debugged
//Validations are implicit
$john = new Employee('John', new HiredWorkingStatus());
$john->name(); //returns 'John'
Having to write explicitly the code makes us reflect on every piece of data we encapsulate.
Photo by
The best smells are something that's easy to spot and most of time lead you to really interesting problems. Data classes (classes with all data and no behavior) are good examples of this. You look at them and ask yourself what behavior should be in this class.
Martin Fowler