📌Problem Statement
Design a library management system for issuing and returning books.
📌Requirements
📌Class Design
Core Entities
public class Book { private String isbn; private String title; private String author; private String publisher; private int totalCopies; private int availableCopies; private BookCategory category; public boolean isAvailable() { return availableCopies > 0; } public void issueBook() { if (availableCopies <= 0) { throw new BookNotAvailableException(isbn); } availableCopies--; } public void returnBook() { if (availableCopies >= totalCopies) { throw new InvalidReturnException(isbn); } availableCopies++; }}public class Member { private String memberId; private String name; private String email; private MembershipType type; private LocalDate membershipExpiry; private List<BookIssue> currentIssues; private double outstandingFine; public int getMaxBooksAllowed() { return type.getMaxBooks(); } public boolean canIssueBook() { return membershipExpiry.isAfter(LocalDate.now()) && currentIssues.size() < getMaxBooksAllowed() && outstandingFine == 0; }}Issue and Return
public class BookIssue { private String issueId; private Book book; private Member member; private LocalDate issueDate; private LocalDate dueDate; private LocalDate returnDate; private double fineAmount; private IssueStatus status; public BookIssue(Book book, Member member, int daysAllowed) { this.issueId = UUID.randomUUID().toString(); this.book = book; this.member = member; this.issueDate = LocalDate.now(); this.dueDate = issueDate.plusDays(daysAllowed); this.status = IssueStatus.ISSUED; } public double returnBook() { this.returnDate = LocalDate.now(); this.status = IssueStatus.RETURNED; if (returnDate.isAfter(dueDate)) { long daysLate = ChronoUnit.DAYS.between(dueDate, returnDate); this.fineAmount = daysLate * FineCalculator.DAILY_FINE; } return fineAmount; }}Library Service
public class LibraryService { private BookRepository bookRepository; private MemberRepository memberRepository; private IssueRepository issueRepository; private ReservationService reservationService; public BookIssue issueBook(String memberId, String isbn) { Member member = memberRepository.findById(memberId); Book book = bookRepository.findByIsbn(isbn); if (!member.canIssueBook()) { throw new MemberNotEligibleException(memberId); } if (!book.isAvailable()) { throw new BookNotAvailableException(isbn); } book.issueBook(); BookIssue issue = new BookIssue(book, member, member.getMaxDays()); issueRepository.save(issue); member.getCurrentIssues().add(issue); return issue; } public double returnBook(String issueId) { BookIssue issue = issueRepository.findById(issueId); double fine = issue.returnBook(); issue.getBook().returnBook(); issue.getMember().getCurrentIssues().remove(issue); if (fine > 0) { issue.getMember().addFine(fine); } reservationService.notifyNextReservation(issue.getBook().getIsbn()); return fine; } public List<Book> searchBooks(String query) { return bookRepository.search(query); }}Reservation
public class Reservation { private String reservationId; private Book book; private Member member; private LocalDateTime reservedAt; private ReservationStatus status;}public class ReservationService { private Map<String, Queue<Reservation>> reservationQueues; public Reservation reserveBook(String memberId, String isbn) { Book book = bookRepository.findByIsbn(isbn); Member member = memberRepository.findById(memberId); Reservation reservation = new Reservation(book, member); reservationQueues.computeIfAbsent(isbn, k -> new LinkedList<>()).add(reservation); return reservation; } public void notifyNextReservation(String isbn) { Queue<Reservation> queue = reservationQueues.get(isbn); if (queue != null && !queue.isEmpty()) { Reservation next = queue.poll(); notificationService.notifyBookAvailable(next.getMember(), next.getBook()); } }}📌Design Patterns
Asked at Infosys, TCS, and Wipro interviews.