Это первый проект, которым я когда-либо занимался, и теперь, когда я довел его до такой степени, что я чувствую себя комфортно, я хотел бы получить несколько отзывов и советов по его улучшению! Я особенно заинтересован в получении советов по дизайну, удобочитаемости и модульному тестированию части моего кода, и любые советы по улучшению приветствуются!
Я целенаправленно записал все данные в файлы класса Storage, но следующим моим шагом будет реализация базы данных SQL вместе с каким-то графическим интерфейсом, если мне посчастливится получить рецензию на мой проект!
Я еще не написал никаких модульных тестов для класса Storage, и мне интересно, обычно ли это вообще делается? Я полагаю, это потому, что они должны возвращать правильную информацию, но я все равно хотел спросить …
Если в коде есть что-то неясное, спрашивайте, и я уточню!
Вот краткое описание каждой функции классов.
Логин-класс: Отвечает за вход пользователя в систему и возврат правильного пользователя вместе с правильным ToDoList.
RegisterNewUsernameAndPassword-класс: Совершенно очевидно, но он отвечает за создание нового пользователя.
Класс хранения: Отвечает за запись пользователей и списков дел в файл, чтобы их можно было сохранить и загрузить. Также пересылает информацию классу входа в систему и некоторым другим классам.
ToDoList-класс: Отвечает за создание ToDoList для пользователя и связанных методов.
ToDoListTest-класс: Модульные тесты для класса ToDoList.
Пользовательский класс: Определяет параметры для нового пользователя и содержит сеттеры и геттеры.
toDo-класс: Каждый элемент, добавленный в ToDoList, является элементом toDo, и этот класс отвечает за их создание.
UI-класс: Пользовательский интерфейс, который в настоящее время передает информацию пользователю через консоль.
public class UI {
private final Scanner reader;
private Storage storage;
private Login login;
private RegisterNewUsernameAndPassword registerNew;
private User user;
public UI() {
this.reader = new Scanner(System.in);
this.storage = new Storage();
this.login = new Login();
this.registerNew = new RegisterNewUsernameAndPassword();
}
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();
registerNew.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")) {
storage.getToDoLists().put(login.returnUsername(), this.user.getUsersToDoList());
storage.saveUsersToDoLists(storage.getToDoLists());
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 class Login {
private User user;
private Storage storage;
private Scanner reader;
private String username;
public Login() {
this.storage = new Storage();
this.reader = new Scanner(System.in);
}
public void logIn() {
storage.loadUserNamesAndPasswords(storage.getUsernamesAndPasswordsFile());
storage.loadUsersToDoLists(storage.getUsersToDoListsFile());
System.out.println("Username:");
this.username = reader.nextLine();
System.out.println("Password:");
String password = reader.nextLine();
try {
if (storage.getUserNamesAndPasswords().get(username).passwordEquals(password) != null) {
this.user = storage.getUserNamesAndPasswords().get(username);
this.user.setList(storage.getToDoLists().get(username));
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 class User implements Serializable {
private String firstName;
private String lastName;
private String password;
private ToDoList toDoList;
public User(String firstName, String lastName, String password) {
this.firstName = firstName;
this.lastName = lastName;
this.password = password;
this.toDoList = new ToDoList();
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public ToDoList getUsersToDoList() {
return this.toDoList;
}
public void setList(ToDoList list) {
this.toDoList = list;
}
public Boolean passwordEquals(String password) {
return this.password.equals(password);
}
}
public class Storage {
private HashMap<String, ToDoList> toDoLists;
private HashMap<String, User> map;
private File UsernamesAndPasswords;
private File UsersToDoLists;
Storage() {
this.UsernamesAndPasswords = new File("UsernamesAndPasswords.ser");
this.UsersToDoLists = new File("ToDoLists.ser");
loadUserNamesAndPasswords(UsernamesAndPasswords);
loadUsersToDoLists(UsersToDoLists);
}
public void saveUsersToDoLists(HashMap<String, ToDoList> usersToDoLists) {
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(toDoLists);
}
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 class RegisterNewUsernameAndPassword {
private Storage storage;
private User user;
public RegisterNewUsernameAndPassword() {
this.storage = new Storage();
}
public void createUser(String userName, String firstName, String lastName, String password) {
this.user = new User(firstName, lastName, password);
this.storage.getUserNamesAndPasswords().putIfAbsent(userName, user);
this.storage.saveUserNamesAndPasswords(storage.getUserNamesAndPasswords());
this.storage.getToDoLists().putIfAbsent(userName, this.user.getUsersToDoList());
this.storage.saveUsersToDoLists(storage.getToDoLists());
}
public class ToDoListTest {
@Test
public void addToDo(){
ToDoList todolist = new ToDoList();
toDo todo = new toDo("Test");
todolist.addToDo(todo);
assertTrue(todolist.getList().contains(todo));
}
@Test
public void removeToDo() {
ToDoList todolist = new ToDoList();
toDo todo = new toDo("Test");
todolist.addToDo(todo);
todolist.removeToDo(1);
assertFalse(todolist.getList().contains(todo));
}
@Test
public void listIsEmptyWhenCreated() {
ToDoList todolist = new ToDoList();
assertTrue(todolist.getList().isEmpty());
}
@Test
public void getList() {
ToDoList todolist = new ToDoList();
ArrayList<toDo> list = new ArrayList<>();
assertEquals(list, todolist.getList());
}
@Test
public void printToDoList() {
ToDoList todolist = new ToDoList();
toDo todo = new toDo("Test");
todolist.addToDo(todo);
PrintStream oldOut = System.out;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
System.setOut(new PrintStream(baos));
todolist.printToDoList();
System.setOut(oldOut);
String output = baos.toString();
assertTrue(output.contains("1: Test"));
}
@Test
public void printEmptyToDoList() {
ToDoList todolist = new ToDoList();
PrintStream oldOut = System.out;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
System.setOut(new PrintStream(baos));
todolist.printToDoList();
System.setOut(oldOut);
String output = baos.toString();
assertTrue(output.contains("List is empty."));
}
}
public class toDo implements Serializable {
private String name;
public toDo(String name) {
this.name = name;
}
public void setName(String setName) {
this.name = setName;
}
public String getName() {
return this.name;
}
public String toString() {
return this.name;
}
}
public class ToDoList implements Serializable {
private ArrayList<toDo> toDoList;
public ToDoList() {
this.toDoList = new ArrayList<>();
}
public void addToDo(toDo toDo) {
this.toDoList.add(toDo);
}
public void removeToDo(int toDo) {
try {
this.toDoList.remove(toDo - 1);
} catch (IndexOutOfBoundsException e) {
System.out.println("The index you have entered is invalid.");
System.out.println("Please enter a number between or equal to 1 or " + toDoList.size() + ".");
}
}
public void printToDoList() {
if (toDoList.isEmpty()) {
System.out.println("List is empty.");
} else {
int i = 1;
for (toDo todo : toDoList) {
System.out.println(i + ": " + todo);
i++;
}
}
}
public ArrayList<toDo> getList() {
return this.toDoList;
}
}
1 ответ
Добро пожаловать на обзор кода! Здесь много кода и есть что сказать, но я сосредоточусь на Storage
поскольку это довольно центральный компонент, влияющий на качество всех других классов.
Вам следует изучить ТВЕРДЫЙ принципы, особенно D (инверсия зависимостей). Ты должен лечить Storage
как услуга, предоставляемая друг другу классом. Не то, что классы должны создавать сами. Итак, создайте класс, который загружает один Storage
экземпляр и передает этот экземпляр в UI
, Login
и кому это нужно.
Когда ты думаешь о Storage
как услугу, вам необходимо определить операции, которые нужный своими клиентами. Знание местоположения файла имен пользователей и паролей вряд ли имеет отношение к пользовательскому интерфейсу или логину, верно? Таким образом, вы не должны подвергать getUsernamesAndPasswordsFile()
. Просто позволь Storage
обрабатывать все файлы, связанные с файлами, в частном порядке. Когда вы определили необходимые операции хранения, определить их в интерфейсе и заставить классы использовать интерфейс, а не конкретный класс, который его реализует.
RegisterNewUsernameAndPassword
не должно быть класса. Его имя описывает действие, поэтому это должен быть метод в Storage
. Вы также должны подумать, имеет ли смысл усложнять ваше приложение учетными записями пользователей, если оно не может использоваться в общей среде. Это проект для начинающих, поэтому вам, возможно, следует сначала сконцентрироваться на создании хорошего однопользовательского приложения, прежде чем подвергать себя множеству сложностей, которые возникают, когда множество пользователей обращаются к одному приложению.