CRUD / OOD• Patterns: Factory, SearchStrategy, Observer
Library Management System
Medium
Problem Summary
Design an enterprise Library Management System managing multi-copy book lending, barcode checkouts, fine calculations, reservation queues, and pluggable catalog search algorithms.
Functional Scope
- Book catalog supporting multiple physical copies with distinct barcodes.
- Search catalog by title, author, subject, or ISBN using pluggable SearchStrategy.
- Enforce lending limits (max 5 books, 14-day loan period) per member.
- Automated daily fine computation on overdue returns.
- Reservation waiting list triggered when checked-out items are returned.
Entity-Relationship (ER) Schema
Library [1] --> [1] Catalog Catalog [1] --> [*] Book Book [1] --> [*] BookItem (Copy) Member [1] --> [*] BookLending Catalog [1] --> [1] SearchStrategy
Design Approach
Distinguish between Book metadata (title, author, ISBN) and physical BookItem copies (barcodes, wear, lending states). Pluggable SearchStrategy allows switching between linear scan, indexed hash maps, and Trie prefix matches.
Core Classes & Models
Library (Central management facade)Book (Metadata: title, author, ISBN)BookItem (Physical barcode copy with BookStatus: AVAILABLE, LOANED, RESERVED)Member (Student, Faculty with different loan quotas)BookLending (Loan checkout record with issue and due date)SearchStrategy (SearchByTitle, SearchByAuthor, SearchByISBN)FineCalculator (Daily overdue charge calculation)
Code Blueprint
126 lines/**
* Low-Level Design: Library Management System
* Design Patterns Applied:
* - Factory Pattern: Creating BookItem and Member objects
* - Strategy Pattern: Pluggable catalog search algorithms (by Title, Author, ISBN)
* - Observer Pattern: Real-time book reservation and fine notifications
*/
import java.util.*;
enum BookStatus { AVAILABLE, RESERVED, LOANED, LOST }
class Book {
private final String isbn, title, author;
public Book(String isbn, String title, String author) {
this.isbn = isbn; this.title = title; this.author = author;
}
public String getIsbn() { return isbn; }
public String getTitle() { return title; }
public String getAuthor() { return author; }
}
class BookItem extends Book {
private final String barcode;
private BookStatus status = BookStatus.AVAILABLE;
public BookItem(String isbn, String title, String author, String barcode) {
super(isbn, title, author);
this.barcode = barcode;
}
public String getBarcode() { return barcode; }
public BookStatus getStatus() { return status; }
public void setStatus(BookStatus status) { this.status = status; }
}
// --- [DESIGN PATTERN: Strategy] Pluggable catalog search strategy ---
interface SearchStrategy {
List<BookItem> search(List<BookItem> items, String query);
}
class SearchByTitle implements SearchStrategy {
@Override
public List<BookItem> search(List<BookItem> items, String query) {
List<BookItem> res = new ArrayList<>();
for (BookItem b : items) {
if (b.getTitle().toLowerCase().contains(query.toLowerCase())) res.add(b);
}
return res;
}
}
class SearchByAuthor implements SearchStrategy {
@Override
public List<BookItem> search(List<BookItem> items, String query) {
List<BookItem> res = new ArrayList<>();
for (BookItem b : items) {
if (b.getAuthor().toLowerCase().contains(query.toLowerCase())) res.add(b);
}
return res;
}
}
// --- [DESIGN PATTERN: Factory] Factory for creating catalog entries ---
class BookItemFactory {
public static BookItem createItem(String isbn, String title, String author, String barcode) {
return new BookItem(isbn, title, author, barcode);
}
}
// --- [DESIGN PATTERN: Observer] Notification listener for loan alerts ---
interface LibraryNotificationListener {
void onBookCheckout(String memberId, String barcode);
void onBookReturned(String barcode);
}
class ConsoleLibraryNotifier implements LibraryNotificationListener {
@Override
public void onBookCheckout(String memberId, String barcode) {
System.out.println(">> [ALERT] Book [" + barcode + "] checked out by Member: " + memberId);
}
@Override
public void onBookReturned(String barcode) {
System.out.println(">> [ALERT] Book [" + barcode + "] returned to shelves.");
}
}
class LibraryCatalog {
private final List<BookItem> items = new ArrayList<>();
private final List<LibraryNotificationListener> listeners = new ArrayList<>();
public void addListener(LibraryNotificationListener l) { listeners.add(l); }
public void addItem(BookItem item) { items.add(item); }
public List<BookItem> search(SearchStrategy strategy, String query) {
return strategy.search(items, query);
}
public boolean checkoutBook(String barcode, String memberId) {
for (BookItem item : items) {
if (item.getBarcode().equals(barcode) && item.getStatus() == BookStatus.AVAILABLE) {
item.setStatus(BookStatus.LOANED);
for (LibraryNotificationListener l : listeners) l.onBookCheckout(memberId, barcode);
return true;
}
}
return false;
}
}
public class Main {
public static void main(String[] args) {
LibraryCatalog catalog = new LibraryCatalog();
catalog.addListener(new ConsoleLibraryNotifier());
BookItem b1 = BookItemFactory.createItem("978-0132350884", "Clean Code", "Robert C. Martin", "BAR-101");
BookItem b2 = BookItemFactory.createItem("978-0201633610", "Design Patterns", "Erich Gamma", "BAR-102");
catalog.addItem(b1);
catalog.addItem(b2);
System.out.println("--- Library Catalog Search ---");
List<BookItem> matches = catalog.search(new SearchByTitle(), "Clean");
matches.forEach(m -> System.out.println("Found: " + m.getTitle() + " by " + m.getAuthor()));
catalog.checkoutBook("BAR-101", "MEM-999");
}
}