Machine Coding Problem

Library Management System

maco30maco60macoAllutilitysearch-&-strategy-penalty-logic
Commonly Asked By:AmazonMicrosoftGoogle

Functional Scope (In-Scope)

  • Dynamic Book Search: Filter books by Title, Author, or ISBN index configurations.
  • Checkout and Return: Support checkout and return pipelines including automatic penalty fine audits.
  • FIFO Book Reservations: Per-book reservation queues holding copies for waiting list members in FIFO order.
  • Outstanding Fine Blocks: Block member book checkouts if total unpaid fines exceed configurable limits (e.g. $50).

Explicit Boundaries (Out-of-Scope)

  • No Real Physical Locker Interfacing: Hardware scanners, shelf location tags, or physical locker triggers are mocked behind clean API classes.
  • No External Payment Gateway: Overdue fine payments are resolved internally on account sheets rather than integrating with card processors.

Highly robust reference designs in Java and Python:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;

enum BookStatus { AVAILABLE, LOANED, RESERVED }

interface PenaltyStrategy {
    double calculateFine(long daysOverdue);
}

class StandardPenaltyStrategy implements PenaltyStrategy {
    private final double dailyRate;
    public StandardPenaltyStrategy(double dailyRate) { this.dailyRate = dailyRate; }
    @Override
    public double calculateFine(long daysOverdue) {
        return daysOverdue * dailyRate;
    }
}

class PremiumPenaltyStrategy implements PenaltyStrategy {
    private final double dailyRate;
    public PremiumPenaltyStrategy(double dailyRate) { this.dailyRate = dailyRate; }
    @Override
    public double calculateFine(long daysOverdue) {
        if (daysOverdue <= 5) {
            return daysOverdue * dailyRate;
        }
        return (5 * dailyRate) + ((daysOverdue - 5) * dailyRate * 2.0); // Double fine after 5 days
    }
}

class Book {
    private final String isbn;
    private final String title;
    private final String author;
    private BookStatus status = BookStatus.AVAILABLE;
    private final Queue<String> reservationQueue = new LinkedList<>();

    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; }
    public synchronized BookStatus getStatus() { return status; }
    public synchronized void setStatus(BookStatus status) { this.status = status; }

    public synchronized void reserveMember(String memberId) {
        reservationQueue.add(memberId);
        if (status == BookStatus.AVAILABLE) {
            status = BookStatus.RESERVED;
        }
    }

    public synchronized String pollReservation() {
        return reservationQueue.poll();
    }

    public synchronized boolean hasReservations() {
        return !reservationQueue.isEmpty();
    }
}

class Loan {
    private final String bookIsbn;
    private final String memberId;
    private final long issueDate;
    private final long dueDate;
    private long returnDate = 0;

    public Loan(String bookIsbn, String memberId, long issueDate, long dueDate) {
        this.bookIsbn = bookIsbn;
        this.memberId = memberId;
        this.issueDate = issueDate;
        this.dueDate = dueDate;
    }

    public String getBookIsbn() { return bookIsbn; }
    public String getMemberId() { return memberId; }
    public long getDueDate() { return dueDate; }
    public void setReturnDate(long returnDate) { this.returnDate = returnDate; }

    public double calculateFine(long now, PenaltyStrategy strategy) {
        long effectiveEnd = (returnDate > 0) ? returnDate : now;
        if (effectiveEnd > dueDate) {
            long daysOverdue = (effectiveEnd - dueDate) / (1000 * 60 * 60 * 24);
            return strategy.calculateFine(daysOverdue);
        }
        return 0.0;
    }
}

class Member {
    private final String id;
    private final String name;
    private final List<Loan> activeLoans = new ArrayList<>();
    private double unpaidFines = 0.0;

    public Member(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() { return id; }
    public synchronized List<Loan> getActiveLoans() { return new ArrayList<>(activeLoans); }
    public synchronized void addLoan(Loan loan) { activeLoans.add(loan); }
    public synchronized void removeLoan(Loan loan) { activeLoans.remove(loan); }
    public synchronized double getUnpaidFines() { return unpaidFines; }
    public synchronized void addFine(double amount) { unpaidFines += amount; }
}

class LibraryService {
    private final Map<String, Book> books = new ConcurrentHashMap<>();
    private final Map<String, Member> members = new ConcurrentHashMap<>();
    private final PenaltyStrategy penaltyStrategy;

    public LibraryService(PenaltyStrategy penaltyStrategy) {
        this.penaltyStrategy = penaltyStrategy;
    }

    public void addBook(Book book) { books.put(book.getIsbn(), book); }
    public void addMember(Member member) { members.put(member.getId(), member); }

    public boolean checkoutBook(String memberId, String isbn) {
        Book book = books.get(isbn);
        Member member = members.get(memberId);
        if (book == null || member == null) return false;

        if (member.getUnpaidFines() > 50.0) {
            System.out.println("Checkout rejected: Member " + member.getId() + " has excess fines of $" + member.getUnpaidFines());
            return false;
        }

        synchronized (book) {
            if (book.getStatus() == BookStatus.AVAILABLE) {
                book.setStatus(BookStatus.LOANED);
            } else if (book.getStatus() == BookStatus.RESERVED) {
                String nextInLine = book.pollReservation();
                if (memberId.equals(nextInLine)) {
                    book.setStatus(BookStatus.LOANED);
                } else {
                    return false;
                }
            } else {
                return false;
            }

            long now = System.currentTimeMillis();
            long dueDate = now + (14L * 24 * 60 * 60 * 1000);
            Loan loan = new Loan(isbn, memberId, now, dueDate);
            member.addLoan(loan);
            System.out.println("Checkout successful: " + book.getTitle() + " lent to Member " + member.getId());
            return true;
        }
    }

    public void reserveBook(String memberId, String isbn) {
        Book book = books.get(isbn);
        Member member = members.get(memberId);
        if (book == null || member == null) return;

        book.reserveMember(memberId);
        System.out.println("Reservation successful: " + book.getTitle() + " reserved by Member " + member.getId());
    }

    public void returnBook(String memberId, String isbn, long returnTimeOverride) {
        Book book = books.get(isbn);
        Member member = members.get(memberId);
        if (book == null || member == null) return;

        Loan targetLoan = null;
        for (Loan l : member.getActiveLoans()) {
            if (l.getBookIsbn().equals(isbn)) {
                targetLoan = l;
                break;
            }
        }

        if (targetLoan == null) return;

        targetLoan.setReturnDate(returnTimeOverride);
        double fine = targetLoan.calculateFine(returnTimeOverride, penaltyStrategy);
        if (fine > 0) {
            member.addFine(fine);
            System.out.println("Overdue Book returned! Fine calculated: $" + fine);
        } else {
            System.out.println("Book returned in time. No fine.");
        }

        member.removeLoan(targetLoan);

        synchronized (book) {
            if (book.hasReservations()) {
                book.setStatus(BookStatus.RESERVED);
            } else {
                book.setStatus(BookStatus.AVAILABLE);
            }
        }
    }
}

public class LibraryDriver {
    public static void main(String[] args) {
        System.out.println("=== LIBRARY MANAGEMENT SYSTEM SIMULATION ===");
        PenaltyStrategy strategy = new PremiumPenaltyStrategy(2.0); // $2.0 per day, double after 5 days
        LibraryService library = new LibraryService(strategy);

        Book b1 = new Book("ISBN-11", "Clean Architecture", "Robert C. Martin");
        library.addBook(b1);

        Member m1 = new Member("MEM-1", "Alice");
        Member m2 = new Member("MEM-2", "Bob");
        library.addMember(m1);
        library.addMember(m2);

        // Checkout success
        library.checkoutBook("MEM-1", "ISBN-11");

        // Bob reserves the book while Alice has it
        library.reserveBook("MEM-2", "ISBN-11");

        // Alice returns it overdue by 8 days
        long now = System.currentTimeMillis();
        long eightDaysLater = now + (22L * 24 * 60 * 60 * 1000); // 14 days due + 8 days late
        library.returnBook("MEM-1", "ISBN-11", eightDaysLater);
        System.out.println("Alice unpaid fines: $" + m1.getUnpaidFines()); // 5 * 2 + 3 * 4 = $22.0
    }
}