Post

Code Smell : Anemic Model / Data Class

Code Smell : Anemic Model / Data Class

{% raw %}

Problem faced:

During our software development, we used to use design a class just to use it as a data structure. But while i was interested in reading about code smells; i got to know that its one of the anti pattern. So what is the problem with using a class like that; what is the design pattern then ?

Introduction

A key goal of Object Oriented design is to minimise dependencies between classes and components by packaging data and behaviour as close together as we can. In practice, a good rule of thumb for class design is to put fields and methods that use those fields in the same classes. Data classes are classes which just have fields and no behaviour (besides simple getters and setters), and they break this rule of thumb, creating serious dependency issues in your code.

TL;DR:

Don’t use objects as data structures. ( Class with no behaviour/functionality apart from getters and setters )

Explanation

Let’s design a CustomerReviewSummary functionality,

class Customer:	def __init__(self, address, title, first_name, last_name):		self.__address = address		self.__title = title		self.__first_name = first_name		self.__last_name = last_name		def get_title(self):		return self.__title		def set_title(self, title):		self.__title = title		def set_first_name(self, first_name):		self.__first_name = first_name		def get_first_name(self):		return self.__first_name		def set_last_name(self, last_name):		self.__last_name = last_name		def get_last_name(self):		return self.__last_name		def set_address(self, address):		self.__address = address		def get_address(self):		return self.__addressclass Address:	def __init__(self, city, post_code, country):		self.__city = city		self.__post_code = post_code		self.__country = country		def get_city(self):		return self.__city		def set_city(self, city):		self.__city = city		def get_country(self):		return self.__country		def set_country(self, country):		self.__country = country		def get_postcode(self):		return self.__postcode		def set_postcode(self, postcode):		self.__postcode = postcode		class CustomerSummaryView:	def __init__(self, customer):		self.customer = customer		def get_customer_summary(self):		address = self.customer.get_address()		return self.customer.get_title() + " " + self.customer.get_first_name() + " " + self.customer.get_last_name() + ", " + address.get_city() + ", " + 		address.get_post_code() + ", " + address.get_country() 

In the above implementation of classes; the class Address and Customer are not having any behaviours. These are used as a database template or like a datastructure. All the logics were residing in the Service CustomerSummaryView.

Let’s See how it could be redesigned with DDD, RDM where the logic/behaviour would reside inside the class itself.

class Customer:	def __init__(self, address, title, first_name, last_name):		self.__address = address		self.__title = title		self.__first_name = first_name		self.__last_name = last_name		def get_title(self):		return self.__title		def set_title(self, title):		self.__title = title		def set_first_name(self, first_name):		self.__first_name = first_name		def get_first_name(self):		return self.__first_name		def set_last_name(self, last_name):		self.__last_name = last_name		def get_last_name(self):		return self.__last_name		def set_address(self, address):		self.__address = address		def get_address(self):		return self.__address		def get_customer_summary(self):		return self.__title + " " + self.__first_name + " " + self.__last_name + self.__address.get_address_summary()class Address:	def __init__(self, city, post_code, country):		self.__city = city		self.__post_code = post_code		self.__country = country		def get_city(self):		return self.__city		def set_city(self, city):		self.__city = city		def get_country(self):		return self.__country		def set_country(self, country):		self.__country = country		def get_postcode(self):		return self.__postcode		def set_postcode(self, postcode):		self.__postcode = postcode		def get_address_summary(self):		return self.__city + ", " + self.__postcode + ", " + self.__country		class CustomerSummaryView:	def __init__(self, customer):		self.customer = customer		def get_customer_summary(self):		return self.customer.get_customer_summary()		 

Solutions

  1. Find Responsibilities to your class.
  2. Protect your attributes.
  3. Hide implementations.
  4. Delegate

There is nothing right or wrong. It’s with the architect 🙂

Advantages of Anemic Model

  • It’s easy to understand. Suppose you’re thinking about how you would store your data in a database. In that case, it becomes very intuitive to create a class that maps to your database schema.
  • Easy to implement. You can start quickly because you must think about the data, not the behavior right away.

Final Thoughts

  • In Anemic Domain Models (ADM), everything happens in services. Validation, data formatting, everything.
  • ADM as consisting of a set of behaviour-free classes containing business data required to model the domain.
  • Avoid anemic models.
  • Focus always on protocol/behaviour instead of data.
  • Behaviour is essential, data is accidental.
  • If you want to create a simple CRUD application, maybe an anemic model with a classic MVC framework is enough. But if you want to implement some kind of logic, anemic model means that you will not do object oriented programming.

References:

  1. codemanship.co.uk/papers.html
  2. Fowler, Martin. Anaemic Domain Model. martinfowler.com/bliki/AnemicDomainModel.html, 2003.
  3. blog.inf.ed.ac.uk/sapm/2014/02/04/the-anaem..

{% endraw %}

This post is licensed under CC BY 4.0 by the author.