经过深思熟虑
事后看来,他们似乎在寻找execute around pattern。它们通常用于执行诸如强制关闭流之类的事情。由于这一行,这也更相关:
是否有设计模式来处理这类情况?
这个想法是你给“执行”一些类的东西做一些事情。您可能会使用Runnable,但这不是必需的。 (Runnable 是最有意义的,你很快就会明白为什么。) 在你的 StopWatch 类中添加一些这样的方法
public long measureAction(Runnable r) {
start();
r.run();
stop();
return getTime();
}
你可以这样称呼它
StopWatch stopWatch = new StopWatch();
Runnable r = new Runnable() {
@Override
public void run() {
// Put some tasks here you want to measure.
}
};
long time = stopWatch.measureAction(r);
这使它万无一失。您不必担心在开始之前处理停止或人们忘记调用一个而不是另一个等等。Runnable 很好的原因是因为
- 标准的 java 类,不是你自己的或第三方的
- 最终用户可以在
Runnable 中添加他们需要做的任何事情。
(如果您使用它来强制关闭流,那么您可以将需要通过数据库连接完成的操作放入其中,这样最终用户就不必担心如何打开和关闭它,同时您可以强制他们正确地关闭它。)
如果您愿意,您可以制作一些 StopWatchWrapper,而不是保持 StopWatch 不变。您也可以让measureAction(Runnable) 不返回时间,而是让getTime() 公开。
Java 8 的调用方式更加简单
StopWatch stopWatch = new StopWatch();
long time = stopWatch.measureAction(() - > {/* Measure stuff here */});
第三个(希望是最终的)想法:似乎面试官在寻找什么并且被赞成最多的是基于状态抛出异常(例如,如果 stop() 在 @ 之前被调用987654335@ 或start() 在stop() 之后)。这是一个很好的做法,事实上,根据StopWatch 中的方法,具有私有/受保护以外的可见性,拥有总比没有好。我的一个问题是单独抛出异常不会强制方法调用序列。
例如,考虑一下:
class StopWatch {
boolean started = false;
boolean stopped = false;
// ...
public void start() {
if (started) {
throw new IllegalStateException("Already started!");
}
started = true;
// ...
}
public void stop() {
if (!started) {
throw new IllegalStateException("Not yet started!");
}
if (stopped) {
throw new IllegalStateException("Already stopped!");
}
stopped = true;
// ...
}
public long getTime() {
if (!started) {
throw new IllegalStateException("Not yet started!");
}
if (!stopped) {
throw new IllegalStateException("Not yet stopped!");
}
stopped = true;
// ...
}
}
仅仅因为它抛出 IllegalStateException 并不意味着正确的序列被强制执行,它只是意味着不正确的序列被拒绝(我认为我们都同意异常很烦人,幸运的是这是不是检查异常)。
我知道真正强制正确调用方法的唯一方法是自己使用执行模式或其他建议执行此操作,例如返回 RunningStopWatch 和 StoppedStopWatch 我认为只有一种方法, 但这似乎过于复杂(并且 OP 提到无法更改界面,诚然我提出的非包装建议这样做了)。因此,据我所知,如果不修改接口或添加更多类,就无法强制执行正确的顺序。
我想这真的取决于人们定义“强制方法调用序列”的含义。如果只抛出异常,则以下编译
StopWatch stopWatch = new StopWatch();
stopWatch.getTime();
stopWatch.stop();
stopWatch.start();
确实它不会运行,但提交Runnable 并将这些方法设为私有似乎要简单得多,让其他人放松并自己处理讨厌的细节。然后没有猜测工作。有了这个类,顺序就很明显了,但是如果有更多的方法或者名称不是那么明显,那就开始头疼了。
原答案
更多事后编辑:OP 在评论中提到,
“这三个方法应该保持不变,并且只是程序员的接口。类成员和方法实现可以改变。”
所以下面是错误的,因为它从界面中删除了一些东西。 (从技术上讲,你可以将它实现为一个空方法,但这似乎是一件愚蠢的事情,而且太混乱了。)如果没有限制,我有点喜欢这个答案,而且它似乎是另一个“傻瓜证明” " 这样做的方法,所以我会离开它。
对我来说,这样的事情似乎很好。
class StopWatch {
private final long startTime;
public StopWatch() {
startTime = ...
}
public long stop() {
currentTime = ...
return currentTime - startTime;
}
}
我认为这很好的原因是记录是在对象创建期间进行的,因此不能忘记或乱序完成(如果不存在,则不能调用 stop() 方法)。
一个缺陷可能是stop() 的命名。起初我想也许lap() 但这通常意味着重新启动或某种形式(或至少从上一圈/开始记录)。也许read() 会更好?这模仿了在秒表上看时间的动作。我选择了stop() 以保持它与原始类相似。
我唯一不能 100% 确定的是如何获得时间。老实说,这似乎是一个更次要的细节。只要上面代码中的...都以相同的方式获取当前时间就可以了。