Search code examples
oopsolid-principles

Which class does a better 'Separation of Concerns'


I have this class, which creates a document and saves it:

public class DocCreator
{
  private IDocumentStore _documentStore;

  public DocCreator(IDocumentStore documentStore)
  {
    _documentStore = documentStore;
  }

  public void CreateAndSave()
  {
    var doc = new Document();
    doc.Title = "this is a title";
    doc.Content = whateverStream;
    doc.Hash = CalculateHash(doc.Content);
    //[do more things to create a doc]

    _documentStore.PersistToDisk(doc);
  }
}

I think it's decent, as the code to save things is hidden in DocumentStore. But we can take it one step further, and remove the call _documentStore.PersistToDisk(doc); to another class, like this:

public class DocCreatorWorkflow
{
  private IDocumentStore _documentStore;

  public DocCreatorWorkflow(IDocumentStore documentStore)
  {
    _documentStore = documentStore;
  }

  public void CreateAndSave()
  {
    var docCreator = new DocCreator();
    var doc = docCreator.Create();

    _documentStore.PersistToDisk(doc);
  }
}

In the example above I've created another class, which calls two lower classes, and so becomes responsible for the 'workflow'. It might be cleaner, but it also complicates things more. Doesn't it?

Or should I always go for the second option?


Solution

  • I would go with Option 2. You would need to modify the DocCreatorClass, though, since it is no longer responsible for saving it to disk:

    public static class DocCreatorClass
    {
        public static Document Create()
        {
            Document doc = new Document();
            // Property assignment code here.
    
            return doc;
        }
    }
    

    It would be static so that you would not need to instantiate a DocCreatorClass. I would also create separate functions for Create and Save in the DocCreatorWorkflow class:

    public class DocCreatorWorkflow
    {
        public IDocumentStore _documentStore;
    
        public DocCreateWorkflow(IDocumentStore documentStore)
        {
        }
    
        public void Document Create()
        {
            return DocCreatorClass.Create();
        }
    
        public void Save(Document doc)
        {
            _documentStore.PersistToDisk(doc);  
        }
    
        public void CreateAndSave()
        {
            Save(Create());
        }
    }
    

    This way, you don't always have to immediately save to disk the newly-created document. CreateAndSave() would be a convenience function that calls both Save() and Create() inside it, in case your program wants to immediately save a new document often enough.

    This type of design is definitely more coding which may come across as more complicated. In the long run, it's easier to look at and maintain because each function does only one thing.

    I personally stick with (most of the time, since there may be exceptions) the one class, one responsibility rule. This makes it easier to locate a part of your project when you notice that a functionality doesn't work. When you work on fixing it, you can be rest assured that the rest of your application (the other tasks, thus classes) is untouched. For functions, I like to create them in such a way that within a class, no code blocks will be repeated in two or more different places. This way, you won't have to hunt down all of those identical code blocks to update.