Skip to main content

A Simple OOP Project: Modeling a Real-World Entity

Theory is important, but the best way to solidify your understanding of Object-Oriented Programming is to build something. In this article, we will put together all the concepts we've learned in this series—classes, objects, attributes, methods, and encapsulation—to model a real-world entity from scratch.

Our project will be to create a Book class that represents a book in a library or bookstore, capable of tracking its own details and inventory status.


📚 Prerequisites

You should be familiar with the core concepts of OOP covered in this series:

  • Classes and Objects
  • Attributes (Instance and Class) and Methods
  • The __init__, __str__, and __repr__ methods
  • Access Modifiers (public, private) and Encapsulation

🎯 Article Outline: What You'll Master

In this article, you will:

  • Design a Class: Plan the attributes and behaviors for a Book class.
  • Implement the Class: Write the Python code for the class, including its constructor, methods, and special methods.
  • Apply Encapsulation: Use private attributes and public methods to protect the object's data.
  • Instantiate and Use Objects: Create instances of your Book class and interact with them to see your design in action.

🧠 Section 1: Designing Our Book Class

Before writing any code, let's plan our blueprint. What data does a book need to hold, and what actions should it be able to perform?

Attributes (The Data):

  • title (string): The title of the book.
  • author (string): The author of the book.
  • isbn (string): The unique ISBN for the book.
  • quantity (integer): How many copies are in stock. This should be private to prevent it from being set to an invalid number (like -5).

Methods (The Behavior):

  • __init__(): To create a new book object with a title, author, ISBN, and initial quantity.
  • __str__(): To provide a nice, user-friendly string representation.
  • __repr__(): To provide a clear, developer-focused representation.
  • check_out(): To decrease the quantity by one if the book is in stock.
  • check_in(): To increase the quantity by one.
  • get_availability(): A public method to check if the book is in stock.

💻 Section 2: Implementing the Book Class

Now, let's translate our design into Python code.

# book_project.py

class Book:
"""
Represents a book in a library's inventory.
Encapsulates book details and stock quantity.
"""
def __init__(self, title: str, author: str, isbn: str, initial_quantity: int):
# Public attributes
self.title = title
self.author = author
self.isbn = isbn

# Private attribute for quantity to protect data integrity
if initial_quantity >= 0:
self.__quantity = initial_quantity
else:
self.__quantity = 0
print("Warning: Initial quantity cannot be negative. Set to 0.")

def __str__(self) -> str:
"""User-friendly string representation."""
return f"'{self.title}' by {self.author}"

def __repr__(self) -> str:
"""Developer-friendly string representation."""
return f"Book(title='{self.title}', author='{self.author}', isbn='{self.isbn}', quantity={self.__quantity})"

# --- Public Methods (The Class API) ---

def check_out(self):
"""Decrements the quantity by one if copies are available."""
if self.__quantity > 0:
self.__quantity -= 1
print(f"Checked out '{self.title}'. Copies remaining: {self.__quantity}")
return True
else:
print(f"Sorry, '{self.title}' is out of stock.")
return False

def check_in(self):
"""Increments the quantity by one."""
self.__quantity += 1
print(f"Checked in '{self.title}'. Copies available: {self.__quantity}")

def get_availability(self) -> str:
"""Returns a string describing the stock status."""
if self.__quantity > 0:
return f"In Stock ({self.__quantity} available)"
else:
return "Out of Stock"

This class is a perfect example of encapsulation. The critical data, __quantity, is kept private. The only way to modify it is through the public check_in() and check_out() methods, which contain the necessary logic to prevent invalid operations (like checking out a book that is out of stock).


🚀 Section 3: Using Our Book Class

Now that our blueprint is complete, let's create some Book objects and interact with them.

# --- Main application logic ---

# Create two book instances
book1 = Book("The Hobbit", "J.R.R. Tolkien", "978-0618260300", 5)
book2 = Book("Dune", "Frank Herbert", "978-0441013593", 1)

# Print the objects to see our __str__ and __repr__ methods at work
print("--- Library Inventory ---")
print(f"User view: {book1}")
print(f"Developer view: {repr(book1)}")
print(f"{book1}: {book1.get_availability()}")
print(f"{book2}: {book2.get_availability()}")
print("-----------------------\n")


# Simulate some library activity
print("--- Library Activity ---")
book1.check_out()
book2.check_out()
book2.check_out() # This should fail
book1.check_in()
print("----------------------\n")


# Check final availability
print("--- Final Inventory ---")
print(f"{book1}: {book1.get_availability()}")
print(f"{book2}: {book2.get_availability()}")
print("---------------------")

Expected Output:

--- Library Inventory ---
User view: 'The Hobbit' by J.R.R. Tolkien
Developer view: Book(title='The Hobbit', author='J.R.R. Tolkien', isbn='978-0618260300', quantity=5)
'The Hobbit' by J.R.R. Tolkien: In Stock (5 available)
'Dune' by Frank Herbert: In Stock (1 available)
-----------------------

--- Library Activity ---
Checked out 'The Hobbit'. Copies remaining: 4
Checked out 'Dune'. Copies remaining: 0
Sorry, 'Dune' is out of stock.
Checked in 'The Hobbit'. Copies available: 5
----------------------

--- Final Inventory ---
'The Hobbit' by J.R.R. Tolkien: In Stock (5 available)
'Dune' by Frank Herbert: Out of Stock
---------------------

✨ Conclusion & Key Takeaways

Congratulations! You've successfully designed and implemented a complete, encapsulated class that models a real-world entity. This project demonstrates how bundling data and methods into objects creates code that is organized, reusable, and robust.

Let's summarize the key takeaways from this project:

  • Plan First: Designing the attributes and methods of your class before coding helps create a logical structure.
  • Encapsulate Your Data: Use private attributes for data that needs to be protected and provide public methods to interact with it safely.
  • Use Special Methods: Implementing __str__ and __repr__ makes your objects much easier to use and debug.
  • Objects Manage Their Own State: Notice how we never modified a book's quantity directly. We told the book object what to do (check_out()), and it managed its own internal state. This is the essence of OOP.

➡️ Next Steps

You now have a solid grasp of the fundamental principles of Object-Oriented Programming. In the next article, we'll explore the final core concept of this introductory series: "Properties: Getters, Setters, and Deleters," a more Pythonic way to manage access to your encapsulated attributes.

Happy building!