Most developers learn OOP backwardsâââmemorize four buzzwords, pass the exam, then go write procedural code inside classes. Thatâs not object-oriented programming. Thatâs just spaghetti with labels on it.
OOP is a design philosophy before itâs a syntax. Itâs a way of breaking a complex system into smaller, self-contained piecesâââpieces that each know their job, protect their own state, and can talk to each other without the whole thing falling apart. The syntax is easy. The thinking is what takes years.
Classes &Â Objects
A class is a blueprint. An object is the thing built from it.
Think of an architectâs floor plan for a house. The blueprint describes two bedrooms, a kitchen, a front door. But you canât live in the blueprint. When a builder follows it and constructs an actual houseâââthatâs the object. Build fifty houses from the same blueprint and each one is its own thing: different paint, different furniture, different family inside.
Same class. Two separate objects. This is the starting point.
State, Behavior &Â Identity
Every object carries three things: what it knows, what it can do, and what makes it uniquely itself.
State is the dataâââa bank accountâs balance, a userâs email address. Behavior is the methodsâââa bank account can deposit, withdraw, generate a statement. Identity is what separates one object from another even if their data is identical. Two bank accounts with the same zero balance are still two separate accounts.
A constructor is where this all begins. Itâs the objectâs moment of birthâââwhere you set up valid, usable initial state before anyone else touches it.
Good constructors do one thing: produce a valid object and get out. No database calls, no network requests, no side effects. Just state.
Methods are where responsibility livesâââbut the most important question about any method isnât âdoes it work?â Itâs âdoes it belong here?â A User class should not have a method called sendInvoice(). That's not the user's job. When a method doesn't belong, it's a signal that your boundaries are wrong.
The Four PillarsâââWhat They Actually Mean
Encapsulation
Encapsulation is not âmake things private.â Thatâs the mechanism. The principle is protecting your state from being changed in ways you didnât intend.
Think of a vending machine. You can see the snacks through the glass, put money in, and press a button. But you cannot reach inside and grab whatever you want. The machine controls its own internals. You interact through a defined interface.
The stock field is private. No external code can set stock = 999. All changes go through purchase(), which enforces the rules. That's the pointââânot hiding data for its own sake, but controlling how it changes.
Abstraction
Abstraction hides complexity behind a simple interface.
When you drive a car, you donât need to understand internal combustion to press the accelerator. The car abstracts all of that away. You get a pedal. You press it. The machinery underneath is none of your business.
The calling code just calls .charge(). It doesn't care how. Tomorrow you add a PayPal gateway. The calling code doesn't change. That's abstraction doing its job.
Inheritance
Inheritance captures the âis-aâ relationship. A Dog is an Animal. A SavingsAccount is a BankAccount. You inherit the shared structure, then specialize.
Hereâs where most people go wrong: they use inheritance for code reuse rather than for actual âis-aâ relationships. If youâre inheriting from a class just to borrow two methods, youâve built a dependency thatâll betray you when the parent class needs to change. Inheritance should model a genuine relationshipââânot be a shortcut.
Polymorphism
Polymorphism means the same interface, different behavior. One method name, many implementations depending on which object youâre actually calling it on.
printArea doesn't know or care whether it's working with a circle or a rectangle. It calls .area() and trusts the object to do the right thing. Add a Triangle tomorrowâââprintArea doesn't need to touch it. This is what makes systems extensible without becoming fragile.
Composition vs. InheritanceâââThe Choice Nobody Teaches Properly
Real OOP design lives in this question: should this object be the other thing, or should it have it?
A Car doesn't inherit from Engine. That would be absurd. A Car has an engine. It uses an engine. That's composition.
If tomorrow you need an ElectricEngine, you swap it in. The Car class doesn't change at all. Inheritance would have made that painful. Composition keeps it trivial.
The general rule: prefer composition. Use inheritance only when you have a genuine âis-aâ relationship and you want to express that relationship explicitly in the design.
Association, Aggregation & Composition
These three describe how tightly objects are bound to each otherâââand it matters more than most tutorials acknowledge.
Association is the loosest connection. A Doctor treats Patients. They know about each other. Neither owns the other. They can both exist independently.
Aggregation is a whole-part relationship where the parts survive without the whole. A Team has Players. Dissolve the team, the players still exist somewhere.
Composition is the tightest bondâââthe part cannot exist without the whole. A House has Rooms. Destroy the house, the rooms aren't standalone things anymore.
Getting this right isnât academic. It determines how you structure your database schemas, how you handle object lifecycles, and what breaks when something gets deleted.
Coupling & CohesionâââThe Two Dials
Every system has two dials youâre constantly tuning.
Coupling measures how much one class depends on another. High coupling means a change in one class ripples into others, sometimes into five others you didnât expect. Low coupling means classes can change independently. You want low coupling.
Cohesion measures how focused a class is on a single purpose. A class that handles user registration, sends confirmation emails, generates PDFs, and logs activityâââthatâs low cohesion. Itâs doing four different jobs. When you split that into four focused classes, each becomes easier to test, easier to change, and easier to understand.
High cohesion and low coupling together produce modular code. A UserRegistrationService that only handles registration and receives its email sender and logger through injection rather than creating themâââthat's what well-designed OOP looks like in practice.
The question to ask every time you write a class: if I had to change this one thing, how many other places would break?
Common OOP Design Mistakes
A few patterns that feel right until they donât:
The God Object. One massive class that knows everything and controls everything. Usually named Manager, Handler, or Utilities. This class grows without limit because "it's already there." Split it ruthlessly.
Anemic domain models. Objects that are just bags of dataââânothing but getters and settersâââwith all the actual logic sitting in a separate service. This is procedural code wearing OOP clothes. Behavior belongs with the data it operates on.
Deep inheritance chains. Five levels deep, the sixth developer canât understand what theyâre actually working with. The deeper the chain, the harder it breaks. Flatten it with composition.
Abstracting too early. Building interfaces and abstract base classes before you have two real concrete variations. Write the concrete thing first. Abstract only when you see the pattern emerge from actual codeââânot from prediction.
A Small Real-World Example
Consider an order system. A bad design puts everything into Order: it calculates totals, applies discounts, sends emails, and updates inventory. Every time any one of those things changes, you're editing this one class.
A better design:
Three classes. Three clear jobs. Order knows its own data. DiscountService knows how pricing works. OrderNotifier knows how to reach the customer. None of them know too much about the others. Swap email notifications for SMS tomorrowâââOrder doesn't care, DiscountService doesn't care. Only OrderNotifier changes.
This is low coupling and high cohesion working together. Itâs not clever. Itâs disciplined.
Conclusion: OOP as a Design Mindset
OOP isnât about memorizing four words. Itâs not a style guide. Itâs a discipline for managing complexityâââfor breaking a system thatâs too big to hold in your head into pieces that each have a clear owner and a clear job.
The programmers who use it well think before they type. They ask: where does this knowledge live? Who is responsible for this behavior? What happens to the rest of the system if this changes? They treat their objects the way an architect treats wallsâââwith respect for what they hold up.
Learn the concepts once. Then spend the rest of your time learning how they work together. The syntax is a dayâs work. The design sense takes years. Thatâs where the actual art is.
References
Gamma, Helm, Johnson, VlissidesâââDesign Patterns: Elements of Reusable Object-Oriented Software
Martin FowlerâââRefactoring: Improving the Design of Existing Code
Robert C. MartinâââClean Code: A Handbook of Agile Software Craftsmanship
MDN Web DocsâââObject-Oriented Programming in JavaScript
Mattias Petter JohanssonâââComposition over Inheritance (FunFunFunction, YouTube)
Find me across the web:
Portfolio: ahmershah.dev
Crunchbase: @syed-ahmer-shah
Crunchbase Company: @syedahmershah
Clutch: @syed-ahmer-shah
Tech Behemoth: @syed-ahmer-shah
Design Rush: @syed-ahmer-shah
Edverise: @syed-ahmer-shah
Trust Pilot: @ahmershah.dev
LinkedIn: Syed Ahmer Shah
GitHub: @ahmershahdev
AWS Builder Profile: @syedahmershah
DEV: @syedahmershah
Medium: @syedahmershah
Hashnode: @syedahmershah
Substack: @syedahmershah
HackerNoon: @syedahmershah
Substack: @syedahmershah
Facebook: @ahmershahdev
Linkedin Page: @syedahmershah
YouTube: @ahmershahdev
Instagram: @ahmershahdev
TikTok: @ahmershahdev
Top comments (0)