Search code examples
javaoopsolid-principlesopen-closed-principle

Am I violating the "open/closed" principle?


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)?


Solution

  • 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.