如何避免静态方法中的长开关/盒块

时间:2019-02-01 11:28:24

标签: java design-patterns

让我说我的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。因此,我永远不会创建包含此方法的对象的实例(所以这可能是这段代码的问题)

1 个答案:

答案 0 :(得分:2)

有很多方法可以解决这个问题。我提出了两个在可读性等方面相似的选项。

选项1

根据您所提供的信息(并假设您的每个doSomething()都在做不同的事情),我只需要维护一个包含每个选项启动方法的映射即可。我在这里假设选项位于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();

选项2

另一种方法是将行为直接放在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();