经过更多的思考
回想起来,他们似乎在寻找执行周围模式。它们通常用于执行诸如强制关闭流之类的操作。由于这一行代码,这也更相关:
有没有一种设计模式来处理这些情况?
这个想法是你给那个做“执行周围”工作的东西一些类去做某些事情。你可能会使用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() {
}
};
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 */});
A third (hopefully final) thought: it seems what the interviewer was looking for and what is being upvoted the most is throwing exceptions based on state (e.g., if stop()
is called before start()
or start()
after stop()
). This is a fine practice and in fact, depending on the methods in StopWatch
having a visibility other than private/protected, it's probably better to have than not have. My one issue with this is that throwing exceptions alone will not enforce a method call sequence.
For example, consider this:
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;
}
}
Just because it's throwing IllegalStateException
doesn't mean that the proper sequence is enforced, it just means improper sequences are denied (and I think we can all agree exceptions are annoying, luckily this is not a checked exception).
The only way I know to truly enforce that the methods are called correctly is to do it yourself with the execute around pattern or the other suggestions that do things like return RunningStopWatch
and StoppedStopWatch
that I presume have only one method, but this seems overly complex (and OP mentioned that the interface couldn't be changed, admittedly the non-wrapper suggestion I made does this though). So to the best of my knowledge there's no way to enforce the proper order without modifying the interface or adding more classes.
I guess it really depends on what people define "enforce a method call sequence" to mean. If only the exceptions are thrown then the below compiles
StopWatch stopWatch = new StopWatch();
stopWatch.getTime();
stopWatch.stop();
stopWatch.start();
True it won't run, but it just seems so much simpler to hand in a Runnable
and make those methods private, let the other one relax and handle the pesky details yourself. Then there's no guess work. With this class it's obvious the order, but if there were more methods or the names weren't so obvious it can begin to be a headache.
原始答案
更多后见之明的编辑: OP在评论中提到,
"这三种方法应该保持完整,只是对程序员的接口。类成员和方法实现可以改变。"
所以下面的方法是错误的,因为它从接口中删除了一些内容。(从技术上讲,你可以将其实现为空方法,但这似乎是一个愚蠢的事情,也太令人困惑了。)如果没有限制,我有点喜欢这个答案,它似乎是另一种“傻瓜式”的方法,所以我会保留它。
对我来说,像这样的东西似乎很好。
class StopWatch {
private final long startTime;
public StopWatch() {
startTime = ...
}
public long stop() {
currentTime = ...
return currentTime - startTime;
}
}
我认为这样做的好处是,在对象创建过程中进行录制,因此不会被遗忘或顺序错误(如果不存在,则无法调用
stop()
方法)。
一个缺陷可能是
stop()
的命名。起初我想到了
lap()
,但这通常意味着重新启动或某种记录(或者至少是自上次圈/开始以来的记录)。也许
read()
会更好?这模仿了看秒表时间的动作。我选择
stop()
以保持与原始类的相似性。
唯一我不确定的是如何获取时间。老实说,这似乎是一个较小的细节。只要上述代码中的两个
...
以相同的方式获取当前时间,就应该没问题。
start()
方法返回一个带有stop()
方法的RunningStopWatch
的StoppedStopWatch
。(然后可以通过将其设置为包级私有来防止直接使用底层的StopWatch
。) - aioobe