I was following Aki's on the SoCraTes2014 conference last week about Legacy Code and Refactoring. In the session a piece of real-world code was shown that contained one of most common code smells in LegacyCode: Feature Envy.
Martin Fowler describes this smell as "a method that seems more interested in a class other than the one it is in. The most common focus of the envy is the data."
This code smell causes two very similar problems in a code base:
- Duplication of rules related to their data throughout the code base.
- Spreading domain concepts throughout the whole code-base.
A simple example can show this problem with DateTime
is wide-spread in many
PHP projects. We often see code such as the following computation to
create a range of dates for a reporting function:
<?php
function calculateReport(DateTime $start, $days)
{
if ($days < 0) {
throw new InvalidArgumentException($days);
}
$endDate = clone $start;
$endDate->modify('+' . $days . ' day');
// calculation code here
}
This is a really simple example for Feature Envy: The computation and
validation of creating an end date some days in the future of a start date
can easily be duplicated throughout the whole code base and implements
business rules that belong to the class DateTime
itself:
<?php
class DateTime
{
// ... other methods
public function getDateDaysInFuture($days)
{
if ($days < 0) {
throw new InvalidArgumentException($days);
}
$endDate = clone $this;
$endDate->modify('+' . $days . ' day');
return $endDate;
}
}
function calculateReport(DateTime $start, $days)
{
$endDate = $start->getDateDaysInFuture($days);
// calculation code here.
}
(Note: You cant extend the builtin DateTime this way and need to subclass).
This is a great way to improve your code base massively and achieve what Object-Oriented and Domain-Driven Design is about: Modelling concepts of the domain by encapsulating data and behavior.
Learning to spot this kind of Feature Envy is a very good way to incrementally improve your legacy code base towards better models. I find it very interesting that the focus on fixing technical code-smells duplication and feature envy actually leads to a better domain model. As a side effect it massively deemphasizes DDD building blocks like Entity or Value Object, which in my opinion are a big cause for confusion. Testability increases as well, because data objects with behavior are much simpler to setup for testing.
That is why I recommend learning about Feature Envy. For me it is a very simple approach to find improvements.