业务场景
写一个数据层的loader,loader的数据拉取有可能走本地或者走网络,所以肯定需要通过回调来返回数据,而且两种情况在view层的表现不一样,也需要通过回调来告知
优化前
于是我一开始设计了这么两个接口来进行区分处理,而对于每个接口,都像网络请求一样写了4个回调,以适应任何意外情况
public interface ShortLoadAction<T> {
void onStartShort();
void onCancelShort();
void onBrokenShort();
void onCompleteShort(int stateCode, String stateMsg, T model);
}
public interface LongLoadAction<T> {
void onStartLong();
void onCancelLong();
void onBrokenLong();
void onCompleteLong(int stateCode, String stateMsg, T model);
}
你会发现这两个接口方法类似,但名字不一样,因为我接下来写一个业务封装适配器类,为view层做了一个翻译,这个类需要实现上面两个接口,所以如果方法名一样就不行,就像这样,虽然这样有点怪,但好像没有办法
public abstract class LoadAction<T> implements ShortLoadAction<T>, LongLoadAction<T> {
@Override
public void onStartShort() {
// do nothing
}
@Override
public void onCancelShort() {
onFailed();
}
@Override
public void onBrokenShort() {
onFailed();
}
@Override
public void onCompleteShort(int stateCode, String stateMsg, T model) {
onSuccess(model);
}
/*-------------------------------------------------*/
@Override
public void onStartLong() {
showLoadingView();
}
@Override
public void onCancelLong() {
hideLoadingView();
onFailed();
}
@Override
public void onBrokenLong() {
hideLoadingView();
onFailed();
}
@Override
public void onCompleteLong(int stateCode, String stateMsg, T model) {
hideLoadingView();
onSuccess(model);
}
/*--------------------------------------------------------------------------------------------*/
public abstract void showLoadingView();
public abstract void hideLoadingView();
public abstract void onFailed();
public abstract void onSuccess(T model);
}
有了这个抽象类,使用者可以知道得更少了,只管实现LoadingView
的显示隐藏、处理成功失败就行了,耗时长短这个信息不需要去关心。
为了进一步方便使用,利用我的ViewHelp
写了一个用起来更简单的adapter:
public abstract class VHelperLoadAction<T> extends LoadAction<T> {
private ViewHelper viewHelper;
public VHelperLoadAction(ViewHelper viewHelper) {
this.viewHelper = viewHelper;
}
@Override
public void showLoadingView() {
viewHelper.showLoadingView();
}
@Override
public void hideLoadingView() {
viewHelper.hideLoadingView();
}
}
这下使用者只需要关心成功失败了
写完一用感觉很不错,完全没有感觉不对或者是不符合设计模式什么的,ShortLoadAction
和LongLoadAction
方法名的奇怪感觉早就忘得一干二净。
比如这样:
VHelperLoadAction<List<WordPage>> loadAction = new VHelperLoadAction<List<WordPage>>(vHelper) {
@Override
public void onFailed() {
vHelper.toastNetworkBroken();
}
@Override
public void onSuccess(List<WordPage> model) {
CheckWordsActivity.start(getActivity(), model);
}
};
pageWordsLoader.load(pages, loadAction, loadAction);
这个loader长这样
public void load(List<String> pageIds, ShortLoadAction<List<WordPage>> loadShortAction, LongLoadAction<List<WordPage>> loadLongAction) {
this.pageIds = pageIds;
dbHelper.createTableIfNotExist();
List<String> unsavedPageNumbers = getUnSavedPageNumbers();
if (unsavedPageNumbers.isEmpty()) {
loadWordsFromLocal(loadShortAction, null);
} else {
loadWordsFromRemote(unsavedPageNumbers, loadLongAction);
}
}
优化中
这个代码还给别人讲过,讲完两人也都没觉得有问题。直到一次codereview中,我刚跟老大解释完这几个action
的关系,老大就表示这个设计有点问题。
开始他是觉得后边这个load()
方法中需要传两个action
进去非常奇怪,但我知道这块是必须这样的,因为loader
的使用者是不知道也不应该关心到底选择哪种action
,loader
应该自己去看本地有没有数据,然后选择short或者long的action。大家都同意这个观点。
(但还有一个同事认为两个action还是不应该,你其实可以只写一个action,然后每个回调方法有一个参数:isLong
,回调方法中就可以根据不同的参数来做不同的事,我只能说这是最差的设计了,)
然后我们老大就指出了这部分代码的最大问题,在于ShortLoadAction
和LongLoadAction
俩个明显有相似关系的东西居然没有一点关系,是两个完全不同的接口,同时,在loader的最里层(关心选择short或者long的action的里层),本来可以不关心具体是short还是long的action,现在直接关心了(持有对象类型是具体的ShortLoadAction或LongLoadAction)。
这个问题可以用继承来,但是相比之下,使用装饰模式更加合适,装饰模式不仅有使用继承的全部好处,而且还有更多好处。
然后最后你会发现,你虽然写不出来一个跟之前VHelperLoadAction
或LoadAction
一样的东西,但使用新的装饰器写法时也是一样方便好用
优化后2
?
但其实后来发现我们的适配设备(是个专门给某个pad开发并且烧录进去一块卖钱的软件)性能实在捉急,以至于在本地取数据花的时间也不短,所以需要跟网络请求一样显示加载圈,于是最后我只用了一个WaitLoadAction(摊手)