模板模式——类似业务的代码整合

阅读更多
    最近在开发客服第三方支付功能的时候,发现了主从表分页查询存在问题,然后解决了这个问题后,想将修改后的核心代码迁移到itv、短代、游戏包历史订购等相关接口时,发现这个完全适用于模板模式。好处就是当有类似的主从表分页查询时,只需要继承父类,实现相应的dao方法即可
先贴一个模板模式的图
[img]图在最下面,不会排版:funk: [img]
先说明下使用场景:因为数据量过大,所以在mysql对表进行了按月切分(并且是跨库),那么分页查询这些表时就有点蛋疼了,下面使用java代码来做下分页(有其他更好的方案请各位提供,一定感谢)
先来做一个abstract类

package cn.egame.data.core.common;

import cn.egame.common.exception.ExceptionCommonBase;
import cn.egame.common.model.PageData;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by dinghw on 2015/4/20.
 *
 * @desc 此类为模板模式的抽象类,主要用于主从分表查询分页的方法
*/
public abstract class TablesTemplate {
    Logger logger = Logger.getLogger(TablesTemplate.class);
    /**
     * 模板方法的子类必须为此dao赋值,并且相应的类必须实现{@link TemplateOnlineServiceDao}
     */
    public TemplateOnlineServiceDao templateOnlineServiceDao;

    public TemplateOfflineServiceDao templateOfflineServiceDao;

    /**
     * @param bean     查询条件对象
     * @param currPage
     * @param pageSize
     * @param       返回对象
     * @return
     * @throws ExceptionCommonBase
     */
    public  PageData getPageDataFromOnlineAndOffline(B bean, int currPage, int pageSize) throws ExceptionCommonBase {
        int totalCount = 0;
        PageData pageData = templateOnlineServiceDao.getPageDataFromOnline(bean, currPage, pageSize);
        int onlineTotalCount = pageData.getTotal();
        totalCount += onlineTotalCount;
        List tableNames = templateOfflineServiceDao.getOfflineTables();
        List pageList = (List) pageData.getContent();
        for (String tableName : tableNames) {
            try {
                int offlineTotalCount = templateOfflineServiceDao.totalCount(bean, tableName);
                if (offlineTotalCount == 0) continue;
                int indexNum = (currPage - 1) * pageSize + 1;
                int endNum = currPage * pageSize;
                if (totalCount >= endNum) {
                    totalCount = totalCount + offlineTotalCount;
                    continue;
                }
                List offlineTableList = templateOfflineServiceDao.listFromOffline(bean, tableName);
                List tempList = getSubList(offlineTableList, pageSize, totalCount, offlineTotalCount, indexNum, endNum);
                pageList.addAll(tempList);
                totalCount = totalCount + offlineTotalCount;
            } catch (ExceptionCommonBase e) {
                logger.error(e.getMessage(), e);
            }
        }
        pageData.setContent(pageList);
        pageData.setTotal(totalCount);
        return pageData;
    }

    /**
     * 分页,获取从表的数据并切分
     *
     * @param offlineTableList
     * @param pageSize
     * @param totalCount
     * @param offlineTotalCount
     * @param indexNum
     * @param endNum
     * @return
     */
    private  List getSubList(List offlineTableList, int pageSize, int totalCount, int offlineTotalCount, int indexNum, int endNum) {
        if (offlineTableList == null || offlineTableList.size() == 0) {
            return new ArrayList();
        }
        int fromIndex = 0;
        int toIndex = 0;
        if (totalCount >= indexNum) {
            if ((totalCount + offlineTotalCount) > endNum) {
                toIndex = endNum - totalCount;
            } else {
                toIndex = offlineTotalCount;
            }
        } else if (totalCount < indexNum && (totalCount + offlineTotalCount) >= indexNum) {
            fromIndex = indexNum - 1 - totalCount;
            if ((totalCount + offlineTotalCount) >= endNum) {
                toIndex = fromIndex + pageSize;
            } else {
                toIndex = offlineTotalCount;
            }
        }
        return offlineTableList.subList(fromIndex, toIndex);
    }
}



上面代码实现规则:先从主表中取数据,然后依次从从表中查询表的总数,当满足当前页的数据则加入到tempList中,直至加满(=pageSize),如果数据不满足则不做处理

其中TemplateOnlineServiceDao、TemplateOfflineServiceDao 均为接口提供方法如下

package cn.egame.data.core.common;

import cn.egame.common.exception.ExceptionCommonBase;
import cn.egame.common.model.PageData;
import cn.egame.data.model.itv.ItvFee;
import cn.egame.data.model.itv.ItvFeeBean;

import java.util.List;


/**
 * Created by dinghw on 2015/4/20.
 */
public interface TemplateOnlineServiceDao {

    /**
     * @param bean
     * @param currPage
     * @param pageSize
     * @return
     * @throws ExceptionCommonBase
     * @throws ExceptionCommonBase
     */
    public  PageData getPageDataFromOnline(B bean, int currPage, int pageSize) throws ExceptionCommonBase;


    /**
     * @param bean
     * @param 
     * @return
     * @throws ExceptionCommonBase
     */
    public  List listFromOnline(B bean) throws ExceptionCommonBase;

}




package cn.egame.data.core.common;

import cn.egame.common.exception.ExceptionCommonBase;

import java.util.List;

/**
 * Created by dinghw on 2015/4/20.
 */
public interface TemplateOfflineServiceDao {

    /**
     * 获取线下所有表名
     *
     * @return
     * @throws cn.egame.common.exception.ExceptionCommonBase
     */
    public List getOfflineTables() throws ExceptionCommonBase;

    /**
     * 获取表中的总数
     *
     * @param bean
     * @param tableName
     * @return
     * @throws ExceptionCommonBase
     */
    public int totalCount(B bean, String tableName) throws ExceptionCommonBase;

    /**
     * 查询出线下库表的数据
     *
     * @param bean
     * @param tableName
     * @param        返回类型
     * @return
     * @throws ExceptionCommonBase
     */
    public  List listFromOffline(B bean, String tableName) throws ExceptionCommonBase;
}



这边TemplateOfflineServiceDao类中getOfflineTables方法,其实可以放到抽象类TablesTemplate,因为这个方法在一定程度上属于service层而不是dao层


上层的架子搭好了,下面就是具体的实现了,拿itv的代码做例子

/**
 *
 */
package cn.egame.data.core.itv;

import cn.egame.common.exception.ExceptionCommonBase;
import cn.egame.common.model.PageData;
import cn.egame.data.core.app.AppServiceFactory;
import cn.egame.data.core.common.TablesTemplate;
import cn.egame.data.core.common.TemplateOfflineServiceDao;
import cn.egame.data.core.common.TemplateOnlineServiceDao;
import cn.egame.data.core.cp.CpServiceFactory;
import cn.egame.data.interfaces.IObjectVisitor;
import cn.egame.data.interfaces.ItvFeeService;
import cn.egame.data.model.ContentProvider;
import cn.egame.data.model.app.AppConsume;
import cn.egame.data.model.app.AppInfo;
import cn.egame.data.model.itv.ItvFee;
import cn.egame.data.model.itv.ItvFeeBean;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;

/**
 * itv计费相关业务实现类
*
 * @author houqq
 * @date 2014年11月28日 上午9:43:22
 */
public class ItvFeeServiceImpl extends TablesTemplate implements ItvFeeService {
    Logger logger = Logger.getLogger(ItvFeeServiceImpl.class);

    private static ItvFeeServiceImpl instance;

    private static byte[] syncRoot = new byte[1];

    private ItvFeeServiceDao itvFeeServiceDao;
    private ItvFeeOfflineDao itvFeeOfflineDao;

    private ItvFeeServiceImpl() throws ExceptionCommonBase {
        itvFeeServiceDao = new ItvFeeServiceDao();
        itvFeeOfflineDao = new ItvFeeOfflineDao();
        templateOnlineServiceDao = new ItvFeeServiceDao();
        templateOfflineServiceDao = new ItvFeeOfflineDao();
    }

    public static ItvFeeServiceImpl getInstance() throws ExceptionCommonBase {
        if (instance == null) {
            synchronized (syncRoot) {
                if (instance == null) {
                    instance = new ItvFeeServiceImpl();
                }
            }
        }
        return instance;
    }

    
    /**
     * 依据条件查询itv计费列表
     *
     * @param itvFeeBean itv计费实体
     * @return itv计费列表
     * @throws ExceptionCommonBase
     */
    private List listItvFee(ItvFeeBean itvFeeBean) throws ExceptionCommonBase {
        List itvFees = itvFeeServiceDao.listFromOnline(itvFeeBean);
        List offLineItvFees = itvFeeOfflineDao.listAllFromOffline(itvFeeBean);
        itvFees.addAll(offLineItvFees);
        return itvFees;
    }


    /**
     * 从主从表中获取数据
     *
     * @param appId
     * @param userId
     * @param itvfeebean
     * @param currPage
     * @param pageSize
     * @return
     * @throws RemoteException
     */
    public PageData listItvFee(int appId, long userId, ItvFeeBean itvfeebean, int currPage, int pageSize) throws RemoteException {
        if (StringUtils.isEmpty(itvfeebean.getLinkageAccountId()) && StringUtils.isEmpty(itvfeebean.getPhone())) {
            PageData pageData = new PageData();
            pageData.setCurrentPage(currPage);
            pageData.setRowsOfPage(pageSize);
            return pageData;
        }
        //此处调用父类方法
        return this.getPageDataFromOnlineAndOffline(itvfeebean, currPage, pageSize);
    }
}


上述代码有一个让我感觉还不够完美的地方,就是dao定义,我必须得重复定义,一方面为了兼容之前的方法,另外一方面,较少TemplateOnlineServiceDao、TemplateOfflineServiceDao两个接口中的方法数量,让子类的dao可存在自己的特性方法

        itvFeeServiceDao = new ItvFeeServiceDao();
        itvFeeOfflineDao = new ItvFeeOfflineDao();
        templateOnlineServiceDao = new ItvFeeServiceDao();
        templateOfflineServiceDao = new ItvFeeOfflineDao();



  • 模板模式——类似业务的代码整合_第1张图片
  • 大小: 9.6 KB
  • 查看图片附件

你可能感兴趣的:(模板模式——类似业务的代码整合)