visit
Most of these smells are just hints of something that might be wrong. Therefore, they are not required to be fixed per se… (You should look into it, though.)
You can find all the previous code smells (Part I - XLIII) here.
You should not define too much behavior together
TL;DR: Split your interfaces
interface Animal {
void eat();
void sleep();
void makeSound();
// This protocol should be common to all animals
}
class Dog implements Animal {
public void eat() { }
public void sleep() { }
public void makeSound() { }
}
class Fish implements Animal
public void eat() { }
public void sleep() {
throw new UnsupportedOperationException("I do not sleep");
}
public void makeSound() {
throw new UnsupportedOperationException("I cannot make sounds");
}
}
class Bullfrog implements Animal
public void eat() { }
public void sleep() {
throw new UnsupportedOperationException("I do not sleep");
}
public void makeSound() { }
}
interface Animal {
void move();
void reproduce();
}
// You can even break these two responsibilities
class Dog implements Animal {
public void move() { }
public void reproduce() { }
}
class Fish implements Animal {
public void move() { }
public void reproduce() { }
}
class Bullfrog implements Animal {
public void move() { }
public void reproduce() { }
}
Code Smell 61 - Coupling to Classes
Code Smell 135 - Interfaces With just One Realization
Code Smells are my opinion.
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
Software Engineering Great Quotes
You create empty methods instead of failing
TL;DR: Don't fill in methods to comply
class MerchantProcessor {
processPayment(amount) {
// no default implementation
}
}
class MockMerchantProcessor extends MerchantProcessor {
processPayment(amount) {
// Empty implementation to comply with the compiler
// Won't do anything
}
}
class MerchantProcessor {
processPayment(amount) {
throw new Error('Should be overridden');
}
}
class MockMerchantProcessor extends MerchantProcessor {
processPayment(amount) {
throw new Error('Will be implemented when needed');
}
}
// or better...
class MockMerchantProcessor extends MerchantProcessor {
processPayment(amount) {
console.log('Mock payment processed: $${amount}');
}
}
Code Smell 30 - Mocking Business
There is an art to knowing where things should be checked and making sure that the program fails fast if you make a mistake. That kind of choosing is part of the art of simplification.
Ward Cunningham
If you miss a comma, you are concatenating
TL;DR: Watch out for fancy language assumptions
tools = [
"Amazon Codewhisperer",
"Bard" # Notice the missing comma
"ChatGPT",
"Dalle-E" # Also here
"Eliza"
]
print(len(tools))
# This will output 3
print(tools)
# ['Amazon Codewhisperer', 'BardChatGPT', 'Dalle-EEliza']
# Missing Commas act as hidden string concatenators
tools = [
"Amazon Codewhisperer",
"Bard",
"ChatGPT",
"Dalle-E",
"Eliza"
]
# We added all the missing commas
print(len(tools))
# 5
print(tools)
# ['Amazon Codewhisperer', 'Bard', 'ChatGPT', 'Dalle-E', 'Eliza']
Code Smell 84 - Max < Min (Javascript)
Code Smell 69 - Big Bang (JavaScript Ridiculous Castings)
Code Smell 06 - Too Clever Programmer
A programming language is low level when its programs require attention to the irrelevant.
Alan J. Perlis
Counting from zero seems natural. Doesn't it?
TL;DR: Start counting from one instead of zero. Like humans do.
package main
import "fmt"
func main() {
numbers := []int{10, 20, 30, 40, 50}
for i := 0; i < len(numbers); i++ {
// Iteration goes from zero to len-1
fmt.Println(numbers[i])
}
}
numbers = [10, 20, 30, 40, 50];
% Looping through the array using one-based indexing
% from 1 to length
for currentIndex = 1:length(numbers)
disp(numbers(currentIndex));
end
Code Smell 53 - Explicit Iteration
Code Smell 123 - Mixed 'What' and 'How'
Pay attention to zeros. If there is a zero, someone will divide by it.
Cem Kaner
You need to return more than one object
TL;DR: Don't return multiple values.
func getNameAndAge() -> (String, Int) {
let name = "John"
let age = 30
return (name, age)
}
struct PeopleProfile {
let name: String
let age: Int
}
// You reify the PeopleProfile object
func getNameAndAge() -> PeopleProfile {
let name = "John"
let age = 30
let profile = PeopleProfile(name: name, age: age)
return profile
}
This is yet another language feature that hinders clean code and blinds us from seeing missing abstractions in the Bijection.
Code Smell 10 - Too Many Arguments
Code Smell 122 - Primitive Obsession
Code Smell 27 - Associative Arrays
By relieving the brain of all unnecessary work, a good notation sets it free to concentrate on more advanced problems, and in effect increases the mental power of the race. Before the introduction of the Arabic notation, multiplication was difficult, and division even of integers called into play the highest mathematical faculties. Our modern power of easy reckoning with decimal fractions is the almost miraculous result of the gradual discovery of a perfect notation.
Alfred North Whitehead