проект: Simple File Manager. структура кода, лучшие практики
задание выполнено и код рабочий. Java17, юнит тесты по заданию не требуются. прошу дать обратную связь:
- по архитектуре и делению на классы/интерфейсы
- по улучшению стиля написания кода
- посоветовать лучшие практики: где, как и почему нужно писать по-другому
- любая, безжалостная обратная связь категорически ПРИВЕТСВУЕТСЯ
- сейчас еще возник вопрос на счет разделения интерфейсов FileSystemAction, FileAction, DirectoryAction, кажется, что это избыточно и достаточно будет только одного интерфейса FileSystemAction, так ли это?
- правильно ли используется JavaDoc? Есть ли какие-либо рекомендации?
чтобы было проще разобраться (надеюсь на это) с тем, как классы и интерфейсы взаимодействуют составил UML диаграмму, ее можно найти в репозитории проекта проект на Гитхаб, либо я по ней задавал вопрос тут же на StackOverflow.
код программы:
package ex02;
import ex02.command.FileManager;
/**
* Entry point for the file manager application.
*/
public class Program {
/**
* Main method to start the file manager.
*
* @param args command line arguments
*/
public static void main(String[] args) {
FileManager fm = new FileManager(args[0]);
boolean exitProgram = false;
while (!exitProgram) {
exitProgram = fm.isExitFileManager();
}
}
}
package ex02.command;
import java.nio.file.Path;
import java.util.Scanner;
import ex02.interfaces.FileSystemAction;
/**
* Manages the current working directory and file operations.
*/
public class FileManager {
private Scanner scanner = new Scanner(System.in);
/**
* Current path.
*/
private Path pwdPath;
/**
* Validate and get user command.
*
* @return string array splited by whitespace
*/
private String[] getUserCommand() {
return CommandHandler.getCommand(scanner.nextLine());
}
/**
* Constructs a FileManager with the given initial working directory.
*
* @param pwd the initial working directory
*/
public FileManager(final String pwd) {
pwdPath = CommandHandler.getValidatedPath(pwd);
System.out.println(pwdPath.toString());
}
/**
* Starts the file manager command loop, processing user commands
* until the exit command is received.
*
* @return true if exit command was received, false otherwise
*/
public boolean isExitFileManager() {
String[] userCommand = getUserCommand();
boolean exitCommand = userCommand.length > 0 ? userCommand[0].equals("exit") : Boolean.getBoolean("false");
while (!exitCommand) {
try {
FileSystemAction fsAction = CommandHandler.getActionType(userCommand);
fsAction.action(this, userCommand);
} catch (RuntimeException e) {
System.out.println(e.toString());
for (StackTraceElement elem : e.getStackTrace()) {
System.out.println(" --- " + elem.toString());
}
return exitCommand;
}
userCommand = getUserCommand();
exitCommand = userCommand[0].equals("exit");
}
return exitCommand;
}
/**
* Gets the current working directory path.
*
* @return the current path
*/
public Path getPwdPath() {
return pwdPath;
}
/**
* Sets the current working directory path.
*
* @param path the new path
*/
public void setPwdPath(Path path) {
this.pwdPath = path;
}
}
package ex02.command;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.Map;
import ex02.directory.DirectoryInfoProvider;
import ex02.directory.WorkDirectoryChanger;
import ex02.file.FileReplacer;
import ex02.interfaces.CommandValidator;
import ex02.interfaces.FileSystemAction;
/**
* Handles parsing, validation, and execution of file manager commands.
*/
public class CommandHandler {
/**
* Validates command args.
*/
private static Map<String, CommandValidator> validators = Map.of(
"ls", args -> {
if (args.length != 1) {
throw new UnsupportedOperationException("ls - does not accept arguments");
}
},
"mv", args -> {
if (args.length != 3) {
throw new UnsupportedOperationException("mv - the source and destination paths are needed");
}
},
"cd", args -> {
if (args.length != 2) {
throw new UnsupportedOperationException("cd - one argument is needed, destination directiry");
}
},
"", args -> {
throw new UnsupportedOperationException("you need to select one of the commands: mv, cd, ls");
});
/**
* Supported operations.
*/
private static Map<String, FileSystemAction> commands = Map.of(
"ls", new DirectoryInfoProvider(),
"mv", new FileReplacer(),
"cd", new WorkDirectoryChanger());
private CommandHandler() {
throw new UnsupportedOperationException("Utility class!");
}
public static String[] getCommand(String input) {
if (input.isEmpty()) {
System.out.println("Enter a command \"ls\" or \"cd\" or \"mv\"");
return new String[] {};
} else {
return input.split(" ");
}
}
private static Path validatePwdPath(final String path) {
Path pwdPath = Path.of(path.replace("--current-folder=", ""));
if (Files.isDirectory(pwdPath, LinkOption.NOFOLLOW_LINKS)) {
return pwdPath;
} else {
throw new IllegalArgumentException("Path does not exist"
+ " оr not a directiry");
}
}
public static Path getValidatedPath(final String path) {
return validatePwdPath(path);
}
public static Path getValidatedPath(final Path path) {
return validatePwdPath(path.toString());
}
public static FileSystemAction getActionType(String[] args) throws UnsupportedOperationException {
FileSystemAction actionType = null;
if (args.length > 0) {
validators.get(args[0]).validate(args);
actionType = commands.get(args[0]);
}
if (actionType == null) {
throw new UnsupportedOperationException(
"""
This type of operation does not support!
""");
}
return actionType;
}
}
package ex02.interfaces;
import ex02.command.FileManager;
/**
* Interface for actions related to the file system.
*/
public interface FileSystemAction {
/**
* Performs an action on the file system.
*
* @param fileManager the file manager instance
* @param command the command arguments
*/
void action(FileManager fileManager, String[] command);
}
package ex02.interfaces;
import ex02.command.FileManager;
/**
* Interface for actions related to directories.
*/
public interface DirectoryAction extends FileSystemAction {
/**
* Performs an action on a directory.
*
* @param fileManager the file manager instance
* @param command the command arguments
*/
void action(FileManager fileManager, String[] command);
}
package ex02.interfaces;
import ex02.command.FileManager;
/**
* Interface for actions related to files.
*/
public interface FileAction extends FileSystemAction {
/**
* Performs an action on a file.
*
* @param fileManager the file manager instance
* @param command the command arguments
*/
void action(FileManager fileManager, String[] command);
}
package ex02.interfaces;
/**
* Validates command arguments for file manager operations.
*/
public interface CommandValidator {
/**
* Validates the command arguments.
*
* @param args command arguments
*/
void validate(String[] args);
}
package ex02.directory;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import ex02.command.FileManager;
import ex02.interfaces.DirectoryAction;
/**
* Provides information about the contents of a directory.
*/
public class DirectoryInfoProvider implements DirectoryAction {
/**
* Lists the contents of the current directory.
*
* @param fileManager the file manager instance
* @param command user command, do not use in this case
*/
@Override
public void action(FileManager fileManager, String[] command) {
try (DirectoryStream<Path> ds = Files.newDirectoryStream(
fileManager.getPwdPath())) {
for (Path entry : ds) {
long countOfBytes = Files.walk(entry)
.filter(Files::isRegularFile)
.mapToLong(x -> {
try {
return Files.size(x);
} catch (IOException e) {
return 0L;
}
})
.sum();
System.out.println(entry.getName(entry.getNameCount() - 1)
+ " "
+ (countOfBytes > 1024 ? countOfBytes / 1024 : countOfBytes)
+ " KB");
}
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
package ex02.directory;
import java.nio.file.Path;
import ex02.command.CommandHandler;
import ex02.command.FileManager;
import ex02.interfaces.DirectoryAction;
/**
* Changes the current working directory in the file manager.
*/
public class WorkDirectoryChanger implements DirectoryAction {
/**
* Changes the current directory to the specified target.
*
* @param fileManager the file manager instance
* @param command the command arguments, where command[1] is the target
* directory
* @throws IllegalArgumentException if the target directory is invalid
*/
@Override
public void action(FileManager fileManager, String[] command)
throws IllegalArgumentException {
Path target = Path.of(command[1]);
if (!target.isAbsolute()) {
target = fileManager.getPwdPath().resolve(target);
}
fileManager.setPwdPath(CommandHandler.getValidatedPath(target).normalize());
System.out.println(fileManager.getPwdPath());
}
}
package ex02.file;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import ex02.command.FileManager;
import ex02.interfaces.FileAction;
/**
* Handles file move and rename operations.
*/
public class FileReplacer implements FileAction {
/**
* Moves or renames a file or directory.
*
* @param fileManager the file manager instance
* @param command which includes source (index 1) & destination (index 2)
* paths
*/
@Override
public void action(final FileManager fileManager, final String[] command) {
try {
Path pwd = fileManager.getPwdPath();
Path src = pwd.resolve(command[1]).toAbsolutePath().normalize();
Path dst = pwd.resolve(command[2]).toAbsolutePath().normalize();
if (!Files.exists(src)) {
System.out.println("File not found!");
}
if (Files.isDirectory(dst, LinkOption.NOFOLLOW_LINKS)) {
Files.move(src, dst.resolve(src.getFileName()),
StandardCopyOption.REPLACE_EXISTING);
} else {
Files.move(src, dst);
}
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
Источник: Stack Overflow на русском