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
.
#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;
}
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
Book
and Member
classes encapsulate attributes and methods for managing books and members.isIssued
, title
, and author
cannot be accessed directly from outside the class.Library
class contains objects of Book
, Member
, and TransactionLogger
instead of extending them.TransactionLogger
class abstracts the logic for storing and retrieving transaction history.Library
class abstracts all operations related to books and members.Library
does not directly manipulate Book
or Member
objects; it uses methods to modify them safely.✅ Loose Coupling – Library
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! 🚀