让我说我有很长的switch/case
块,这让我内心的我说'它看起来不太好'
switch (option) {
case ERROR:
doSomething();
break;
case SUCCESS:
doSomething();
break;
case WARNING:
doSomething();
break;
case SUCCESS_WITH_WARNING:
doSomething();
break;
case SUCCESS_WITH_ERROR:
doSomething();
break;
case FATAL:
doSomething();
break;
case INFO:
doSomething();
break;
default:
break;
}
请记住,我们总是可以添加更多的枚举等。假设case
块中的方法正在向控制台打印一些东西。所以他们只是void
。
我试图使用访问者,但似乎有更好的选择,所以对于访问者它看起来像这样:
public enum ImportOption {
SUCCEEDED{
@Override
public void accept(ImportOptionVisitor visitor) {
visitor.visitSucceededImport();
}
},
FAILED{
@Override
public void accept(ImportOptionVisitor visitor) {
visitor.visitFailedImport();
}
},
WARNING{
@Override
public void accept(ImportOptionVisitor visitor) {
visitor.visitWarningImport();
}
};
public abstract void accept(ImportOption.ImportOptionVisitor visitor);
public interface ImportOptionVisitor{
void visitSucceededImport();
void visitFailedImport();
void visitWarningImport();
}
而不是代替开关我们得到:
option.accept(new ImportOptionVisitor() {
@Override
public void visitSucceededImport() {
// TODO Auto-generated method stub
}
@Override
public void visitFailedImport() {
// TODO Auto-generated method stub
}
@Override
public void visitWarningImport() {
// TODO Auto-generated method stub
}
但即使是像100枚枚举这样的长开关,它似乎也被夸大了。我读了一些关于命令模式的内容。但我不知道如何实现它。任何想法如何替换switch
并使代码更具可读性等?
好的,更多信息。此代码在Utils
类中实现,需要此开关块的方法如下所示:
public static void monitorDirectory(String zipDir, ImportOption option) {
String dirPath = FILE_PATH_TO_QUEUES_DIR + File.separator + zipDir;
try {
WatchService watchService = FileSystems.getDefault().newWatchService();
Path path = Paths.get(dirPath);
path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent<?> event : key.pollEvents()) {
String fileName = event.context().toString();
if (fileName.substring(fileName.lastIndexOf('.'), fileName.length()).equals(".7z")) {
//switch block here
break;
}
}
key.reset();
}
}
catch (IOException | InterruptedException e) {
e.printStackTrace();
}
它是监视目录,当出现.7z文件时,它将开始处理此文件与ImportOption
枚举的对应关系。该方法用作:Utils.monitorDirectory
。所以我永远不会创建包含此方法的对象的实例(所以这可能是这个代码的问题)
有很多方法可以解决这个问题。我提出了两个在可读性方面类似的选项。
根据您提供的信息(并假设您的每个doSomething()
s正在做一些不同的事情),我只想维护一个包含每个选项启动方法的地图。我假设选项在enum
中,但如果选项是整数或字符串,则适用相同的原则。
private static final EnumMap<ExitCode, Runnable> runOnOption = new EnumMap<> (ExitCode.class);
static {
runOnOption.put(ERROR, () -> LOG.error("an error occurred"));
runOnOption.put(SUCCESS, () -> LOG.info("success"));
runOnOption.put(FATAL, this::runOnFatal);
}
private static void runOnFatal() {
//more complex recovery code
}
然后你的主要方法变成:
Runnable r = runOnOption(option);
if (r == null) throw new AssertionError("Missing option: " + option);
r.run();
另一种方法是将行为直接放在enum
中:
public enum ExitCode {
ERROR(() -> LOG.error("an error occured")),
SUCCESS(() -> LOG.info("success")),
FATAL(() -> {
//more complex code here
};
private final Runnable r;
ExitCode(Runnable r) { this.r = r; }
public void log() { r.run(); }
}
你的主要代码变成:
option.log();