Why does encapsulation seem to contradict the Open-Closed principle?

1.5k views Asked by At

In encapsulation the idea is that you hide what your class is doing with its variables by declaring them private, which achieves the OCP. But then why would you then add getters and setters, which then opens up your variables for modification?

Why go through the whole trouble of setting your variables as private, to then add a public setter on them? That doesn't seem very restrictive, which is one of the ideas of encapsulation.

8

There are 8 answers

2
Yogesh Patil On BEST ANSWER

Setting a variable directly gives control to outside code like

class A {
    public String a;
}
A a = new A();
a.a = "bad value which will cause code fail"

But in case of getters and setters,

class A {
    private String a;

    public void setA(String a) {
       if(! "some bad value".equals(a) ) {
           this.a = a;
       }

    }
}

So, using getters and setters will give the control with our class instance and not other possible bad class.

0
Tim B On

Encapsulation is about hiding implementation details.

Your getter and setter methods are about part of the published API for your class and provide a well defined way to see what is happening.

Internally you could rip out all the implementation and do something completely different, but by maintaining the same set and get methods this change is invisible to every other class.

An API should be about the things that the person using it wants to do, not the internal details of how you do it.

For example if you have a class to connect to a server and have a string containing the name and port and expose that string now you are tied to that.

If in the future you want to split it and store name and port separately you cannot do so because people might be reading or setting that string directly - and you will break the code for everyone who does so.

Or even worse suppose you keep it stored the same - but you need to know when it changes. It's impossible to do that (well actually there are a number of hacky ways like storing what the string held last time you looked and checking for changes, or having a thread running to look for changes etc, all these methods are hacky and inefficient though)

0
Nicola Musatti On

In general you should design separately the interface to a class, i.e. how you expect your class to be used, from its implementation, i.e. how it works. By doing so it may happen that some of your methods end up doing little more than setting or getting an attribute's value.

However you should do it only when it makes sense rather than as a general approach.

What really doesn't make any sense are classes with only private attributes, a setter/getter public method for each attribute and no other method. There are situations were you do need to move a few values together without any specific logic that binds them; in that case you should define a class with only public attributes and no methods, except possibly a constructor.

2
Marko Topolnik On

opens up your variables for modification

In Open-Closed Principle, the word "open" means something entirely different.

It means that the source code of the class is closed for modification, but the resulting class artifact is open for further modification by extension. Neither "open" nor "closed" refers to any aspect of the instances of the class.

On the other hand, I am with you on the critique of getters/setters from a slightly different angle: they add a huge amount of boilerplate just to revert your overall design to something almost identical to a class with public fields.

In some cases, which are less commonplace, it still pays to gave getters/setters. This is true for classes which belong to a public API of a library, and generally to any class which has some non-trivial behavior, configured through the setters.

For pure data classes, getters/setters are mainly forced into them by the requirements of outdated frameworks which refuse to work with public fields. Spring, Hibernate, and Jackson are examples of state-of-the-art frameworks which do not force this.

17
Ahmed Hamdy On

Here you are.
Assuming this is my instance variable

private double x;

By using Setters, you can make your own validation on the value will be set to the instance variable.

public void setX(double x)
{
    if (x <= 0)
        System.out.println("Wrong value, value should be greater than 0");
    else
        this.x = x;
}

By using Getters, you will be able to return the data in the format you want the user to see.

public double getX()
{
    return Double.valueOf(String.format("%.3f", this.x));
}
0
Peter Walser On

By having setters and getters, you can

  • Hide the actual implementation (not every property has to be backed up by a variable, could as well be a caculated property, or a converted property, or the value is read/written from elsewhere)
  • Restrict access, by only providing getters for those properties you want to expose, and only providing setters for properties you want to allow to be modified
  • Control access, for example rejecting values when they are not correct, or when accessing with insufficient privileges
  • Add observer support, by guarding state changes and firing change events when something changes
  • Trigger logic when values are read/) written
  • and much more.
0
Fuhrmanator On

You're absolutely right about blatantly adding setters/getters to every private member being a violation of encapsulation and OCP.

To explain the paradox, OCP requires some context (i.e., closed relative to what?). At a class-design level, OCP means clients of the (public part of the) class won't have to change if you change the (private) implementation details. The private parts of a class are open to change, relative to the clients using the closed (public) interface.

Adding a getter/setter for every private variable will violate OCP. It's the same as making every private attribute a public one (except for the validation aspects some other answers have mentioned, but those are not big points in my opinion). For more explanation about why this is bad, see Why getter and setter methods are evil.

Rather than design methods based on implementation details (getters and setters), try to consider the public interface of a class as a set of "services" or "responsibilities" that class offers to the system. Those services should hide the implementation details if possible. Think of how clients of the class are going to use it. See more in Responsibility-driven design.

As for getters and setters, they aren't always bad. Just think twice before adding them. Public methods are a kind of promise to clients of the class that you're supporting a service (clients who use that class will break if you change the public interface). You might make it harder for yourself to change the details of the class if you're revealing them in the public interface.

There's another angle to consider when you return (via get) a reference to an object that is part of an implementation detail. The client who gets that reference can then possibly manipulate the object (get reveals implementation details). For example, Student.admissionDate:Date has a getter that returns a reference to the Date. A client of the Student class could get the date, and change it. To avoid this, the getter should return a clone of the object. In Java it would be return (Date)admissionDate.clone()

0
Alexander Sokolov On

Example in accepted answer does not fulfill the "open" part of OCP. Some extending class can have to get the "a" property. It will cause error.

class B extends A
{
    private String b;
    public void setDefaultB()
    {
        this.b = this.a;
    }
}

To fulfill OCP the "A" class should contain protected getter for "a" property.

protected String getA()
{
    return this.a;
}

So the "B" class can use

this.b = this.getA();