我被分配了一个项目来开发一组类,这些类充当存储系统的接口。要求是该类支持具有以下签名的get方法:
public CustomObject get(String key, Date ifModifiedSince)
基本上,当且仅当在CustomObject
之后修改了对象时,该方法才会返回与key
关联的ifModifiedSince
。如果存储系统不包含key
,则该方法应返回null。
我的问题是:
如何处理密钥存在但对象未已被修改的情况?
这很重要,因为使用此类的一些应用程序将是Web服务和Web应用程序。这些应用程序需要知道是返回404(未找到),304(未修改)还是200(OK,这是数据)。
我正在权衡的解决方案是:
key
ifModifiedSince
失败。我对这三个选项中的任何一个都不满意。我不喜欢选项1和2,因为我不喜欢使用流控制的异常。当我的目的是表明没有值时,我也不喜欢返回值。
尽管如此,我倾向于选择3。
有没有我不考虑的选择?有没有人对这三种选择中的任何一种都有强烈的感受?
本问题的答案,转述:
contains
方法并要求调用者调用它
在致电get(key,
ifModifiedSince)
之前,扔掉
例如,如果密钥不存在,
如果对象尚未返回null
修改。UNMODIFIED, KEY_DOES_NOT_EXIST
)。为什么我不能选择答案#1
我同意这是理想的解决方案,但我已经(不情愿地)被解雇了。这种方法的问题在于,在大多数使用这些类的情况下,后端存储系统将是第三方远程系统,如Amazon S3。这意味着contains
方法需要往返存储系统,在大多数情况下会进行另一次往返。因为这会花费时间和金钱,所以它不是一种选择。
如果没有这个限制,这将是最好的方法。
(我意识到我没有在这个问题中提到这个重要元素,但我试图保持简短。显然它是相关的。)
结论:
在阅读完所有答案之后,我得出的结论是,在这种情况下,包装器是最好的方法。基本上我会模仿HTTP,包括响应代码和内容正文(消息)的元数据(标题)。
答案 0 :(得分:7)
听起来你真的想要返回两个项目:响应代码和找到的对象。您可以考虑创建一个包含两者并将它们一起返回的轻量级包装器。
public class Pair<K,V>{
public K first;
public V second;
}
然后,您可以创建一个包含响应代码和数据的新对。作为使用泛型的副作用,您可以将此包装重用于您实际需要的任何对。
此外,如果数据尚未过期,您仍然可以返回它,但是给它一个303代码,让他们知道它没有变化。 4xx系列将与null
配对。
答案 1 :(得分:5)
根据给定的要求,您无法执行此操作。
如果您设计了合同,那么添加条件并调用调用者
exists(key): bool
服务实现如下所示:
if (exists(key)) {
CustomObject o = get(key, ifModifiedSince);
if (o == null) {
setResponseCode(302);
} else {
setResponseCode(200);
push(o);
}
} else {
setResponseCode(400);
}
客户端保持不变,从未注意到您已经预先验证过。
如果你没有设计合同可能有一个很好的理由,或者可能只是设计师(或建筑师)的错误。但既然你无法改变它,那么你也不必担心。
然后你应该遵守规范并按照以下步骤进行:
CustomObject o = get(key, ifModifiedSince);
if (o != null) {
setResponseCode(200);
push(o);
} else {
setResponseCode(404); // either not found or not modified.
}
好的,在这种情况下你不会发送302,但可能就是它的设计方式。
我的意思是,出于安全原因,服务器不应该返回比更多的信息[探测器获取(密钥,日期)只返回null或object]
所以不要担心。与您的经理交谈,让他知道这个决定。用这个决定评论代码。如果你有建筑师手中确认这个奇怪的限制背后的理由。
有可能你没有看到这一点,他们可以根据你的建议修改合同。
有时候,如果想要正确行事,我们可能会出错并损害我们应用的安全性。
与您的团队沟通。
答案 2 :(得分:3)
您可以创建一个特殊的最终CustomObject作为“标记”以表示未更改:
static public final CustomObject UNCHANGED=new CustomObject();
并测试匹配“==”而不是.equals()。
它可能也可以在未更改时返回null并且抛出异常不存在?如果我必须从你的3中选择一个,我会选择1,因为这似乎是最特殊的情况。
答案 3 :(得分:3)
寻找一个不存在的对象对我来说似乎是个例外。与允许调用者确定某个对象是否存在的方法相结合,我认为如果不存在则抛出该异常是可以的。
public bool exists( String key ) { ... }
来电者可以这样做:
if (exists(key)) {
CustomObject modified = get(key,DateTime.Today.AddDays(-1));
if (modified != null) { ... }
}
or
try {
CustomObject modified = get(key,DateTime.Today.AddDays(-1));
}
catch (NotFoundException) { ... }
答案 4 :(得分:2)
异常的问题是它们意味着发出“快速失败”的情况(即如果没有处理,例外将 停止 一个应用程序) 和异常行为。
我不认为“密钥存在但对象未被修改的情况”是一个例外情况,当然不是一个异常情况。
因此我不会使用异常,而是会记录调用者为正确解释结果(属性或特殊对象)而需要执行的操作。
答案 5 :(得分:1)
该方法签名的要求有多严格?
好像您正在处理一个仍在进行中的项目。如果你班级的消费者是其他开发者,你能说服他们他们要求的方法签名是不够的吗?也许他们还没有意识到应该有两种独特的失败模式(密钥不存在,对象没有被修改)。
如果可以选择,我会与你的主管讨论。
答案 6 :(得分:1)
我仍然会返回null。
属性的意图是返回在指定日期之后修改的对象。如果没有对象返回null是可以的,那么对于未修改的对象肯定返回null也是可以的。
我个人会为未修改的对象返回null,并为不存在的对象抛出异常。这似乎更自然。
你没有使用流量控制BTW的异常,所以如果你只有这3个选项,那么你的直觉是正确的。
答案 7 :(得分:1)
您可以遵循.Net库模式,并在自定义对象中有一个名为 CustomObject.Empty 的公共静态只读字段,类型为 CustomObject (如字符串。空和Guid.Empty)。如果未修改对象,则可以返回此对象(函数使用者需要与之进行比较) 编辑:我只是发现您使用的是Java,但原则仍然适用
这为您提供以下选项
如果密钥不存在,则返回null。
如果密钥存在但尚未修改对象,则返回 CustomObject.Empty 。
缺点是消费者需要知道null返回值和CustomObject.Empty返回值之间的区别。
也许该属性更适合称为 CustomObject.NotModified ,因为Empty实际上是用于Value类型,因为它们不能为null。此外, NotModified 会更容易向消费者传达该领域的含义。
答案 8 :(得分:1)
关于要求的(预期)界面严重受损。你尝试在一种方法中做不相关的事情。这是软件地狱之路。
答案 9 :(得分:1)
提供一个Callback作为Callback类可以是事件驱动或setter驱动的参数。
您的类的接口定义了可能发生的各种错误,如果需要,将CustomObject作为事件的参数传递。
public interface Callback {
public void keyDoesNotExist();
public void notModified(CustomObject c);
public void isNewlyModified(CustomObject c);
.
.
.
}
通过这种方式,您允许Callback接口的实现者定义事件发生时要执行的操作,并且您可以通过接口选择是否需要传递检索到的对象。最后,它降低了返回时逻辑的复杂性。你的方法做了一次。 API的实现者根本不需要这样做,因为它是为它们完成的。
答案 10 :(得分:0)
如果可以接受,您可以返回一个放大的CustomObject(包装器),它包含表示对象及其修改状态的值(如果有的话)等。