← Back to all projects

LEARN OBJECT ORIENTED PROGRAMMING

Learn Object-Oriented Programming: From First Principles to Design Patterns

Goal: Deeply understand Object-Oriented Programming (OOP)—not just the definitions, but why it exists, why each concept is important, and how to use Design Patterns to solve real-world problems elegantly.


Why Does Object-Oriented Programming Exist?

Before OOP, we had procedural programming. We wrote programs as a sequence of steps, with data and functions separated. For a small script, this is fine. But as programs grow, you get a mess of global variables being modified by dozens of functions. It becomes impossible to track who changes what, leading to fragile, spaghetti-like code that is terrifying to modify.

OOP was created to solve this problem by bundling data and the functions that operate on that data together into “objects.” This simple idea—creating self-contained little bundles of logic and state—is the key to managing complexity in large software systems.

After completing these projects, you will:

  • Understand the purpose behind Encapsulation, Abstraction, Inheritance, and Polymorphism.
  • Structure your code to be more organized, reusable, and less prone to bugs.
  • Recognize common problems in software and apply the correct Design Pattern to solve them.
  • Write code that is not just functional, but also clean, flexible, and maintainable.

Core Concept Analysis

The Four Pillars of OOP

OOP stands on four main pillars. They work together to help you build robust and flexible software.

┌─────────────────────────────────────────────────────────────────────────┐
│                          POLYMORPHISM (Many Forms)                       │
│      "Treat different objects the same way. Call draw() on any shape."     │
│      (Achieved through Abstraction and Inheritance)                      │
└─────────────────────────────────────────────────────────────────────────┘
                   ▲
                   │ Relies on
┌──────────────────┴──────────────────┐ ┌───────────────────────────────────┐
│      ABSTRACTION (Show Essential)     │ │     INHERITANCE (IS-A Relationship)   │
│ "You don't need to know how the engine │ │ "A Circle IS-A Shape. It gets all  │
│  works. Just use the steering wheel."  │ │    the basic properties of a shape."  │
└─────────────────────────────────────┘ └───────────────────────────────────┘
                   ▲
                   │ Built upon
┌──────────────────┴────────────────────────────────────────────────────────┐
│                   ENCAPSULATION (Hide Complexity)                         │
│ "Bundle data and methods together. Protect the car's engine from tampering."│
└─────────────────────────────────────────────────────────────────────────┘

Design Patterns: The Next Level

Once you understand the four pillars, Design Patterns are the next step. They are reusable, battle-tested solutions to commonly occurring problems within a given context. They aren’t code you can copy-paste, but rather templates for how to structure your objects to solve a problem efficiently.

  • Creational Patterns: Deal with object creation mechanisms (e.g., Factory, Singleton).
  • Structural Patterns: Deal with object composition (e.g., Decorator, Adapter).
  • Behavioral Patterns: Deal with communication between objects (e.g., Strategy, Observer).

Project List

These projects are designed to introduce OOP concepts by first showing you the problem and then letting you use the OOP concept as the solution.


Project 1: From Procedural Mess to a Simple Object

  • File: LEARN_OBJECT_ORIENTED_PROGRAMMING.md
  • Main Programming Language: Python
  • Alternative Programming Languages: Java, C#
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 1: Beginner
  • Knowledge Area: OOP Fundamentals / Encapsulation
  • Software or Tool: Any text editor and a runtime environment.
  • Main Book: “Head First Design Patterns, 2nd Edition” by Eric Freeman and Elisabeth Robson

What you’ll build: You’ll start with a small, procedural text-based game where player data (health, position, score) is stored in global variables and manipulated by standalone functions. You will then refactor this mess into a single Player class.

Why it teaches OOP: This project reveals why OOP was invented. You will feel the pain of managing disconnected data and functions. Refactoring it into a class will be a “eureka!” moment, as you see how Encapsulation naturally organizes your code and protects your data.

Core challenges you’ll face:

  • Tracking which function modifies which global variable → maps to the problem of unstructured state
  • Bundling player data and functions into a single class → maps to the solution: Encapsulation
  • Making data private and exposing public methods → maps to protecting invariants and creating a stable interface
  • Realizing how much cleaner the main game loop becomes → maps to the benefit of organized code

Key Concepts:

  • Encapsulation: “Head First Java” by Kathy Sierra and Bert Bates, Chapter 2.
  • State management: The core problem this project highlights.

Difficulty: Beginner Time estimate: Weekend Prerequisites: Basic programming knowledge in any language.

Real world outcome: You’ll have two versions of the same simple program. The first will be hard to read and easy to break. The second will be clean, organized, and robust.

Before (Procedural):

player_health = 100
player_pos_x = 0

def damage_player(amount):
  global player_health
  player_health -= amount
# ... more functions, all using global state

After (OOP):

# A simple console output showing the game state.
# You will see how interactions become clearer.
# > player.move('north')
# Player moved to (0, 1)
# > player.take_damage(10)
# Player health: 90

Implementation Hints:

  • Start by writing the procedural version. Make at least 3-4 global variables and 5-6 functions that modify them. Try to make a change and see how many places you have to update.
  • Now, create a Player class. Move the global variables inside the class as properties (e.g., self.health).
  • Convert the global functions into methods of the class (e.g., def take_damage(self, amount)).
  • In your main game loop, create an instance of the player: player1 = Player(). Call methods on this instance.
  • Try making a property “private” (e.g., _health in Python) and see how it prevents accidental modification from outside the class.

Learning milestones:

  1. You feel the frustration of managing global state → You understand the problem.
  2. You successfully create a Player class that holds its own data → You have implemented Encapsulation.
  3. The main program logic now reads like a story (player.move(), player.attack()) → You see the readability benefits.
  4. You realize you can create player2 = Player() and they won’t interfere with each other → You understand the power of instances.

Project 2: Vending Machine Simulator

  • File: LEARN_OBJECT_ORIENTED_PROGRAMMING.md
  • Main Programming Language: Java
  • Alternative Programming Languages: C#, Python
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 1: Beginner
  • Knowledge Area: OOP Fundamentals / Abstraction
  • Software or Tool: Any IDE.
  • Main Book: “The Pragmatic Programmer: your journey to mastery” by David Thomas and Andrew Hunt

What you’ll build: A console-based simulation of a vending machine. It will have an internal state (inventory of items, money collected) but will only expose a simple public interface to the user.

Why it teaches OOP: This project is a perfect lesson in Abstraction. The user of the VendingMachine object doesn’t need to know how it manages inventory or gives change. They just interact with simple methods like insertCoin(), selectItem(). This hides complexity and makes the object easy to use.

Core challenges you’ll face:

  • Defining the public interface → maps to what are the essential operations a user needs?
  • Hiding the internal implementation details → maps to keeping internal data private
  • Managing the machine’s state safely → maps to ensuring methods correctly update internal state
  • Separating the VendingMachine logic from the Console UI → maps to separation of concerns

Key Concepts:

  • Abstraction: “Clean Code” by Robert C. Martin, Chapter 2.
  • Interfaces (Public API of a class): “Effective Java” by Joshua Bloch, Item 18.

Difficulty: Beginner Time estimate: Weekend Prerequisites: Project 1.

Real world outcome: A running console application that simulates interacting with a vending machine.

Welcome to the Vending Machine!
Items available: [Soda: $1.50 (5 left)], [Chips: $1.00 (3 left)]
Your balance: $0.00
> insert 1.00
Your balance: $1.00
> select Soda
Not enough money. Please insert $0.50 more.
> insert 0.50
Your balance: $1.50
> select Soda
Dispensing Soda. Your change is $0.00.

Implementation Hints:

  • Create a VendingMachine class.
  • Inside, have private fields for Map<String, ItemSlot> (inventory) and double moneyCollected.
  • ItemSlot could be another small class containing an Item and a quantity.
  • The public interface should be simple: void insertMoney(double amount), Item dispenseItem(String itemName), double getChange().
  • All the logic (checking price, updating quantity, calculating change) should be hidden inside these public methods.

Learning milestones:

  1. You have a VendingMachine class that works, but the main program only calls 3-4 simple methods → You have created a successful abstraction.
  2. You can change the internal logic (e.g., how inventory is stored) without changing the main program → You understand the benefit of hiding implementation details.
  3. The machine cannot be put into an invalid state from the outside → You have protected the object’s integrity.

Project 3: Geometric Shapes and an Area Calculator

  • File: LEARN_OBJECT_ORIENTED_PROGRAMMING.md
  • Main Programming Language: C#
  • Alternative Programming Languages: Java, Python
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: OOP Fundamentals / Inheritance & Polymorphism
  • Software or Tool: Any IDE.
  • Main Book: “Head First Java, 3rd Edition” by Kathy Sierra, Bert Bates, and Trisha Gee

What you’ll build: A set of classes representing geometric shapes (Circle, Rectangle, Triangle). You will create a base Shape class from which these concrete shapes inherit, and then write a single function that can calculate the area of any shape.

Why it teaches OOP: This is the classic, perfect introduction to Inheritance and Polymorphism. You’ll learn how to share common code with a base class (Inheritance) and how to treat different objects as if they were the same type (Polymorphism), which dramatically simplifies your code.

Core challenges you’ll face:

  • Identifying common attributes and behaviors for a base Shape class → maps to finding the right abstraction
  • Implementing the is-a relationship (Circle is-a Shape) → maps to Inheritance
  • Forcing subclasses to implement their own version of a method → maps to abstract methods
  • Writing a single loop that processes a list of different shapes → maps to Polymorphism

Key Concepts:

  • Inheritance: “Head First Java”, Chapter 7.
  • Polymorphism/Abstract Classes: “Head First Java”, Chapter 8.
  • Liskov Substitution Principle: A core principle that makes polymorphism work reliably.

Difficulty: Intermediate Time estimate: Weekend Prerequisites: Project 2.

Real world outcome: A console application that calculates and prints the area of various shapes held in a single list.

Calculating areas...
- Rectangle (4x5): Area = 20.0
- Circle (radius 3): Area = 28.27
- Triangle (base 6, height 4): Area = 12.0
Total area of all shapes: 60.27

The magic is that your calculation loop doesn’t have if (shape is Circle) or if (shape is Rectangle). It just calls shape.CalculateArea() on every object.

Implementation Hints:

  • Create an abstract class Shape (or an interface). It should define an abstract double CalculateArea();.
  • Create concrete classes like Circle, Rectangle that extend (or implement) Shape.
  • Each concrete class will have its own private fields (e.g., radius for Circle, width and height for Rectangle).
  • Each concrete class must provide its own implementation for the CalculateArea() method.
  • In your main program, create a List<Shape>. Add new Circle, Rectangle, and Triangle objects to this list.
  • Loop through the list and print the result of calling CalculateArea() on each element.

Learning milestones:

  1. You have Circle and Rectangle classes that inherit from Shape → You have used Inheritance.
  2. You cannot create an instance of Shape itself → You understand what abstract means.
  3. Your main loop iterates through a List<Shape> without checking the specific type of each shape → You have achieved Polymorphism.
  4. You can add a new Pentagon class and the main loop works without any changes → You understand the power of the Open/Closed Principle enabled by polymorphism.

Project 4: The Factory Pattern - A Document Creator

  • File: LEARN_OBJECT_ORIENTED_PROGRAMMING.md
  • Main Programming Language: Java
  • Alternative Programming Languages: C#, Python
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Creational Design Patterns
  • Software or Tool: Any IDE.
  • Main Book: “Design Patterns: Elements of Reusable Object-Oriented Software” by the Gang of Four (GoF)

What you’ll build: A program that creates different types of documents (Report, Resume, Memo). Instead of using the new keyword directly in your main logic (e.g., new Report()), you will use a DocumentFactory to create the objects for you.

Why it teaches patterns: This introduces you to your first Creational Design Pattern. The Factory Pattern decouples your client code from the concrete classes it needs to instantiate. This makes your system more flexible; you can introduce new document types or change how they are created without touching the code that uses the documents.

Core challenges you’ll face:

  • The problem of if/else or switch statements for object creation → maps to the inflexibility the factory solves
  • Creating a DocumentFactory class → maps to centralizing creation logic
  • Decoupling the client from the concrete document classes → maps to programming to an interface
  • Extending the system with a new document type without changing client code → maps to the benefit of the pattern

Key Concepts:

  • Factory Method Pattern: GoF book, or “Head First Design Patterns”, Chapter 4.
  • Decoupling: A core theme of all design patterns.

Difficulty: Intermediate Time estimate: Weekend Prerequisites: Project 3.

Real world outcome: A console application that asks the user what type of document they want and then generates it.

What type of document do you want to create? (Report, Resume)
> Report
Creating Report...
Report created with sections: [Title Page, Table of Contents, Body, Appendix]

> Resume
Creating Resume...
Resume created with sections: [Contact Info, Experience, Education]

The key is that your main application logic will just say Document doc = factory.createDocument(userInput); and have no idea how a Report or Resume is actually constructed.

Implementation Hints:

  • Create a Document interface or abstract class with some common methods (e.g., getSections()).
  • Create concrete classes Report, Resume, etc., that implement Document.
  • Create a DocumentFactory class with a method createDocument(String type).
  • Inside this method, use an if/else or switch to decide which concrete class to new up and return.
  • Your main client code should only know about the Document interface and the DocumentFactory, not the concrete classes.

Learning milestones:

  1. Your client code no longer uses the new keyword for documents → You have decoupled the client from creation.
  2. The creation logic is centralized in the factory → You have a single place to manage instantiation.
  3. You can add a new Memo document type by only changing the factory → The rest of your application doesn’t need to be touched.
  4. You realize the factory can do complex work, like configuring the object or pulling from a cache, all hidden from the client → You see the true power of abstracting creation.

Project 5: The Decorator Pattern - Coffee Shop Orders

  • File: LEARN_OBJECT_ORIENTED_PROGRAMMING.md
  • Main Programming Language: Java
  • Alternative Programming Languages: C#, Python
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Structural Design Patterns
  • Software or Tool: Any IDE.
  • Main Book: “Head First Design Patterns, 2nd Edition”

What you’ll build: A coffee shop ordering system where you can dynamically add condiments (milk, soy, mocha, whip) to a beverage. Each condiment adds to the cost and description.

Why it teaches patterns: This project is the canonical example of the Decorator Pattern. It solves the problem of “class explosion” (e.g., CoffeeWithMilk, CoffeeWithMilkAndSoy, DarkRoastWithWhip…). It teaches you how to add responsibilities to objects dynamically by wrapping them in other objects.

Core challenges you’ll face:

  • The inflexibility of using inheritance for optional features → maps to the problem the pattern solves
  • Creating a common interface for beverages and decorators → maps to interchangeability
  • Implementing decorators that wrap other beverages → maps to the core of the pattern
  • Composing an order by “decorating” a base beverage → maps to dynamic composition

Key Concepts:

  • Decorator Pattern: “Head First Design Patterns”, Chapter 3.
  • Composition over Inheritance: A major OOP principle demonstrated by this pattern.

Difficulty: Advanced Time estimate: Weekend Prerequisites: Strong understanding of inheritance and polymorphism (Project 3).

Real world outcome: A console application that creates and describes complex coffee orders.

Ordering a Dark Roast...
- Order: Dark Roast, Cost: $0.99

Ordering a House Blend with Soy, Mocha, and Whip...
- Order: House Blend, Soy, Mocha, Whip, Cost: $2.04

The elegance is that you never created a HouseBlendWithSoyMochaAndWhip class. You composed it at runtime.

Implementation Hints:

  • Create an abstract Beverage class with getDescription() and cost() methods.
  • Create concrete beverages like HouseBlend and DarkRoast that extend Beverage.
  • Create an abstract CondimentDecorator class that also extends Beverage. This is the key trick.
  • The CondimentDecorator must have a Beverage instance variable (it wraps another beverage).
  • Create concrete decorators like Milk, Soy, Mocha that extend CondimentDecorator.
  • Their getDescription() will call the wrapped beverage’s getDescription() and add their own.
  • Their cost() will call the wrapped beverage’s cost() and add their own.
  • To create an order, you do: Beverage myCoffee = new Mocha(new Soy(new HouseBlend()));.

Learning milestones:

  1. You have decorators and beverages that share the same Beverage type → You understand the “is-a” and “has-a” relationship in the pattern.
  2. You can wrap a beverage in multiple decorators → You see the dynamic composition in action.
  3. The final cost and description are calculated correctly by delegating down the chain of wrappers → You understand how decorators augment behavior.
  4. You can add a new condiment (e.g., Caramel) without changing any existing code → You see the Open/Closed Principle at work.

Project 6: The Strategy Pattern - A Character’s Attack

  • File: LEARN_OBJECT_ORIENTED_PROGRAMMING.md
  • Main Programming Language: C#
  • Alternative Programming Languages: Java, Python
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Behavioral Design Patterns
  • Software or Tool: Any IDE.
  • Main Book: “Head First Design Patterns, 2nd Edition”

What you’ll build: A set of characters for a game (e.g., Knight, Archer, Wizard). Instead of having attack logic inside each character’s class, you will “delegate” the attack behavior to a separate IAttackStrategy object. This will allow characters to change their attack style at runtime.

Why it teaches patterns: This project teaches the Strategy Pattern, a powerful behavioral pattern for making parts of your algorithm swappable. It’s a clean alternative to a massive if/else in your methods or a rigid inheritance hierarchy. It teaches you to encapsulate “how” something is done into its own family of objects.

Core challenges you’ll face:

  • The problem of duplicated code or rigid inheritance for behaviors → maps to the problem the pattern solves
  • Defining a family of algorithms in separate strategy classes → maps to encapsulating behavior
  • Allowing a context object (the Character) to hold a reference to a strategy → maps to composition
  • Changing a character’s behavior at runtime by swapping strategy objects → maps to the flexibility of the pattern

Key Concepts:

  • Strategy Pattern: “Head First Design Patterns”, Chapter 1.
  • Encapsulating what varies: A core design principle.
  • Favor composition over inheritance: This pattern is a prime example.

Difficulty: Advanced Time estimate: Weekend Prerequisites: Project 3.

Real world outcome: A console simulation where game characters can fight and change their weapons/tactics.

A Knight appears!
Knight attacks with a Sword!
> A mighty swing!

An Archer appears!
Archer attacks with a Bow and Arrow!
> Thwip! An arrow flies true.

The Knight picks up a magic staff...
Knight attacks with a Fireball!
> Whoosh! A burst of flame!

Implementation Hints:

  • Create a Character abstract class. It should have a field for IAttackStrategy _attackBehavior;.
  • The Character class will have a performAttack() method that simply calls _attackBehavior.Attack();. It will also have a setAttackBehavior() method.
  • Create an IAttackStrategy interface with an Attack() method.
  • Create concrete strategy classes: SwordAttack, BowAttack, FireballAttack, each implementing the interface.
  • In your concrete character classes (Knight, Archer), set the initial attack behavior in their constructor.
  • Demonstrate changing the Knight’s behavior by calling knight.setAttackBehavior(new FireballAttack());.

Learning milestones:

  1. Your Character class doesn’t know how it attacks, only that it can attack → You have successfully delegated the behavior.
  2. You can create a new CrossbowAttack strategy and use it with the Archer without changing the Archer class → The system is open for extension.
  3. A character changes its attack style at runtime → You understand the dynamic power of the pattern.
  4. You realize this is a much cleaner solution than having an attackType enum and a giant switch statement → You can identify where to apply the Strategy pattern.

Project 7: The Observer Pattern - A Weather Station

  • File: LEARN_OBJECT_ORIENTED_PROGRAMMING.md
  • Main Programming Language: Java
  • Alternative Programming Languages: C#, Python
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 3. The “Service & Support” Model
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Behavioral Design Patterns
  • Software or Tool: Any IDE.
  • Main Book: “Head First Design Patterns, 2nd Edition”

What you’ll build: A WeatherData station (the “Subject”) that tracks temperature and humidity. You will also build several “display” objects (the “Observers”), like CurrentConditionsDisplay and StatisticsDisplay, that get automatically updated whenever the weather data changes.

Why it teaches patterns: This project teaches the Observer Pattern, which defines a one-to-many dependency between objects. When one object (the subject) changes state, all its dependents (the observers) are notified and updated automatically. This creates a highly decoupled system for event-driven programming.

Core challenges you’ll face:

  • The problem of the WeatherData object needing to know about all possible displays → maps to tight coupling, the problem the pattern solves
  • Creating Subject and Observer interfaces → maps to defining the contract for communication
  • Allowing observers to subscribe and unsubscribe from the subject → maps to dynamic registration
  • Notifying all observers when the subject’s state changes → maps to the core notification loop

Key Concepts:

  • Observer Pattern: “Head First Design Patterns”, Chapter 2.
  • Loose Coupling: The primary benefit of the pattern.

Difficulty: Advanced Time estimate: Weekend Prerequisites: Understanding of interfaces and lists/collections.

Real world outcome: A console application that simulates a weather station with multiple, independent displays that all update in sync.

--- Weather Station Starting Up ---
New weather measurements: Temp=80F, Humidity=65%
  >> Current Conditions Display: 80.0F degrees and 65.0% humidity
  >> Statistics Display: Avg/Max/Min temperature = 80.0/80.0/80.0

New weather measurements: Temp=82F, Humidity=70%
  >> Current Conditions Display: 82.0F degrees and 70.0% humidity
  >> Statistics Display: Avg/Max/Min temperature = 81.0/82.0/80.0

A new Forecast Display has been added!

New weather measurements: Temp=78F, Humidity=90%
  >> Current Conditions Display: 78.0F degrees and 90.0% humidity
  >> Statistics Display: Avg/Max/Min temperature = 80.0/82.0/78.0
  >> Forecast Display: Improving weather on the way!

Implementation Hints:

  • Create a Subject interface with methods: registerObserver(Observer o), removeObserver(Observer o), notifyObservers().
  • Create an Observer interface with one method: update(float temp, float humidity).
  • The WeatherData class implements Subject. It will have a list of Observers.
  • In WeatherData, create a setMeasurements() method. After it updates its internal temperature and humidity, it should call notifyObservers().
  • notifyObservers() simply loops through the list of observers and calls the update() method on each one.
  • Create display classes like CurrentConditionsDisplay that implement the Observer interface. In their constructor, they should register themselves with the WeatherData object.

Learning milestones:

  1. The WeatherData subject knows nothing about the concrete display classes → It only knows it has a list of Observers. This is loose coupling.
  2. When the weather data changes, all displays update automatically → The notification mechanism is working.
  3. You can add a completely new type of display without ever changing the WeatherData class → The system is open for extension.
  4. You understand that this is the foundation of many UI frameworks (like event listeners) and message queues → You can see the pattern in the real world.

Project Comparison Table

Project Difficulty Time Key Concept Taught Fun Factor
1. Procedural to OOP Beginner Weekend Encapsulation 3/5
2. Vending Machine Beginner Weekend Abstraction 3/5
3. Geometric Shapes Intermediate Weekend Inheritance, Polymorphism 4/5
4. Factory Pattern Intermediate Weekend Creational Patterns 3/5
5. Decorator Pattern Advanced Weekend Structural Patterns 5/5
6. Strategy Pattern Advanced Weekend Behavioral Patterns 4/5
7. Observer Pattern Advanced Weekend Behavioral Patterns 4/5

Recommendation

For a true understanding of why OOP exists, you must start with Project 1: From Procedural Mess to a Simple Object. It viscerally teaches the problem that OOP was born to solve.

From there, I recommend proceeding in order. The first three projects build up the four pillars of OOP. The subsequent projects introduce you to the world of Design Patterns, which are the masterful application of those pillars to solve common, recurring problems in software design.

Final Overall Project: A Text-Based Adventure Game

A fantastic project to tie all these concepts together is to build a simple text-based adventure game (like Zork).

  • Encapsulation: Player, Room, and Item classes will encapsulate their own state and logic.
  • Abstraction: A Room’s internal connections are hidden behind a simple getExit(direction) method.
  • Inheritance: Sword and Shield could be subclasses of a GameItem base class. Goblin and Dragon could inherit from Monster.
  • Polymorphism: You could have a List<GameCharacter> that contains both the Player and multiple Monsters, and you can call an update() method on each one during a game turn.
  • Factory Pattern: A MonsterFactory could create different monsters based on the difficulty level of a room.
  • Strategy Pattern: The Player’s fight() method could use an IAttackStrategy that can be changed by equipping different weapons.
  • Observer Pattern: The player’s inventory could be a “Subject,” and when an item is added, it could notify an “Observer” that updates the UI display.

This single, fun project allows you to see how all the core concepts and several key design patterns work together to create a flexible and expandable game world.


Summary

  • Project 1: From Procedural Mess to a Simple Object: Python
  • Project 2: Vending Machine Simulator: Java
  • Project 3: Geometric Shapes and an Area Calculator: C#
  • Project 4: The Factory Pattern - A Document Creator: Java
  • Project 5: The Decorator Pattern - Coffee Shop Orders: Java
  • Project 6: The Strategy Pattern - A Character’s Attack: C#
  • Project 7: The Observer Pattern - A Weather Station: Java