I recently posted a bunch of code of my to-do list application and after getting some very helpful and good suggestions on how to improve, I took a shot at it! Here's a link to the former post I made -> A to-do list application written with Java
I have now used the one and the same instance of storage in each of the classes that need it, instead of creating new instances for each class. Namely, the Login-class and the UI-class. I've also created two interfaces and defined methods to deal with the Storage-class. Also I adjusted some of the Storage-classes methods and added new ones, in order to hide the details of the storage-operations from the Login and UI-classes.
I think I nailed it with the dependency injection by passing that one instance of Storage on the constructors of Login and UI-classes. In the StorageInstance-class, I have a method which returns that instance of a storage. Is this the correct way to go about the dependency injection? Namely, that I use the method returnStorage() to do storage-related actions?
As for the interfaces, I wanted to hide the details of what happens in the Storage-class so the UI and Login wouldn't have any idea what is going on under the surface, except that something is happening that relates to the storage-class. Is this the correct way to go about it? Any and all improvements and help are welcome!
I will not post the whole application code in here, only the classes I've changed. The whole project can be viewed from the link above.
public class Storage {
private HashMap<String, ToDoList> toDoLists;
private HashMap<String, User> map;
private File UsernamesAndPasswords;
private File UsersToDoLists;
private User user;
Storage() {
this.UsernamesAndPasswords = new File("UsernamesAndPasswords.ser");
this.UsersToDoLists = new File("ToDoLists.ser");
loadUserNamesAndPasswords(UsernamesAndPasswords);
loadUsersToDoLists(UsersToDoLists);
}
public void saveUsersToDoLists() {
try {
FileOutputStream fosTwo = new FileOutputStream(UsersToDoLists);
ObjectOutputStream oosTwo = new ObjectOutputStream(fosTwo);
oosTwo.writeObject(this.toDoLists);
oosTwo.flush();
oosTwo.close();
fosTwo.close();
} catch (IOException e) {
System.out.println("Exception happened. saveUsersList");
}
}
public void loadUsersToDoLists(File file) {
if (file.length() == 0) {
toDoLists = new HashMap<>();
this.saveUsersToDoLists();
}
try {
FileInputStream fisTwo = new FileInputStream(UsersToDoLists);
ObjectInputStream oisTwo = new ObjectInputStream(fisTwo);
toDoLists = (HashMap<String, ToDoList>) oisTwo.readObject();
oisTwo.close();
fisTwo.close();
} catch (Exception e) {
System.out.println("Exception happened. loadUsersList");
}
}
public void saveUserNamesAndPasswords(HashMap<String, User> loginInfo) {
try {
FileOutputStream fos = new FileOutputStream(UsernamesAndPasswords);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(this.map);
oos.flush();
oos.close();
fos.close();
} catch (IOException e) {
System.out.println("Exception happened. saveUsernames");
}
}
public void loadUserNamesAndPasswords(File file) {
//If the file is empty then this method creates a new empty hashmap and saves it
//in the file
if (file.length() == 0) {
map = new HashMap<>();
this.saveUserNamesAndPasswords(map);
}
try {
FileInputStream fis = new FileInputStream(UsernamesAndPasswords);
ObjectInputStream ois = new ObjectInputStream(fis);
map = (HashMap<String, User>) ois.readObject();
ois.close();
fis.close();
} catch (Exception e) {
System.out.println("Exception happened. loadUserNames");
}
}
public HashMap<String, User> getUserNamesAndPasswords() {
return this.map;
}
public File getUsernamesAndPasswordsFile() {
return this.UsernamesAndPasswords;
}
public HashMap<String, ToDoList> getToDoLists() {
return this.toDoLists;
}
public File getUsersToDoListsFile() {
return this.UsersToDoLists;
}
public void createUser(String userName, String firstName, String lastName, String password) {
this.user = new User(firstName, lastName, password);
this.getUserNamesAndPasswords().putIfAbsent(userName, user);
this.saveUserNamesAndPasswords(this.getUserNamesAndPasswords());
this.getToDoLists().putIfAbsent(userName, this.user.getUsersToDoList());
this.saveUsersToDoLists();
}
public void validateUsername(String username, String password) {
if (this.getUserNamesAndPasswords().get(username).passwordEquals(password)) {
this.user = getUserNamesAndPasswords().get(username);
this.user.setList(this.getToDoLists().get(username));
}
}
public User returnUser() {
return this.user;
}
}
public class UI implements InterfaceUI {
private final Scanner reader;
private final Storage storage;
private Login login;
private User user;
public UI(StorageInstance si) {
this.reader = new Scanner(System.in);
this.storage = si.getStorage();
this.login = new Login(si);
}
public void start() {
System.out.println("Login or register");
String fromUser = reader.nextLine().trim();
if (fromUser.equalsIgnoreCase("register")) {
System.out.print("Your username:");
String userName = reader.nextLine();
System.out.print("Your first name:");
String firstName = reader.nextLine();
System.out.print("Your last name:");
String lastName = reader.nextLine();
System.out.print("Your password:");
String password = reader.nextLine();
createUser(userName, firstName, lastName, password);
}
login.logIn();
this.user = login.returnUser();
this.user.getUsersToDoList().printToDoList();
while (true) {
System.out.println("");
System.out.println("1: Add a to-do item.");
System.out.println("2. Remove a to-do item.");
System.out.println("3. Print a list of my to-do items.");
System.out.println("4. Quit and save");
System.out.print("Type the number of desired action: ");
String input = reader.nextLine();
if (input.equals("4")) {
saveUsersList();
System.out.println("Quitting!");
break;
} else if (input.equals("1")) {
System.out.println("What would you like to add?");
String add = reader.nextLine();
toDo item = new toDo(add);
this.user.getUsersToDoList().addToDo(item);
} else if (input.equals("2")) {
if (this.user.getUsersToDoList().getList().isEmpty()) {
System.out.println("List is empty.");
continue;
}
System.out.println("");
this.user.getUsersToDoList().printToDoList();
System.out.print("Type the index of the item you wish to remove: ");
int remove = Integer.parseInt(reader.nextLine());
this.user.getUsersToDoList().removeToDo(remove);
} else if (input.equals("3")) {
System.out.println("");
this.user.getUsersToDoList().printToDoList();
}
}
}
public void createUser(String userName, String firstName, String lastName, String password) {
storage.createUser(userName, firstName, lastName, password);
}
public void saveUsersList() {
storage.getToDoLists().put(login.returnUsername(), this.user.getUsersToDoList());
storage.saveUsersToDoLists();
}
}
public class Login implements LoginInterface {
private User user;
private final Storage storage;
private Scanner reader;
private String username;
public Login(StorageInstance si) {
this.storage = si.getStorage();
this.reader = new Scanner(System.in);
}
public void logIn() {
System.out.println("Username:");
this.username = reader.nextLine();
System.out.println("Password:");
String password = reader.nextLine();
try {
validateUser(this.username, password);
System.out.println("Welcome " + user.getFirstName() + "!");
} catch (NullPointerException npe) {
System.out.println("Incorrect username or password. Please try again!");
this.logIn();
}
}
public User returnUser() {
return this.user;
}
public String returnUsername() {
return this.username;
}
public void validateUser(String username, String password) {
storage.validateUsername(username, password);
this.user = storage.returnUser();
}
}
public class StorageInstance {
private Storage storage;
public StorageInstance() {
this.storage = new Storage();
}
public Storage getStorage() {
return this.storage;
}
}
public interface InterfaceUI {
public void createUser(String userName, String firstName, String lastName, String password);
public void saveUsersList();
}
public interface LoginInterface {
public void validateUser(String username, String password);
}