我有这样的界面:
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);
}
这里是最佳的多的世界一个实现。
// 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语句。
我会坚持使用Void
明确。 人们很容易看到正在发生的事情,而不涉及其他类。 这会是很好,如果你可以覆盖一个Void
,返回void
(和Integer
与int
,等等),但是这不是一个优先事项。
这看起来好像没什么问题。 正如有人说, Void
最初设计用于反射机制,但它现在是在使用泛型经常描述的情况,如你的。
更妙的是:谷歌,在他们的GWT框架,使用同样的想法在他们的榜样的空隙返回回调( 例如这里 )。 我说:如果谷歌这样做,它必须至少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>
为模板类型,但允许其与实现组合“命令<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){
但是,这说明了问题的根源,你的客户端代码开始不必知道实现细节。 接口应该在可能的情况,帮助找出代码依赖性。 在这一点上添加它们似乎是糟糕的设计。
底线是,你需要使用一个设计时,你期望的命令有副作用,并通过你会如何处理一组指令想昭示,即未知的命令阵列。
这可能的情况下, 泄漏的抽象 。
保持接口,因为它是:这就是为什么虚空是标准资源库中 正如只要无论是调用命令期望空回来了。
是的,空是可以返回太虚唯一的价值。
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;
}
什么是你的榜样有趣的是,使用参数化类型的。 通常你必须
interface Command<T> {
public T execute(T someObject);
}
你的情况,你只有T
的返回值。 在这种情况下使用不过是虚空一个很好的解决方案。 返回值应为null
。
这问题 不是常见,但也不罕见。我想我已经看到了关于前一段时间,关于返回任何可调用的讨论。
我同意其他海报,这是一个很好的解决方案,比使用对象或其他一些虚拟占位好得多。
如果不是有类似的界面:
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。
这似乎是最干净的解决方案给我。