Ok, so here is my answer.
I cannot attest to the historical origin of the principle, but it is still often used today. I don’t think it’s dangerous to change the existing code (although it certainly is) to let you highlight ideas.
Suppose we have a component
public class KnownFriendsFilter{ IList<People> _friends; public KnownFriendsFilter(IList<People> friends) { _friends = friends; } public IList<Person> GetFriends(IList<Person> people) { return people.Where(p=>_friends.Contains(p)).ToList(); } }
Now tell me the algorithm in which this particular component needs a little modification - for example, you want to make sure that the original list passed to contains individual people. This is what would be a problem for KnownFriendsFilter, so be sure to change the class.
However, there is a difference between this class and the supported function.
- This class is really designed to filter many people for famous friends.
- The function that he supports is to find all friends from an array of people.
The difference is that a function is associated with a function, while a class is associated with an implementation. Most of the requests we receive to change this function go beyond the specific responsibility of the class.
For example, let's say we want to add a blacklist of any names that start with the letter “X” (because these people are obviously cosmic, not our friends). Something that supports this function, but is not really part of what this class is, sticking to it in a class would be inconvenient. How about when the next request arrives, now the application is used exclusively by misogynists, and any female names should also be excluded? Now you need to add logic to decide if the name is a man or a woman in the class — or at least know about some other class that knows how to do this — the class grows in responsibilities and becomes very bloated! What about cross-cutting issues? Now we want to register whenever we filter an array of people, does this also happen?
It would be better to split the IFriendsFilter interface and wrap this class in a decorator or reinstall it as an IList responsibility chain. Thus, you can place each of these responsibilities in a class that supports this particular problem. If you add dependencies, then any code that uses this class (and is centrally used in our application) should not change at all!
So, the principle is not to never change existing code - it does not end in a situation where you are faced with a decision between inflating the responsibilities of a commonly used class or editing every place that it uses.