A short excursion into the history of programming, on a way to understanding its fundamental concepts
This is the first post on SOLID principles and what values and principles they all have in common.
1972, David Parnas
Back in 1972, David Parnas, a man behind the concepts of encapsulation and modular programming, wrote his famous paper called “On the Criteria To Be Used in Decomposing Systems into Modules”. There he elaborated on criteria to be used when deciding which pieces of code should reside together and which should be separate.
What it is about
What was so revolutionary in Parnas’ paper? What is it basically about? Well, it’s about two related things.
First, it’s about choosing right concepts to operate upon. On domain level, these are the concepts of your problem space. These concepts are in high degree independent of each other. When you talk about purchasing an item, you don’t think about how it’s stored. The implementation of these concepts should be independent of each other as well.
On the infrastructure level, these are the main technical decisions you’ve made. All the code that works with a database should not be spread all over a project. It’s definitely a single module.
Second, it’s encapsulation. When properly decomposed domain concepts are modeled, they should not indulge spreading their implementation details all over a codebase. It’s more like a consequence of a previous point.
This principle is applicable at all levels, not just on a module level. It’s about classes and services as well.
First, if you need to make some change, with this approach it’s more likely that it would be confined within a clearly defined and comprehensible area. You won’t need to fix half of all your project classes. If you change the way an item is purchased, you won’t have to modify code responsible for storing it. If you change your database, you won’t have to fix every single place that uses a database’s capabilities.
Second, it’s apprehensibility. Every piece of code has some clear purpose.
1979, Yordon and Constantine
Later in 1979, Yordon and Constantine coined the term of cohesion in their book “Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design”. It went like
degree to which the elements inside a module belong together
Wikipedia elaborates on it a bit:
In one sense, it is a measure of the strength of relationship between the methods and data of a class and some unifying purpose or concept served by that class. In another sense, it is a measure of the strength of relationship between the class’s methods and data themselves.
It brings us to the first point: there should be only one such purpose. The second point is more subtle. It’s about conforming a class name to its responsibilities: the abstraction level of these two should match. Here is an example I gave to a relevant question on softwareengineering q&a site, that illustrates the violation of the second point:
public function __construct()
public function drive()
public function giveFuelToEngine()
Car’s responsibility, its main purpose — driving — doesn’t correlate to abstraction level of the method
giveFuelToEngine.Hence different reasons to change.
2003, Robert Martin
In 2003, Robert Martin in his book “Agile Software Development, Principles, Patterns, and Practices” stated the Single Responsibility Principle:
A class should have only one reason to change.
Wikipedia says that Martin based this principle on cohesion, inspired by Structured Analysis and System Specification book. I doubt that. Tom DeMarco took data-centric approach, where data flow is the primary concern. So the cohesion there is just a means for enabling functional decomposition, which is fine nevertheless.
2004, Eric Evans
Eric Evans, in his book “Domain-Driven Design: Tackling Complexity in the Heart of Software”, introduced the concept of an aggregate. It’s a set of objects that can be treated as a single unit. It has one prominent feature: aggregates can refer to each other only by an identificator. And keeping in mind the fact that one can not pass any repositories to it, it’s easy to conclude that there is no way for an aggregate to operate upon other aggregates within its boundaries. Their communication can be only via events.
This approach promotes the creation of really cohesive domain concepts, which besides that have explicit boundaries. Here is where cohesion met encapsulation.
Wrapping it up
So if a class is cohesive, if there is a single higher-level purpose, if its responsibilities conform to its name, the SRP would come out naturally. SRP is just a practical consequence, it’s not a goal by itself. The cohesive object identifying and endowing it with correct responsibilities is. Moreover, the whole SOLID is just simplified implementational consequence of fundamental OO principles. It’s just a form, not the essence.