Appearance
Code smell
Bloaters
Bloaters are code, methods and classes that have increased to such gargantuan proportions that they’re hard to work with. Usually these smells don’t crop up right away, rather they accumulate over time as the program evolves (and especially when nobody makes an effort to eradicate them).
Long method
A method contains too many lines of code. Generally, any method longer than ten lines should make you start asking questions.
java
class OrderProcessor {
public void processOrder(Order order) {
// Validate order
if (order.getItems().isEmpty()) {
throw new IllegalArgumentException("Order is empty");
}
if (order.getCustomer() == null) {
throw new IllegalArgumentException("Customer is missing");
}
// Calculate totals
double subtotal = 0;
for (Item item : order.getItems()) {
subtotal += item.getPrice() * item.getQuantity();
}
double tax = subtotal * 0.1;
double total = subtotal + tax;
// Apply discount
if (order.getCustomer().isVip()) {
total = total * 0.9;
}
// Update inventory
for (Item item : order.getItems()) {
Inventory.decrease(item.getId(), item.getQuantity());
}
// Save order
order.setTotal(total);
Database.save(order);
}
}Large class
A class contains many fields/methods/lines of code.[
java
class UserManager {
private String username;
private String password;
private String email;
private String address;
private String phoneNumber;
private List<Order> orders;
private PaymentDetails paymentDetails;
private ShippingPreferences shippingPreferences;
public void createUser() { /* ... */ }
public void updateProfile() { /* ... */ }
public void changePassword() { /* ... */ }
public void processOrder() { /* ... */ }
public void calculateShipping() { /* ... */ }
public void handlePayment() { /* ... */ }
// Many more methods...
}Primitive Obsession
- Use of primitives instead of small objects for simple tasks (such as currency, ranges, special strings for phone numbers, etc.)
- Use of constants for coding information (such as a constant USER_ADMIN_ROLE = 1 for referring to users with administrator rights.)
- Use of string constants as field names for use in data arrays.
java
class User {
private String phoneNumber; // Format: "XXX-XXX-XXXX"
private int userType; // 1 = admin, 2 = regular, 3 = guest
private double money; // USD
public boolean isAdmin() {
return userType == 1;
}
}Long Parameter List
More than three or four parameters for a method.
java
class Report {
public void generateReport(String title, String date, String author,
String content, String format, String fontSize,
String color) {
// Generate report with all parameters
}
}Data Clumps
Sometimes different parts of the code contain identical groups of variables (such as parameters for connecting to a database). These clumps should be turned into their own classes.
java
class OrderProcessor {
public void connect(String url, String username, String password, int port) {
// Connect to database
}
}
class ReportGenerator {
public void connect(String url, String username, String password, int port) {
// Connect to database
}
}Object-Orientation Abuser
Switch Statements
You have a complex switch operator or sequence of if statements.
java
class PaymentProcessor {
double processPayment(String paymentType, double amount) {
double fee;
switch (paymentType) {
case "CREDIT_CARD":
fee = amount * 0.03;
break;
case "DEBIT_CARD":
fee = amount * 0.02;
break;
case "PAYPAL":
fee = amount * 0.04;
break;
default:
fee = 0;
}
return amount + fee;
}
}Temporary Field
Temporary fields get their values (and thus are needed by objects) only under certain circumstances. Outside of these circumstances, they’re empty.
java
class Order {
private double subtotal;
private double tax; // Only used during calculation
private double discount; // Only used during calculation
public double calculateTotal() {
tax = subtotal * 0.1;
discount = subtotal > 100 ? subtotal * 0.05 : 0;
return subtotal + tax - discount;
}
}Refused Bequest
If a subclass uses only some of the methods and properties inherited from its parents, the hierarchy is off-kilter. The unneeded methods may simply go unused or be redefined and give off exceptions.
java
class Bird {
void fly() { /* implementation */ }
void makeSound() { /* implementation */ }
void layEggs() { /* implementation */ }
}
class Penguin extends Bird {
@Override
void fly() {
throw new UnsupportedOperationException("Penguins can't fly!");
}
}Alternative Classes with Different Interfaces
Two classes perform identical functions but have different method names.
java
class UserFileHandler {
void saveUser(User user) { /* saves to file */ }
User loadUser(int id) { /* loads from file */ }
}
class UserDbManager {
void writeUserToDatabase(User user) { /* saves to DB */ }
User getUserFromDatabase(int id) { /* loads from DB */ }
}Change Preventers
These smells mean that if you need to change something in one place in your code, you have to make many changes in other places too. Program development becomes much more complicated and expensive as a result.
Divergent Change
You find yourself having to change many unrelated methods when you make changes to a class. For example, when adding a new product type you have to change the methods for finding, displaying, and ordering products.
java
class Product {
private String name;
private double price;
// Methods change for different reasons
public String getDisplayInfo() {
if (isBook()) {
return name + " - Author: " + getAuthor();
} else if (isElectronics()) {
return name + " - Warranty: " + getWarranty();
}
return name;
}
public double calculateShipping() {
if (isBook()) {
return 2.99;
} else if (isElectronics()) {
return 9.99;
}
return 4.99;
}
public String getInventoryStatus() {
if (isBook()) {
return "Books section, aisle " + getBookAisle();
} else if (isElectronics()) {
return "Electronics section, shelf " + getElectronicsShelf();
}
return "General section";
}
}Shotgun Surgery
Making any modifications requires that you make many small changes to many different classes.
java
class Customer {
private String email;
public void sendEmail(String message) {
System.out.println("Sending email to: " + email);
}
}
class Order {
private String customerEmail;
public void notifyCustomer() {
System.out.println("Sending email to: " + customerEmail);
}
}
class Support {
private String userEmail;
public void sendTicketUpdate() {
System.out.println("Sending email to: " + userEmail);
}
}Parallel Inheritance Hierarchies
Whenever you create a subclass for a class, you find yourself needing to create a subclass for another class.
java
class Animal {
protected String name;
}
class Dog extends Animal {
public void bark() {}
}
class Cat extends Animal {
public void meow() {}
}
class AnimalFeeder {
protected String foodType;
}
class DogFeeder extends AnimalFeeder {
public void feedDog() {}
}
class CatFeeder extends AnimalFeeder {
public void feedCat() {}
}Dispensables
A dispensable is something pointless and unneeded whose absence would make the code cleaner, more efficient and easier to understand.
Comments
A method is filled with explanatory comments.
java
class UserValidator {
public boolean validateUser(User user) {
// Check if the user is null
if (user == null) {
return false;
}
// Check if the username is empty
if (user.getUsername().isEmpty()) {
return false;
}
// Check if the email is valid
if (!user.getEmail().contains("@")) {
return false;
}
// If all checks pass, return true
return true;
}
}Duplicate Code
Two code fragments look almost identical.
java
class PaymentProcessor {
public double calculateRegularPayment(Order order) {
double total = 0;
for (Item item : order.getItems()) {
double price = item.getPrice();
double tax = price * 0.2;
double shipping = 5.0;
total += price + tax + shipping;
}
return total;
}
public double calculatePrimePayment(Order order) {
double total = 0;
for (Item item : order.getItems()) {
double price = item.getPrice();
double tax = price * 0.2;
double shipping = 0.0; // Only difference is free shipping
total += price + tax + shipping;
}
return total;
}
}Lazy Class
Understanding and maintaining classes always costs time and money. So if a class doesn't do enough to earn your attention, it should be deleted.
java
class Address {
private String street;
private String city;
}
class AddressFormatter {
public String format(Address address) {
return address.getStreet() + ", " + address.getCity();
}
}Data Class
A data class refers to a class that contains only fields and crude methods for accessing them.
java
class Customer {
private String name;
private String email;
private int age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}Dead Code
A variable, parameter, field, method or class is no longer used.
java
class OrderProcessor {
private boolean isLegacySystem = false; // Never used
public void processOrder(Order order) {
validateOrder(order);
// Old payment method, not used anymore
if (isLegacySystem) {
processLegacyPayment(order);
}
saveOrder(order);
}
private void processLegacyPayment(Order order) {
// Legacy payment logic
}
}Speculative Generality
There's an unused class, method, field or parameter.
java
interface Animal {
void eat();
void sleep();
void fly(); // What if some animals need to fly?
void swim(); // What if some animals need to swim?
}
class Cat implements Animal {
public void eat() { /* implementation */ }
public void sleep() { /* implementation */ }
public void fly() { /* empty - cats don't fly */ }
public void swim() { /* empty - most cats don't swim */ }
}Couplers
All the smells in this group contribute to excessive coupling between classes or show what happens if coupling is replaced by excessive delegation.
Feature Envy
A method that's more interested in another class than the one it's in.
java
class Order {
private Customer customer;
public double calculateTotal() {
double basePrice = customer.getBasePrice();
double discount = customer.getDiscount();
double tax = customer.getTaxRate();
return basePrice * (1 - discount) * (1 + tax);
}
}Inappropriate Intimacy
Classes that are too tightly coupled, accessing each other's private members.
java
class Student {
private List<Course> courses;
public void enrollInCourse(Course course) {
courses.add(course);
course.studentList.add(this); // Directly accessing private field
course.numberOfStudents++; // Directly accessing private field
}
}
class Course {
List<Student> studentList;
private int numberOfStudents;
}Message Chains
Long chains of method calls that create tight coupling between classes.
java
class Client {
public void printUserAddress() {
String zip = getCompany()
.getDepartment()
.getManager()
.getAddress()
.getZipCode();
System.out.println(zip);
}
}Middle Man
A class that delegates all its work to another class.
java
class PersonProxy {
private Person person;
public String getName() {
return person.getName();
}
public void setName(String name) {
person.setName(name);
}
public String getAddress() {
return person.getAddress();
}
public void setAddress(String address) {
person.setAddress(address);
}
}