Create a list of assignments

Posted on

Problem

Develop a program to maintain a list of homework assignments. When an assignment is assigned, add it to the list, and when it is completed, remove it. You should keep track of the due date. Your program should provide the following services:

  1. Add a new assignment.
  2. Remove an assignment.
  3. Provide a list of the assignments in the order they were assigned.
  4. Find the assignment(s) with the earliest due date.

Just want to make sure I am doing this correctly. Should these methods in the Main class be happening in the Assignments class or is it fine as it is? Here is my code for both classes:

Assignments Class:

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

public class Assignments {

private String assignmentName;
private LocalDate date;

public Assignments() {


}

public Assignments(String assignmentName, LocalDate date) {

   this.assignmentName = assignmentName;
   this.date = date;

}

public String getAssignmentName() {
  return assignmentName;
}

public void setAssignmentName(String assignmentName) {
  this.assignmentName = assignmentName;
}

public LocalDate getDate() {
  return date;
}

public void setDate(LocalDate date) {
  this.date = date;
}


@Override
public String toString() {

  return "Assignment: " + assignmentName + "nDue Date: " + date;

}

}

Main Class:

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

public class Main {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    Assignments assignment = new Assignments();
    
    Scanner indata = new Scanner(System.in);
    
    //Creating an ArrayList of assignments
    List<Assignments> assignmentList = new ArrayList<>();
    
    assignmentList.add(new Assignments("Assignment1", LocalDate.of(2021, 12, 10)));
    assignmentList.add(new Assignments("Assignment2", LocalDate.of(2021, 12, 14)));
    assignmentList.add(new Assignments("Assignment3", LocalDate.of(2021, 12, 9)));
    assignmentList.add(new Assignments("Assignment4", LocalDate.of(2021, 12, 7)));
    assignmentList.add(new Assignments("Assignment5", LocalDate.of(2021, 12, 13)));
    assignmentList.add(new Assignments("Assignment6", LocalDate.of(2021, 12, 7)));
    
    menu(assignment, indata, assignmentList);
    

}

private static void getEarliestDate(List<Assignments> assignmentList) {
    selectionSort(assignmentList);
    System.out.println();
    int j = 0;
    for(int i = 0; i < assignmentList.size(); i++) {
        
        if(assignmentList.get(i).getDate().isEqual(assignmentList.get(j).getDate())) {
            
            System.out.println(assignmentList.get(i));
            
        }
        
    }
}

private static void printSortedList(List<Assignments> assignmentList) {
    selectionSort(assignmentList);
    for(int i = 0; i < assignmentList.size(); i++) {
        
        System.out.println(assignmentList.get(i));
        
    }
    
}

//Using the selection sort algorithm to sort list from earliest due date to latest due date
private static void selectionSort(List<Assignments> assignmentList) {
    
    for(int i = 0; i < assignmentList.size(); i++) {
        
        int pos = i;
        for(int j = i; j < assignmentList.size(); j++) {
            if(assignmentList.get(j).getDate().isBefore(assignmentList.get(pos).getDate())) {
                pos = j;
                
            }
            
        }
        
        Assignments min = assignmentList.get(pos);
        assignmentList.set(pos, assignmentList.get(i));
        assignmentList.set(i, min);

        
    }
    
    
}

//Method that gives menu options
private static void menu(Assignments assignment, Scanner indata, List<Assignments> 
  assignmentList) {
    int userInput = 1;
    
    while(userInput != 0)
    {
    
    System.out.println("Enter 1 to add an assignment");
    System.out.println("Enter 2 to remove an assignment");
    System.out.println("Enter 3 to get ordered list of assignments");
    System.out.println("Enter 4 to find earliest due date");
    
    
    
    userInput = indata.nextInt();
    
        switch(userInput) 
        {
    
        case 1:
            addAssignment(assignment, indata, assignmentList);
            System.out.println();
            break;
        case 2:
            removeAssignment(indata, assignmentList, assignment);
            System.out.println();
            break;
        case 3:
            printSortedList(assignmentList);
            System.out.println();
            break;
        case 4:
            getEarliestDate(assignmentList);
            System.out.println();
            break;
        default:
            userInput = 0;
            break;
    
        }
    }
    
}

private static void removeAssignment(Scanner indata, List<Assignments> assignmentList, 
  Assignments assignment) {
    String assignmentInput;
    
    System.out.print("Remove an assignment: ");
    assignmentInput = indata.next();

    for(int i = 0; i < assignmentList.size(); i++) 
    {
        
        if(assignmentList.get(i).getAssignmentName().equals(assignmentInput)) 
        {
            
            assignmentList.remove(i);
            
        }
        
        
    }
    
    
}

private static void addAssignment(Assignments assignment, Scanner indata, List<Assignments> 
 assignmentList) {
    String assignmentInput;
    String dateInput;
        
    System.out.print("Assignment: ");
    assignmentInput = indata.next();
    assignment.setAssignmentName(assignmentInput);
    System.out.println();   
    System.out.print("Due date: (YYYY-MM-DD)");
    dateInput = indata.next();
    assignment.setDate(LocalDate.parse(dateInput));
        
    assignmentList.add(assignment);
    
}

}

Solution

I’m going to borrow some from other answers but present it differently.

Assignment Class

  • As @Joop Eggen said, the right names are important and pluralization of classes often trips developers up. It’s pretty rare that a class should be pluralized, though there are some cases. Assignment is a better name. It looks like the class used to be a list of assignments and then you changed it to be a single assignment.
  • Within that, assignmentName is redundant. You already know that the name belongs to an assignment. Further, date is ambiguous. Is it the date that it was assigned or due? Your requirements suggest that both are needed, so let’s keep track of both.
  • You need the assigned date time in order to display the assignments in the order that they were assigned rather than just the date, so that field should be a LocalDateTime.
  • That gives us name, assignedDateTime, and dueDate.
  • These fields in the Assignment class do not need to change (according to your requirements), so you can remove the setter methods and mark those fields as final. These denote the fields as immutable, which is often valued as it can make a class easier to work with. If you make this change right away, it’s going to break something that you do that I talk about later one. (Because I’m suggesting that you remove the setter methods.)
  • I marked the Assignment class as final too, because you’re not intending to create a subclass for it. If you haven’t heard of inheritance yet, don’t worry about this.
  • I also removed the default constructor (the one with no parameters) because that enforces all users of this class to use the other constructor.
  • In a professional environment, each parameter should be validated to ensure that it has good date. For example, making sure that the name is not null or an empty string. I’m leaving this out for now, but adding argument validation can make development easier (despite having more code).
  • If you can use Java 15, then a record is exactly what you want.

Pre-Java 15:

import java.time.LocalDate;

public final class Assignment {
    private final String name;
    private final LocalDate assignedDate;
    private final LocalDate dueDate;

    public Assignment(String name, LocalDate assignedDate, LocalDate dueDate) {
        this.assignmentName = assignmentName;
        this.assignedDate = assignedDate;
        this.dueDate = dueDate;
    }

    public String getName() {
        return name;
    }

    public LocalDate getAssignedDate() {
        return assignedDate;
    }

    public LocalDate getDueDate() {
        return dueDate;
    }

    @Override
    public String toString() {
        return "Assignment: " + name + "nAssigned Date: " + assignedDate + "nDue Date: " + dueDate;
    }
}

Java 15+

public record Assignment(String name, LocalDate assignedDate, LocalDate dueDate)
{ }

HomeworkList Class

  • I do actually like the idea of having a class that represents the list of assignments, though I’d suggest a different name to help provide clarity. HomeworkList. Assignments just looks too similar to Assignment.
  • See if you can come up with the contents of HomeworkList. What fields does it need? Are those immutable? (This is a bit tricky because you want the contents of the list to change, but not the list itself. Ask me if this doesn’t make sense.)
  • This HomeworkList should know absolutely nothing about Scanners or System.out. It’s just holding data.
  • It should be able to have an assignment added to it or removed.
  • You currently load up the assignmentList inside your main method. You’ll want to remove that before you submit your program, but it’s helpful to have while you are testing it.
  • The HomeworkList should be about to sort the assignments by assignedDate.
  • The HomeworkList should be able to return the one or more assignments that have the earliest due date.

Main Class

  • What’s left is a Main class that handles just the menu portion. How would you rewrite it to work with a HomeworkList?
  • Something that might be tripping you up now, you create an Assignment in the main method, but why? Let’s walk through what’s really happening in your addAssignment method.
  1. You create a box to hold some data in the main method
  2. Then that box is passed to addAssignment
  3. In that box you place a name and a date taken from user input. Let’s say “Quiz” due “2021-12-06”
  4. Then you place that box at the end of the list
  5. When you later print the list you see at the end

“Quiz due 2021-12-06”

So far that doesn’t seem too far off. But let’s add another assignment.

  1. That same box is passed to addAssignment
  2. You replace the name and date with new user input. “Test” due “2021-12-10”
  3. When you look at the list now, you see at the end

“Test due 2021-12-10”

“Test due 2021-12-10”

?!?! What happened to the previous assignment? Why wasn’t a new assignment created?

Hopefully this all gets you going in the right direction. There are a bunch of handy little things that can be done to make your program more and more simple, but get the basics down first.

@Joop Eggen has covered some good points. Another few other things to think about…

  • Blank lines can help to break up logical sections of code. However it can be quite distracting to read code that uses too much, or inconsistent blank lines. Typically your IDE can be configured to help you maintain consistency for some of this type of spacing. (blank lines after for statements, at start of functions etc). It’s very unusual to see 2, or 3 blank lines in the middle of a function/class.
for(int i = 0; i < assignmentList.size(); i++) {
    
    int pos = i;
    for(int j = i; j < assignmentList.size(); j++) {
        if(
  • “Provide a list of the assignments in the order they were assigned.” I seem to have interpreted this differently to you. To me this says, I should if I assign tasks “1,2,3,4” in that order, then I should be able to get them back in that order even if they’re due to be completed “4,3,2,1”. All of your methods return the list ordered by due date.

  • None of your methods give any feedback if they don’t do anything. Typically you’d expect something like “No Assignments”, “That assignment doesn’t exist” etc.

  • Your menu’s default interpretation is exit. Consider adding a specific option to exit, or checking if the user really wants to exit. It’s quite frustrating if you hit the wrong key by accident and the application just exits and throws away the in memory database. Consider also how you might handle invalid input. For example what happens if the user types “Important Assignment” in response to:

System.out.print("Due date: (YYYY-MM-DD)");
dateInput = indata.next();
assignment.setDate(LocalDate.parse(dateInput));
  • There’s no validation to ensure that assignments don’t have the same name. Is this a valid scenario? I might put call all my Maths assignments the same thing for example.

If so, consider how your remove function would behave in various combinations. If not, consider if you might could stop looking after you find the first occurence of the assignment name…

for(int i = 0; i < assignmentList.size(); i++) 
{        
    if(assignmentList.get(i).getAssignmentName().equals(assignmentInput)) 
    {            
        assignmentList.remove(i);
  • get is usually used to prefix property getters. It sets an expectation that the method will return something. You getEarliestDate doesn’t return anything. This feels like the code encouraging you to refactor the printing out of the method…
  • Right names.

    The class should be called Assignment singular, and instead of assignmentName one can drop assignment, maybe call it topic as name is too generic, misleading.

  • Your question: where what methods.

    You could create a class Assignments holding a List<Assignment> and providing operations on the list, scheduling, no conflicts, listing by date etcetera. This is defining an abstraction, a small algebra of operations that can be done on assigments: how many assignments there are and so on.

    The usage could then be done high level in the Main class; using System.out there.

  • Test Driven Development using unit tests, needs some learning; is worth it.

    There is also a solution I use: make a unit test, and test all functionality in a unit test:

    @Test
    public void testAdd() {
        Assignments assignments = new Assignments();
        assignments.add(...);
        assertEquals(1, assignments.size());
    }
    

    This is Test Driven Development, and you rapidly develop the functionality
    and has a nice overview, sample code.

    In this case this would probably overkill. Main suffices.

  • Remove this irritating comment:

      // TODO Auto-generated method stub
    
  • A utility function Collections.addAll:

    List<Assignment> assignmentList = new ArrayList<>();
    Collections.addAll(assignmentList,
        new Assignment("Assignment1", LocalDate.of(2021, 12, 10)),
        new Assignment("Assignment2", LocalDate.of(2021, 12, 14)),
        new Assignment("Assignment3", LocalDate.of(2021, 12, 9)),
        new Assignment("Assignment4", LocalDate.of(2021, 12, 7)),
        new Assignment("Assignment5", LocalDate.of(2021, 12, 13)),
        new Assignment("Assignment6", LocalDate.of(2021, 12, 7)));
    

    There exists also List.of(...).

  • There are some variations on for-like loops:

    for (int i = 0; i < assignmentList.size(); i++) {
        System.out.println(assignmentList.get(i));
    }
    
    for (Assigment assignment: assignmentList.size()) {
        System.out.println(assignment);
    }
    
    assignmentList.forEach(a -> System.out.println(a));
    
  • In international software ZonedDateTime is more appriopriate and in the database a UTC time (Zero Zone). I just mention it, as LocalDate is fine here, but a future internationalized product should make that transition.

  • Your own sort is wasted effort.

    assignmentList.sort(Comparator.comparing(Assignment::getDate)
        .thenComparing(Assigment::getAssignmentName));
    
    List<Assignment> sorted = assignmentList().stream()
        .sorted(Comparator.comparing(Assignment::getDate))
        .collect(Collectors.toList());
    

    This might be a bit much for a beginner (some new complex concepts), but has quite some expressive power.

  • The operations you want to provide determine data structures too.

    A List is quite unstructured. If you want to to have a list by date, or search for a specific assignment. For a first version you can start with a List, and hide such implementation details inside a class Assignments (plural).

    First compose a list of operations you want to provide. This is the API, Application Programming Interface, the class.

  • The usage in Main:

    System.out.println("Enter 1 to add an assignment");
    

    Could simply be:

    System.out.println("1. Add an assignment");
    ...
    System.out.println("Enter a choice (1-4)");
    int userInput = indata.nextInt();
    

    Declare variables at their first point of use.
    The above has one problem: validation. One would need to do:

    int userInput;
    if (indata.hasNextInt()) {
        userInput = indata.nextInt();
    } else {
       indate.next(); // Discard non-numeric input.
       userInput = 0;
    }
    ...
    

Leave a Reply

Your email address will not be published. Required fields are marked *