1

I am learning SOLID principles. I read a lot of things about the single responsibility principle but I don't get it exactly. I will give an example that what do I want to say.

Let's say we have an article service for managing articles. How should I design article service by single responsibility.

Is like that:

class ArticleService{

  create(){};  
  read(){};
  update(){};
  delete(){};

}

Or create a class for each operations like that :

class ArticleCreateService{

  create(){};

}

class ArticleReadService{

  read(){};

}

// and so on ...

According to single responsibility which is the best way to managing articles?

Thanks.

mka
  • 57
  • 6
  • Your first approach is better. The responsibility is "Manage articles in a data store." – Robert Harvey Oct 12 '22 at 11:50
  • https://stackoverflow.com/tags/solid-principles/info – Teemu Oct 12 '22 at 11:52
  • Honestly, does it matter? You most likely want to present the top interface for consumers. Whether or not it's implemented using the bottom approach is an implementation detail that is really of no interest to them. – VLAZ Oct 12 '22 at 11:52
  • @VLAZ: Yes, it does matter, for maintainability and a bunch of other reasons. – Robert Harvey Oct 12 '22 at 11:52
  • 3
    https://softwareengineering.stackexchange.com/questions/345018 – Robert Harvey Oct 12 '22 at 11:53
  • 1
    @RobertHarvey I've done both and I can't really say it does for sure. Consumers will still see the ArticleService since they probably want to interact with it in several ways. They might delete and reload the data for the latest state. However, *for maintainability* that might actually be in several submodules. When operations start to become more complex, (e.g., deletion goes through pre-checks, also cascades through related data, and may even send out notification messages) the rest of the code does not need to deal with that, so it can be separated into its own module. – VLAZ Oct 12 '22 at 11:56
  • @RobertHarvey Thanks. So as I understand it when each operation has more complex method like updateTitle(), readOneByID(), readAll(), it is time separate it like second approach – mka Oct 12 '22 at 12:05
  • No, it's more subtle than that. @Vlaz is right; there are other considerations. – Robert Harvey Oct 12 '22 at 12:08
  • 1
    Never learn a principle with the expectation that you use it 100% in all code, balance is king, especially the balance between performant code and readable/strict principle standards code – Barkermn01 Oct 12 '22 at 12:10

2 Answers2

2

As Robert C. Martin (Uncle Bob) says about Single Responsibility Principle:

There should never be more than one reason for a class to change

So your first option is better to comply with Single Responsibility principle:

class ArticleService {
    create(){};  
    read(){};
    update(){};
    delete(){};
}

As when you will want to edit ArticleService, then there is just one reason to edit this class. And this reason is to edit Article.

The second version looks like Interface Segregation principle. However, it should be slightly modified.

class ArticleCreateService
{
    void Create() { }
}

class ArticleReadService
{
    void Read() { }
}

At first, we need to segregate methods of class. We can segregate by Create() and Read() methods. So let's create these interfaces:

public interface IReadable
{
    void Read();
}

public interface ICreatable
{
    void Create();
}

And modified version of ArticleService could look like this:

public class ArticleService : IReadable, ICreatable
// public class ArticleService implements IReadable, ICreatable // in TypeScript
{
    public void Read()
    {
        throw new NotImplementedException();
    }

    void void Create()
    {
        throw new NotImplementedException();
    }
}
StepUp
  • 36,391
  • 15
  • 88
  • 148
0

The S in solid stands for that a class or method should only have one reason to change. That means that a class, module or method should have a single well defined responsibility.

In this particular case you might want to (for whatever reason) extend read, or write etc to read and write from/to different sources for example. Therefore keeping those responsibilities in a class each, will make it easier to extend i.e:

read class -> only reads data -> this class can then be extended with more methods like readFromExcel or readFromDB. Reading is a single responsibility. In that class each method can have separate niches of that one responsibility i.e readFromExcel only has one responsibility i.e readingFromExcel only.

class Read {
readFromExcel();
readFromDB();

}

A good rule of thumb is: does my class have one single responsibility? and what is that responsibility? Can my method and classes be extended without them losing that one single responsibility? In the above example class read has (S)ingle responsibility of only reads data and within it method readFromDB(); only reads files from the database.