繁体   English   中英

Void值作为返回参数

[英]Void value as return parameter

我有这个界面:

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);
}

我会坚持使用Void明确。 没有其他课程,很容易看到发生了什么。 如果你可以使用void覆盖Void返回(以及使用int等的Integer ),那会很好,但这不是优先事项。

它看起来很好。 正如其他人所说, Void最初是为反射机制而设计的,但它现在经常用于泛型,以描述你的情况。

更好的是:Google在他们的GWT框架中,在他们的示例中使用相同的想法来进行返回空白的回调( 例如这里 )。 我说:如果谷歌这样做,它必须至少好...... :)

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

// 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; 您将被迫插入的陈述。

保持界面不变:这就是Void在标准库中的原因。 只要调用Command的任何内容都需要返回null。

是的,null是你可以为Void返回的唯一值。

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;
}

这不是常见问题。 您需要解决的问题是您的界面的期望。 您将非副作用界面的行为与允许副作用的界面相结合。

考虑一下:

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>作为模板类型但允许它与“Command <T> ”的实现混合是没有错的,这意味着接口的某些客户端可能不会期望无效结果。 在不更改界面的情况下,您已允许实现创建意外结果。

当我们使用Collection类传递数据集时,我的团队同意永远不会返回null,即使它在语法上很好。 问题是使用返回值的类将不断检查空白以防止NPE。 使用上面的代码,你会看到无处不在:

    if (results != null ){

因为现在有办法知道实现是否实际上有一个对象或是否为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的第二次调用仍然调用期望Object的版本(当然)。 我打算换到

public static void reportResults(String results){

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

最重要的是你需要使用一种设计,当你期望一个命令产生副作用时,它会清楚地表明你将如何处理一组命令,即一组未知命令。

这可能是泄漏抽象的情况。

您的示例中有趣的是使用参数化类型。 通常你会有

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

在您的情况下,您只有T作为返回值。 然而,在这种情况下使用Void是一个很好的解决方案。 返回值应为null

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

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

如果没有像以下那样的界面怎么办?

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() ...
    }
}

如果愿意,您还可以创建一个扩展Command的接口VoidCommmand。

这对我来说似乎是最干净的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM