我有以下方法:
attemptToGetToken(TokenRequestAttempt attempt);
我使用第三方组件来获取令牌但在某些情况下可能会失败(在尝试中无效或缺少参数)。
没有理由为这种情况抛出异常,因为它实际上预计会失败。
但是,根据结果,最终用户可能会采取相应行动。
这意味着我需要返回一个Object,允许用户决定它能做什么/应该做什么。
可能最明显的答案就是简单地拥有这样的课程:
class AttemptResult {
constructor(status, token, errorMessage);
getStatus();
getToken();
getErrorMessage();
isSuccessful();
}
简单地返回正确填充的对象(我可以为成功/失败状态添加特定的工厂方法,但它只是一个工具)。
我对这种方法的问题是合同不明确。
如果失败,token
将为null
,但开发人员必须使用isSuccessful()
方法检查尝试是否成功。
但没有什么可以阻止他拨打getToken()
并获得null
结果,这可能会在某处冒出错误。
所以我想知道实际归还两种物体是不是更好。
interface TokenAttemptResult {}
class FailedTokenAttempt implements TokenAttemptResult {
getError();
}
class SuccessfulTokenAttempt implements TokenAttemptResult {
getToken();
}
因为我实现了一个空接口来定义两个结果之间的关系,所以我现在可以在我的服务中使用这个接口作为返回类型:
TokenAttemptResult attemptToGetToken(TokenRequestAttempt attempt);
当用户调用上面的方法时,他负责检查返回类型并采取相应的行动,这会阻止他调用不应被调用的方法,并可能产生副作用。
您如何看待这种设计? 你们中的任何人都试过它并有任何反馈意见吗?
在没有实际论据的情况下,请避免涉及个人偏好的非建设性答案,我真的在寻找严谨的设计理论。
谢谢!
答案 0 :(得分:1)
子类化方法将起作用,但是由于接口是开放的,客户端无法确定它们已经详尽地检查了接口的所有可能的实现类型。关闭可能案例的OO方法是visitor pattern例如
public interface TokenResultVisitor<TResult> {
TResult Visit(FailedTokenAttempt failure);
TResult Visit(SuccessfulTokenAttempt success);
}
然后
interface TokenAttemptResult {
T Accept<T>(TokenResultVisitor visitor);
}
如果您只有两种可能的情况,这是一种相当重要的方法,因此如果您的语言支持一流函数,您可以简单地定义一个需要两个处理程序的方法,例如
interface TokenAttemptResult {
T Handle<T>(success: Result => T, failure: TokenError => T);
}