Reputation: 1890
I understand the basic concept and every example out there shows two completely different concerns to illustrate the point but my question is regarding related, but separate, concerns.
Take a simple calculator. It has the following 4 methods:
I think most developers would create a class with 4 methods. But that breaks SRP right? Should this be broken into 4 separate classes, one for each operation?
Maybe yes, because lets say we have overloads for each method. If we want to perform operations on int, float, and decimal our method list expands to 12. So maybe separate classes would be better. This makes a bit more sense (to separate them into 4 classes) but should you do that when there are no overloads and only 4 methods?
Lets add a few more methods:
Do you add these 3 methods to the 4 above? Do you create a separate class for each one? Do you group the 4 above in a Basic class and these 3 in a Geometry class?
Upvotes: 2
Views: 938
Reputation: 1
If you try to implement different methods for different operations in the same class then definitely it would violate SRP. And if you try to follow SRP then with each operation a new class will come into the picture and it will bloat up the number of classes.
I think a better designer would implement a single method where the operation will also be passed as a parameter. The method would be something like this:- compute(firstOperand, secondOperand, operation)
And this will return the result as per the operation passed to it.
Upvotes: 0
Reputation: 4763
Depends on how you look at the responsibilities. From this point forward I will restrain from using "computation" and use "calculate" in order to describe the result of mathematic operation (computation can have a broader meaning).
The single responsibility class of the calculator is to "Calculate". To the outside world, what's under the hood is magic, but the calculator can only Calculate.
Implementation wise , how you manage to make this "Calculate" is your choice (multiple computational classes, interpretor of input , just functions, etc etc ).
So regardless of the implementation your Calculator will respect the Single Responsibility principle.
You will break the principle if your Calculator, besides "Calculate" can also take pictures and make french fires.
Now that we established the Responsibility of the Calculator , let's zoom in deeper and see if we can define some SRP within the implementation of the Calculator.
Let's say that instead of making a huge Class with 1 function for each possible calculation you decide to make your Calculator extensible and you can add different objects that can Calculate different math functions.
Each of that object has a single responsibility principle, which is to Calculate one, and only one , mathematic expression.
So, the following classes :
Will match the SRP we defined as long as they don't do more then one computation.
If we were instead to add the function
SRP Calculate would be broken for the because you would have :
Upvotes: 3
Reputation: 31648
As with all designs - "it depends".
If you are just writing a simple calculator with add, subtract, multiply and delete on ints, by all means just make them methods.
However, as you rightly point out, when you start adding other operations it will bloat the Calculator class. Even worse if you support floating point or BigDecimal type data.
A good design might be to have an Operation
interface:
interface Operation<T>{
T apply(T a, T b);
}
Then each operation like Add
, Sin
etc would be classes (or enums if your language provides that) that implement that operation interface. The template type would allow adding integers vs adding floats vs adding BigDecimal etc.
This also lets others supply their own operations (or optimized versions of operations)
Although it might be worth considering only implementing the BigDecimal version and convert ints and floats to BigDecimal.
Upvotes: 2