DEV Community

Cover image for Code Smell 01 - Anemic Models
Maxi Contieri
Maxi Contieri

Posted on • Edited on • Originally published at maximilianocontieri.com

Code Smell 01 - Anemic Models

Your objects have no behavior.

TL;DR: Don't use objects as data structures

Protocol is empty (with setters/getters).

If we ask a domain expert to describe an entity he/she would hardly tell it is 'a bunch of attributes'.

Problems

  • No Encapsulation.

  • No mapping to real world entities.

  • Duplicate Code

  • Coupling

  • Writer / Reader mismatch.

Solutions

1) Find Responsibilities.

2) Protect your attributes.

3) Hide implementations.

4) Delegate

Examples

  • DTOs

Sample Code

Wrong

<?

class Window {
    public $height;
    public $width;

    function getHeight() {
        return $this->height;
    }

    function setHeight($height) {
        $this->height = $height;
    }

    function getWidth() {
        return $this->width;
    }

    function setWidth($width) {
        $this->width = $width;
    }

}
Enter fullscreen mode Exit fullscreen mode

Right

<?

final Class Window{ 

  function area(){
    //...
  }

  function open(){
    //..
  }

  function isOpen(){
    //..
  }

}
Enter fullscreen mode Exit fullscreen mode

Detection

Sophisticated linters can automate detection.
They should ignore setters and getters and count real behavior methods.

Also Known as

  • Data Class

Tags

  • Anemic
  • OOP as Data
  • Encapsulation
  • Setters/Getters
  • Mutability

Conclusion

Avoid anemic models. Focus always on protocol instead of data.
Behaviour is essential, data is accidental.

Relations

More info


Object-oriented programming increases the value of these metrics by managing this complexity. The most effective tool available for dealing with complexity is abstraction. Many types of abstraction can be used, but encapsulation is the main form of abstraction by which complexity is managed in object-oriented programming.

Rebecca Wirfs-Brock

Credits

Photo by Stacey Vandergriff on Unsplash


This article is part of the CodeSmell Series.

Last update: 2021/05/30

Top comments (11)

Collapse
 
190245 profile image
Dave

Your examples seem to imply that a window must not have a height & width, but can calculate area... I wonder how they can achieve this without knowing their height/width?

Are getters/setters the "evil" here?

You seem to imply that a data model should exist, that anaemia is a bad thing, and that data transport objects (DTOs) - which are essentially model + transport mechanism are a bad thing?

Elsewhere in this series, you seem to imply that data storage in a database is a bad thing?

Have I missed something obvious?

Collapse
 
mcsee profile image
Maxi Contieri

Hi. A database is not a bad thing. It is just an accidental issue we should only tackle once we have built good models.

A window's resposability might or might not be related to answer it's width. Since it is a resposability I'd never call it a getter . The window might not it's width . But it is also accidental and should not return it

Collapse
 
190245 profile image
Dave

I'm clearly confused... which object should we talk to, to obtain the window's height/width, since it is intrinsically a property of the window?

Let's say that a window cleaner wants to give a quote over the phone... they will probably want to know the number & size of the windows...

Maybe the fix here is that we introduce yet another model, the GlassPane, since a window could be single/double/triple glazed etc... and then (to me), it becomes the job of the GlassPane to know it's dimensions. That feels a little too over-zealous application of Single Responsibility Principle though.

If the responsibility might, or might not lay with the Window to know it's width, at what point do we decide who's responsibility is it? If a bug is raised about it, how do we know where to fix if it responsibilities aren't tied down?

Also, I would argue that to a certain degree, the database should be considered before the models. There may be factors affecting your choice of database, which then force you down certain roads in model design. For example, if your employer says "We already have PostGres instances that DBAs know how to support... we can't justify the cost of licence & training for MSSQL" - that might affect the length of fields you can allow, etc.

Collapse
 
winstonpuckett profile image
Winston Puckett

I'm so so glad we found each other's articles. I'm excited to work through the other 27 parts.

Stefan Tilkov describes something similar with the Anemic Service (in reference to microservices). I had no idea the term originally came from this.

Collapse
 
aderchox profile image
aderchox • Edited

I sometimes think if functions are pure, we won't need any global variables (I don't have credible reference to confirm this), so even Window with the right protocol will not need width and height. Or at least that's how I understood it, so please fix me otherwise.

Collapse
 
dakujem profile image
Andrej Rypo

So, DTOs are an example of the "wrong"? I would use an anemic DTO to transfer low-level data, for example for the database layer. These DTOs are mapped to domain models once they hit the application layer.

Collapse
 
mcsee profile image
Maxi Contieri

This is accidental complexity imposed by the language/architecture you chose and not a developer code smell. If the mapping is done through constructors with validations I guess we can skip it as long as you don't give responsibility to those DTOs.

Collapse
 
biffbaff64 profile image
Richard Ikin

Am I missing something here? Width and Height are important properties of a window as is access to them.

Collapse
 
oloryn profile image
Ben Coleman • Edited

Hmmmm. The "Wrong" and "Right" sample code is identical - both are the same file. This also happens in some (but not all) of the other articles in this series.

Collapse
 
oloryn profile image
Ben Coleman

Never mind. This appears to be a bug in the Android DEV app. Refreshing the page clears it up.

Collapse
 
mcsee profile image
Maxi Contieri

Thank you. I ve seen this erratic behavior before in my articles. Hope they will solve it