8 Object-Oriented Programming Concepts
Eight OOP concepts covering encapsulation, inheritance, polymorphism, and abstraction.
Object-oriented programming is often introduced as a list of definitions, but the useful question is what each concept helps you protect. Good OOP makes change safer by keeping state, behaviour, and collaboration understandable. Bad OOP hides simple logic behind inheritance, mutable state, and too many layers.
1. Abstraction
Abstraction means exposing the capability a caller needs while hiding unnecessary detail. A payment object might offer authorise() and capture() without revealing HTTP retries or credential rotation. Good abstraction lowers cognitive load. Poor abstraction merely hides important facts and forces callers to learn internal quirks anyway.
2. Encapsulation
Encapsulation keeps state changes behind controlled operations so invariants hold. If any caller can set balance, status, or inventory directly, the object cannot defend its own rules. Encapsulation is not privacy for its own sake. It is about ensuring objects move through legal state transitions rather than arbitrary mutations.
3. Inheritance
Inheritance lets one type reuse or refine behaviour from another, but it comes with tight coupling. Deep hierarchies become fragile because subclasses depend on parent assumptions that may change later. Inheritance works best when the relationship is genuinely stable and natural, not when it is used as the first answer to code reuse.
4. Polymorphism
Polymorphism allows different types to satisfy the same contract with their own implementations. A notification workflow can call send() on email, SMS, or push channels without branching on concrete classes. This helps variation stay local. New behaviour can be added by implementing the contract rather than rewriting every caller.
5. Composition
Composition builds behaviour by assembling smaller objects. Instead of inheriting everything from one base class, an object can depend on collaborators for pricing, validation, storage, or formatting. Composition usually ages better than inheritance because the relationships stay explicit and can be swapped or tested independently.
6. Interfaces and contracts
A stable contract lets code depend on behaviour rather than on a concrete class. Interfaces, protocols, or abstract types make this possible in many languages. The value is not abstraction for its own sake. It is the ability to describe what a collaborator must do without forcing the entire system to know how it does it.
7. Message passing
Objects collaborate by sending requests to each other. Thinking in messages clarifies responsibility: which object should decide, which should validate, and which should store state? This mindset often produces better designs than treating objects as passive data bags with utility functions attached from the outside.
8. Immutability
Immutability is not exclusive to OOP, but it improves object models by shrinking the number of surprising states an object can enter. Immutable value objects, configuration objects, and result types are easier to reason about because they do not change behind the caller's back.
What good OOP looks like
Strong object models are usually smaller and plainer than textbooks suggest. They protect invariants, express behaviour clearly, and use composition more often than inheritance. The goal is not to make a class diagram look sophisticated. It is to make the next change local, understandable, and safe.
If an object model mostly produces getters, setters, and indirection, it is not really buying you much. The concept matters less than whether the design makes behaviour clearer and invalid states harder to reach.