Infinite code smells!
We see several symptoms and situations that make us doubt the quality of our development.
Let's look at some possible solutions.
Most of these smells are just hints of something that might be wrong. They are not rigid rules.
Previous Parts
Let's continue...
Code Smell 66 - Shotgun Surgery
Say it only once
Problems
- Bad Responsibilities Assignments
- Code Duplication
- Maintainability
- Single Responsibility Violation.
- Copy-pasted code.
Solutions
- Refactor
Sample Code
Wrong
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');
}
///...
}
}
Right
<?
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.
}
}
}
Detection
Some modern linters can detect repeated patterns (not just repeated code) and also while performing our code reviews we can easily detect this problem and ask for a refactor.
Tags
- Code Duplication
Conclusion
Adding a new feature should be straightforward it our model maps 1:1 to real world and our responsibilities are in the correct places. We should be alert for small changes spanning in several classes.
More info
Credits
Photo by
Duplication is the primary enemy of a well-designed system.
Robert Martin
Code Smell 67 - Middle Man
Let's break Demeter's Law.
Problems
- Unnecessary Indirection
- Empty Classes
- Readability
Solutions
- Remove Middle man.
Sample Code
Wrong
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();
}
Right
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();
}
Detection
Same as its opposite smell, We can detect this small using parsing trees.
Tags
- Coupling
- Declarative
- Readability
Conclusion
This is exactly the opposite to Message Chain. We make explicit the message chain.
Relations
Code Smell 08 - Long Chains Of Collaborations
More info
Credits
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
Code Smell 68 - Getters
Getting things is widespread and safe. But it is a very bad practice.
Problems
- Naming
- Information Hiding
- Coupling
- Encapsulation Violation
- Mutability
- Anemic Models
Solutions
- Avoid Getters
- Use domain names instead
- Protect your implementation decisions.
Sample Code
Wrong
<?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;
}
}
Right
<?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;
}
}
Detection
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 bijection since it is implementative and has no real counterpart on our mappers.
Most linters can warn us if they detect anemic models with getters and setters.
Tags
- Information Hiding
Conclusion
Getters and Setters are a bad established practice. Instead of focusing on object behavior (essential), we are desperate to know object guts (accidental) and violate their implementation.
Relations
Code Smell 64 - Inappropriate Intimacy
More info
Credits
Photo by Vidar Nordli-Mathisen on Unsplash
The value of a prototype is in the education it gives you, not in the code itself.
Alan Cooper
Code Smell 69 - Big Bang (JavaScript Ridiculous Castings)
This handy operator is a trouble maker.
TL;DR: Don't mix booleans with non-booleans.
Problems
- Not Declarative Code
- Hard to debug
- Magic Castings
- Accidental Complexity
Solutions
- Be Explicit
- Don't mix Booleans with non-booleans.
- Fail Fast
- Be Smarter than your compiler.
- Stay loyal to the bijection.
The One and Only Software Design Principle
Sample Code
Wrong
!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
Right
!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
Detection
Since this is a "feature" in some languages it would be hard to test. We can set programming policies or choose more strict languages.
We should detect ! !! usages in non-boolean objects and warn our programmers.
Tags
- Casting
- Coercion
- Javascript
Conclusion
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.
Relations
Code Smell 24 - Boolean Coercions
Code Smell 06 - Too Clever Programmer
More info
Credits
Photo by
It is easier to write an incorrect program than understand a correct one.
Alan J Perlis
Code Smell 70 - Anemic Model Generators
TL;DR: Do not create anemic objects. Much less with automatic tools.
Problems
- Anemic Objects
- Coupling to Implementation
- Harder to Debug
Solutions
- Create your objects manually.
- Focus on essential behavior instead of accidental data storage.
Sample Code
Wrong
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'
Right
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'
Detection
Often, anemic models are generated with meta-programming.
We need to track these magic code generators.
Tags
- Anemic
Conclusion
Code Wizards, Meta-programming, and anemic models are all code smells.
We need to avoid these dark techniques.
Having to write explicitly the code makes us reflect on every piece of data we encapsulate.
Relations
More info
Credits
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
And that’s all for now…
Next article will explain 5 more code smells!