Let's say you write your own Logger utility, and since it starts with a simple one (singleton with methods that write to stdout), you decide to put all your functions in one class. Since you are using this, you are thinking of additional features to add to it:
you want to be able to register for different things (in stderr, in a file, in the queue, whatever)
Do you want to change message formatting
you want separate registrars for different categories
you want to be able to filter messages for a category by log level
you want to be able to change logging levels on the fly
add new logging levels (ERROR, WARN, INFO, DEBUG, etc.)
etc., and you add an implementation for each function to the same class you started with. Each change you make means returning to one class, sorting by it to see what is important, and then make changes. Since the whole code is overflowing with one class, and the methods contain code that implements various functions, it can be difficult to track all the relevant parts for your change, and there is always the possibility that the change may inadvertently affect some other functions, so some previously existing functions may stop working in some circumstances. Therefore, you need to check all the functions in the class and, even worse, all the combinations of all the functions in order to catch these errors. This means that testing will be incomplete and errors may sneak.
Now let's see how a real project works, for example log4j. There, the separation of concerns, when different classes handle formatting, convey how the output is written, providing a registrar for the category, and the interaction between all these elements is well defined, as a result of which, when you mess around or replace one part, it does not affect the other parts. Users can create their own classes that add the functionality they need (where they donβt need to know everything about the structure of the magazine, they only need to know the contract for this specific part that they want to add) and connect it, and users can mix and match different plugins. If the plugin is damaged, this gap does not extend beyond this plugin, and changes to individual parts do not threaten the integrity of the entire project. To the extent that contracts govern the interaction of parts, you do not need to check all the various combinations of specific functions.
With a single-class approach, you can never do this, every bit of new functionality added by someone would be a fork of the project. The point of principle of shared responsibility and other SOLID rules is a common strategy that allows you to change software in a controlled way without breaking anything.
source share