Sometimes, when we are learning how to code, it’s difficult to understand the classes responsibilities concept. That’s the reason because our first projects become unmaintainable, containing many lines by class in our code and, much more critical, many amount of responsibilities.

A good way to really know the responsibility for a class is thinking in scalability.

Let’s see an example of how to use that. But before I would like to introduce a bit of theory and explain why this concept is important in software design.

Introduction to Single Responsibility Principle

The idea of each class having a single responsibility in a software project, and that responsibility being encapsulated in one unique class has a name: Single Responsibility Principle

This is one of the 5 main principles of software design SOLID where, in Object Oriented Programming, they try to define a guide to have a more understandable, flexible and maintainable software.

These principles are:

  • Single responsibility principle
  • Open/closed principle
  • Liskov substitution principle
  • Interface segregation principle
  • Dependency inversion principle

The author of these concepts, Robert C. Martin, (he wrote one of the most important books in software architecture, Clean Code) talks about ” A class should have only one reason to change”, so he defines a responsibility as a reason to change.

For instance, let’s consider a module that compiles and prints a report. Imagine such a module can be changed for two reasons. First, the content of the report could change. Second, the format of the report could change. These two things change for very different causes; one substantive, and one cosmetic.

The Single Responsibility Principle says that these two aspects of the problem are really two separate responsibilities, and should therefore be in separate classes or modules. It would be a bad design to couple two things that change for different reasons at different times.

The reason it is important to keep a class focused on a single concern is that it makes the class more robust. Continuing with the foregoing example, if there is a change to the report compilation process, there is greater danger that the printing code will break if it is part of the same class. (Example extracted from Wikipedia )

Why is it important to define correctly each class responsibility

If we define our classes knowing which is their responsibility in our project we can:

  • Easily understand which functionality it does in each part of the code.
  • Modify existent logic faster and in detail.
  • Find with less problems the origin of the bugs or unwanted behaviours.
  • Abstract logic in different classes or modules.
  • Split without major problems implementations so they can be completely replaced later.
  • Define unit tests by class or module in a more efficient way, so we can test a little piece of the code, and no more that we really want to test.

Thinking in the scability to define responsibilities

As I said before, you can think in the classes scability to define responsibilities. This is as simple as thinking if in our project might happen that the requirements will be modified, and look in our architecture how these modifications would take place.

If we see that for a small view change we have to modify or touch business logic, we are not correctly defining the responsibilities in our project, for instance.

Let’s see a concrete example in Swift.

Swift example

Let’s say that I have an app which it shows a list of items from a store. By now I only have a ItemsViewController which is in charge of all the logic of that flow, the data and the presentation of that. Also, it prints a log when the user selects an item.

ItemsViewController: A simple list of items

You can see the code in https://github.com/fedejordan/SRPExample

To do that, ItemsViewController uses a UITableView to show the items in a list. Also we’re using a UITableViewCell subclass called ItemTableViewCell to show these elements.

But let’s say that we want to change the view, for instance, with a UICollectionView. What would be the problem in this situation?

The view code is very coupled to the items data logic. If we change the view it ‘s very likely that we’ll modify the class in charge of the items as well.

The real problem is in these lines:

let item = items[indexPath.row]

What is the problem here?

Because we are using the UITableView index to get the specific item in the array. We should abstract in some way that the view uses anUITableView.

To avoid that, we’ll refactor ItemsViewController and we’ll move the model logic to another class called ItemsInteractor.

The interactor concept has its origin in VIPER architecture. As it is said in the definition, an Interactor contains the business logic to manipulate model objects (Entities) to carry out a specific task. In this case, our ItemsInteractor is in charge of retrieving information about any Item.

You can get this code in https://github.com/fedejordan/SRPExample, in the branch interactor_refactor.

As we can see, ItemsViewController doesn’t know anything about the data model. It simply ask the Interactor what it needs to draw the view.

With this approach we can, for example, change the data type Item for any other thing, and we would only change ItemsInteractor. It would retrieve the same information (we wouldn’t change the public methods) and finally ItemsViewController could keep working as always.

So, with that refactor, if we want to change just the layout of our app, we simply change ItemsViewController to use anUICollectionView:

ItemsViewController screenshot, using a UICollectionView

You can see the final result in https://github.com/fedejordan/SRPExample , collection_view_refactor branch.

Did we change something in ItemsInteractor? Just nothing. We simply changed the presentation of the feature.

This, also, allow us to test all in a more modularized way. We can first start to test the view, and then the business logic. Of course that, for doing tests correctly, we need to do something more injectable, se we can initialize the modules with mocked classes and we depend of interfaces or protocols in Swift. Basically it means, for instance, not creating the ItemsInteractor inside ItemsViewController, because it’s an dependency, and it should be abstracted from its implementation. But all of this exceeds the article purpose.

Conclusion

Seems that we didn’t do so much work. We simply removed the view logic from the data model. Was it really worth it? In this example, maybe it wasn’t too useful, in fact, could be that we simply need a list and nothing else.

But when we have more complex UIs, we have to adapt the same view to different data sources, or share the data model with different flows. Applying this concept allow us to reuse the code and scale the project in a more proper way. And to do that, it’s important to understand which responsibility should have each class that we maintain.

I would like to add that, in my opinion, this concept is a necessary condition to have a good code. In fact, in any code that is badly written, we will always find any class with more than one responsibility. It’s because of that we saw the importance of Single Responsibility Principle.

Our Swift example allowed us to understand why it’s important to define correctly the responsibilities. How a correct modularized architecture can be the key in the scability of our project, simply because we want to do a layout change.

As a summary I would like to conclude that, looking always in our project scability , we will be able to see if we are correctly defining our classes or modules responsibilities.

Thank you very much for reading the post!

I hope you enjoyed it. You can send any message or suggestion about the topics in the comments section, or simply sending me a mail to fedejordan99@gmail.com

This article is also available on Medium