C++ Code Example: Library Management System

In Object-Oriented Programming (OOP), we often rely on inheritance and polymorphism to create reusable and extensible code. However, without using inheritance or polymorphism, we can still implement advanced OOP principles, such as:

Encapsulation – Hiding data and exposing controlled access.
Composition – Combining multiple independent classes into a larger, modular system.
Abstraction – Separating logic into different classes and modules.

This example models a Library Management System where a Library class manages multiple independent components such as Book, Member, and TransactionLogger.


C++ Code Example: Library Management System

#include <iostream>
#include <vector>
#include <string>
#include <ctime>

using namespace std;

// Class to represent a Book
class Book {
private:
    string title;
    string author;
    bool isIssued;

public:
    Book(string title, string author) : title(title), author(author), isIssued(false) {}

    void issueBook() {
        if (!isIssued) {
            isIssued = true;
            cout << "Book \"" << title << "\" has been issued.\n";
        } else {
            cout << "Book \"" << title << "\" is already issued.\n";
        }
    }

    void returnBook() {
        if (isIssued) {
            isIssued = false;
            cout << "Book \"" << title << "\" has been returned.\n";
        } else {
            cout << "Book \"" << title << "\" was not issued.\n";
        }
    }

    string getTitle() { return title; }
    string getAuthor() { return author; }
    bool isBookIssued() { return isIssued; }
};

// Class to represent a Library Member
class Member {
private:
    string name;
    int memberId;

public:
    Member(string name, int id) : name(name), memberId(id) {}

    string getName() { return name; }
    int getId() { return memberId; }
};

// Class to log transactions in the library
class TransactionLogger {
private:
    vector<string> logs;

public:
    void logTransaction(string transaction) {
        time_t now = time(0);
        char* dt = ctime(&now);
        logs.push_back(dt + transaction);
    }

    void showLogs() {
        cout << "\nTransaction History:\n";
        for (const auto& log : logs) {
            cout << log << endl;
        }
    }
};

// Library class that manages books and members (Composition)
class Library {
private:
    vector<Book> books;
    vector<Member> members;
    TransactionLogger logger;

public:
    void addBook(string title, string author) {
        books.push_back(Book(title, author));
        logger.logTransaction("Added Book: " + title);
    }

    void addMember(string name, int id) {
        members.push_back(Member(name, id));
        logger.logTransaction("New Member Registered: " + name);
    }

    void issueBook(string title, int memberId) {
        for (auto& book : books) {
            if (book.getTitle() == title) {
                if (!book.isBookIssued()) {
                    book.issueBook();
                    logger.logTransaction("Book Issued: " + title + " to Member ID: " + to_string(memberId));
                } else {
                    cout << "Error: Book already issued.\n";
                }
                return;
            }
        }
        cout << "Error: Book not found.\n";
    }

    void returnBook(string title) {
        for (auto& book : books) {
            if (book.getTitle() == title) {
                book.returnBook();
                logger.logTransaction("Book Returned: " + title);
                return;
            }
        }
        cout << "Error: Book not found.\n";
    }

    void showLibraryLog() {
        logger.showLogs();
    }
};

// Main Function
int main() {
    Library myLibrary;

    // Adding Books
    myLibrary.addBook("The Great Gatsby", "F. Scott Fitzgerald");
    myLibrary.addBook("1984", "George Orwell");

    // Adding Members
    myLibrary.addMember("Alice", 101);
    myLibrary.addMember("Bob", 102);

    // Issuing and Returning Books
    myLibrary.issueBook("1984", 101);
    myLibrary.returnBook("1984");

    // Displaying transaction logs
    myLibrary.showLibraryLog();

    return 0;
}

Expected Output:

Book "1984" has been issued.
Book "1984" has been returned.

Transaction History:
Added Book: The Great Gatsby
Added Book: 1984
New Member Registered: Alice
New Member Registered: Bob
Book Issued: 1984 to Member ID: 101
Book Returned: 1984

Code Explanation:

1️⃣ Encapsulation

  • The Book and Member classes encapsulate attributes and methods for managing books and members.
  • Private attributes such as isIssued, title, and author cannot be accessed directly from outside the class.

2️⃣ Composition Instead of Inheritance

  • The Library class contains objects of Book, Member, and TransactionLogger instead of extending them.
  • This allows flexibility in managing library operations without inheritance.

3️⃣ Abstraction

  • The TransactionLogger class abstracts the logic for storing and retrieving transaction history.
  • The Library class abstracts all operations related to books and members.

4️⃣ Dependency Management

  • Library does not directly manipulate Book or Member objects; it uses methods to modify them safely.
  • The logger is separate from business logic, making the system modular.

Why Use This Approach?

Loose CouplingLibrary does not depend on a base class for Book, Member, or TransactionLogger.
Scalability – New features can be added without modifying existing code.
Reusability – The TransactionLogger can be reused in other systems.
Modularity – Each class has a single responsibility, making the system easier to maintain.

This example shows how to structure an advanced OOP system in C++ without inheritance or polymorphism, using composition, encapsulation, and abstraction to create a scalable and maintainable Library Management System! 🚀