Java – Is encapsulation still one of the elephants OOP stands on

javaobject-oriented

Encapsulation tells me to make all or almost all fields private and expose these by getters/setters. But now libraries such as Lombok appear which allow us to expose all private fields by one short annotation @Data. It will create getters, setters and setting constructors for all private fields.

Could somebody explain to me what is the sense of hiding all fields as private and after that to expose all of them by some extra technology? Why do we simply not use only public fields then? I feel we have walked a long and hard way only to return to the starting point.

Yes, there are other technologies that work through getters and setters. And we cannot use them through simple public fields. But these technologies appeared only because we had those numerous properties – private fields behind public getters/setters. If we had not the properties, these technologies would develop another way and would support public fields. And everything would be simple and we wouldn't have any need for Lombok now.

What is the sense of the whole this cycle? And has encapsulation really any sense now in real life programming?

Best Answer

If you expose all your attributes with getters/setters you are gaining just data structure which is still used in C or any other procedural language. It is not encapsulation and Lombok is just making to work with procedural code less painful. Getters/setters as bad as plain public fields. There is no difference really.

And data structure is not an object. If you will start creating an object from writing an interface you will never add getters/setters to the interface. Exposing your attributes leads to spaghetti procedural code where manipulation with data is outside of an object and spread allover the codebase. Now you are dealing with data and with manipulations with data instead of talking to objects. With getters/setters, you will have data-driven procedural programming where manipulation with that done in the straight imperative way. Get data - do something - set data.

In OOP encapsulation is an elephant if done in right way. You should encapsulate state and implementation details so that object has full control on that. Logic will be focused inside object and will not be spread all over the codebase. And yes - encapsulation is still essential in programming as the code will be more maintainable.

EDITS

After seeing the discussions going on I want to add several things:

  • It doesn't matter how many of your attributes you expose through getters/setters and how carefully you're doing that. Being more selective will not make your code OOP with encapsulation. Every attribute you expose will lead to some procedure working with that naked data in imperative procedural way. You will just spread your code less slowly with being more selective. That doesn't change the core.
  • Yes, in boundaries within the system you get naked data from other systems or database. But this data is just another encapsulation point.
  • Objects should be reliable. The whole idea of objects is being responsible so that you don't need to give orders that are straight and imperative. Instead you're asking an object to do what it does well through the contract. You safely delegate acting part to the object. Objects encapsulate state and implementation details.

So if we return to the question why we should do this. Consider this simple example:

public class Document {
    private String title;

    public String getTitle() {
        return title;
    }
}

public class SomeDocumentServiceOrHandler {

    public void printDocument(Document document) {
        System.out.println("Title is " + document.getTitle());
    }
}

Here we have Document exposing internal detail by getter and have external procedural code in printDocument function which works with that outside of the object. Why is this bad? Because now you just have C style code. Yes it is structured but what's really the difference? You can structure C functions in different files and with names. And those so called layers are doing exactly that. Service class is just a bunch of procedures that work with data. That code is less maintainable and has many drawbacks.

public interface Printable {
    void print();
}

public final class PrintableDocument implements Printable {
    private final String title;

    public PrintableDocument(String title) {
        this.title = title;
    }

    @Override
    public void print() {
        System.out.println("Title is " + title);
    }
}

Compare with this one. Now we have a contract and the implemantation details of this contract is hidden inside of the object. Now you can truly test that class and that class is encapsulating some data. How it works with that data is an object concerns. In order to talk with object now you need to ask him to print itself. That's encapsulation and that is an object. You will gain the full power of dependency injection, mocking, testing, single responsibilities and a lot bunch of benefits with OOP.

Related Topic