【开源】QuickPager 分页控件的内部结构,和OO原则与设计模式

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介: 关键字:提出需求、需求分析、原则、设计模式、索引        先说一下讨论的范围:使用数据库保存信息的项目,b/s结构,asp.net编写。请不要讨论这个范围之外的事情哦,谢谢!        这里想说的并不仅限于一个控件,而是一个关于分页的解决方案。

 

关键字:提出需求、需求分析、原则、设计模式、索引

 

     先说一下讨论的范围:使用数据库保存信息的项目,b/s结构,asp.net编写。请不要讨论这个范围之外的事情哦,谢谢!

 

     这里想说的并不仅限于一个控件,而是一个关于分页的解决方案。信息都是放在数据库里的,在b/s结构里面一次提取所有的数据显示并不是一个好的方法,所以就需要一个把数据分成多个页的形式来显示。关于分页的解决方案有多种,一种实现方式可以用一个“分页控件”(我的解决方案),也可以用其他的方式来体现(比如LinQ、ORM等)。这里想说的是我的这个解决方案,我想从提出需求、需求分析到解决方案的步骤来说明。

 

一、提出需求

 

     客户的信息(产品信息、员工信息、合同信息、金额相关等)都会放在数据库里面,由于是采用b/s结构,信息多了如果不分页的话,显示起来就会比较慢。数据库有多种类型,asp.net常用的是SQL Server ,小一点的网站会采用Access,数据量多了,或者比较重要的会采用Orcale。避免版权问题的会采用mySQL。还有特殊的会采用其他的数据库。

 

     分页方式也会有多种,网站为了照顾SEO,一般要采用URL的方式来分页(包括URL重写)。而OA、CRM这一类的(包括网站的后台管理)就不必考虑SEO了,采用Postback的分页方式会更方便,可以很容易的保存状态,比如查询条件等。

 

     总结一下就是:

          1、多种数据库(SQL Server、Access、Orcale等)。
          2、多种分页方式(URL、Postback等)。
          3、提高提取数据的速度。
          4、对SEO要友好。
          5、使用要方便。
          6、便于阅读、便于扩展。
          7、支持多种显示数据的控件(比如GridView、repeater等)。


二、需求分析

 

     要求基本就是这么多了,那么怎么来实现呢?我们先开看看分页的基本步骤:
          1、绘制UI,生成分页事件或者URL地址。
          2、选择分页算法。
          3、提交给数据库。
          4、得到记录集,绑定到控件。

          

     第一步是比较独立的,每一个方案就差不多会把它独立出来。而下面的就不太一致了。基本就是这样。


     然后再来看看常用的几种解决方案。

          1、GridView

               一般简单的可以使用GridView自带的分页功能来实现,优点就是使用起来非常的方便,但是他有一个明显的缺点,就是记录多了会比较慢,原因是他是把所有的数据都提取出来放在内存里面,然后在计算显示哪些数据。

 

          2、LinQ

               利用LinQ来分页的话,那么他就会把生成分页算法(SQL语句)、提交到数据库、得到记录集、填充到实体类都包含进去了。ORM好像也是这样(没用过ORM,只好用“好像”这个词了)。

          

          3、AspNetPager
               就是吴旗娃的分页控件,他只实现了第一部分,其他的都要自己另想办法了。最近又推出了Socut.Data.dll,可以用它来提取数据,但是这样又把原来的存储过程给“扔”掉了。另外配合起来也不是很“默契”,或者说不是“无缝连接”吧。


          4、QuickPager
               就是我的分页控件了,我要把这几个都包含进去,目的就是要简化操作,让使用的时候达到最简单。

 

          5、其他的方案
               这个我就不太了解了,应该还有很多的分页方案的,只是我还不太了解。


三、如何解决

 

     分页控件的基本结构已经完成了,又看了王涛的《你必须知道的.net》和两本设计模式的书(都还没有看完),不能白看呀,理论联系实际,实际配合理论,看看分页控件的内部代码的设计方式有哪些优缺点,符合了哪些原则,违反了哪些原则,还有和哪些设计模式有点像。

 

     多种数据库,一般是SQL Server2000、SQL Server2005、Orcale、mySQL这几种数据库,而这几种数据库对于“分页算法”又各有不同,SQL Server2000只能用表变量、颠倒Top、Max等,而SQL Server2005可以使用Row_Number,Orcale可以使用number、mySQL可以使用limit。各个数据库有各自的特点。虽然一个项目一般只用一种数据库,只考虑一种数据库的情况也是可以的,但是给自己增加一点难度,我的分页方案可以适合多种数据库,而且还可以扩展。

 

 

 

 

OO原则:

     1、单一职责。


          从表面上看,QuickPager是严重违反了这个原则,不仅负责绘制UI,还负责拼接SQL语句,还要处理回发事件,还要提交给数据库,还要绑定显示数据的控件。一个分页控件要实现这么多的事情,是不是会很复杂、耦合度也高了,严重的违反了单一职责呢?

简单的看确实是这样,但是仔细看一下分页控件内部结构,就会发现不是这样的。

class QuickPager:有一点“实体类”的感觉,他本身基本什么功能都不能实现,只是负责记录一些分页需要的信息。

class PageSQL:负责分页算法,说白了就是拼接SQL语句的。当然不是随意的拼接,而是按照分页算法来拼接。分页算法又有好多种。

class PageUI:绘制UI,包括总记录数、总页数、第一页、上一页、下一页、页号导航、分页事件等。

class PageGetData:负责把PageSQL生成SQL语句交给“数据访问函数库”,然后返回记录集。

 

          这么看的话,每一个类的职责都是比较单一的,一个类负责一件事情,实现一个功能,和在一起配合工作,才实现了最终的分页效果。

如果有变化的话只需要修改一个类就可以,不影响其他的类。比如要加一个分页算法的话,只需要添加一个子类继承PageSQL就可以了。对其他的类,和其他的分页算法都没有关系。

这样是不是说可以符合了单一职责呢?

 

     2、开放封闭原则。


          上面说了,要增加一个分页算法的话,只需要加一个子类就可以了,不用修改其他的代码,这个就是开放封闭原则吧。同样,URL分页、Postback分页也是这样的思路。

 

     3、依赖倒置


          就是依赖抽象,分页控件就是根据分页的需求进行的一个“抽象”,内部的各个类也是依据抽象才能够协同工作的。

 

设计模式

 

     1、策略模式
     引用:http://www.cnblogs.com/justinw/archive/2007/02/06/641414.html

     定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。

 

这个图和上面的是不是很像?

 

 

     Context(应用场景): 是QuickPager
     Strategy(抽象策略类):是PageSQL、PageUI
     ConcreteStrategy(具体策略类):是SQL_Max、SQL_Row_Number等和UI_PostBack、UI_URL。

 

     一个分页控件要提供多种分页算法,一个分页算法就相当于一个策略。同理,URL分页、Postback分页都可以看做是一种策略。看深入浅出设计模式,里面的例子采用的是接口,而分页控件采用的是基类。

 

     在QuickPager里面定义下面几个成员,这时并没有实例化。

 

img_405b18b4b6584ae338e0f6ecaf736533.gif img_1c53668bcee393edac0d7b3b3daff1ae.gif /**/ /// <summary>
        
/// 访问数据库用的实例
        
/// </summary>

         private   DataAccessHelp dal  =   null ;

img_405b18b4b6584ae338e0f6ecaf736533.gifimg_1c53668bcee393edac0d7b3b3daff1ae.gif        
/**/ /// <summary>
        
/// 生成SQL语句的部分
        
/// </summary>

         private  PageManage.PageSQL MgrPageSQL  =   null ;

img_405b18b4b6584ae338e0f6ecaf736533.gifimg_1c53668bcee393edac0d7b3b3daff1ae.gif        
/**/ /// <summary>
        
/// 提取数据的部分
        
/// </summary>

         private  PageManage.PageGetData MgrGetData  =   null ;

img_405b18b4b6584ae338e0f6ecaf736533.gifimg_1c53668bcee393edac0d7b3b3daff1ae.gif        
/**/ /// <summary>
        
/// 提取数据的部分
        
/// </summary>

         private  PageManage.PageUI MgrPageUI  =   null ;

 

 


然后再设计几个属性,来处理如何实例化这几个成员。

     

img_1c53668bcee393edac0d7b3b3daff1ae.gif img_405b18b4b6584ae338e0f6ecaf736533.gif     数据访问实例的设置 #region 数据访问实例的设置
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
/**//// <summary>
        
/// 设置数据访问层的实例
        
/// </summary>

        public DataAccessHelp DAL
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
set { dal = value; }
            
get
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
{
                
if (dal == null)
                    dal 
= new DataAccessHelp();

                
return dal;
            }

        }

        
#endregion


img_1c53668bcee393edac0d7b3b3daff1ae.gifimg_405b18b4b6584ae338e0f6ecaf736533.gif        
分页算法的实例 #region 分页算法的实例
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
/**//// <summary>
        
/// 分页算法的实例
        
/// </summary>

        public PageManage.PageSQL ManagerPageSQL
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
set { MgrPageSQL = value; }
            
get
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
{
                
if (this.SetRunKind == myPageRunKind.Customer)
                    
return null;        //自定义方式不提供分页算法

                PageManage.PageSQL tmp 
= MgrPageSQL;
                
if (tmp == null)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif                
{
                    
switch (SetSQLKind)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif                    
{
                        
case myPageSQLKind.Row_Number:
                            tmp 
= new JYK.Controls.PageManage.SQL_Row_Number();
                            
break;

                        
case myPageSQLKind.Max_TopTop :
                            
if (this.TableOrderColumns.Contains(","))
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif                            
{
                                
//多字段排序
                                
//CommandClass.MsgBox("Max_TopTop2", false);
                                tmp = new JYK.Controls.PageManage.SQL_TopTop();
                            }

                            
else
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif                            
{
                                
//一个排序字段
                                
//CommandClass.MsgBox("Max_TopTop1", false);
                                tmp = new JYK.Controls.PageManage.SQL_Max();
                            }

                            
break;

                        
//其他的算法的判断就省略了。
                    }


                    MgrPageSQL 
= tmp;
                }


                
if (tmp.myPage == null)
                    tmp.myPage 
= this;

                
return tmp;
            }

        }

        
#endregion


img_1c53668bcee393edac0d7b3b3daff1ae.gifimg_405b18b4b6584ae338e0f6ecaf736533.gif        
分页方式的实例 #region 分页方式的实例
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
/**//// <summary>
        
/// 分页方式的实例
        
/// </summary>

        public PageManage.PageUI ManagerPageUI
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif        
{
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
set { MgrPageUI = value; }
            
get
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif            
{
                PageManage.PageUI tmp 
= MgrPageUI;
                
if (tmp == null)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif                
{
                    
switch (this.SetUIKind)
img_2887d91d0594ef8793c1db92b8a1d545.gifimg_7a2b9a960ee9a98bfd25d306d55009f8.gif                    
{
                        
case myPageUIKind.PostBack:
                            tmp 
= new JYK.Controls.PageManage.UI_PostBack();
                            
break;
                       
                    }


                    MgrPageUI 
= tmp;
                }


                
if (tmp.myPage == null)
                    tmp.myPage 
= this;

                
return tmp;
            }

        }

        
#endregion


 


     这样就非常的灵活了,不仅可以根据不同的属性设置来实例化不同的子类。比如根据不同的分页算法来实例化不同的子类。而且还可以在外部设置实例,就是说可以再分页控件的外部自己定义一个子类,然后实例化。
再有一个好处就是,用到的时候才实例化,不用就不实例化,比如如果采用自定义的运行方式的话,那么dal 、 MgrPageSQL、 MgrGetData都不会被实例化,这样就不会有浪费的感觉了。

自定义的运行方式,就是像吴旗娃的分页控件那样的使用方式。是需要自己处理数据的,所以dal 、 MgrPageSQL、 MgrGetData这三个类就用不到了,用不到也就不需要实例化了。


     2、模板模式

          策略模式只是规定了这几个类的关系,至于类的内部的实现方式,可以考虑使用模板模式。MgrPageSQL和子类的实现方式就是模板模式。


          模板模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
          http://www.cnblogs.com/webabcd/archive/2007/03/13/673658.html

          在MgrPageSQL里面定义了分页算法必须做的几件事情,和几个步骤的实现代码,然后在子类里面完善。


 

总结:


     这样来分析一下,会对QuickPager更加了解一点了吧。如果想看源码的话请到这里下载:http://www.cnblogs.com/jyk/archive/2008/07/29/1255891.html

 

     下一篇会说一下QuickPager里面已经实现的几种分页算法的SQL语句、适用范围、压力测试、几种算法之间的对比和与存储过程的对比。


      


 
相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
相关文章
|
17天前
|
设计模式
设计模式七大原则
这篇文章介绍了设计模式中的七大原则,特别强调了单一职责原则,即一个类应该只有一个引起其行为变化的原因,以确保类功能的高内聚和低耦合。
|
16天前
|
设计模式 存储 前端开发
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
|
3月前
|
设计模式 供应链
设计模式六大原则之迪米特法则
设计模式六大原则之迪米特法则
|
3月前
|
设计模式
设计模式六大原则之依赖倒置原则
设计模式六大原则之依赖倒置原则
|
16天前
|
设计模式 算法 开发者
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
设计模式问题之最小知识原则(迪米特法则)对代码设计有何影响,如何解决
|
16天前
|
设计模式 前端开发 JavaScript
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些
React开发设计模式及原则概念问题之什么是HOC(Higher-order component),HOC遵循的设计原则都有哪些
|
16天前
|
设计模式 前端开发 JavaScript
React开发设计模式及原则概念问题之什么是设计模式,单一职责原则如何理解
React开发设计模式及原则概念问题之什么是设计模式,单一职责原则如何理解
|
3月前
|
设计模式 uml
设计模式学习心得之前置知识 UML图看法与六大原则(下)
设计模式学习心得之前置知识 UML图看法与六大原则(下)
22 2
|
3月前
|
设计模式 Java 数据库
深入理解设计模式六大原则
深入理解设计模式六大原则
|
3月前
|
设计模式 数据可视化 程序员
设计模式学习心得之前置知识 UML图看法与六大原则(上)
设计模式学习心得之前置知识 UML图看法与六大原则(上)
23 0