For my Intro to Programming module, I made a TicketDesk system in Java that can track tickets, track login information and has a role-based authentication system. This devlog will explore the main features and challenges I've faced on the way, along with useful comments on key points that I thought are worth mentioning.
Brief Description of the program:
The main goal of this TicketDesk system was to have a program that would have the ability to sort users who used the software into different roles, and each would have a different experience when interacting with the ticket desk. Admins would have the most functionality, being able to delete tickets and manage accounts, users would be able to create and manage tickets but no account management, where guests would only get the ability to read into tickets, with a limited amount of information.
I'll first explain through three key features of the program.
Feature 1: User Input
The program relies on user input to be able to navigate through the system, as users will need to constantly make menu selections and enter data into fields so that tickets can be recorded. I primarily used the Scanner class for this, as it was simply easier than using other methods such as BufferedReader, which was not as intuitive, such as with the checked IOException. I've declared the scanner variable as private in my Main class, as it would only be read from within that specific class.
Rather than using nextInt(), I went for using parseInt() as shown, as the former doesn't consume the next line and having to add a new scanner.nextline() when it's been used dozens of times isn't very practical.
private static Scanner sc = new Scanner(System.in);
int choice = Integer.parseInt(sc.nextLine());
The input captured through the Scanner is what drives the control of the ticket desk, where integers will navigate through menus and string inputs are used for populating ticket fields such as titles and descriptions, making it central for operating the system. This was a common issue that I came across in this thread online, https://stackoverflow.com/questions/13102045/scanner-is-skipping-nextline-after-using-next-or-nextfoo.
Feature 2: Methods
There are many use cases of methods throughout the program, with it helping to separate functionality into sections and being able to reference them throughout the different classes, such as TicketManager, Authentication and Main. For options such as creating tickets, for the users who have the permissions this method will be called, instead of rewriting this code over and over.
// method for creating tickets, for only users and admins
public void createTicket(Person loggedIn, Scanner sc) {
loadTickets();
if(loggedIn.isGuest()){ // if the user is a guest
System.out.println("Unauthorised to create tickets. Returning to main menu...");
return;
}
String title = getInput("Enter the ticket title (or 'CANCEL' to go back): ", sc);
The createTicket() method here is used when a user of appropriate permissions decides to make a new ticket, so instead of repeating the code for creating a ticket repeatedly, this method can be called to simplify the code and have it be more efficient. The checks for whether the user is of a high enough level to be able to create tickets will also be checked within this method, so it will only have to be called once for the entire menu to be prompted.
Methods like this work with the Person class for authentication and Main for menu navigation, which shows how breaking code into smaller parts makes the whole system easier to manage, which is also links in relation with the single responsibility principle, as explored through in this link https://www.theserverside.com/tip/How-to-apply-the-single-responsibility-principle-in-Java.
However, I have recognised after reading through that the createTicket() method does handle a few tasks, such as loading the tickets and checking permissions, so isn’t perfectly following the single responsibility principle, so for the future I would probably look to separating those checks just to allow it to be called in any future features that may added, but for this project I believe that it works nicely as is.
Feature 3: Object-Orientated Programming
For the third feature I'll go into object orientated programming (OOP) as it is used frequently within my code, especially within the Authentication side. The ticket desk system has different user roles such as guest, user and admin, each of the roles has specific menus and features which they are able to access, and menus which will be blocked. For each role there was a specific class created, such as Guest.java, which extends off of the Person.java class, which handles processes such as the assignment of roles. In the Person class there is
public class Person {
// used private fields instead of protected for better encapsulation (getters and setters used)
// can change requirements and access for the variables
private String username;
private String password;
private String role;
public Person(String username, String password, String role) {
this.username = username;
this.password = password;
this.role = role;
}
----------------------
public class Admin extends Person {
// constructor for admin class
public Admin(String username, String password) {
super(username, password, "admin");
}
}
One of the main advantages of why I stuck with using OOP is because it makes the code a lot more flexible, as it means I don’t need to constantly redefine, for example, what an admin user consists of every time one is created, and allow for more roles to be added in the future without having to dig through all of the code. Another benefit is OOP being more intuitive when reading through the code, as all my Ticket information will be stored in the ticket class with variables for each part, such as description or tag. The hierarchical structure that has been implemented also makes sense for this ticket desk system as admins will have more permissions than any of the regular users, and then guests being heavily restricted, an example of inheritance and encapsulation. This can be explored further from the articles linked here.
https://www.geeksforgeeks.org/java/encapsulation-in-java/
https://www.tutorialspoint.com/java/java_encapsulation.htm
Issues Encountered
I also wanted to go through some of the key issues that I faced when writing this system, as many were encountered to get it to the working state that it is currently in.
Issue 1: Delimiter Input
When admins or users where creating tickets, or doing any action to these tickets such as modifying them, if the user were to type a comma into one of the sections, such as the title, it would break when displaying the ticket as all the information would have shifted back, as my delimiter in the code was a comma.
There were a few ways that came to mind to solve this, first was to choose a character that isn't commonly used, but if a user managed to somehow mistype this character or intentionally tried to break the system then it would just be a way for the system to break, so instead, creating a specific helper method to deal with this seemed to be the a nice way around it.
// very useful helper method to stop users from typing commas in the fields, as well as empty inputs
private String getInput(String prompt, Scanner sc) {
while (true) {
System.out.println(prompt);
String userInput = sc.nextLine();
if (userInput.contains(",")) {
System.out.println("Comma's cannot be entered! Please try again.");
} else if (userInput.trim().isEmpty()){
System.out.println("Input cannot be empty, try again.");
} else {
return userInput;
}
}
}
This method would be used anytime I had to take a user's input, for example when editing ticket fields, and what it would do is if there was a comma used in their input then it would just loop back and ask again for another input, and it also seemed wise to add a way so that if nothing was typed to also loop the menu again. This method can be used directly, so all I would need is to use getInput(), and type the prompt in while using the scanner imported from the class I'm using it from and it stopped there being a way for the program to have an easy way to break.
Issue 2: Console output overflow
When displaying large lists, such as a list of all tickets or all accounts on the system, a big pain point was that the console would immediately return to the menu, which would chuck everything the user was looking for to be scrolled off screen before the user would be able to read it. This was especially a concern with the admin menu, with there being quite a lot of options, and so having to scroll back, past the menu that was outputted, just to see the information they were wanting isn't exactly ideal.
System.out.println("\n========== Limited Ticket View ============");
for (int i = 0; i < ticketList.size(); i++) {
Ticket t = ticketList.get(i);
// formatted manually for the so it will only display these fields
System.out.println("ID: " + t.getTicketId() + " | Title: " + t.getTitle() + " | Status: " + t.getStatus() + " | Tag: " + t.getTag());
}
System.out.println("==============================================");
System.out.println("\nTap ENTER to continue.."); // so you aren't kicked back to menu, gives time to look
sc.nextLine();
A simple solution that I found for this problem was to add a simple prompt that would output so that the user would need to tap ENTER to allow them to have read what they need and move on with the system. This also worked when deleting users as viewing all users would be shown automatically to be able to know which accounts to delete, so giving the admins time to read through was a slight improvement to the user experience. In a GUI based system this just wouldn’t be an issue, but for this app which is console based it was something that would just be a nuisance, especially when using different IDE’s.
Issue 3: Ticket File not found
One of the goals for this project was to eliminate all major ways in which the program could break, and one of them happened when the system couldn't find the text file for where tickets are stored. If someone where to just drop a file in to one of the folder directories, but wasn't in the exact position that the program was looking for, then the whole program would break as it couldn't load tickets so nothing could be saved or read from. So instead, adding an option that has the file created automatically was a nice way to make sure that no unintentional mishaps were to come up when using the ticket desk. A simple method to how this was done is explored in this
if(!myFile.exists()) {
// can also add your own files directly, would just need to be formatted the same way as how its done below for parsing and be called ticketlist.txt
System.out.println("There is currently no file to load tickets from... A new file has been created called " + filename + " !");
try {
myFile.createNewFile();
System.out.println("The file ticketlist.txt has now been created!");
return;
} catch(IOException e) { // need to handle this IO exception as its a checked exception
System.out.println("Error creating file, returning to main menu..." + e.getMessage());
return;
}
}
This part helps as when a new file is created and an admin wishes to load premade tickets, then it will say that “0 tickets” are loaded on file, allowing for the admin to go into the created text file and modify it to allow for the system to be able to read the tickets that have been put in.
These were the main features and issues I wanted to discuss on my program, thanks for reading. Overall, this reflection on the project taught me the ways of how to structure my code in a way that would be read by other people, and to make it reliable and to address all edge cases that came to mind to avoid any unexpected situations to happen.
HD
Top comments (0)