[英]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返回的唯一值。
在过去的几年里,除了反射之外,我已经避免使用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.