LESSON 1.4
Inheritance versus composition
Vary a parking fee through a pricing collaborator instead of growing a hierarchy of special parking lots.
6 min read
A parking operator needs hourly pricing, then weekend pricing, then weekend pricing with a member discount. A subclass for each combination quickly turns into a tree of names. The parking lot has not become a different kind of object; one decision inside it varies.
The problem
class ParkingLot {}
class WeekendParkingLot extends ParkingLot {}
class MemberWeekendParkingLot extends WeekendParkingLot {}
// Each independent pricing option multiplies the subclasses.The object boundary
Inheritance says that a subtype can stand in for a base type under the same contract. Composition gives an object a collaborator that performs part of its work. The question is not whether extends saves a few lines today. Ask whether the inherited contract remains true and whether the decisions need to vary independently. If pricing and allocation change separately, tying them to one inheritance tree couples two different axes of change.
The lot below delegates only the fee calculation to a pricing policy. Hourly pricing is one implementation; a test can supply a constant policy. Both preserve the contract that a non-negative duration produces a non-negative fee in integer cents. The lot validates its input before delegating, and rejects a policy result that violates the rule. That protects callers from a collaborator that technically has the right method but returns nonsense.
Composition makes the dependency visible. It also creates a decision about ownership and lifetime: can two lots share one policy, and is that policy immutable? Sharing an immutable rate is straightforward. Sharing a mutable discount policy means a change can affect several lots at once. Use explicit construction and stable values rather than treating every collaborator as a global singleton.
In code
Both versions run the same small example. Java uses a single Example class; save it as Example.java.
When the boundary earns its place
Use inheritance when the subtype genuinely preserves the base contract and a shared algorithm is intentional. Use composition when a policy should be replaceable or independently testable. Delegation adds an extra object and a call, so a fixed calculation can stay a function until variation appears. Do not turn every arithmetic expression into a hierarchy. This example becomes Strategy when policy substitution is the central design intent; the smaller idea is simply to pass the work to a collaborator.
JavaScript and Java
Java has single class inheritance and can implement several interfaces. JavaScript uses a prototype chain for inherited behavior and can compose ordinary objects without a declared interface. Neither mechanism makes an invalid substitution safe. Overriding a method to throw unsupported-operation errors often signals that the supposed subtype does not satisfy the base contract. Prefer a narrower capability to a base class that promises operations some children cannot support.
Try it
Parking Lot LLD
Identify the objects and state the rule each one owns before adding a pattern.