The four pillars are encapsulation, abstraction, inheritance and polymorphism.
Encapsulation keeps an object's state private. The object exposes behaviour (methods) that protects its rules. Example: an Account offers withdraw(amount) instead of a public balance field. Because of this, no caller can push the balance below zero.
Abstraction hides how something works and shows only what it does. Example: a PaymentGateway interface has a charge() method. The order code calls charge() and does not care whether Stripe or PayPal is behind it.
Inheritance lets a class reuse and extend another class's code. Example: a BaseHttpClient has retry logic, and each specific API client extends it. Inheritance ties the child class tightly to the parent class (tight coupling). So use it only a little.
Polymorphism lets code call the same method on different types, and each type runs its own version. Example: notifier.send(msg) works the same way for EmailNotifier and SmsNotifier. This removes long if/switch chains that check the type.
- Encapsulation hides state and exposes behaviour that protects the object's rules (invariants).
- Abstraction means you depend on what something does, not how it does it.
- Inheritance reuses code through an is-a relationship, but it couples the child tightly to the parent.
- Polymorphism means one call gives type-specific behaviour, so you need fewer type switches.