Majority of time, software engineers, work with existing code. This article, along with few next, will cover techniques of making the exsiting code more testable, easier to maintain and possibly less error prone. We’ll look into anemic model code which stopped being object oriented and learn various refactorings which bring it back to object oriented form.

The anemic model

Take a look at below code, which is a very common pattern observed widely. You’ll see two classes carying data – the WorkingDay and Training classes. It’s very common that such classes come from and ORM solution.

The third class is a service which, based on two other classes’ data, calculates the total salary. As you can see, it has only one funtion and its main task is to reach down parameter’s throats, take what’s inside, perform some calculation and return result. Both of the parameter classes expose their internals to the outside world and that’s what the service exploits. This is what Martin Fowler described as Data Envy in his book on Refactoring.


public class SalaryService {

    public static final double NORMAL_HOURLY_RATE = 1.33;
    public static final double OVERTIME_HOURLY_RATE = 1.90;

    public BigDecimal calculateSalary(List workingDays, List trainings) {
        BigDecimal result = BigDecimal.ZERO;
        for (WorkingDay workingDay : workingDays) {
            result = result.add(BigDecimal.valueOf(workingDay.getHours() * NORMAL_HOURLY_RATE));
            result = result.add(BigDecimal.valueOf(workingDay.getHoursOvertime() * OVERTIME_HOURLY_RATE));
        }

        for (Training training : trainings) {
            result = result.add(BigDecimal.valueOf(training.getHours() * 2.5));
            if (training.isOutsideOfResidence()) {
                result = result.add(BigDecimal.valueOf(3));
            }
        }

        if (Month.DECEMBER.equals(LocalDate.now().getMonth())) {
            result = result.add(BigDecimal.valueOf(150));
        }
        return result;
    }
}

public class WorkingDay {
    private final LocalDate date;
    private final double hours;
    private final double hoursOvertime;

    WorkingDay(LocalDate date, double hours, double hoursOvertime) {
        this.date = date;
        this.hours = hours;
        this.hoursOvertime = hoursOvertime;
    }

    public LocalDate getDate() {
        return date;
    }

    public double getHours() {
        return hours;
    }

    public double getHoursOvertime() {
        return hoursOvertime;
    }
}

public class Training {
    private final LocalDate date;
    private final double hours;
    private final boolean outsideOfResidence;

    Training(LocalDate date, double hours, boolean outsideOfResidence) {
        this.date = date;
        this.hours = hours;
        this.outsideOfResidence = outsideOfResidence;
    }

    public LocalDate getDate() {
        return date;
    }

    public double getHours() {
        return hours;
    }

    public boolean isOutsideOfResidence() {
        return outsideOfResidence;
    }
}

Drawbacks of anemic model

The drawback of such coding style is that it’s very hard to track bugs. The bug can be anywhere in that code. It can be in the logic, in the extraction of variables from Working Day and Training classes, or maybe somewhere within instructions joining all the results.
This code is also very hard to test. Well it’s not really difficult to write the test code, but since there are few branching instructions around if statements, it requires some effort to capture all the combinations. We could go with usual – provide null parameters, empty lists, empty training list while workday list containing elements, empty workday list and training list containing elements etc. There is definitely some sweat required to ensure all cases are covered.
The third thing is that such code requires change whenever there’s any new type of work unit at play, which seems to act against the Open Closed Principle. Addition of “Day at customer’s office” which is billed differently would require adding a few lines of code here. Ideally we’d like new requirement (which is somewhat predictable) not to require changes to existing code which may already be in production.
Additionally such approach is often a source of code duplication. If any other class ever needs to calculate the total salary of training hours only, the code fragment will likely be duplicated, or we’ll need to expose another public method in the salary service (watch out for Single Responsibility Principle violation).

The above approach is called the anemic model and it can be very frequently seen in various tutorials. If you look closer you’ll notice that the code is very much procedural. What it does is “take a value from inside the parameter, apply some arithmetics and assign to temporary variable. Return the temporary variable value”. Our parameters (WorkingDay and Training) act as data structures carrying data only. This approach is very far from using any benefits of object orientation.
All of the problems in above example come from the fact that the calculations are made in the wrong classes. Instead of making decisions (in SalaryService) based on someone else’s publicly exposed data, we’ll move the computation inside classes who can do the same thing based on their own fields.

Refactoring anemic to object oriented model

The below video will guide you through the process of refactoring calculateSalary method’s body to become more object oriented. You’ll observe how the code becomes easy to test, easy to read and extend when the need comes. We’ll reach a state when our classes and methods follow the SOLID principles much better, become short and concise. We’ll reach a state where the data inside WorkingDay and Training classes is encapsulated and SalaryService does not need to care what’s inside of them, it will just ask WorkingDay and Training classes to perform the compuation for it.
As you’ll clearly see, this will all be achieved by a simple “move method” refactoring.
If you’d like to practice this refactoring feel free to fork this github repository.

Take a look here for some more knowledge

Leave a Comment