空值作为返回参数(Void value as return parameter)

2019-07-30 18:05发布

我有这样的界面:

public interface Command<T> {
    T execute(String... args);
}

它的工作原理适用于大部分的用途。 但是,当我尝试模式,只有副作用命令(例如无返回值)我很想写:

public class SideEffectCommand implements Command<Void> {

    @Override
    public Void execute(String... args) {
        return null; // null is fine?
    }
} 

这是个常见的问题吗? 对模型有最佳实践Commands没有返回值?

我试过这个适配器,但我认为这不是最佳的有以下几个原因:

public abstract class VoidCommand implements Command<Void> {

    @Override
    public Void execute(String... args) {
       execute2(args);
       return null;
    }

    public abstract void execute2(String... args);
}

Answer 1:

这里是最佳的多的世界一个实现。

// Generic interface for when a client doesn't care
// about the return value of a command.
public interface Command {
    // The interfaces themselves take a String[] rather
    // than a String... argument, because otherwise the
    // implementation of AbstractCommand<T> would be
    // more complicated.
    public void execute(String[] arguments);
}

// Interface for clients that do need to use the
// return value of a command.
public interface ValuedCommand<T> extends Command {
    public T evaluate(String[] arguments);
}

// Optional, but useful if most of your commands are ValuedCommands.
public abstract class AbstractCommand<T> implements ValuedCommand<T> {
    public void execute(String[] arguments) {
        evaluate(arguments);
    }
}

// Singleton class with utility methods.
public class Commands {
    private Commands() {} // Singleton class.

    // These are useful if you like the vararg calling style.
    public static void execute(Command cmd, String... arguments) {
        cmd.execute(arguments);
    }

    public static <T> void execute(ValuedCommand<T> cmd, String... arguments) {
        return cmd.evaluate(arguments);
    }

    // Useful if you have code that requires a ValuedCommand<?>
    // but you only have a plain Command.
    public static ValuedCommand<?> asValuedCommand(Command cmd) {
        return new VoidCommand(cmd);
    }

    private static class VoidCommand extends AbstractCommand<Void> {
        private final Command cmd;

        public VoidCommand(Command actual) {
            cmd = actual;
        }

        public Void evaluate(String[] arguments) {
            cmd.execute(arguments);
            return null;
        }
    }
}

使用这种实现,客户可以谈论一个Command ,如果他们不关心的返回值,和ValuedCommand<T>如果需要返回一个特定值的命令。

关于唯一理由不去用Void直线上升是所有有碍观瞻return null; 你将被迫insert语句。



Answer 2:

我会坚持使用Void明确。 人们很容易看到正在发生的事情,而不涉及其他类。 这会是很好,如果你可以覆盖一个Void ,返回void (和Integerint ,等等),但是这不是一个优先事项。



Answer 3:

这看起来好像没什么问题。 正如有人说, Void最初设计用于反射机制,但它现在是在使用泛型经常描述的情况,如你的。

更妙的是:谷歌,在他们的GWT框架,使用同样的想法在他们的榜样的空隙返回回调( 例如这里 )。 我说:如果谷歌这样做,它必须至少OK .. :)



Answer 4:

这不是一个普遍的问题。 你需要解决的问题是,你的界面的期望。 你用它允许副作用的接口相结合的非副作用界面的行为。

试想一下:

public class CommandMonitor {

    public static void main(String[] args)  {       
        Command<?> sec = new SideEffectCommand();       
        reportResults(sec);     
    }   

    public static void reportResults(Command<?> cmd){

        final Object results = cmd.execute("arg");
        if (results != null ) {
            System.out.println(results.getClass());
        }
    }
}

没有什么错用<Void>为模板类型,但允许其与实现组合“命令<T> ”指的是接口的一些客户可能没有想到一个空的结果。 在不改变接口,您允许实施创造一个意想不到的结果。

当我们绕过使用集合类数据集,我的团队同意不能为null返回即使它的罚款语法。 问题是,所用的返回值类会不断地把检查的空隙,以防止NPE。 使用上面的代码,你会到处看到这一点:

    if (results != null ){

因为现在有办法知道是否执行实际上有一个对象或为空。 对于特定的情况下,相信你就会知道,因为你熟悉的落实。 但只要你开始聚集他们或他们越过你的编码地平线(用作图书馆,将来的维护等)空问题将提出自己。

接下来,我尝试这样做:

public class SideEffectCommand implements Command<String> {

    @Override
    public String execute(String... args) {
        return "Side Effect";
    }

}

public class NoSideEffectCommand implements Command<Void>{
    @Override
    public Void execute(String... args) {
        return null;
    }   
}
public class CommandMonitor {

    public static void main(String[] args)  {       
        Command<?> sec = new SideEffectCommand();
        Command<?> nsec = new NoSideEffectCommand();

        reportResults(sec.execute("args"));
        reportResults(nsec.execute("args")); //Problem Child
    }   

    public static void reportResults(Object results){
        System.out.println(results.getClass());
    }

    public static void reportResults(Void results){
        System.out.println("Got nothing for you.");
    }
}

重载没有工作,因为reportResults第二个呼叫仍称为版本期待(当然)的对象。 我曾考虑要改变

public static void reportResults(String results){

但是,这说明了问题的根源,你的客户端代码开始不必知道实现细节。 接口应该在可能的情况,帮助找出代码依赖性。 在这一点上添加它们似乎是糟糕的设计。

底线是,你需要使用一个设计时,你期望的命令有副作用,并通过你会如何处理一组指令想昭示,即未知的命令阵列。

这可能的情况下, 泄漏的抽象 。



Answer 5:

保持接口,因为它是:这就是为什么虚空是标准资源库中 正如只要无论是调用命令期望空回来了。

是的,空是可以返回太虚唯一的价值。

2017年更新

在过去的几年中,我已经避免Void的返回类型,除非反射有关。 我已经使用了不同的模式,我认为是比较明确的,并且避免了空。 也就是说,我有成功的类型,我称之为Ok被返回对于喜欢OP的所有命令。 这工作得非常好我的团队,并且还传播到其他团队的使用。

public enum Ok { OK; }

public class SideEffectCommand implements Command<Ok> {
    @Override
    public Ok execute(String... args) {
        ...
        return Ok.OK; // I typically static import Ok.OK;
}


Answer 6:

什么是你的榜样有趣的是,使用参数化类型的。 通常你必须

interface Command<T> {
    public T execute(T someObject);
}

你的情况,你只有T的返回值。 在这种情况下使用不过是虚空一个很好的解决方案。 返回值应为null



Answer 7:

问题 不是常见,但也不罕见。我想我已经看到了关于前一段时间,关于返回任何可调用的讨论。

我同意其他海报,这是一个很好的解决方案,比使用对象或其他一些虚拟占位好得多。



Answer 8:

如果不是有类似的界面:

public interface Command<T> {
    T execute(String... args);
}

你不是有:

public interface Command<T> {
    void execute(String... args);
    T getResult();
    bool hasResult();
}

然后,呼叫者会做:

public void doSomething(Command<?> cmd) {
    cmd.execute(args);
    if(cmd.hasResult()) {
        // ... do something with cmd.getResult() ...
    }
}

你也可以创建一个扩展命令,如果你喜欢的界面VoidCommmand。

这似乎是最干净的解决方案给我。



文章来源: Void value as return parameter