分頁是一種常用的頁面數據顯示技術,分頁能夠通過減少頁面數據處理量從而提高了系統的性能。分頁應該是做WEB開發必須掌握的一個小技術。而分頁卻是復雜的,倒不是它的技術有多復雜;而是有太多的重復代碼,這些代碼都難以重用。能不能實現一個通用的分頁框架?每次只需要去覆寫一兩個方法,通過少量的代碼就能實現分頁的功能?
一般分頁應該要具有的功能有
1. 靈活的設置分頁大小。可以動態的設置分頁大小,而不是寫死到代碼中。
2. 自動計算總頁數。根據分頁大小和總記錄數自動計算總頁數。
3. 獲得當前頁的頁號。
4. 獲得當前頁的總記錄數。一般是最后一頁的時候可能會小于分頁大小。
5. 判斷當前頁是否為第一頁。
6. 判斷當前頁是否為最后一頁。
7. 判斷當前頁是否有上一頁。
8. 判斷當前頁是否有下一頁。
9. 獲得當前頁的數據列表。
10. 獲得當前頁的第一條記錄的索引號
11. 獲得當前頁的最后一條記錄的索引號。
二、常用的分頁技術
目前常用的分頁技術有兩種:
1. 第一次訪問是讀取所有記錄,放入session中,然后每次從session對象中讀取當前頁的數據
2. 每次都訪問數據庫,從數據庫中讀取當前頁的記錄。
這兩種方法都各有優缺點,當數據量比較少時,第一種方法無疑是要快一些,因為減少與數據庫的連接訪問。而當數據量比較大時,比如查詢結果可能會是上萬條,那么內存的開銷是十分大的,放到session中還有一個問題是能不能及時的清除無用的對象。而且這么大數據量在網絡中傳輸也會使系統變得很慢。
第二種方法就是專門解決這個問題的,它每次訪問數據庫,只讀取當前頁所需的記錄,大大的減少網絡傳輸量;它不會把頁數據放到session中,大大提高服務器的性能。
所以第二種方式要優于第一種方法。Session不要亂用,要用也僅僅是存放一些公共變量,相對于占用空間比較少的對象。不適合存放大量的數據,否則在很多個用戶同時訪問時那么系統會很慢,因為服務器內存被銷耗的很厲害
三、通用分頁框架需要解決的問題
作為一個通用分頁框架,
1. 應該不依賴于任何其它框架
2. 應該支持多種數據庫
3. 應該可以應用于任何web框架中,如:struts,spring等。
4. 應該把數據訪問的具體實現留給用戶去實現。
5. 應該實現關鍵的算法和過程,如:計算總頁數,所需的實始化動作。
6. 應該減化Contrller控制器的代碼,以往的分頁技術在Contrller中存在太多的if…else代碼。十分難懂,應該由一個輔助類來實現。
7. 應該減化jsp頁面的代碼,頁面應該沒有任何與分頁相關的計算。應該由分頁對象來實現。
8. 應該支持兩種分頁方式,采用session或不采用session由用戶控制。
四、具體實現
1.通用分頁接口。定義接口可以有更多不同的實現,接口只聲明了分頁應該具有的公共行為。
ViewPage.java
/**
* 分頁接口
* @author ex_yuanguangdong
*
*/
public interface ViewPage {
/**
* 獲取總頁數
* @return 總頁數
*/
public int getPageCount();
/**
* 獲得頁面大小
* @return 頁面大小
*/
public int getPageSize();
/**
* 設置頁面大小
* @param size
*/
public void setPageSize(int size);
/**
* 獲得當前頁數據
* @return 數據列表
*/
public List getPageData();
/**
* 獲得當前頁索引號
* @return 當前頁索引號
*/
public int getPageIndex();
/**
* 獲得當前頁記錄總數
* @return 當前頁記錄總數
*/
public int getPageRows();
/**
* 是否有下一頁
* @return
*/
public boolean getHashNextPage();
/**
* 是否有上一頁
* @return
*/
public boolean getHashPreviousPage();
/**
* 轉到尾頁
*
*/
public void gotoLastPage();
/**
* 轉到首頁
*
*/
public void gotoFirstPage();
/**
* 是否首頁
* @return
*/
public boolean isFirstPage();
/**
* 是否尾頁
* @return
*/
public boolean isLastPage();
/**
* 轉到上一頁
*
*/
public void gotoPreviousPage();
/**
* 轉到下一頁
*
*/
public void gotoNextPage();
/**
* 轉到指定頁面,pageIndex小于1時,轉到第一頁;pageIndex大于總頁數時,轉到最尾頁
* @param pageIndex 指定的頁號
*/
public void gotoPage(int pageIndex);
/**
* 獲取當前頁第一條記錄的記錄號
* @return int 當前頁第一條記錄的記錄號
*/
public int getPageFirstRecord();
/**
* 獲取當前頁最后一條記錄的記錄號
* @return int 當前頁最后一條記錄的記錄號
*/
public int getPageLastRecord();
}
2. 分頁抽像實現類,實現關鍵的算法
AbstractViewPage.java
/**
* 分頁默認抽象實現
* 初始時,分頁類有下列默認值:
* 分頁大小為-1,為不分頁;
* 總頁數為1頁
* 當前頁為第一頁
* 總記錄數為0條
* 當前頁數據列表為沒有任何記錄的列表
* @author ex_yuanguangdong
*
*/
public abstract class AbstractViewPage implements ViewPage {
//-----------------------------------------
//私有靜態常量
//-----------------------------------------
private static final int DEFAULT_PAGE_INDEX = 1;
private static final int DEFALT_PAGE_COUNT = 1;
private static final int DEFAULT_PAGE_SIZE = -1;
private static final int DEFAULT_ROWS = 0;
//-----------------------------------------
//私有成員變量
//-----------------------------------------
/**當前頁索引號**/
private int pageIndex = DEFAULT_PAGE_INDEX;
/**總頁數**/
private int pageCount =DEFALT_PAGE_COUNT;
/**分頁大小**/
private int pageSize = DEFAULT_PAGE_SIZE ;
/**數據總記錄數**/
private int rows = DEFAULT_ROWS;
//------------------------------------------
//本地成員變量getter,setter方法
//------------------------------------------
/**
* 設置新頁號,只有大于等于1而且小于等于總頁數并且不為當前頁時,才允許設置
* @param pageIndex The pageIndex to set.
*/
private void setPageIndex(int newPageIndex) {
if( newPageIndex >= this.DEFAULT_PAGE_INDEX && newPageIndex <= this.getPageCount() && newPageIndex != this.pageIndex) {
this.pageIndex = newPageIndex;
}
}
/**
* @return Returns the rows.
*/
private int getRows() {
return rows;
}
/**
* @param rows The rows to set.
*/
private void setRows(int rows) {
this.rows = rows;
}
/**
* @param pageCount The pageCount to set.
*/
private void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
//--------------------------------------
//實現Page接口方法
//--------------------------------------
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageData()
*/
public List getPageData() {
List pageList =null;
//獲得當前頁數據
pageList = this.pageList(this.getPageFirstRecord(), this.getPageRows());
//保證不返回null
if(pageList == null) {
pageList =new ArrayList();
}
return pageList;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageIndex()
*/
public int getPageIndex() {
return this.pageIndex;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#isFirstPage()
*/
public boolean isFirstPage() {
return this.DEFAULT_PAGE_INDEX ==this.pageIndex;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#isLastPage()
*/
public boolean isLastPage() {
//當前頁索引為總頁數時為最后一頁
return this.pageIndex == this.pageCount;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getHashNextPage()
*/
public boolean getHashNextPage() {
//當前頁索引號小于總頁數
return this.pageIndex < this.pageCount ;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getHashPreviousPage()
*/
public boolean getHashPreviousPage() {
//當前頁索引號大于默認的初始頁號,這里為1
return this.pageIndex > this.DEFAULT_PAGE_INDEX ;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageCount()
*/
public int getPageCount() {
return this.pageCount;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageSize()
*/
public int getPageSize() {
return this.pageSize;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageRows()
*/
public int getPageRows() {
//當頁面大小為-1 時,返回總記錄數
if(this.DEFAULT_PAGE_SIZE == this.pageSize ) {
return this.rows;
}
//不為最后一頁時,返回pageSize
if(!this.isLastPage()) {
return this.pageSize;
}
//最后一頁時
return this.rows - (this.pageSize * (this.pageCount -1));
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageFirstRecord()
*/
public int getPageFirstRecord() {
//頁大小為-1 時
if(this.DEFAULT_PAGE_SIZE== this.pageSize ) {
return 0;
}
return (this.pageIndex -1)* this.pageSize;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#getPageLastRecord()
*/
public int getPageLastRecord() {
//頁大小為-1時,返回總記錄數
if(this.DEFAULT_PAGE_SIZE == this.pageSize) {
return this.rows;
}
return this.getPageFirstRecord() + this.getPageRows() ;
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoFirstPage()
*/
public void gotoFirstPage() {
this.gotoPage(this.DEFAULT_PAGE_INDEX);
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoLastPage()
*/
public void gotoLastPage() {
this.gotoPage(this.getPageCount());
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoPreviousPage()
*/
public void gotoPreviousPage() {
this.gotoPage(this.getPageIndex() -1);
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoNextPage()
*/
public void gotoNextPage() {
this.gotoPage(this.getPageIndex() + 1);
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#gotoPage(int)
*/
public void gotoPage(int newPageIndex) {
if( newPageIndex >= this.DEFAULT_PAGE_INDEX && newPageIndex <= this.getPageCount() ) {
this.setPageIndex(newPageIndex);
}
}
/**
* @see com.palic.elis.pos.junit.counter.web.mvc.ViewPage#setPageSize(int)
*/
public void setPageSize(int size) {
if(size < 1) {
size = 1;
}
this.pageSize = size;
//進行初始化
this.doInit();
}
//-----------------------------------
//輔助方法
//-----------------------------------
/**
* 分頁初始化方法,為了保證總是能正確的初始化,所以聲明為final ,為了讓子類可以調用聲明為protected
*
*/
protected final void doInit() {
int rows = 0;
//獲得總記錄數
rows= totalRows();
//設置總記錄數
this.setRows(rows);
//設置新的總頁數
//計算并設置總頁數
int pages = calculatePageCount();
this.setPageCount(pages);
//轉到第一頁
this.gotoPage(this.DEFAULT_PAGE_INDEX);
onInit();
}
/**
* 記算總頁數
* @return 總頁數
*/
private int calculatePageCount() {
//總記錄數為0條,則返回的總頁數為1
if(this.getRows() == 0) {
return this.DEFALT_PAGE_COUNT;
}
//如果頁面大小為-1,則返回的總頁數為1
if(this.DEFAULT_PAGE_SIZE == this.getPageSize() ) {
return this.DEFALT_PAGE_COUNT;
}
return this.getRows() / this.getPageSize() + ( this.getRows() % this.getPageSize() ==0 ? 0 :1);
}
/**
* 獲得總記錄數,調用queryTotalRows(),將異常封裝為non-checked 異常
* @return 總記錄數
* @throws ApplicationRuntimeException
*/
private int totalRows() throws ApplicationRuntimeException{
try{
return queryTotalRows();
}
catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}
/**
* 獲得當前頁數據,調用queryPageList()方法,將異常封裝為non-checked異常
* @param startRow 開始記錄號
* @param rowCount 記錄總數
* @return 當前頁數據
* @throws ApplicationRuntimeException
*/
private List pageList(int startRow, int rowCount) throws ApplicationRuntimeException{
try{
return queryPageList(startRow, rowCount);
}catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}
//-----------------------------------------
//子類實現的方法
//-----------------------------------------
/**
* 初始化附加方法,由子類擴展
*/
protected void onInit() {
}
/**
* 查詢獲得總記錄數,由子類具體實現
* @return 總記錄數
* @throws Exception
*/
protected abstract int queryTotalRows() throws Exception;
/**
* 查詢當前頁數據,從startRow 開始的rowCount條記錄
* @param startRow 開始記錄號
* @param rowCount 記錄總數
* @return 當前頁數據
* @throws Exception
*/
protected abstract List queryPageList(int startRow, int rowCount) throws Exception;
}
3. 分頁輔助類
ViewPageHelper.java/**
* 分頁輔助類,用于減化Controller中的代碼
* @author yuanguangdong
* date: Oct 22, 2006
*/
public class ViewPageHelper {
private static final int FIRST_PAGE_VALUE = 1;
private static final int PREVIOUS_PAGE_VALUE = 2;
private static final int NEXT_PAGE_VALUE = 3;
private static final int LAST_PAGE_VALUE = 4;
private static final int SPECIAL_PAGE_VALUE = 5;
public static final String FIRST_PAGE = "FIRST_PAGE";
public static final String PREVIOUS_PAGE = "PREVIOUS_PAGE";
public static final String NEXT_PAGE = "NEXT_PAGE";
public static final String LAST_PAGE = "LAST_PAGE";
public static final String SPECIAL_PAGE = "SPECIAL_PAGE";
/**分頁動作參數名**/
public static final String PAGE_ACTION = "page_action";
/**分頁對象屬性名**/
public static final String SESSION_PAGE = "session_page";
/**頁號參數名**/
public static final String PAGE_NO = "page_no";
private static Map actionMap = new HashMap();
static {
actionMap.put(FIRST_PAGE, new Integer(FIRST_PAGE_VALUE));
actionMap.put(PREVIOUS_PAGE, new Integer(PREVIOUS_PAGE_VALUE));
actionMap.put(NEXT_PAGE, new Integer(NEXT_PAGE_VALUE));
actionMap.put(LAST_PAGE, new Integer(LAST_PAGE_VALUE));
actionMap.put(SPECIAL_PAGE, new Integer(SPECIAL_PAGE_VALUE));
}
/**
* 執行分頁動作
* @param page 分頁對象
* @param action 分頁動作參數
* @param pageIndex 頁號
*/
public static void doAction(ViewPage page, String action, int pageIndex) {
int actionIndex = 0;
if (page == null) {
throw new NullPointerException("Page對象null"
;
}
if (action == null || "".equals(action)) {
throw new IllegalArgumentException("無效的分頁動作參數null"
;
}
action = action.toUpperCase();
if (!actionMap.containsKey(action)) {
throw new UnsupportedOperationException("不支持的分頁動作參數:" + action);
}
Integer index = (Integer) actionMap.get(action);
actionIndex = index.intValue();
switch (actionIndex) {
case FIRST_PAGE_VALUE:
page.gotoFirstPage();
break;
case PREVIOUS_PAGE_VALUE:
page.gotoPreviousPage();
break;
case NEXT_PAGE_VALUE:
page.gotoNextPage();
break;
case LAST_PAGE_VALUE:
page.gotoLastPage();
break;
case SPECIAL_PAGE_VALUE:
page.gotoPage(pageIndex);
}
}
public static void doAction(ViewPage page, String action){
doAction(page, action, 1);
}
}
五、應用通用分頁框架
1.繼承AbstractViewPage類,實現queryPageList(int startRow, int endRow)和
queryTotalRows()方法。
protected int queryTotalRows() throws Exception
獲得查詢條件的總記錄數
protected List queryPageList(int startRow, int rowCount)
用于查詢指定范圍的數據。startRow為開始記錄號, rowCount為查詢的記錄數
queryPageList(0,20)為查詢從第一條開始的20條記錄。
使用Ibatis可以由queryPageList調用queryForList()方法。
/**
* 用戶信息分頁內部類
* @author yuanguangdong
* date: Oct 22, 2006
*/
class UserInfoPage extends AbstractViewPage{
//------------------------------------------------
//實現AbstractViewPage抽象類的抽象方法
//------------------------------------------------
/**
* @see com.prs.application.ehld.web.mvc.AbstractViewPage#getPageDate(int, int)
*/
protected List queryPageList(int startRow, int endRow) throws Exception {
return sampleAction.getUserInfoList(startRow, endRow);
}
/**
* @see com.prs.application.ehld.web.mvc.AbstractViewPage#getRows()
*/
protected int queryTotalRows() throws Exception {
return sampleAction.getUserCount();
}
}
3. 在Contrller中的實現
public ModelAndView listUser(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String pageAction =
RequestUtils.getStringParameter(request,ViewPageHelper.PAGE_ACTION);
Integer pageIndex =
RequestUtils.getIntParameter(request,ViewPageHelper.PAGE_NO);
//聲明分頁對象
ViewPage userPage =
(ViewPage) request.getSession().getAttribute(ViewPageHelper.SESSION_PAGE);
//第一次請求
if(pageAction == null || userPage == null){
//構建一個新的分頁對象
userPage = new UserInfoPage();
//設置分頁大小
userPage.setPageSize(2);
}else{
if(ViewPageHelper.SPECIAL_PAGE.equals(pageAction)){
//如果頁數為空,則默認為1
if (pageIndex == null)
pageIndex = new Integer(1);
ViewPageHelper.doAction(userPage,pageAction,pageIndex.intValue());
}else{
ViewPageHelper.doAction(userPage,pageAction);
}
}
//從分頁對象中獲得當前頁數據
List userInfoList = userPage.getPageData();
ModelAndView mav = new ModelAndView(userInfoListView);
mav.addObject(this.userInfoListKey,userInfoList);
request.getSession().setAttribute(ViewPageHelper.SESSION_PAGE,userPage);
return mav;
}
4. jsp頁面實現
<%@ page contentType="text/html;charset=utf-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
<%@ taglib prefix="tiles" uri="http://jakarta.apache.org/struts/tags-tiles" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<html>
<head>
<title>顯示所有員工</title>
<SCRIPT language="javaScript">
function pageNoChange(pageNo){
location.href= "ehld.sample.getuserinfolist.do?page_action=SPECIAL_PAGE&page_no="+pageNo.value;
}
</SCRIPT>
</head>
<body>
<table width="80%" border="0">
<tr>
<td bgcolor="#F0FEFF"><div align="left"> 用戶列表</div></td>
</tr>
</table>
<br>
<input name="adduser" type="submit" id="adduser" value="新增用戶" onclick="location.href='ehld.sample.edituserinfo.do'">
<table width="80%" border="0">
<tr bgcolor="#58ED64">
<th width="25%">id</th>
<th width="34%">姓名</th>
<th colspan="2">操作</th>
</tr>
<c:forEach items="${userInfoList}" var="userInfoDTO">
<tr bgcolor="#D6EBF8">
<td><c
ut value="${userInfoDTO.userID}"/></td>
<td><c
ut value="${userInfoDTO.userName}"/></td>
<td width="21%"><a " src="http://www.itpub.net/images/smilies/33.gif" border=0 smilieid="204">ut value='${userInfoDTO.userID}'/>">編輯</a></td>
<td width="20%"><a href="#">刪除</a></td>
</tr>
</c:forEach>
</table>
<c:if test="${session_page.firstPage}">
首頁
</c:if>
<c:if test="${! session_page.firstPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=FIRST_PAGE">首頁</a>
</c:if>
<c:if test="${! session_page.hashPreviousPage}">
上一頁
</c:if>
<c:if test="${session_page.hashPreviousPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=PREVIOUS_PAGE">上一頁</a>
</c:if>
<c:if test="${!session_page.hashNextPage}">
下一頁
</c:if>
<c:if test="${session_page.hashNextPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=NEXT_PAGE">下一頁</a>
</c:if>
<c:if test="${session_page.lastPage}">
尾頁
</c:if>
<c:if test="${!session_page.lastPage}">
<a href="ehld.sample.getuserinfolist.do?page_action=LAST_PAGE">尾頁</a>
</c:if>
共有<c
ut value="${session_page.pageCount}" />頁,第
<select name = "pageNo" onChange = "javaScript
ageNoChange(this);">
<c:forEach begin="1" end = "${session_page.pageCount}" var = "pageIndex">
<option " src="http://www.itpub.net/images/smilies/33.gif" border=0 smilieid="204">ut value='${pageIndex}'/>" <c:if test = "${pageIndex ==session_page.pageIndex }">selected</c:if>>
<c
ut value="${pageIndex}"/>
</option>
</c:forEach>
</select>
頁
</body>
</html>
六、設計探討
1.通過提供queryTotalRows() 和queryPageList(int startRow, int rowCount)方法,交由用戶具體的去實現,所以能夠支持任何數據庫。
對于Ibatis用戶可以使用queryForList()方法,對于用jdbc實現也可以有多種方法來支持各種數據庫。
Ms sql 可以使用top 關鍵字,來獲得指定范圍的數據
ORACEL可以使用rowid 偽列來獲得指定范圍的數據
具體怎么去讀取數據,完全交由用戶控制
2.分頁對象與具體的業務對象分離。分頁對象如果不能與具體的業務對象分離那么就不可能實現分頁對象的重用,不可以實現代碼的最大的重用。這不符合oo的按職責來設計對象的原則。
3. ViewPageHelper幫助類的使用有兩個好處,統一為分頁代碼所需的字符參數進行定義,便于contrller和jsp頁面代碼的維護。第二便于代碼重用,減少在contrller中的if分支句語。如果不使用幫助類,則在每個controller中都會產生大量相同的代碼。
4. final關鍵字的使用,protected final void doInit()用于分頁對象的實始化,它讀取并設置總記錄數,計算總頁數,默認為第一頁等。為什么不在構造函數中來做它呢?如果在構造函數來做它,子類就不可以擴展了。像這樣的初始化方法的位置應該由擴展類來靈活控制。聲明為protected是不讓它由外部對象來進行訪問,但是子類又可以進行調用。聲明為final是為了子類不能重寫它,如果子類重寫不當就會造成分頁對象的執行邏輯錯誤。但是如果子類又想擴展它怎么辦?子類重寫protected void onInit()方法就可以了。這樣就能保證父類的邏輯,又能夠讓子類進行擴展。
5.異常處理的思考,queryTotalRows()和queryPageList方法都是要求由子類實現的抽象類,這兩個類的特點都是可能會調用業務對象去實現相應的功能,業務對象可能會訪問業務數據庫等,可能會拋出任何Exception,但是分頁對象類去調用queryTotalRows()和queryPageList的方法是不應該對這些Exception進行任何處理的,如果進行try…catch那么就會隱藏了異常的細節,這是十分可怕的。如果這些方法拋出異常,分頁對象應該是不能處理的,不能處理的異常應該封裝為運行時異常,所以就有了下面的實現
private List pageList(int startRow, int rowCount) throws ApplicationRuntimeException{
try{
return queryPageList(startRow, rowCount);
}catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}
private int totalRows() throws ApplicationRuntimeException{
try{
return queryTotalRows();
}
catch(Exception ex){
throw new ApplicationRuntimeException(ex);
}
}
分頁對象內部調用pageList和totalRows方法,這樣就很好的解決了異常的問題,把異常交由外部調用者去決定是否處理,而不是強制調用者去處理。
5. 模板方法模式的使用,這是一個典型的模板方法模式的運用。在父類實現關鍵的算法代碼,實現分頁對象的處理邏輯,而把某些會發生改變的方法交由子類去實現,使得子類完全不用去關心父類的實現細節,子類只需要重寫兩個簡單的方法就可以實現父類的功能。這就是模板方法帶來的最大好處。模板方法模式在各種開源框架中有著廣泛的運用,看看spring的源碼就知道。子類只需要去實現自己最關心的細節,而父類實現那些不變的邏輯或算法。
6. 針對接口編程,而不是針對類編程。接口可以實現多重繼承,而類卻不能。接口有比類獲得更多的好處,更利于擴展。比如說分頁接口,它可以讓用戶有更多不同的實現,完全不依賴于任何類。只需要為它定制了共同的行為就可以了。在使用委托的時候接口比抽像類更好用。比如在裝飾模式的使用中,可能需要實現一個接口,而其中還要有一個本接口的引用。 如果是抽象類,則不可以實現。
7. 通用框架應該具有靈活性,不應該依懶于任何具體的框架。如果通用框架依懶于某一技術細節,某一框架,那么它就有一定的局限性。所以通用分頁不應該依懶于ibatis或hibernate 或spring的某一特點。更不應該依懶于sql或oralce某種數據庫。