用装饰模式优化的一个实例2

业务场景

写一个数据层的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();
    }
}

这下使用者只需要关心成功失败了

写完一用感觉很不错,完全没有感觉不对或者是不符合设计模式什么的,ShortLoadActionLongLoadAction方法名的奇怪感觉早就忘得一干二净。

比如这样:

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的使用者是不知道也不应该关心到底选择哪种actionloader应该自己去看本地有没有数据,然后选择short或者long的action。大家都同意这个观点。

(但还有一个同事认为两个action还是不应该,你其实可以只写一个action,然后每个回调方法有一个参数:isLong,回调方法中就可以根据不同的参数来做不同的事,我只能说这是最差的设计了,)

然后我们老大就指出了这部分代码的最大问题,在于ShortLoadActionLongLoadAction俩个明显有相似关系的东西居然没有一点关系,是两个完全不同的接口,同时,在loader的最里层(关心选择short或者long的action的里层),本来可以不关心具体是short还是long的action,现在直接关心了(持有对象类型是具体的ShortLoadAction或LongLoadAction)。

这个问题可以用继承来,但是相比之下,使用装饰模式更加合适,装饰模式不仅有使用继承的全部好处,而且还有更多好处。

然后最后你会发现,你虽然写不出来一个跟之前VHelperLoadActionLoadAction一样的东西,但使用新的装饰器写法时也是一样方便好用

优化后2

?



但其实后来发现我们的适配设备(是个专门给某个pad开发并且烧录进去一块卖钱的软件)性能实在捉急,以至于在本地取数据花的时间也不短,所以需要跟网络请求一样显示加载圈,于是最后我只用了一个WaitLoadAction(摊手)

时间: 2024-12-17 02:02:08

用装饰模式优化的一个实例2的相关文章

Html代码seo优化最佳布局实例讲解

搜索引擎对html代码是非常优化的,所以html的优化是做好推广的第一步.一个符合seo规则的代码大体如下界面所示. 1.<!–木庄网络博客–> 这个东西是些页面注释的,可以在这里加我的"木庄网络博客",但过多关键字可能被搜索引擎惩罚! 2.<html> 这个是代码开头 结尾时和</html>对应. 3.<head> 头标记结尾用</head> 4.<title>(木庄网络博客-勤记录 懂分享)</title

第一种方式:cookie的优化与购物车实例

一 Cookie 的优化 1.1 一般而言,我们设置cookie是在php中设置 例如: <?php setcookie('testKey1','hello world',0,'/'); //# 当 expires = 0 时,此Cookie随浏览器关闭而失效, ?> 而在验证的时候,我们通常是: <?php if(isset($_COOKIE['testKey2'])) echo "The New COOKIE is : testKey2 = ".$_COOKIE[

JavaScript性能优化小窍门实例汇总

JavaScript性能优化小窍门实例汇总在众多语言中,JavaScript已经占有重要的一席之地,利用JavaScript我们可以做很多事情 , 应用广泛.在web应用项目中,需要大量JavaScript的代码,将来也会越来越多. 但是由于JavaScript是一个作为解释执行的语言,而且它的单线程机制,决定了性能问题是JavaScript的弱点,也是开发者在写JavaScript的时候需注意的一个问题. 因为经常会遇到Web 2.0应用性能欠佳的问题,主因就是JavaScript性能不足,导

只运行一个实例以及内存泄漏检测

unit 使应用程序只运行一个实例; interface uses Windows; const  // - 互斥体唯一的名字  _Mutex_Name = '{19631971-1976-1981-1989-199319941995}'; var  _Mutex_Handle: THandle; implementation initialization // - 载入时调用的代码 // - 创建互斥体对象_Mutex_Handle := CreateMutex(nil, False, LPC

C# WinForm 判断程序是否已经在运行,且只允许运行一个实例

static class Program {   /// <summary>   /// 应用程序的主入口点.   /// </summary>   [STAThread]   static void Main()   {     Application.EnableVisualStyles();     Application.SetCompatibleTextRenderingDefault(false);     //1.这里判定是否已经有实例在运行     //只运行一个实

转 C# 只允许运行一个实例

来源:http://blog.csdn.net/jin20000/article/details/3136791 互斥进程(程序), 简单点说,就是在系统中只能有该程序的一个实例运行. 现在很多软件都有这功能,如Maxthon 可以设置为"只允 许打开一个窗体",还有Bitcomet等. 我也是看到这些软件的这个功能才来研究这个问题的.  要实现程序的互斥,通常有三中方式,下面 用 C#  语言来实现: 实现方式一: 使用线程互斥变量. 通过定义互斥变量来判断是否已运行实例.C#实现如

C++静态变量本身可否是一个实例对象

一般书上总是用int来举例,那个太简单.如果静态变量本身可否是一个实例对象呢?应该是可以,但是这样涉及到它的构造函数以及它内部的静态变量如何初始化两个问题,换而言之,这个静态变量本身应该如何初始化?这个问题和单例模式有些关系,回头查查. 以下是一个不成熟的例子,但是能编译运行通过. #include "stdafx.h" #include <iostream> using namespace std; class AAA { public: static int sss;

使程序只运行一个实例

//APP 里面 //1.// 此程序只能运行一次,用互斥量来判断程序是否已运行 1 HANDLE m_hMutex=CreateMutex(nullptr,TRUE, m_pszAppName); 2 if(GetLastError()==ERROR_ALREADY_EXISTS) { return FALSE; } //2. 1 HWND hWnd = FindWindow(nullptr, m_pszAppName/*_T("MyDlg")*/); //第一个参数: 窗口的类名

ArcGIS API for JavaScript开发环境搭建及第一个实例demo

原文:ArcGIS API for JavaScript开发环境搭建及第一个实例demo ESRI公司截止到目前已经发布了最新的ArcGIS Server for JavaScript API v3.9,它提供了更为丰富而又强大的功能.     一.安装前准备 1.ArcGIS Server for JavaScript API各版本下载地址:http://support.esrichina-bj.cn/2011/0223/960.html,我们选择下载最新的"ArcGIS API for Ja