3
\$\begingroup\$

Following is my code for a library management system. I am pretty new to OOD and trying to learn it. I am specially looking for ways where any design patterns can be useful here. I tried to make classes so that I follow Single Responsibility principle. Would appreciate any feedback for it.

/*
This class is similar to admin with more priviliges
1. Add books
2. remove Books
 */
public class Admin extends Person{

    private String libname;
    Admin(String name, String phoneNo, String address, String libname){
        this.name = name;
        this.address = address;
        this.phoneNo = phoneNo;
        this.libname = libname;
    }

    public void addBook(int id, String author, Genre gen){
        Library.getInstance(libname).addBookItem(id, author, gen);
    }

    public void removeBook(int id){
        Library.getInstance(libname).removeBookItem(id);
    }

}

public class Book {

    int id;
    String author;
    Genre genre;    // can be converted to enums

}

/*
1. extends Book class
2. It handles additional fields like reservations
 */


import java.sql.Time;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalTime;

public class BookItem extends Book{

    private LocalTime resStartdate;
    private LocalTime resEndDate;

    BookItem(int id, String author, Genre genre){
        this.id = id;
        this.author = author;
        this.genre = genre;
        this.resStartdate = LocalTime.now();
        this.resEndDate = this.resStartdate;
    }

    public LocalTime getResEndDate() {
        return resEndDate;
    }

    public void setResEndDate(LocalTime resEndDate) {
        this.resEndDate = resEndDate;
    }

    public LocalTime getResStartdate() {
        return resStartdate;
    }

    public void setResStartdate(LocalTime resStartdate) {
        this.resStartdate = resStartdate;
    }
}

import java.util.ArrayList;
import java.util.List;
public class Catalog {

     static String libName;

//    Catalog(String libName){
//        Catalog.l = Library.getInstance(libName);
//    }

    public int temp(){
        return 0;
    }
    public static List<BookItem> searchByAuthor(String author){
        List<BookItem> result = new ArrayList<>();
        // logic
        List<BookItem> bookItemList = Library.getInstance(libName).bookItemList;
        for(BookItem temp : bookItemList){
            if(temp.author == author){
                result.add(temp);
            }
        }
        printResult(result);
        return result;
    }

    public static List<BookItem> searchByGenre(Genre g){
        List<BookItem> result = new ArrayList<>();
        // logic
        List<BookItem> bookItemList = Library.getInstance(libName).bookItemList;
        for(BookItem temp : bookItemList){
            if(temp.genre == g){
                result.add(temp);
            }
        }
        printResult(result);
        return result;
    }

    private static void printResult(List<BookItem> result){
        if(result.size() == 0){
            System.out.println("No Books Found.");
            return ;
        }
        System.out.println("Following books Found: ");
        for(BookItem item : result) {
            System.out.println("ID:: " + item.id +
                    " Author:: " + item.author +
                    " Genre:: " + item.genre
            );
        }
    }
}

public enum Genre {

    HORROR,
    SCIFI,
    ROMANTIC,
    FICTION;
}
import java.util.ArrayList;
import java.util.List;

public class Library {

    String name;

    private static Library instance;

    List<BookItem> bookItemList;

    private Library(String libName){
        this.name = libName;
        bookItemList = new ArrayList<>();
    }

    public static Library getInstance(String libname){
        if(instance == null){
            instance = new Library(libname);
            return instance;
        }
        return instance;
    }

    public void addBookItem(int id, String author, Genre gen){
        bookItemList.add(new BookItem(id, author, gen));
    }

    public void removeBookItem(int id){

        for(int i = 0;i<bookItemList.size();i++){
            if(bookItemList.get(i).id == id){
                bookItemList.remove(i);
                break;
            }
        }
    }

}

// Responsibilty of Member class
/*
1. search books by title, author -- dne
2. Reserve a book
3. Return the book
 */

import java.util.ArrayList;
import java.util.List;

public class Member extends Person{

    private List<Reservation> myActiveReservations;


    Member(String name, String phoneNo, String address){
        this.name = name;
        this.phoneNo = phoneNo;
        this.address = address;
        this.myActiveReservations = new ArrayList<>();
    }


    public List<BookItem> searchByGenre(Genre bookGenre){
        return Catalog.searchByGenre(bookGenre);
    }

    public List<BookItem> searchByAuthor(String author){
        return Catalog.searchByAuthor(author);
    }

    public boolean reserve(BookItem item){
        // try to create a reservation for it.
        // If successful, add it to the active reservations list and return true
        // If not successful, return false
        Reservation res = Reservation.createReservation(item, this);
        if(res == null){
            System.out.println("Not able to reserve Book");
            return false;
        }
        this.myActiveReservations.add(res);
        System.out.println("Reserved Book");
        return true;
    }

    public void returnBook(Reservation res){

        // fine calculator
        int fine = Payment.fineCalculator(res);
        Reservation.checkout(res);
        myActiveReservations.remove(res);
        System.out.println("Returned Book");
    }

    public List<Reservation> getActiveReservations(){
        return this.myActiveReservations;
    }
}

/*
This class handles matter related to fine calculation
 */

import java.time.Duration;
import java.time.LocalTime;

public class Payment {


    private static int fineRate = 5;

    public static int fineCalculator(Reservation res){
        BookItem item = res.item;

        LocalTime currTime = LocalTime.now();

        if(currTime.isAfter(item.getResEndDate())){
            Duration duration = Duration.between(currTime, item.getResEndDate());
            int days = (int) duration.toDays();
            int fine = days*fineRate;
            System.out.println(" Please pay fine: "+days);
            return fine;
        }

        return 0;
    }

}
public abstract class Person {
    String name;
    String phoneNo;
    String address;
}
/*
Class Responsiblity
Create a reservation against a book item and a user

 */

import java.sql.Time;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalTime;

public class Reservation {

     BookItem item;
     Member member;


     // private constructor
     private Reservation(BookItem item, Member member){
         this.item = item;
         this.member = member;
     }

     public static Reservation createReservation(BookItem item, Member member){

         LocalTime currTime = LocalTime.now();

         if(item.getResEndDate().isBefore(currTime)){
             // reservation possible
             // return a reservation object
             item.setResStartdate(currTime);
             item.setResEndDate(currTime.plusHours(24*7));
             return new Reservation(item, member);
         }
         return null;
     }

     public static void checkout(Reservation res){

         BookItem item = res.item;
         LocalTime currTime = LocalTime.now();
         item.setResStartdate(currTime);
         item.setResEndDate(currTime);
     }
}
import java.util.List;

public class Main {
    public static void main(String[] args) {

        // create a library
        String libName  = "UW-Madison";
        Catalog.libName = libName;
//        Library ourLib = Library.getInstance(libName);

        // create admins
        Admin ad1 = new Admin("Pankaj","7896","Madison",libName);

        // create members
        Member m1 = new Member("Pranavi","6758","Atlanta");

        // add books to library
        ad1.addBook(1,"Oscar", Genre.FICTION);
        ad1.addBook(2,"Piastri", Genre.HORROR);
        ad1.addBook(3,"Pedro", Genre.ROMANTIC);

        // search functionality
        List<BookItem> res = m1.searchByAuthor("Piastri");
//        m1.searchByGenre(Genre.FICTION);
//        m1.searchByAuthor("Pankaj");

        // reserving a book
        if(res.size() > 0){
            m1.reserve(res.get(0));
            // print member's active res list
            for(Reservation resItem : m1.getActiveReservations()){
                System.out.println(resItem.item.author +
                        resItem.item.genre+
                        resItem.item.getResStartdate().toString()+
                        resItem.item.getResEndDate().toString()+
                        resItem.member.name
                        );
            }
        }

        m1.returnBook(m1.getActiveReservations().get(0));
        System.out.println(m1.getActiveReservations().size());
    }
}

Trying to learn better ways to make my code more modular. Also trying to learn where I can use some design patterns in the code if they make it better.

\$\endgroup\$
3
  • 2
    \$\begingroup\$ Perhaps you should add some prose briefly and specifically explaining the requirements. What is the business problem addressed by this software? \$\endgroup\$ Commented Mar 30 at 21:26
  • \$\begingroup\$ Those concerns/goals you mention at the very bottom of your Question belong at the top. \$\endgroup\$ Commented Mar 30 at 23:34
  • \$\begingroup\$ common practice is to have user, role, and permissions...so not saying Admin, Member, Person is wrong but definitely not usual \$\endgroup\$ Commented Mar 31 at 11:19

1 Answer 1

5
\$\begingroup\$

You said:

I tried to make classes so that I follow Single Responsibility principle.

See Wikipedia pages, Single responsibility principle and Separation of concerns.

Your first class Admin extends Person violates this principle. You have methods addBook and removeBook that then passes on the workload to a Library object. The Admin class should contain state and logic that represents a particular administrator. That class should not know about the bureaucracy of book inventory.

Look at it this way: Suppose you were to expand your software to encompass more of a library's work. Besides adding/removing books from the shelves, an administrator of a library also is involved in daily opening & closing procedures, hiring/firing staff, building maintenance, legal compliance, fire safety inspections, budget planning, generating reports to executives, and so on. Should all that code be added to your Admin class? Obviously, no, that would be overwhelming and confusing, making your software difficult to change or maintain. Object-oriented programming is all about organizing code in a way that is intelligible and convenient for humans.

To add and remove books from the library's inventory, create an Inventory class with add & remove methods. Those methods might take an Admin object as an argument if the requirements say we need to track which person authorized or executed the addition/removal.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.