Intro

Imagine you're going to a big dance party with your crush. You're really into him/her, so you don't want to mess up. You want to be sexy, charming and funny. Now, WHAT COULD POSSIBLY GO WRONG?

For the sake of this article, you and your crush are objects, and your life is the component you're in. I'll show you why you need encapsulation at various levels of abstraction to succeed.

Method Level

So you come to the party and notice that nobody's dancing yet, everybody's shy, staying by the tables. Of course, you don't want to lean out, so you offer your crush something to eat. Imagine his/her reaction if you asked like this:

Maybe we should go to the table, take some food, put it in our mouths, chew for a bit, yummy, yummy, and then swallow?

Or imagine everybody's dancing already and you want to hit the dancefloor, so you say:

Maybe we should go in the middle of those people and start roaming with our legs interacting to the rythm of music?

You'd be speaking like an idiot!

Methods are the way your objects speak and act. When you name a method by enumerating the things it does, you make your object speak like an idiot.

// bad
you.putInMouthAndChewAndYummyYummyAndSwallow(food);  
you.roamAndInterleaveYourLegsToTheRythm(yourCrush);  

Therefore

Encapsulate your methods' behaviors using short, descriptive, intention-revealing names to make your objects speak and act normally.

//good
you.eat(food);  
you.dance(yourCrush);  

Class Level: Fields

You might know how to speak normally, but still be pretty nervous and do something stupid e.g. get drunk. Of course, you really care that things work out well with your crush, so you won't drink too much on purpose. But what if I told you that you have no control over your alcohol digestion? Any of your stupid friends around can go by and make you drunk. Seems insane?

Well, this is what happens to objects with public fields. By making them public, everyone can read or, even worse, change them!

class You {  
    // bad
    public Mouth mouth;
}
you.mouth.contents.add(lotsOfAlcohol); // ugh!  

Therefore

Encapsulate your objects' fields by making them private. This way, you can fully control access to them, i.e. prevent unwanted access.

class You {  
    // good
    private Mouth mouth;
}

Class Level: Accessors

Now imagine you have some control — people have to ask you to drink, but you still agree every time they ask. Has the situation gotten any better? You might ask, "Why do I agree every time?"

Well, auto-generated getters and setters never say no! Of course, there are some questions you always answer positively, e.g. someone offering you a free pizza, and there are some cases when auto-generated getter or setter is what your object needs. But in most cases, it's doing more harm than good.

class You {  
    private Mouth mouth;

    // bad
    public Mouth getMouth() {
        return mouth;
    }
}
you.getMouth().getContents().add(lotsOfAlcohol); // ugh!  

Therefore

Don't auto-generate getters and setters for all of your object's fields. That's not any better than making the fields public.

class You {  
    private Mouth mouth;

    // nothing. good!
}

Class Level: Solution

Okay, getting drunk is not the right way to impress your crush. You want to show what's best about you without doing anything stupid. How do you keep things under control?

As written above: Methods are your objects' way of speaking and acting.

Therefore

Expose a few public methods with meaningful names and clear intentions that will show the very best of your object!

class You {  
    private Mouth mouth;

    // good
    public void drink(Beverage beverage) {
        if (beverage.containsAlcohol()) {
            throw new NoThankYouException();
        }
        mouth.put(beverage);
    }

    public void dance(YourCrush yourCrush) {
        embrace(yourCrush);
        sway();
        yourCrush.whisper("I'm glad you're here");
    }

    ...
}

Component Level: Separation

You didn't speak like an idiot and you didn't get drunk. Everything goes well until you see your ex running at you and screaming. You look around and wonder how this happened. Your ex is not supposed to be at this party. Who let him/her in?! And then you see it... There's no door! Everyone can come in and mess with your plans! What's going on?!

Making all classes public and leaving no indication of which classes should and which shouldn't be used outside of the component means anyone can use any class, in any way he/she likes. An object (like your ex) can come in and mess around. This is the missing door of your component.

Therefore

Encapsulate your components internals by making it explicit what is your components public API. You can achieve that by marking only selected classes as public or through expressive package structure (or both).

Component Level: Abstraction

Your ex didn't get into the party, but they still knew that you went there and you're into someone else already. This made him/her so depressed that he/she committed suicide. Now, this is crazy! How did your ex know this stuff in the first place? Well, this is what happens when you give your ex your phone, when you meant to just give the phone number.

Making the public API of your component consist of concrete classes can be equivalent to giving your ex the phone instead of the number. Your client objects depend on your internals and can, or worse, have to adapt to changes inside your component.

Therefore

Unless you don't care about such dependencies, make the public API of your component consist of interfaces instead of concrete classes.

Finale

You had the talk, you didn't get drunk and your ex didn't cause you any trouble. Your crush is another step closer to falling in love with you! Most importantly, there's no negative tension around — the relationship between you, your crush, and your ex remained healthy.

Do you know what we call a healthy relationship in programming? LOOSE COUPLING!