TLDR; Robert C. Martin adviced here: http://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html to split the multi responsibility classes into "single reason to change"-classes. It was not explained, how properties(data), used by more than one of the splitted parts, should be organized/stored/passed on. I tried to explain the matter by an example and discussed possible solutions.
Accepted Answer: Doc Brown pointed out, my approach was similiar to the "pipes & filters" architecture. He suggested a functional approach where each Single Responsibility class gets the data, it operates on, handed as parameter. This is organized by overseeing methods which handle each a business case. Read and upvote his great answer below for details.
I have read a lot about separating methods and function into "single reason to change" classes. But when it comes to data, I find very little details. I am not sure where to put the data, each separated part needs as Input and provides as Output
An example scenario:
I got a data source, a list of some kind, maybe in a csv or excel format. Requirements could be:
- import the file (Import)
- check the list for certain conditions (business logic, validation)
- calculate stuff based on a few fields (business logic)
- convert the list to a different format, e.g. json (business logic, conversion)
- write the changed list to the excel file (Output)
- write the changed list to the json (Output)
I would divide the responsibility into following parts(Lets call them "Helpers"):
- Import/Export of the List -> ListSerializer (produces List from disk)
- Check the List ->ListChecker (takes List, produces ErrorList)
- Apply business logic to the List->ListProcessor (takes List and ErrorList, produces List (with changes))
- Convert the List to JSONList ->ListToJSONListConverter (takes List(with changes), produces JSONList)
- Export the JSONList to file ->JSONListSerializer (takes JSONList, writes to disk)
AFAIK a classical SRP design. changes to the list format, check logic, business logic or JSONformat requiring to change very few classes
Now I want to implement it. All classes get Interfaces for exposing their function. An Orchestrating Class (lets call it "Superior") is created to handle user input/other triggers and calling the sub routines This class needs to know every interface of the "Helpers"(so it can call the methods) and must be initialized with concrete objects (implementations of the interfaces) by the init logic lets call it "Boss". Basic Dependency Injection stuff.
Basically the Boss creates the Helpers and the Superior and tells the Superior these are your Helpers, Jack ListSerializer as your ListSerializer, Amy ListChecker as ListChecker, ... When a new Task arrives, the Superior gets notified and he/she splits and delegates the task to the Helpers. Lets take that picture a little further:
Now there are multiple variants how things could be done:
1st: Orchestrating class manages all data storing Superior: Mr. ListSerializer please load the list. Jack the ListSerializer: Superior, I have completed the task, here is the List. Superior: Thanks. Mrs. ListChecker, please check this list, it is right here. ...
2nd: Every dataproducer stores its output for itself, the others know how to retrieve it Superior: Mr. ListSerializer please load the list. Jack the ListSerializer: Superior, I have completed the task Superior: Thanks. Mrs. ListChecker, please check the list(, you can find it at the ListSerializers desk.) ..
3rd: One big variable data storage, where the helpers know how to access the info relevant to them Superior: Mr. ListSerializer please load the list. Here is the magic drawer to put your results in. Jack the ListSerializer: Superior, I have completed the task Superior: Thanks. Mrs. ListChecker, please check the list, you can find it in the magic drawer, please put your results in there, too. ...
every variant has its advantages, but is bad to some degree
1st: The Superior needs to know the data objects up front, one could reason to use interfaces for that. feels little object oriented to store data centrally, but one could try to live with it
2nd: Feels more object oriented, the Superior doesn't need to know the details, downside is the additional coupling between the Helpers they need to know each other
3rd: most decoupled, no Helper needs to know any other, only the way how to retrieve and store data from and to the all knowing data storage object
Does the SRP (or SOLID) provide a guideline for that? What are best practices in that matter?
I refer to http://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html who, as I understand, propagates such a splitting in his example. But the example is not covering how the classes interact, how the data or object is passed on.
Edit 2 Added TLDR; and accepted answer