Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
重构java代码_Java_Refactoring - Fatal编程技术网

重构java代码

重构java代码,java,refactoring,Java,Refactoring,好吧,我猜这个问题看起来很像: 考虑一下这个问题 我想重构如下代码: String input; // input from client socket. if (input.equals(x)) { doX(); } else if (input.equals(y)) { doY(); } else { unknown_command(); } 它是检查套接字输入以执行某些操作的代码,但我不喜欢if-else结构,因为每次向服务器(代码)添加新命令时,都必须添加新的if-el

好吧,我猜这个问题看起来很像:

考虑一下这个问题


我想重构如下代码:

String input; // input from client socket.
if (input.equals(x)) {
  doX();
} else if (input.equals(y)) {
  doY();
} else {
  unknown_command();
}

它是检查套接字输入以执行某些操作的代码,但我不喜欢
if-else
结构,因为每次向服务器(代码)添加新命令时,都必须添加新的if-else,这很难看。删除命令时,还必须修改
if else

映射中收集这些命令,其中
命令
是一个带有
执行()
方法的
接口

Map<String, Command> commands = new HashMap<String, Command>();
// Fill it with concrete Command implementations with `x`, `y` and so on as keys.

// Then do:
Command command = commands.get(input);
if (command != null) {
    command.execute();
} else {
    // unknown command.
}
Map命令=newhashmap();
//用带有'x'、'y'等键的具体命令实现来填充它。
//然后做:
Command=commands.get(输入);
if(命令!=null){
command.execute();
}否则{
//未知命令。
}

进一步了解,您可以考虑通过扫描实现特定接口的类(在本例中为代码>命令/代码>)或类路径中的特定注释来动态地填充该映射。在这方面可能会有很大帮助


<强>更新< /强>(从评论中)你也可以考虑将我的答案结合起来。在

buildExecutor()
方法中,首先从
Map
获取命令,如果该命令在
Map
中不存在,则尝试加载关联的类并将其放入
Map
中。有点延迟加载。这比在我的答案中扫描整个类路径并在Instantsoup的答案中每次创建它更有效。

一种方法是使用一个接口
ICommand
,它是命令的一般约定,例如:

public interface ICommand {
    /** @param context The command's execution context */
    public void execute(final Object context);
    public String getKeyword();
}
然后,您可以使用Java的机制自动发现各种实现,并在
Map
中注册它们,然后执行
knownCommandsMap.get(input)、execute(ctx)
或类似操作

这实际上使您能够将服务与命令实现分离,有效地使这些实现可插拔

向SPI注册实现类是通过添加一个名为ICommand类的完全限定名的文件来完成的(因此,如果它在package dummy中,则该文件将是类路径中的
META-INF/dummy.ICommand
),然后加载并注册为:

final ServiceLoader<ICommand> spi = ServiceLoader.load(ICommand.class);
for(final ICommand commandImpl : spi)
    knownCommandsMap.put(commandImpl.getKeyword(), commandImpl);
final ServiceLoader spi=ServiceLoader.load(ICommand.class);
对于(最终ICommand命令impl:spi)
knownCommandsMap.put(commandImpl.getKeyword(),commandImpl);

界面、工厂和一点反射怎么样?您仍然需要处理错误输入上的异常,但始终需要这样做。使用此方法,您只需为新输入添加Executor的新实现

public class ExecutorFactory
{
    public static Executor buildExecutor(String input) throws Exception
    {
        Class<Executor> forName = (Class<Executor>) Class.forName(input);
        return (Executor) executorClass.newInstance();
    }
}

public interface Executor
{
    public void execute();
}


public class InputA implements Executor
{
    public void execute()
    {
        // do A stuff
    }
}

public class InputB implements Executor
{
    public void execute()
    {
        // do B stuff
    }
}

在enum类上构建命令模式可以减少一些样板代码。让我们假设
input.equals(x)
中的x是“XX”,而
input.equals(y)
中的y是“YY”


您说您正在处理来自套接字的输入。投入多少?它有多复杂?它的结构如何



根据这些问题的答案,您最好编写语法,让解析器生成器(例如)生成输入处理代码。

Map
?事实上,这只是转移了问题。不过,我会将函数放在另一个实例中,可能会滑入
界面
(Psst第2行和第4行缺少右括号。)Oops:)。你是对的。修复了它们。可能重复的可能重复,但每次添加/删除新命令时,我都必须修改映射?我不能动态地这样做吗?是的,我后来也意识到了这一点,并在看到你的评论之前编辑了它:)@Alfred。你可以尝试反思,但这并不是最好的选择。使用映射,将它放在一个安全的地方,在那里可以很容易地添加新命令。您可以使用类似Spring的东西将实现注入映射,但您无法回避这样一个事实,即您必须通过一些代码来添加新的逻辑分支,无论是if()块还是映射。add(new Command());或者在SpringXML文件中。或者你可以使用我的答案和Java的SPI机制。这只在JDK 6中可用。你能更好地解释一下SPI部分吗?@fuzzy lollipop:实际上它已经在JDK 5中了,但是在一个非公共包(sun.misc…)中。一开始它应该是JDK5的一部分。@Alfred:添加了SPI使用示例,假设您在某个地方有一个初始化的映射等等。。。(我刚刚为SPI thingie添加了相关代码)javadocs说“因为1.6”sun中的任何东西。*都不应该被使用这也是一个好主意,它只需要每次创建一个新实例。执行者的名字来自哪里?如果是客户端/用户输入,恶意用户可以实例化系统上的任何类。如果类初始化更改了系统上的状态,则攻击者将获得对系统的某种程度的控制。然后在实例化和抛出的异常之间会出现竞争条件(假设该类不是Executor的子类),在此期间,攻击者可以利用其他类打开的任何功能…@atk True。我假设存在一些未表示的错误处理和输入检查。我将添加一个检查,以确保该命令至少是一个执行者。这也是一个好主意,但这是一个非常紧密的耦合。你不能再提供来自“外部”的命令了。只是一个简单的协议。例如memcached.@Alfred-对于memcached,它是一个非常简单的协议,有六个操作,不太可能改变,我会使用if-else构造。没有理由仅仅为了“面向对象”而使代码模式复杂化。但是,我将创建一个对象来包装命令和/或响应,并处理所有解析。并可能创建一个枚举来表示该命令(这将把if-else链变成一个开关)
String input;
ExecutorFactory.buildExecutor(input).execute();
enum Commands {
   XX {
     public void execute() { doX(); }        
   },
   YY {
     public void execute() { doY(); }        
   };

   public abstract void execute();
}

String input = ...; // Get it from somewhere

try {
  Commands.valueOf(input).execute();
}
catch(IllegalArgumentException e) {
   unknown_command();
}