Moh*_*itt 57 java oop design-patterns
最近有一个问题在接受我采访时被问到.
问题:有一个类用于分析代码的执行时间.这堂课就像:
Class StopWatch {
    long startTime;
    long stopTime;
    void start() {// set startTime}
    void stop() { // set stopTime}
    long getTime() {// return difference}
}
期望客户端创建StopWatch的实例并相应地调用方法.用户代码可能会搞乱使用导致意外结果的方法.Ex,start(),stop()和getTime()调用应该是有序的.
必须"重新配置"该类,以便可以防止用户弄乱序列.
如果在start()之前调用stop(),或者做一些if/else检查,我建议使用自定义异常,但是面试官不满意.
是否有设计模式来处理这种情况?
编辑:可以修改类成员和方法实现.
Wao*_*aog 84
首先,有一个事实是,实现一个自己的Java分析器是浪费时间,因为好的可用(可能这是问题背后的意图).
如果要在编译时强制执行正确的方法顺序,则必须返回链中每个方法的内容:
start()必须WatchStopper使用stop方法返回a .WatchStopper.stop()必须WatchResult用getResult()方法返回一个.当然,必须防止外部构建这些辅助类以及访问其方法的其他方法.
Pet*_*ček 37
通过对界面进行微小更改,您可以使方法序列成为唯一可以调用的方法 - 即使在编译时也是如此!
public class Stopwatch {
    public static RunningStopwatch createRunning() {
        return new RunningStopwatch();
    }
}
public class RunningStopwatch {
    private final long startTime;
    RunningStopwatch() {
        startTime = System.nanoTime();
    }
    public FinishedStopwatch stop() {
        return new FinishedStopwatch(startTime);
    }
}
public class FinishedStopwatch {
    private final long elapsedTime;
    FinishedStopwatch(long startTime) {
        elapsedTime = System.nanoTime() - startTime;
    }
    public long getElapsedNanos() {
        return elapsedTime;
    }
}
用法很简单 - 每个方法都返回一个只有当前适用方法的不同类.基本上,秒表的状态被封装在类型系统中.
在评论中,有人指出,即使采用上述设计,您也可以拨打stop()两次电话.虽然我认为这是附加价值,但理论上可以将自己搞砸.那么,我能想到的唯一方法就是这样:
class Stopwatch {
    public static Stopwatch createRunning() {
        return new Stopwatch();
    }
    private final long startTime;
    private Stopwatch() {
        startTime = System.nanoTime();
    }
    public long getElapsedNanos() {
        return System.nanoTime() - startTime;
    }
}
这与通过省略stop()方法的分配不同,但这也是可能很好的设计.所有这些都取决于具体要求......
vel*_*s4j 23
我们通常使用来自Apache Commons StopWatch的StopWatch检查它们提供的模式.
当秒表状态错误时,抛出IllegalStateException.
public void stop()
Stop the stopwatch.
This method ends a new timing session, allowing the time to be retrieved.
Throws:
    IllegalStateException - if the StopWatch is not running.
直接前进.
Cap*_*Man 20
事后看来,听起来他们正在寻找围绕模式的执行.它们通常用于执行诸如强制关闭流之类的操作.由于这条线,这也更相关:
是否有设计模式来处理这种情况?
这个想法是你给那些"执行"某些类来做事情的东西.你可能会使用,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很好是因为
Runnable.(如果您使用它来强制关闭流,那么您可以将需要完成的操作放在数据库连接中,这样最终用户就不必担心如何打开和关闭它并同时强制它们关闭它恰当.)
如果你想,你可以做一些StopWatchWrapper而不做StopWatch修改.您也可以measureAction(Runnable)不返回时间getTime()而是公开.
Java 8调用它的方式更简单
StopWatch stopWatch = new StopWatch();
long time = stopWatch.measureAction(() - > {/* Measure stuff here */});
第三个(希望是最终的)思想:看起来面试官正在寻找什么以及最受欢迎的是基于国家抛出异常(例如,如果
stop()在之前start()或start()之后被调用stop()).这是一个很好的做法,实际上,取决于StopWatch具有私人/受保护以外的可见性的方法,最好是拥有而不是拥有.我的一个问题是单独抛出异常不会强制执行方法调用序列.例如,考虑一下:
Run Code Online (Sandbox Code Playgroud)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提到界面无法更改,诚然,我做的非包装建议虽然这样做.因此,据我所知,如果不修改界面或添加更多类,就无法强制执行正确的顺序.我想这实际上取决于人们定义"强制执行方法调用序列"的含义.如果只抛出异常,则以下编译
Run Code Online (Sandbox Code Playgroud)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%肯定的是如何获得时间.说实话,这似乎是一个更小的细节.只要...在上面的代码中都以相同的方式获得当前时间就应该没问题.
Ada*_*ker 19
也许他期望这种"重新配置"并且问题根本不是方法序列:
class StopWatch {
   public static long runWithProfiling(Runnable action) {
      startTime = now;
      action.run();
      return now - startTime;
   }
}
我建议像:
interface WatchFactory {
    Watch startTimer();
}
interface Watch {
    long stopTimer();
}
它会像这样使用
 Watch watch = watchFactory.startTimer();
 // Do something you want to measure
 long timeSpentInMillis = watch.stopTimer();
你不能以错误的顺序调用任何东西.如果你调用stopTimer两次,你会得到有意义的结果(也许最好将它重命名为measure每次调用时返回实际时间)
在未以正确顺序调用方法时抛出异常是常见的.例如,Thread的start将抛出IllegalThreadStateException,如果叫了两声.
您应该更好地解释实例如何知道方法是否以正确的顺序调用.这可以通过引入状态变量,并在每个方法的开头检查状态(并在必要时更新它)来完成.