最近项目中有个需求是关于下载进度条的。原来我们在 cloud 上面下载文件,点击下载之后,实际上要先计算出要下载哪些文件(因为可能包含文件夹,文件夹内的文件需要网络请求才知道),然后开始真正的多线程下载。我们把之前的计算阶段称为 calculating 阶段或 preparing 阶段,后面的下载阶段称为 downloading 阶段。如下两幅图所示:


以前在 calculating 阶段,进度条上进度显示为0,要等 calculating 结束,downloading 开始了进度条才会前进。这样 calculating 阶段 UI 看起来是没有响应的,对用户不太友好。现在我们要在 calculating 阶段显示一个 Marquee style progress bar.像这样:

本来这是一个很简单的需求,progress bar是用 windows form 写的,我们只需要在 calculating 阶段的开始和结束分别设置 Marquee style 和 Blocks style 即可。但是项目中的 progress bar 做得比较复杂,做了很多封装。在 download worker thread 中修改 progress bar 的进度是通过一个叫作 IProgressBarReporter 的接口来实现的。这个接口里面定义了一个 TransferStatus 方法用来告诉progress bar当前下载了多少,然后设置好当前进度。
public interface IProgressReporter
{
...
void TransferStatus(int serviceId, string serviceName, string fileId, string current, double percent, double speed);
...
}
我一开始直接在 progress bar 初始化的时候设置了当前 style 为 Marquee,然后传递了一个设置progress bar style 的 callback 给 download worker thread。当 download worker thread 中 calculating 阶段结束时再通过这个 callback 设置 progress bar style 为 Blocks. 领导说这有2个问题,一个是在 progress bar 初始化时设置 Marquee style 改变了以前的行为,可能会造成 progress bar 闪烁(实际测试并未出现闪烁情况);另一个是原来的代码都是通过 IProgressBarReporter 接口来操作 progress bar UI的,加一个 callback 与之前的行为不太和谐。所以领导建议我在 IProgressBarReporter 接口里面加一个改变 Progress bar style 的方法。不过这样也有一个问题,就是这个 IProgressBarReporter 从名称上来看是报告进度的,也就是在 Blocks style 情况下设置具体进度用的,把改变 Progress bar style 的方法加进来有一点名不副实。当然这个如果能够忍受的话也没什么太大的问题。
不过,最后并没有采用上面所说的2种办法。因为我们发现这个 TransferStatus 方法居然还有一个功能是设置 progress bar style. 这个是在接口实现代码里面看见的。所以最后直接用这个方法在 calculating阶段的开始和结束分别设置了 Marquee 和 Blocks style.
// this will make progress bar type marquee
progress.TransferStatus(0, string.Empty, string.Empty, Properties.Resources.PREPARE_OPERATION_LABEL, -1, 0);
由于这个方法,代码改动很少,但我觉得这个方法确实是一个很坏的设计。因为它做了它声明中没有说明的事情。单单从名称和参数上来看,你绝无可能知道它居然还能设置 progress bar style. 而且,即使你看了它的实现,也很难知道如何设置它的各个参数才能设置 progress bar style. 我想如果没有找到以前使用这个方法设置progress bar style的代码,我大概不可能这样做。我想到了 《Effective C++》中说的一条:让接口容易被正确使用,不易被误用。这个接口的使用应该说非常令人费解了,其实应该重构一下的,不过已有的代码都是这样用的,估计领导不会让我重构了。