Design Patterns · Foundations
Objects, Classes, and the Four Pillars
Refresh the object-oriented vocabulary every pattern uses: classes, objects, hierarchies, abstraction, encapsulation, inheritance, and polymorphism.
This lesson follows What a Design Pattern Is. Skip it if terms like superclass, interface, and polymorphism already feel automatic.
Object-oriented programming (OOP) bundles data and the behavior that works on that data into units called objects. Objects are built from blueprints called classes. Every pattern in this track is a particular arrangement of classes and objects, so the vocabulary has to be solid first.
Objects are built from classes
Take a music app. Every track has a title, an artist, a duration, and a current playback position. Every track can play, pause, and seek. The class Track declares those fields and methods. Together they are the class's members.
The data stored in an object's fields is its state. The methods define its behavior. Two Track objects share the same structure and behavior but hold different state.
UML class diagrams are the shorthand for every lesson
A class box has three parts: the name, the fields, and the methods. A minus sign marks a private member, a plus sign marks a public one, and a hash marks a protected one. Lessons drop the fields or methods when only the relationships matter.
Classes form hierarchies
A real program has many classes, and related classes share a lot. Songs, podcasts, and audiobooks all have a title and a duration, and all of them can play. Put the shared parts in a superclass called MediaItem. Each subclass inherits that state and behavior and declares only what is different.
A subclass can override a method it inherits. It can replace the behavior completely, or it can call the parent version and add to it. Podcasts and audiobooks override play() to resume from where the listener stopped.
Learn the four pillars
Four concepts separate OOP from other paradigms. Every pattern leans on at least one of them.
Abstraction
A program object does not model its real-world counterpart completely. It models only the details that matter in one context and ignores the rest. A music player and a royalty system both have a Song class, and they share almost no fields.
Encapsulation
To change a room's temperature, you set a number on a thermostat. You do not open the wall and wire the furnace. The thermostat hides its internals behind a small public interface: a display and two buttons.
Encapsulation is an object's ability to hide parts of its state and behavior and expose only a limited interface. In code, private members are visible only inside their class. protected members are also visible to subclasses.
class Wallet {
private balanceCents = 0; // nobody outside can set this directly
deposit(cents: number) {
if (!Number.isSafeInteger(cents) || cents <= 0) {
throw new Error("Deposit requires positive integer cents");
}
const nextBalance = this.balanceCents + cents;
if (!Number.isSafeInteger(nextBalance)) throw new Error("Balance exceeds safe integer range");
this.balanceCents = nextBalance;
}
balance(): number {
return this.balanceCents;
}
}
The public method protects the private state. It accepts positive integer cents and rejects totals outside JavaScript's safe integer range. Hiding the field alone would not stop invalid values entering through a method.
Most languages also have an interface keyword, which declares a contract of methods with no state. Two meanings of interface now exist: the public part of any object, and the language construct. Context usually makes the meaning clear.
Inheritance
Inheritance builds a new class on top of an existing one. Its main benefit is code reuse. Its main cost is that a subclass gets the full interface of its parent. It cannot hide an inherited method, and it must implement every abstract method, even ones that make no sense for it.
Most languages let a class extend only one superclass, but implement many interfaces. If a superclass implements an interface, every subclass implements it too.
Polymorphism
Polymorphism lets a program call a method on an object without knowing the object's concrete class, and still run the right implementation. The object can pretend to be its superclass or interface. The correct subclass behavior runs anyway.
const queue: Playable[] = [new Song(/* … */), new Podcast(/* … */), new RadioStream(/* … */)];
for (const item of queue) {
item.play(); // the same call, three different behaviors
}
Try it
Press play on a mixed queue
The loop never changes. Pick the object that happens to sit in the queue.
item.play()
→ Song.play(): stream audio from 0:00
→ shows album art and lyrics
item.play()
→ Podcast.play(): resume at 18:42
→ shows episode notes
item.play()
→ RadioStream.play(): join the live stream
→ seeking is disabled
The caller only knows Playable. The runtime picks the method from the object's real class. That is polymorphism.
Keep the pillars straight
| Pillar | One-line meaning | In this lesson |
|---|---|---|
| Abstraction | Model only what the context needs | Two different Song classes for two systems |
| Encapsulation | Hide state, expose a small interface | Wallet keeps its balance private |
| Inheritance | Build a class on top of another | Podcast extends MediaItem |
| Polymorphism | Call through a general type, run specific behavior | One play() loop over mixed items |
Check yourself
A caller invokes play() through Playable and the actual podcast implementation runs. Which concept explains the selection?
Not quite. Abstraction chooses relevant details. Polymorphism selects behavior through a shared contract.
Correct. One contract can dispatch to different concrete implementations.
Retrieve and apply
Check yourself
A Wallet makes its balance private and only allows changes through deposit(). Which pillar is that?
Not quite. Abstraction is about which details you model at all. The balance is modeled. It is just hidden.
Correct. Hiding state and exposing a limited interface is exactly encapsulation. It also lets Wallet reject a negative deposit.
Not quite. Nothing here calls through a general type to reach different implementations.
Check yourself
Which statement about inheritance is true in most languages?
Not quite. A subclass keeps the full interface of its parent. That limitation is one reason patterns often prefer composition.
Correct. Single inheritance plus multiple interfaces is the common rule in Java, C#, and TypeScript.
Not quite. Every abstract method must be implemented, even when it makes no sense for the subclass.
Continue to Relationships Between Objects to learn the six links that appear in every pattern diagram.
Source: Alexander Shvets, Dive Into Design Patterns (深入设计模式), Chinese edition v2021-1.25. Introduction to object-oriented programming, printed pages 7–19. Explanations, examples, and exercises are adapted for this course.