My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more
Single Responsibility Principle in C++ | SOLID as a Rock

Single Responsibility Principle in C++ | SOLID as a Rock

Vishal Chovatiya's photo
Vishal Chovatiya
·May 4, 2020

This article is the first part of a five-part series about SOLID as Rock design principle series. The SOLID design principles focus on developing software that is easy to maintainable, reusable & extendable. In this article, we will see an example of the Single Responsibility Principle in C++ along with its benefits & generic guideline.

/!\: Originally published @ www.vishalchovatiya.com.

By the way, If you wants to directly jumps to other design principles, then below is the quick links:

  1. SRP -- Single Responsibility Principle
  2. OCP -- Open/Closed Principle
  3. LSP -- Liskov Substitution Principle
  4. ISP -- Interface Segregation Principle
  5. DIP -- Dependency Inversion Principle

Intent

A class should have only one reason to change

In other words, SRP states that classes should be cohesive to the point that it has a single responsibility, where responsibility defines as "a reason for the change."

Motivation: Violating the Single Responsibility Principle

class Journal {
    string          m_title;
    vector<string>  m_entries;

public:
    explicit Journal(const string &title) : m_title{title} {}
    void add_entries(const string &entry) {
        static uint32_t count = 1;
        m_entries.push_back(to_string(count++) + ": " + entry);
    }
    auto get_entries() const { return m_entries; }
    void save(const string &filename) {
        ofstream ofs(filename);
        for (auto &s : m_entries) ofs << s << endl;
    }
};

int  main() {
    Journal journal{"Dear XYZ"};
    journal.add_entries("I ate a bug");
    journal.add_entries("I cried today");
    journal.save("diary.txt");
    return EXIT_SUCCESS;
}
  • Above C++ example seems fine as long as you have a single domain object i.e. Journal. but this is not usually the case in a real-world application.
  • As we start adding domain objects like Book, File, etc. you have to implement save method for everyone separately which is not the actual problem.
  • The real problem arises when you have to change or maintain save functionality. For instance, some other day you will no longer save data on files & adopted database. In this case, you have to go through every domain object implementation & need to change code all over which is not good.
  • Here, we have violated the Single Responsibility Principle by providing Journal class two reason to change i.e.
    • Things related to Journal
    • Saving the Journal
  • Moreover, code will also become repetitive, bloated & hard to maintain.

Solution: Single Responsibility Principle Example in C++

class Journal {
    string          m_title;
    vector<string>  m_entries;

public:
    explicit Journal(const string &title) : m_title{title} {}
    void add_entries(const string &entry) {
        static uint32_t count = 1;
        m_entries.push_back(to_string(count++) + ": " + entry);
    }
    auto get_entries() const { return m_entries; }
    //void save(const string &filename)
    //{
    //    ofstream ofs(filename);
    //    for (auto &s : m_entries) ofs << s << endl;
    //}
};

struct SavingManager {
    static void save(const Journal &j, const string &filename) {
        ofstream ofs(filename);
        for (auto &s : j.get_entries())
            ofs << s << endl;
    }
};

SavingManager::save(journal, "diary.txt");
  • Journal should only take care of entries & things related to the journal.
  • And there should be one separate central location or entity which does the work of saving. In our case, its SavingManager.
  • As your SavingManager grows, you have all the saving related code will be at one place. You can also templatize it to accept more domain objects.

Benefits of Single Responsibility Principle

=> Expressiveness

  • When the class only does one thing, its interface usually has a small number of methods which is more expressive. Hence, It also has a small number of data members.
  • This improves your development speed & makes your life as a software developer a lot easier.

=> Maintainability

  • We all know that requirements change over time & so does the design/architecture. The more responsibilities your class has, the more often you need to change it. If your class implements multiple responsibilities, they are no longer independent of each other.
  • Isolated changes reduce the breaking of other unrelated areas of the software.
  • As programming errors are inversely proportional to complexity, being easier to understand makes the code less prone to bugs & easier to maintain.

=> Reusability

  • If a class has multiple responsibilities and only one of those needs in another area of the software, then the other unnecessary responsibilities hinder reusability.
  • Having a single responsibility means the class should be reusable without or less modification.

Yardstick to Craft SRP Friendly Software in C++

  • SRP is a double-edged sword. Be too specific & you will end up having hundreds of ridiculously interconnected classes, that could easily be one.
  • You should not use SOLID principles when you feel you are over-engineering. If you boil down the Single Responsibility Principle, the generic idea would be like this:

The SRP is about limiting the impact of change. So, gather together the things that change for the same reasons. Separate those things that change for different reasons.

  • Adding more to this, If your class constructor has more than 5-6 parameters then it means either you are not followed SRP or you are not aware of builder design pattern.

Conclusion

The SRP is a widely quoted justification for refactoring. This is often done without a full understanding of the point of the SRP and its context, leading to fragmentation of codebases with a range of negative consequences. Instead of being a one-way street to minimally sized classes, the SRP is actually proposing a balance point between aggregation and division.

Have Any Suggestions, Query or Wants to Say Hi? Take the Pressure Off, You Are Just a Click Away.🖱️