CRUD applications tend to involve classes with many accessor methods but little business logic, the behaviour being implemented at a higher level in manager (or ‘god’) classes. Usually this signals that the domain model is anaemic. While many developers know the symptoms, few know how to resolve the problem.
Anaemic classes have no responsibility other than to write and read attributes. Not only the utility of object-oriented programming is diminished in such cases, but the amount of boilerplate code also increases to implement data transfer objects and manager classes to move them around.
Unit-testing can help to identify when a domain model is anaemic.
Unit testing demands that the ‘usage code’ be written before the classes are implemented. By first writing tests, one can see how the interfaces of a domain model will be used and can spot easily anything that is out of place or redundant. For example, consider a simple
Account class that has the following responsibilities:
- represent a user account
- hold information about a user (ie. username, password, email address, status)
- used for authentication
This class seems dumb at first, and it is tempting to implement only getter and setter methods as the interface.
Say, we write a unit test for user authentication. What most people probably do is this:
This, in the unit test, should immediately signal that the model is anaemic. Here the responsibility for checking the validity of the input password is handled by the calling code, but in truth it should belong to the
An easy fix is to refactor the unit test as follows.
Now the behaviour is encapsulated within the
Account class, making the implementation of the ‘authenticate user’ use-case more robust.
Many such small refactorings can be performed in order to make richer classes. One has only to write good unit tests and refactor whenever one thinks that things can be written in a better way.
I follow these principles:
- If calling code reads an attribute from an object in order to act on that piece of information, the behaviour should be moved to the object.
- Getters and setters should not be systematically implemented; instead, write setters and setters when unit tests indicate that they are needed.
- If methods on the same object are called in a daisy-chain fashion, this indicates that the object is anamic and lacks behaviour.
I am sure other programmers have their own ways of resolving anaemic domain models. I would be very interested in reading what techniques they use.