All Together
See all relationships working together in a real-world system.
Let’s build a complete Library Management System that demonstrates all four class relationships working together.
System Overview
Section titled “System Overview”We’ll design a library system with:
- Library (main system)
- Book (items in library)
- Member (people who borrow books)
- Librarian (staff managing library)
- Loan (borrowing record)
- Fine (penalty for late returns)
Complete Implementation
Section titled “Complete Implementation” 💡 Tip: Click dropdown to switch between languages
from abc import ABC, abstractmethodfrom datetime import datetime, timedelta
# ========== INHERITANCE ==========class Person(ABC): """Base class using Inheritance""" def __init__(self, name: str, email: str): self.name = name self.email = email
@abstractmethod def get_role(self): pass
# Inheritance - Member IS-A Personclass Member(Person): def __init__(self, name: str, email: str, member_id: str): super().__init__(name, email) self.member_id = member_id self.loans = [] # COMPOSITION - loans belong to member
def get_role(self): return "Member"
def borrow_book(self, book, librarian): # DEPENDENCY - uses Book and Librarian temporarily loan = Loan(self, book, librarian) self.loans.append(loan) # COMPOSITION return loan
# Inheritance - Librarian IS-A Personclass Librarian(Person): def __init__(self, name: str, email: str, employee_id: str): super().__init__(name, email) self.employee_id = employee_id
def get_role(self): return "Librarian"
def process_return(self, loan): # DEPENDENCY - uses Loan temporarily loan.return_book() if loan.is_overdue(): fine = Fine(loan) # COMPOSITION - fine created with loan loan.fine = fine
# ========== COMPOSITION ==========class Book: """Book is part of Library - COMPOSITION""" def __init__(self, isbn: str, title: str, author: str): self.isbn = isbn self.title = title self.author = author self.available = True
# COMPOSITION - Loan belongs to Memberclass Loan: def __init__(self, member, book, librarian): self.member = member # ASSOCIATION - references member self.book = book # ASSOCIATION - references book self.librarian = librarian # ASSOCIATION - references librarian self.borrow_date = datetime.now() self.due_date = self.borrow_date + timedelta(days=14) self.return_date = None self.fine = None # COMPOSITION - optional fine
def return_book(self): self.return_date = datetime.now() self.book.available = True
def is_overdue(self): if self.return_date: return self.return_date > self.due_date return datetime.now() > self.due_date
# COMPOSITION - Fine belongs to Loanclass Fine: def __init__(self, loan): self.loan = loan # ASSOCIATION - references loan days_overdue = (datetime.now() - loan.due_date).days self.amount = days_overdue * 0.50 # $0.50 per day
# ========== COMPOSITION & AGGREGATION ==========class Library: """Library contains Books (COMPOSITION) and has Members/Librarians (AGGREGATION)""" def __init__(self, name: str): self.name = name self.books = [] # COMPOSITION - books belong to library self.members = [] # AGGREGATION - members can exist independently self.librarians = [] # AGGREGATION - librarians can exist independently
def add_book(self, book): self.books.append(book) # COMPOSITION
def register_member(self, member): self.members.append(member) # AGGREGATION
def hire_librarian(self, librarian): self.librarians.append(librarian) # AGGREGATION
def find_book(self, isbn: str): """DEPENDENCY - uses string parameter""" for book in self.books: if book.isbn == isbn: return book return None
# ========== USAGE ==========# Create librarylibrary = Library("City Library")
# Create books (COMPOSITION - belong to library)book1 = Book("12345", "Python Guide", "Author A")book2 = Book("67890", "Java Guide", "Author B")library.add_book(book1)library.add_book(book2)
# Create members (AGGREGATION - can exist independently)library.register_member(member1)library.register_member(member2)
# Create librarian (AGGREGATION)library.hire_librarian(librarian)
# Member borrows book (creates ASSOCIATION and COMPOSITION)loan = member1.borrow_book(book1, librarian)print(f"{member1.name} borrowed {book1.title}")
# Librarian processes return (uses DEPENDENCY)librarian.process_return(loan)print(f"Book returned. Fine: ${loan.fine.amount if loan.fine else 0:.2f}")import java.time.LocalDateTime;import java.time.temporal.ChronoUnit;import java.util.ArrayList;import java.util.List;
// ========== INHERITANCE ==========abstract class Person { protected String name; protected String email;
public Person(String name, String email) { this.name = name; this.email = email; }
public abstract String getRole();}
// Inheritance - Member IS-A Personclass Member extends Person { private String memberId; private List<Loan> loans; // COMPOSITION - loans belong to member
public Member(String name, String email, String memberId) { super(name, email); this.memberId = memberId; this.loans = new ArrayList<>(); }
@Override public String getRole() { return "Member"; }
// DEPENDENCY - uses Book and Librarian temporarily public Loan borrowBook(Book book, Librarian librarian) { Loan loan = new Loan(this, book, librarian); loans.add(loan); // COMPOSITION return loan; }}
// Inheritance - Librarian IS-A Personclass Librarian extends Person { private String employeeId;
public Librarian(String name, String email, String employeeId) { super(name, email); this.employeeId = employeeId; }
@Override public String getRole() { return "Librarian"; }
// DEPENDENCY - uses Loan temporarily public void processReturn(Loan loan) { loan.returnBook(); if (loan.isOverdue()) { Fine fine = new Fine(loan); // COMPOSITION - fine created with loan loan.setFine(fine); } }}
// ========== COMPOSITION ==========class Book { private String isbn; private String title; private String author; private boolean available;
public Book(String isbn, String title, String author) { this.isbn = isbn; this.title = title; this.author = author; this.available = true; }
public String getTitle() { return title; }
public boolean isAvailable() { return available; }
public void setAvailable(boolean available) { this.available = available; }}
// COMPOSITION - Loan belongs to Memberclass Loan { private Member member; // ASSOCIATION - references member private Book book; // ASSOCIATION - references book private Librarian librarian; // ASSOCIATION - references librarian private LocalDateTime borrowDate; private LocalDateTime dueDate; private LocalDateTime returnDate; private Fine fine; // COMPOSITION - optional fine
public Loan(Member member, Book book, Librarian librarian) { this.member = member; this.book = book; this.librarian = librarian; this.borrowDate = LocalDateTime.now(); this.dueDate = borrowDate.plusDays(14); }
public void returnBook() { this.returnDate = LocalDateTime.now(); book.setAvailable(true); }
public boolean isOverdue() { if (returnDate != null) { return returnDate.isAfter(dueDate); } return LocalDateTime.now().isAfter(dueDate); }
public void setFine(Fine fine) { this.fine = fine; }}
// COMPOSITION - Fine belongs to Loanclass Fine { private Loan loan; // ASSOCIATION - references loan private double amount;
public Fine(Loan loan) { this.loan = loan; long daysOverdue = ChronoUnit.DAYS.between(loan.dueDate, LocalDateTime.now()); this.amount = daysOverdue * 0.50; // $0.50 per day }
public double getAmount() { return amount; }}
// ========== COMPOSITION & AGGREGATION ==========class Library { private String name; private List<Book> books; // COMPOSITION - books belong to library private List<Member> members; // AGGREGATION - members can exist independently private List<Librarian> librarians; // AGGREGATION - librarians can exist independently
public Library(String name) { this.name = name; this.books = new ArrayList<>(); this.members = new ArrayList<>(); this.librarians = new ArrayList<>(); }
public void addBook(Book book) { books.add(book); // COMPOSITION }
public void registerMember(Member member) { members.add(member); // AGGREGATION }
public void hireLibrarian(Librarian librarian) { librarians.add(librarian); // AGGREGATION }
// DEPENDENCY - uses string parameter public Book findBook(String isbn) { for (Book book : books) { if (book.getIsbn().equals(isbn)) { return book; } } return null; }}
// ========== USAGE ==========public class Main { public static void main(String[] args) { // Create library Library library = new Library("City Library");
// Create books (COMPOSITION - belong to library) Book book1 = new Book("12345", "Python Guide", "Author A"); Book book2 = new Book("67890", "Java Guide", "Author B"); library.addBook(book1); library.addBook(book2);
// Create members (AGGREGATION - can exist independently) library.registerMember(member1); library.registerMember(member2);
// Create librarian (AGGREGATION) library.hireLibrarian(librarian);
// Member borrows book (creates ASSOCIATION and COMPOSITION) Loan loan = member1.borrowBook(book1, librarian); System.out.println(member1.name + " borrowed " + book1.getTitle());
// Librarian processes return (uses DEPENDENCY) librarian.processReturn(loan); System.out.println("Book returned. Fine: $" + (loan.fine != null ? loan.fine.getAmount() : 0)); }}// ========== INHERITANCE ==========abstract class Person { protected name: string; protected email: string;
constructor(name: string, email: string) { this.name = name; this.email = email; }
abstract getRole(): string;}
// Inheritance - Member IS-A Personclass Member extends Person { private memberId: string; private loans: Loan[]; // COMPOSITION - loans belong to member
constructor(name: string, email: string, memberId: string) { super(name, email); this.memberId = memberId; this.loans = []; }
getRole(): string { return "Member"; }
// DEPENDENCY - uses Book and Librarian temporarily borrowBook(book: Book, librarian: Librarian): Loan { const loan = new Loan(this, book, librarian); this.loans.push(loan); // COMPOSITION return loan; }}
// Inheritance - Librarian IS-A Personclass Librarian extends Person { private employeeId: string;
constructor(name: string, email: string, employeeId: string) { super(name, email); this.employeeId = employeeId; }
getRole(): string { return "Librarian"; }
// DEPENDENCY - uses Loan temporarily processReturn(loan: Loan): void { loan.returnBook(); if (loan.isOverdue()) { const fine = new Fine(loan); // COMPOSITION - fine created with loan loan.setFine(fine); } }}
// ========== COMPOSITION ==========class Book { private isbn: string; private title: string; private author: string; public available: boolean;
constructor(isbn: string, title: string, author: string) { this.isbn = isbn; this.title = title; this.author = author; this.available = true; }
getTitle(): string { return this.title; }}
// COMPOSITION - Loan belongs to Memberclass Loan { private member: Member; // ASSOCIATION - references member private book: Book; // ASSOCIATION - references book private librarian: Librarian; // ASSOCIATION - references librarian private borrowDate: Date; private dueDate: Date; private returnDate: Date | null; private fine: Fine | null; // COMPOSITION - optional fine
constructor(member: Member, book: Book, librarian: Librarian) { this.member = member; this.book = book; this.librarian = librarian; this.borrowDate = new Date(); this.dueDate = new Date(this.borrowDate.getTime() + 14 * 24 * 60 * 60 * 1000); this.returnDate = null; this.fine = null; }
returnBook(): void { this.returnDate = new Date(); this.book.available = true; }
isOverdue(): boolean { if (this.returnDate) { return this.returnDate > this.dueDate; } return new Date() > this.dueDate; }
setFine(fine: Fine): void { this.fine = fine; }
getFine(): Fine | null { return this.fine; }}
// COMPOSITION - Fine belongs to Loanclass Fine { private loan: Loan; // ASSOCIATION - references loan public amount: number;
constructor(loan: Loan) { this.loan = loan; const daysOverdue = Math.floor( (new Date().getTime() - loan['dueDate'].getTime()) / (24 * 60 * 60 * 1000) ); this.amount = daysOverdue * 0.50; // $0.50 per day }}
// ========== COMPOSITION & AGGREGATION ==========class Library { private name: string; private books: Book[]; // COMPOSITION - books belong to library private members: Member[]; // AGGREGATION - members can exist independently private librarians: Librarian[]; // AGGREGATION - librarians can exist independently
constructor(name: string) { this.name = name; this.books = []; this.members = []; this.librarians = []; }
addBook(book: Book): void { this.books.push(book); // COMPOSITION }
registerMember(member: Member): void { this.members.push(member); // AGGREGATION }
hireLibrarian(librarian: Librarian): void { this.librarians.push(librarian); // AGGREGATION }
// DEPENDENCY - uses string parameter findBook(isbn: string): Book | null { return this.books.find(book => book['isbn'] === isbn) || null; }}
// ========== USAGE ==========// Create libraryconst library = new Library("City Library");
// Create books (COMPOSITION - belong to library)const book1 = new Book("12345", "Python Guide", "Author A");const book2 = new Book("67890", "Java Guide", "Author B");library.addBook(book1);library.addBook(book2);
// Create members (AGGREGATION - can exist independently)library.registerMember(member1);library.registerMember(member2);
// Create librarian (AGGREGATION)library.hireLibrarian(librarian);
// Member borrows book (creates ASSOCIATION and COMPOSITION)const loan = member1.borrowBook(book1, librarian);console.log(`${member1['name']} borrowed ${book1.getTitle()}`);
// Librarian processes return (uses DEPENDENCY)librarian.processReturn(loan);const fine = loan.getFine();console.log(`Book returned. Fine: $${fine ? fine.amount.toFixed(2) : '0.00'}`);#include <iostream>#include <string>#include <vector>#include <memory>#include <chrono>#include <ctime>#include <iomanip>
using namespace std;
// Forward declarationsclass Loan;class Fine;class Book;class Librarian;
// ========== INHERITANCE ==========class Person {protected: string name; string email;
public: Person(const string& name, const string& email) : name(name), email(email) {}
virtual ~Person() = default; virtual string getRole() const = 0;
string getName() const { return name; }};
// Inheritance - Member IS-A Personclass Member : public Person {private: string memberId; vector<shared_ptr<Loan>> loans; // COMPOSITION - loans belong to member
public: Member(const string& name, const string& email, const string& memberId) : Person(name, email), memberId(memberId) {}
string getRole() const override { return "Member"; }
// DEPENDENCY - uses Book and Librarian temporarily shared_ptr<Loan> borrowBook(shared_ptr<Book> book, shared_ptr<Librarian> librarian);
void addLoan(shared_ptr<Loan> loan) { loans.push_back(loan); // COMPOSITION }};
// Inheritance - Librarian IS-A Personclass Librarian : public Person {private: string employeeId;
public: Librarian(const string& name, const string& email, const string& employeeId) : Person(name, email), employeeId(employeeId) {}
string getRole() const override { return "Librarian"; }
// DEPENDENCY - uses Loan temporarily void processReturn(shared_ptr<Loan> loan);};
// ========== COMPOSITION ==========class Book {private: string isbn; string title; string author;
public: bool available;
Book(const string& isbn, const string& title, const string& author) : isbn(isbn), title(title), author(author), available(true) {}
string getTitle() const { return title; } string getIsbn() const { return isbn; }};
// COMPOSITION - Loan belongs to Memberclass Loan {private: shared_ptr<Member> member; // ASSOCIATION - references member shared_ptr<Book> book; // ASSOCIATION - references book shared_ptr<Librarian> librarian; // ASSOCIATION - references librarian time_t borrowDate; time_t dueDate; time_t returnDate; shared_ptr<Fine> fine; // COMPOSITION - optional fine bool returned;
public: Loan(shared_ptr<Member> member, shared_ptr<Book> book, shared_ptr<Librarian> librarian) : member(member), book(book), librarian(librarian), returned(false) { borrowDate = time(nullptr); dueDate = borrowDate + (14 * 24 * 60 * 60); // 14 days returnDate = 0; }
void returnBook() { returnDate = time(nullptr); returned = true; book->available = true; }
bool isOverdue() const { if (returned) { return returnDate > dueDate; } return time(nullptr) > dueDate; }
void setFine(shared_ptr<Fine> f) { fine = f; }
shared_ptr<Fine> getFine() const { return fine; }
time_t getDueDate() const { return dueDate; }};
// COMPOSITION - Fine belongs to Loanclass Fine {private: shared_ptr<Loan> loan; // ASSOCIATION - references loan
public: double amount;
Fine(shared_ptr<Loan> loan) : loan(loan) { time_t now = time(nullptr); int daysOverdue = (now - loan->getDueDate()) / (24 * 60 * 60); amount = daysOverdue * 0.50; // $0.50 per day }};
// Member method implementation (after Loan and Fine are defined)shared_ptr<Loan> Member::borrowBook(shared_ptr<Book> book, shared_ptr<Librarian> librarian) { auto loan = make_shared<Loan>(shared_from_this(), book, librarian); addLoan(loan); return loan;}
// Librarian method implementationvoid Librarian::processReturn(shared_ptr<Loan> loan) { loan->returnBook(); if (loan->isOverdue()) { auto fine = make_shared<Fine>(loan); // COMPOSITION - fine created with loan loan->setFine(fine); }}
// ========== COMPOSITION & AGGREGATION ==========class Library {private: string name; vector<shared_ptr<Book>> books; // COMPOSITION - books belong to library vector<shared_ptr<Member>> members; // AGGREGATION - members can exist independently vector<shared_ptr<Librarian>> librarians; // AGGREGATION - librarians can exist independently
public: Library(const string& name) : name(name) {}
void addBook(shared_ptr<Book> book) { books.push_back(book); // COMPOSITION }
void registerMember(shared_ptr<Member> member) { members.push_back(member); // AGGREGATION }
void hireLibrarian(shared_ptr<Librarian> librarian) { librarians.push_back(librarian); // AGGREGATION }
// DEPENDENCY - uses string parameter shared_ptr<Book> findBook(const string& isbn) { for (auto& book : books) { if (book->getIsbn() == isbn) { return book; } } return nullptr; }};
// ========== USAGE ==========int main() { // Create library auto library = make_shared<Library>("City Library");
// Create books (COMPOSITION - belong to library) auto book1 = make_shared<Book>("12345", "Python Guide", "Author A"); auto book2 = make_shared<Book>("67890", "Java Guide", "Author B"); library->addBook(book1); library->addBook(book2);
// Create members (AGGREGATION - can exist independently) library->registerMember(member1); library->registerMember(member2);
// Create librarian (AGGREGATION) library->hireLibrarian(librarian);
// Member borrows book (creates ASSOCIATION and COMPOSITION) auto loan = member1->borrowBook(book1, librarian); cout << member1->getName() << " borrowed " << book1->getTitle() << endl;
// Librarian processes return (uses DEPENDENCY) librarian->processReturn(loan); auto fine = loan->getFine(); cout << fixed << setprecision(2); cout << "Book returned. Fine: $" << (fine ? fine->amount : 0.0) << endl;
return 0;}using System;using System.Collections.Generic;using System.Linq;
// ========== INHERITANCE ==========public abstract class Person{ protected string name; protected string email;
public Person(string name, string email) { this.name = name; this.email = email; }
public abstract string GetRole();
public string Name => name;}
// Inheritance - Member IS-A Personpublic class Member : Person{ private string memberId; private List<Loan> loans; // COMPOSITION - loans belong to member
public Member(string name, string email, string memberId) : base(name, email) { this.memberId = memberId; this.loans = new List<Loan>(); }
public override string GetRole() { return "Member"; }
// DEPENDENCY - uses Book and Librarian temporarily public Loan BorrowBook(Book book, Librarian librarian) { Loan loan = new Loan(this, book, librarian); loans.Add(loan); // COMPOSITION return loan; }}
// Inheritance - Librarian IS-A Personpublic class Librarian : Person{ private string employeeId;
public Librarian(string name, string email, string employeeId) : base(name, email) { this.employeeId = employeeId; }
public override string GetRole() { return "Librarian"; }
// DEPENDENCY - uses Loan temporarily public void ProcessReturn(Loan loan) { loan.ReturnBook(); if (loan.IsOverdue()) { Fine fine = new Fine(loan); // COMPOSITION - fine created with loan loan.SetFine(fine); } }}
// ========== COMPOSITION ==========public class Book{ private string isbn; private string title; private string author; public bool Available { get; set; }
public Book(string isbn, string title, string author) { this.isbn = isbn; this.title = title; this.author = author; this.Available = true; }
public string GetTitle() => title; public string GetIsbn() => isbn;}
// COMPOSITION - Loan belongs to Memberpublic class Loan{ private Member member; // ASSOCIATION - references member private Book book; // ASSOCIATION - references book private Librarian librarian; // ASSOCIATION - references librarian private DateTime borrowDate; private DateTime dueDate; private DateTime? returnDate; private Fine fine; // COMPOSITION - optional fine
public Loan(Member member, Book book, Librarian librarian) { this.member = member; this.book = book; this.librarian = librarian; this.borrowDate = DateTime.Now; this.dueDate = borrowDate.AddDays(14); this.returnDate = null; }
public void ReturnBook() { this.returnDate = DateTime.Now; book.Available = true; }
public bool IsOverdue() { if (returnDate.HasValue) { return returnDate.Value > dueDate; } return DateTime.Now > dueDate; }
public void SetFine(Fine fine) { this.fine = fine; }
public Fine GetFine() => fine;
internal DateTime GetDueDate() => dueDate;}
// COMPOSITION - Fine belongs to Loanpublic class Fine{ private Loan loan; // ASSOCIATION - references loan public double Amount { get; private set; }
public Fine(Loan loan) { this.loan = loan; int daysOverdue = (int)(DateTime.Now - loan.GetDueDate()).TotalDays; this.Amount = daysOverdue * 0.50; // $0.50 per day }}
// ========== COMPOSITION & AGGREGATION ==========public class Library{ private string name; private List<Book> books; // COMPOSITION - books belong to library private List<Member> members; // AGGREGATION - members can exist independently private List<Librarian> librarians; // AGGREGATION - librarians can exist independently
public Library(string name) { this.name = name; this.books = new List<Book>(); this.members = new List<Member>(); this.librarians = new List<Librarian>(); }
public void AddBook(Book book) { books.Add(book); // COMPOSITION }
public void RegisterMember(Member member) { members.Add(member); // AGGREGATION }
public void HireLibrarian(Librarian librarian) { librarians.Add(librarian); // AGGREGATION }
// DEPENDENCY - uses string parameter public Book FindBook(string isbn) { return books.FirstOrDefault(book => book.GetIsbn() == isbn); }}
// ========== USAGE ==========class Program{ static void Main() { // Create library Library library = new Library("City Library");
// Create books (COMPOSITION - belong to library) Book book1 = new Book("12345", "Python Guide", "Author A"); Book book2 = new Book("67890", "Java Guide", "Author B"); library.AddBook(book1); library.AddBook(book2);
// Create members (AGGREGATION - can exist independently) library.RegisterMember(member1); library.RegisterMember(member2);
// Create librarian (AGGREGATION) library.HireLibrarian(librarian);
// Member borrows book (creates ASSOCIATION and COMPOSITION) Loan loan = member1.BorrowBook(book1, librarian); Console.WriteLine($"{member1.Name} borrowed {book1.GetTitle()}");
// Librarian processes return (uses DEPENDENCY) librarian.ProcessReturn(loan); Fine fine = loan.GetFine(); Console.WriteLine($"Book returned. Fine: ${(fine != null ? fine.Amount : 0):F2}"); }}package main
import ( "fmt" "math" "time")
// ========== INHERITANCE ==========type Person interface { GetRole() string Name() string}
type personBase struct { name, email string}
func (p *personBase) Name() string { return p.name }
// Member IS-A Person (interface satisfaction)type Member struct { personBase memberID string loans []*Loan // COMPOSITION - loans belong to member}
func NewMember(name, email, memberID string) *Member { return &Member{ personBase: personBase{name: name, email: email}, memberID: memberID, loans: []*Loan{}, }}
func (m *Member) GetRole() string { return "Member" }
// DEPENDENCY - uses Book and Librarian temporarilyfunc (m *Member) BorrowBook(book *Book, librarian *Librarian) *Loan { loan := NewLoan(m, book, librarian) m.loans = append(m.loans, loan) // COMPOSITION return loan}
// Librarian IS-A Persontype Librarian struct { personBase employeeID string}
func NewLibrarian(name, email, employeeID string) *Librarian { return &Librarian{ personBase: personBase{name: name, email: email}, employeeID: employeeID, }}
func (l *Librarian) GetRole() string { return "Librarian" }
// DEPENDENCY - uses Loan temporarilyfunc (l *Librarian) ProcessReturn(loan *Loan) { loan.ReturnBook() if loan.IsOverdue() { fine := NewFine(loan) // COMPOSITION - fine created with loan loan.SetFine(fine) }}
var _ Person = (*Member)(nil)var _ Person = (*Librarian)(nil)
// ========== COMPOSITION ==========type Book struct { isbn string title string author string Available bool}
func NewBook(isbn, title, author string) *Book { return &Book{isbn: isbn, title: title, author: author, Available: true}}
func (b *Book) GetTitle() string { return b.title }func (b *Book) GetIsbn() string { return b.isbn }
// COMPOSITION - Loan belongs to Membertype Loan struct { member *Member book *Book librarian *Librarian borrowDate time.Time dueDate time.Time returnDate *time.Time fine *Fine // COMPOSITION - optional fine}
func NewLoan(member *Member, book *Book, librarian *Librarian) *Loan { now := time.Now() return &Loan{ member: member, book: book, librarian: librarian, borrowDate: now, dueDate: now.AddDate(0, 0, 14), returnDate: nil, fine: nil, }}
func (l *Loan) ReturnBook() { now := time.Now() l.returnDate = &now l.book.Available = true}
func (l *Loan) IsOverdue() bool { if l.returnDate != nil { return l.returnDate.After(l.dueDate) } return time.Now().After(l.dueDate)}
func (l *Loan) SetFine(f *Fine) { l.fine = f}
func (l *Loan) GetFine() *Fine { return l.fine }
func (l *Loan) getDueDate() time.Time { return l.dueDate }
// COMPOSITION - Fine belongs to Loantype Fine struct { loan *Loan // ASSOCIATION - references loan Amount float64}
func NewFine(loan *Loan) *Fine { daysOverdue := int(math.Ceil(time.Since(loan.getDueDate()).Hours() / 24.0)) if daysOverdue < 0 { daysOverdue = 0 } return &Fine{ loan: loan, Amount: float64(daysOverdue) * 0.50, // $0.50 per day }}
// ========== COMPOSITION & AGGREGATION ==========type Library struct { name string books []*Book // COMPOSITION - books belong to library members []*Member // AGGREGATION librarians []*Librarian // AGGREGATION}
func NewLibrary(name string) *Library { return &Library{ name: name, books: []*Book{}, members: []*Member{}, librarians: []*Librarian{}, }}
func (lib *Library) AddBook(book *Book) { lib.books = append(lib.books, book) // COMPOSITION}
func (lib *Library) RegisterMember(m *Member) { lib.members = append(lib.members, m) // AGGREGATION}
func (lib *Library) HireLibrarian(l *Librarian) { lib.librarians = append(lib.librarians, l) // AGGREGATION}
func (lib *Library) FindBook(isbn string) *Book { for _, b := range lib.books { if b.GetIsbn() == isbn { return b } } return nil}
func main() { library := NewLibrary("City Library")
book1 := NewBook("12345", "Python Guide", "Author A") book2 := NewBook("67890", "Java Guide", "Author B") library.AddBook(book1) library.AddBook(book2)
library.RegisterMember(member1) library.RegisterMember(member2)
library.HireLibrarian(librarian)
loan := member1.BorrowBook(book1, librarian) fmt.Printf("%s borrowed %s\n", member1.Name(), book1.GetTitle())
librarian.ProcessReturn(loan) var fineAmt float64 if f := loan.GetFine(); f != nil { fineAmt = f.Amount } fmt.Printf("Book returned. Fine: $%.2f\n", fineAmt)
_ = book2 _ = member2}// ========== INHERITANCE-LIKE POLYMORPHISM ==========trait Person { fn role(&self) -> &'static str; fn name(&self) -> &str;}
struct Member { name: String, email: String, member_id: String,}
impl Member { fn new(name: impl Into<String>, email: impl Into<String>, member_id: impl Into<String>) -> Self { Self { name: name.into(), email: email.into(), member_id: member_id.into(), } }
// DEPENDENCY - uses Book and Librarian temporarily to create a Loan fn borrow_book<'a>(&'a self, book: &'a mut Book, librarian: &'a Librarian) -> Loan<'a> { Loan::new(self, book, librarian) }}
impl Person for Member { fn role(&self) -> &'static str { "Member" }
fn name(&self) -> &str { &self.name }}
struct Librarian { name: String, email: String, employee_id: String,}
impl Librarian { fn new(name: impl Into<String>, email: impl Into<String>, employee_id: impl Into<String>) -> Self { Self { name: name.into(), email: email.into(), employee_id: employee_id.into(), } }
// DEPENDENCY - uses Loan temporarily fn process_return(&self, loan: &mut Loan<'_>) { loan.return_book(); if loan.is_overdue() { loan.fine = Some(Fine::new(loan)); } }}
impl Person for Librarian { fn role(&self) -> &'static str { "Librarian" }
fn name(&self) -> &str { &self.name }}
// ========== COMPOSITION ==========struct Book { isbn: String, title: String, author: String, available: bool,}
impl Book { fn new(isbn: impl Into<String>, title: impl Into<String>, author: impl Into<String>) -> Self { Self { isbn: isbn.into(), title: title.into(), author: author.into(), available: true, } }}
struct Loan<'a> { member: &'a Member, // ASSOCIATION - references member book: &'a mut Book, // ASSOCIATION - references book librarian: &'a Librarian, // ASSOCIATION - references librarian fine: Option<Fine>, // COMPOSITION - optional fine returned: bool,}
impl<'a> Loan<'a> { fn new(member: &'a Member, book: &'a mut Book, librarian: &'a Librarian) -> Self { book.available = false; Self { member, book, librarian, fine: None, returned: false, } }
fn return_book(&mut self) { self.returned = true; self.book.available = true; }
fn is_overdue(&self) -> bool { false // Keep the example focused on relationships. }}
struct Fine { amount: f64,}
impl Fine { fn new(_loan: &Loan<'_>) -> Self { Self { amount: 0.50 } }}
// ========== COMPOSITION & AGGREGATION ==========struct Library<'a> { name: String, books: Vec<Book>, // COMPOSITION - books belong to library members: Vec<&'a Member>, // AGGREGATION - members can exist independently librarians: Vec<&'a Librarian>, // AGGREGATION - librarians can exist independently}
impl<'a> Library<'a> { fn new(name: impl Into<String>) -> Self { Self { name: name.into(), books: Vec::new(), members: Vec::new(), librarians: Vec::new(), } }
fn add_book(&mut self, book: Book) { self.books.push(book); // COMPOSITION }
fn register_member(&mut self, member: &'a Member) { self.members.push(member); // AGGREGATION }
fn hire_librarian(&mut self, librarian: &'a Librarian) { self.librarians.push(librarian); // AGGREGATION }
fn find_book_mut(&mut self, isbn: &str) -> Option<&mut Book> { self.books.iter_mut().find(|book| book.isbn == isbn) }}
fn main() {
let mut library = Library::new("City Library"); library.add_book(Book::new("12345", "Python Guide", "Author A")); library.add_book(Book::new("67890", "Java Guide", "Author B")); library.register_member(&member1); library.register_member(&member2); library.hire_librarian(&librarian);
let book = library.find_book_mut("12345").expect("book exists"); let mut loan = member1.borrow_book(book, &librarian); println!("{} borrowed {}", loan.member.name(), loan.book.title);
librarian.process_return(&mut loan); let fine_amount = loan.fine.as_ref().map_or(0.0, |fine| fine.amount); println!("Book returned. Fine: ${:.2}", fine_amount);}Relationship Summary
Section titled “Relationship Summary”Relationship Breakdown
Section titled “Relationship Breakdown”1. Inheritance (Person → Member, Librarian)
Section titled “1. Inheritance (Person → Member, Librarian)”- Why: Both share common attributes (name, email) and have roles
- Type: “Is-a” relationship
2. Composition (Library → Book, Member → Loan, Loan → Fine)
Section titled “2. Composition (Library → Book, Member → Loan, Loan → Fine)”- Why: Books are part of library, loans belong to members, fines belong to loans
- Type: Strong ownership, cannot exist independently
3. Aggregation (Library → Member, Library → Librarian)
Section titled “3. Aggregation (Library → Member, Library → Librarian)”- Why: Members and librarians can exist independently (can transfer/leave)
- Type: Weak ownership, independent lifecycle
4. Association (Loan → Member, Book, Librarian)
Section titled “4. Association (Loan → Member, Book, Librarian)”- Why: Loan references these classes but doesn’t own them
- Type: “Knows-a” relationship
5. Dependency (Member → Book, Librarian → Loan)
Section titled “5. Dependency (Member → Book, Librarian → Loan)”- Why: Used temporarily as method parameters
- Type: Temporary use, no storage