menu
Bloatersarrow_forward_ios
Primitive Obsession
zoom_outPrimitive Obsession

Overview

Primitive Obsession refers to the excessive use of primitive data types (like strings, numbers, or arrays) to represent domain concepts, instead of creating dedicated classes for them. This can lead to code that's harder to understand, maintain, and extend.

Problems

  • Code Duplication-when the same logic for manipulating primitive data types (e.g., strings or hashes) is repeated throughout your codebase, it can lead to code duplication.
  • Lack of Semantic Meaning-primitive data types like strings or integers lack semantic meaning.
  • Limited Expressiveness-primitive data types offer limited expressiveness compared to custom classes. By creating dedicated classes for domain concepts, you can provide meaningful names for attributes and methods, making the code more self-documenting and easier to understand.
  • Difficulty in Extensibility-when using primitive data types, extending the behavior or adding new features related to a domain concept can be challenging.
  • Error-Prone Data Manipulation-manipulating primitive data types directly can be error-prone, especially when dealing with complex data structures or business rules.
  • Testing Complexity-testing code that relies heavily on primitive data types can be complex, as you need to mock or stub out various interactions with these types.

Real World Example

The most common data that represents this code smell is data such as phone numbers or money. At first, we assign them to a regular variable, but as we develop the code, it turns out that we need to add more functionality to them.
Imagine that you receive your salary in dollars (unless you don't have to imagine). You want to buy a house in Spain, the price of which is set in euros. Let's write a simple program that will allow us to calculate how many months we need to afford such a house.
# Your salary in dollars
salary = 5000

# House cost in euro
house_cost = 100_000

eur_to_usd_rate = 1.09

((house_cost * eur_to_usd_rate) / salary).ceil # => 21
As we can see, it doesn't look very good. To know what value is in what currency, we need to add additional comments to our code. Additionally, we need to define a variable that will allow us to convert the value to another currency.
To make it easier to continue working with this program, we will need to create a new class that will allow us to work with money more easily.
class Money
  attr_reader :amount, :currency

  def initialize(amount, currency)
    @amount = amount
    @currency = currency
  end

  def dollar?
    currency == "$"
  end

  def euro?
    currency == "€"
  end

  def to_euro
    return amount if euro?

    amount * 0.92
  end
  
  def to_dollar
    return amount if dollar?

    amount * 1.09
  end
end
Now let's use our new class.
house_cost = Money.new 100_000, "€"
salary = Money.new 5000, "$"

(house_cost.to_dollar / salary).ceil # => 21

Pros & Cons

Pros

  • Improved Readability and Expressiveness-domain-specific classes provide meaningful abstractions that better reflect the domain concepts.
  • Enhanced Type Safety-domain-specific classes allow for encapsulation of behavior and validation logic, providing stronger type safety.
  • Centralized Logic-refactoring Primitive Obsession allows for centralization of logic related to domain concepts within the domain-specific classes.
  • Easier Maintenance and Extensibility-with domain-specific classes, maintenance and extensibility become easier as changes to domain logic can be made in one place.

Cons

  • Increased Complexity-introducing domain-specific classes can add complexity to the codebase, especially if not managed properly. Developers need to ensure that the added abstraction does not overly complicate the code.
  • Learning Curve-adopting domain-specific classes may require developers to learn new concepts and APIs, especially if they are not familiar with the domain or the design patterns used.
  • Performance Overhead-domain-specific classes may introduce a slight performance overhead compared to using primitive types, especially if they involve additional validation or formatting logic. However, this overhead is usually negligible in most cases.
  • Potential Over-Engineering-there's a risk of over-engineering when refactoring Primitive Obsession, where developers create overly complex abstractions that are not justified by the requirements of the application.
  • Dependency Management-introducing domain-specific classes may increase dependencies between different parts of the codebase, requiring careful management to avoid tight coupling and maintain modularity.