Type: Assignment | Subject: Programming / Java | Level: Undergraduate | Word Count: ~2000 words
This model assignment was produced by an Essays UK specialist as reference material for learning purposes only. For support in this field, see our our Java assignment specialists.
Design and document an object-oriented Library Management System in Java for a university library. Your submission should identify the core classes and their relationships, apply appropriate object-oriented design principles (encapsulation, inheritance, interfaces), and include a worked complexity comparison of at least two candidate data structures for locating a book. Word limit: approximately 2,000 words.
A university library management system must track a catalogue of books, a population of members, and the loans that link them, while enforcing rules such as loan limits, due dates and fines. Object-oriented design is well suited to this problem because each of these concepts – a book, a member, a loan – maps naturally onto a class with its own state and behaviour, and the relationships between them (a loan belongs to one book and one member) map onto associations between classes (Larman, 2005). This assignment presents an object-oriented design for such a system: it identifies the core classes and their relationships, explains how encapsulation, inheritance and interfaces are applied, and provides a worked complexity comparison of two candidate data structures for the most frequent operation in the system, locating a book by its ISBN.
The design follows a standard object-oriented analysis process: first identifying the domain nouns as candidate classes (Book, Member, Loan, Library, Librarian), then identifying the responsibilities and collaborations of each class, and finally refining the design against established principles – particularly the single-responsibility and open/closed principles from SOLID (Martin, 2003) – before considering the concrete data structures needed to make the most common operations efficient. Class relationships are documented in the standard UML sense: association (one class uses another), aggregation (a “has-a” relationship where the parts can exist independently), composition (a stronger “has-a” where the parts cannot outlive the whole) and inheritance (an “is-a” relationship) (Fowler, 2003).
Five primary use cases were used to drive the design and were mapped onto a specific method before any class attribute was finalised, so that each attribute chosen was the minimum needed to support an identified behaviour rather than a speculative superset of every field a library system might conceivably need (Larman, 2005): a librarian adding a new title to the catalogue; a librarian registering a new member; a member borrowing an available copy; a member returning a copy, which may trigger a fine calculation if the return is late; and any user searching the catalogue by ISBN.
Table 1 summarises the five core classes identified for the system, their key attributes and methods, and their relationship to the rest of the design.
| Class | Key attributes | Key methods | Relationship |
|---|---|---|---|
| Book | isbn, title, author, copiesTotal, copiesAvailable | isAvailable(), reserve() | Aggregated by Library |
| LibraryUser (abstract) | userId, name, contactEmail | getActiveLoans() | Superclass of Member and Librarian |
| Member extends LibraryUser | membershipType, borrowedBooks | borrowBook(), returnBook() | Inherits LibraryUser; associated with Loan |
| Librarian extends LibraryUser | staffId | addBook(), registerMember() | Inherits LibraryUser; uses Library |
| Loan implements Fineable | book, member, dueDate, returnedDate | isOverdue(), calculateFine() | Composed by Library; references Book and Member |
| Library | catalogue, members, loans | findBookByIsbn(), issueLoan() | Aggregates Book, Member, Loan |
Table 1. Core classes, attributes, methods and relationships in the library management system design.
The abstract class LibraryUser captures state and behaviour shared by both members and staff, avoiding duplicated code between Member and Librarian while still allowing each subclass to add its own specialised attributes and methods (Bloch, 2018). Loan is deliberately composed by Library rather than existing independently, because a loan record has no meaning outside the context of the library that issued it: if the Library object were destroyed, its loans should not persist.
Encapsulation is applied throughout by making all fields private and exposing behaviour, rather than raw data, through public methods: for example, copiesAvailable on Book is never modified directly from outside the class, but only via reserve() and returnCopy(), which can enforce the invariant that available copies never fall below zero (Bloch, 2018). Inheritance, via the abstract LibraryUser class, avoids repeating the shared identity fields and methods in both Member and Librarian while still allowing Librarian to expose administrative methods that Member does not have, which is a cleaner alternative to a single class with role flags and conditional logic. A Fineable interface is introduced so that fine calculation is defined as a contract rather than tied to a single concrete class, illustrated below in simplified form:
public interface Fineable {
double calculateFine(LocalDate today);
}
public class Loan implements Fineable {
private final Book book;
private final Member member;
private final LocalDate dueDate;
private LocalDate returnedDate;
private static final double DAILY_FINE_RATE = 0.20; // GBP per day
@Override
public double calculateFine(LocalDate today) {
LocalDate endDate = (returnedDate != null) ? returnedDate : today;
long daysLate = ChronoUnit.DAYS.between(dueDate, endDate);
return (daysLate > 0) ? daysLate * DAILY_FINE_RATE : 0.0;
}
}
Using an interface rather than hard-coding fine logic into Loan means a future requirement, such as a different fine policy for reference-only items, could be met by adding a new class that implements Fineable without modifying the existing Loan class, consistent with the open/closed principle (Martin, 2003). As a worked check of the method above: a book due on 2024-03-01 and returned on 2024-03-06 is five days late, giving a fine of 5 × £0.20 = £1.00, correctly computed by the calculateFine method shown.
The most frequent operation in the system is looking up a Book by its ISBN, so the data structure chosen to store the catalogue has a direct effect on system performance as the collection grows. Two candidates are compared: an ArrayList<Book> searched linearly, and a HashMap<String, Book> keyed on ISBN.
For a catalogue of n = 5,000 books stored in an ArrayList, an unsuccessful linear search (the ISBN is not present, or the search must check every element to be sure) requires all n = 5,000 comparisons in the worst case. A successful linear search requires, on average, (n + 1) ÷ 2 = (5,000 + 1) ÷ 2 = 2,500.5 ≈ 2,501 comparisons, since the target is, on average, equally likely to be found at any position in the list. This gives a lookup cost of O(n).
By contrast, a HashMap computes a hash of the ISBN string and, assuming a reasonable hash function and a load factor kept below the default threshold of 0.75 (which Java’s HashMap maintains automatically by resizing), locates the corresponding bucket in constant time, requiring on average approximately 1 comparison regardless of n. This gives an average lookup cost of O(1), a saving of roughly 2,500 comparisons per lookup compared with the ArrayList at n = 5,000, growing linearly larger as the catalogue grows further (Goodrich, Tamassia and Goldwasser, 2014). The HashMap’s worst case degrades to O(n) only if many keys collide into the same bucket, which is unlikely in practice with a well-distributed String hash such as Java’s default String.hashCode() implementation (Horstmann, 2019).
This result does not make ArrayList redundant, however: a HashMap keyed on ISBN cannot efficiently answer a different query, such as “find all books whose title contains a given substring”, because that query is not an exact-key lookup. Such searches remain O(n) regardless of the underlying structure unless a further index, such as a sorted structure or a text index, is built specifically for that access pattern (Weiss, 2014). The system design therefore uses a HashMap for the primary catalogue, keyed on ISBN, while retaining the ability to iterate its values for less frequent, non-exact queries.
Insertion into the HashMap carries a related advantage: adding a new Book is O(1) on average, though any individual insertion that happens to trigger a resize, once the table exceeds its load factor of 0.75, costs O(n) to rehash every existing entry into a larger table. Because resizes occur only occasionally and each one is followed by many more O(1) insertions before the next resize is triggered, the amortised cost per insertion remains O(1) overall (Goodrich, Tamassia and Goldwasser, 2014), which is why a HashMap is the appropriate default choice for a catalogue that grows steadily over the system’s lifetime rather than being fixed in size at creation.
Each core class was designed to be independently testable using JUnit. Fineable’s calculateFine() is a pure function of its inputs, the due date, the return date and today’s date, with no hidden state, so it can be tested directly against fixed dates: for instance, asserting that a loan due on 2024-03-01 and returned on 2024-03-06 produces a fine of exactly £1.00, and that a loan returned on or before its due date produces a fine of £0.00. Library.issueLoan() was tested against three cases: issuing a loan when copies are available, where copiesAvailable should decrement by exactly one and a new Loan should be added to the loans collection; issuing a loan when copiesAvailable is already zero, where the method should refuse the request and leave state unchanged rather than allowing copiesAvailable to go negative; and issuing a loan to a member who has already reached their borrowing limit. Testing each class through its public interface, rather than inspecting private fields directly, keeps the tests valid even if the internal representation of Library’s catalogue were later changed from a HashMap to some other structure, which is itself a direct practical benefit of the encapsulation applied throughout the design (Bloch, 2018).
The design presented here balances two competing pressures common to introductory object-oriented coursework: fidelity to real design principles, and a scope that remains implementable within a single assignment. The use of an abstract LibraryUser class and a Fineable interface demonstrates inheritance and interface-based polymorphism without over-engineering the system with unnecessary abstraction layers, a common criticism of designs that apply design patterns for their own sake rather than in response to an actual requirement (Gamma et al., 1994; Sommerville, 2016). One limitation of the design as presented is that it does not yet address concurrency: if issueLoan() were called from multiple threads simultaneously against the same Book, the check on copiesAvailable and its subsequent decrement would need to be made atomic, for example using synchronized blocks or a concurrent map, to avoid two members being issued the last available copy at once. This was judged out of scope for the assignment brief but would be a necessary extension for a genuinely multi-user deployment.
This assignment has presented an object-oriented design for a university library management system, comprising five core classes connected by inheritance, composition and association relationships, and has explained how encapsulation, inheritance and interfaces are applied to keep the design extensible. A worked complexity comparison showed that a HashMap keyed on ISBN reduces the average cost of the system’s most frequent operation, book lookup, from O(n) (approximately 2,501 comparisons at n = 5,000 for an ArrayList) to O(1) (approximately 1 comparison), a substantial and growing saving as the catalogue scales, while an ArrayList remains useful for non-exact queries that a hash-based index cannot efficiently support.
Need a Model Assignment Written to Your Exact Brief?
Our 350+ UK-qualified writers deliver referenced model documents from £15 per 250 words, with free plagiarism and AI-detection reports.
You May Also Like