Feature Envy — Code Smells Catalog Skip to content

Feature Envy

Couplers Responsibility Code SmellDesign Smell Between Class

A method that touches another class's fields more than its own. It was written in the wrong place and belongs closer to the data it can't stop reaching for.

2 min read 1 source

Overview

If a method inside a class manipulates more features (be it fields or methods) of another class more than from its own, then this method has a Feature Envy. In Object-Oriented Programming, developers should tie the functionality and behavior close to the data it uses. The instance of this smell indicates that the method is in the wrong place and is more tightly coupled to the other class than to the one where it is currently located. [1]

This was the explanation based on Fowler’s book from 1999. In his recent “book update”, he rephrased the class into module, generalizing the concept from a zone perspective. Depending on the size of the system, the Feature Envy code smell may apply accordingly.

Causation

The root cause of this smell is misplaced responsibility.

Problems

🧪
Low Testability

Difficult to create proper test or tests in separation. Mocking is required.

Inability to Reuse

Coupled objects have to be used together. This can cause lousy duplication issues if one tries to reuse applicable code by extracting and cutting off what he does not need.

Bijection Problems

Real-world domain concepts and their code representations drift apart when behavior lives far from the data it operates on.

Violated Principles
Tell, Don’t Ask

Example

1@dataclass(frozen=True)
2class ShoppingItem:
3    name: str
4    price: float
5    tax: float
6
7
8class Order:
9    ...
10    def get_bill_total(self, items: list[ShoppingItem]) -> float:
11        return sum([item.price * item.tax for item in items])
12
13    def get_receipt_string(self, items: list[ShoppingItem]) -> list[str]:
14        return [f"{item.name}: {item.price * item.tax}$" for item in items]
15
16    def create_receipt(self, items: list[ShoppingItem]) -> float:
17        bill = self.get_bill_total(items)
18        receipt = self.get_receipt_string(items).join('\n')
19        return f"{receipt}\nBill {bill}"
PYTHON

Refactoring

  • Move Method
  • Move Field
  • Extract Method

Sources

Browse All 56 Smells