Scenario: I stored some information (e.g. an array of doubles) in a class field (say field Measurements
, array of integers in a class MeasureData
). Now I would like to use this data to perform some calculations (e.g compute the arithmetic mean of the array, the maximum and the minimum). At the moment, I don't know if in the future I'll need to do any other operation on those data (e.g. maybe I will need to get the standard deviation, the sum or whatever). I'll have many objects of type MeasureData
.
Solution: I could write a class Calculator
, declare it final, use a private constructor and use several static methods to perform the calculations I need. This seems to make sense, since Calculator
acts as an utility class, without any field, much like the standard Math
class.
Problem: if, in a couple of months, I'll need to do any other calculation, I'll be needing to write another static method in Calculator
. Does this mean to violate the open/closed principle (after all, I'm modifying the implementation of the class Calculator
)?
You are violating this principle if you directly work with a concrete implementation instead of an abstraction. The violation becomes visible when you must extend existing code to accommodate new functionality.
The Open-Close principle (OCP) is the O in the well known SOLID acronym. A module will be said to be open if it is still available for extension. For example, it should be possible to add fields to the data structures it contains, or new elements to the set of functions it performs.
the open/closed principle is generally achieved by using inheritance and polymorphism.
The Open Closed Principle is a design principle that states that software components (such as classes and methods) should be open for extension but closed for modification. In other words, it means that you can add new functionality to your software without having to change the existing code.
The strict answer is yes; OCP states that a class is open for extension but closed for modification. You would be modifying Calculator
, and, hence, violating OCP (as you've already concluded).
This leads to two points:
First, is violating OCP a big deal in this case? You're additively changing Calculator
to add a new method to it. Calculator
is a static helper class used to get meaningful data from your objects. Adding a new method, like calculating SD, is not going to affect any of the other operations within it. With a proper implementation, is there really a way that adding this method could compromise your project?
Second, if you feel like the OCP violation is not acceptable, then this is a textbook example of where Strategy Pattern can be utilized. Consider:
Measurements.Java
public class Measurements {
private int[] data;
public Measurements(int[] data) {
this.data = data;
}
public Number performCalculation(Calculation c) {
return c.performCalculation(data);
}
}
Calculation.java
public interface Calculation {
Number performCalculation(int[] data);
}
You can then make a calculation class for each different calculation you want to do on the data (eg: MeanCalculation
, StdDevCalculation
, etc.). If you want a new calculation (eg: MedianCalculation
), you can make this without modifying any of the other code in this area (closed for modification, open for extension; OCP compliant). The end result looks like:
Measurements values = ...
Number mean = values.performCalculation(new MeanCalculation());
Number SD = values.performCalculation(new StdDevCalculation());
// etc.
I'm not saying this is the best approach (or best implementation of the approach even) for your specific case; you need to answer that for yourself. But I hope this answer provides a decent external perspective on the matter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With