代码改变世界

实体类的变形【1】—— 餐盘原理

2008-08-30 14:56  金色海洋(jyk)  阅读(2549)  评论(32编辑  收藏  举报

 

    在亚历山大同学的post里面我说可以让实体类和表不必一一对应,但是并没有详细说明如何来做,也有人想问我是怎么做的,那么我就说一下。先说一个简单一点的,那就是在网页里面显示列表数据的情况,其他的下次再说。我们先来看一个生活中的情况,然后再说程序里面如何来做。

 

餐盘原理——模糊对应

 


     餐盘,大家去食堂吃饭的时候,是不是会用一个长方形的餐盘来盛饭和菜呢?长方形的餐盘里有一个大一点的长方形的阁子,可以用来盛饭,当然也可以放馒头、花卷、面条等;有两、到四个个小一点的阁子,可以盛菜;由一个圆形的阁子可以放小碗;还有一个细长的可以放筷子。

     无论食堂做什么饭菜,我们都是用这个餐盘来盛的。这个餐盘既可以放鸡蛋炒黄瓜、烧茄子、黄花鱼还可以放宫保鸡丁等。一个餐盘应对了好多种菜。

     

     餐盘规定了有几个阁子,阁子里面可以放食物,但是并没有规定阁子里面必须放什么食物,阁子里面到底放什么食物就是一种模糊的对应关系。


     好了让我们回到程序中来,假设我们要仿照博客园的社区来做一个小程序。社区里面有新闻、小组、博文、闪存、博友等内容,假设数据库里面有这几个表:cmt_News_NewsInfo(存放新闻内容)、cmt_Group_topic(小组里面的话题)、cmt_FAQ_Questions(存放博友提问)、cmt_Flash_FlashInfo(存放闪存内容)

 

     如果现在我们想要做一个社区首页,要如何设置实体类呢?按照我对OO的一知半解,我可能会设计下面这几个类,这几个类都是和表(或者是视图)一一对应的。不管和谁对应,并不是重点。

 

public class Group_topic
 {
  
public string Topic;  //话题名称
  public string TopicURL;  //话题的连接地址
  public string Group;  //话题所属小组
  public string GroupURL;  //小组的连接地址
  public string View;   //回应/浏览
  public string SendDate;  //话题发表日期
  public string Author;  //作者姓名
  public string AuthorURL; //作者的连接地址
   
 }

 
public class FlashInfo
 {
  
public string Author;  //作者姓名
  public string AuthorURL; //作者的连接地址
  public string Message;  //闪存的内容
  public string SendDate;  //话题发表日期
  public string ToAuthor;  //to 作者姓名
  public string ToAuthorURL; //to 作者的连接地址
  
 }

public class Group_Lesson
 {
  
public string GroupName; //小组名称
  public string GroupURL; //小组名称
  public string GroupImage; //小组名称
  public string MembersCount; //成员数量
   

 }

 

其他的就省略了。


     不知道这么设计对不对,先假设这么设计是对的吧,那么由于属性不同,就需要设计多个不同的实体类,给实体类赋值的部分也要写多个,业务逻辑的部分也要针对各个实体类的属性名称来编写,UI也要根据实体类的属性名称来取值。

我们来看看程序的步骤:

 

1、定义实体类。有几个“列表”就要定义几个实体类。
2、给实体类赋值。由于是多种实体类,那么给实体类赋值就有点麻烦,不能用一个函数搞定,当然我们可以请来ORM帮忙。但是ORM的使用也并不是很轻松。
3、业务逻辑的处理。依据业务需求对实体类的属性名称来做处理。
4、显示数据。依据页面布局和实体类的属性名称来提取数据。


     这样各个部分都和实体类的属性名称发生了关联(这个就是内容耦合吧?),如果这时候字段名称发生了变化,那么每个部分都要做些修改。而修改的原因仅仅是实体类的属性名称变化了。

 

     这样设计实体类对吗?面向对象,一切皆为对象,见到猫猫了就 class cat, 见到狗狗了就 class dog,见到新闻就 class News。真的有这么简单吗?面向对象的精华是“抽象”吧?猫和狗可以抽出来一个class Animals,那么这么做抽象的依据是什么呢?往往被忽略了。

 

     请注意:我们讨论的前提和目的:在网页里面显示列表性质的数据,这个例子的要求:实现社区的首页。

首页里面是最新的新闻、最新的小组话题、最新的问题等。那么我们是不是要根据这个要求来进行一下抽象呢?

要显示最新(最多的、最高的等)的n条数据,有“名称”、连接、点击量、介绍等。那么是不是可以根据这个来抽象呢?

您可能会眼前一亮,对呀,设计一个基类,然后派生出四个子类对应新闻、博文、小组话题等。

比如这样 class WebDataList {}

 

     这样是抽象了,但是其实还是多种不同的实体类,对上面的步骤不会有什么的改进和帮助。

那么到底要如何来做呢呢?想想上面的餐盘,我们是不是可以设置一个这样的“通用”实体类?(请注意通用的范围:网页里的列表数据的显示)
 

/// 一般的列表
 public struct TitleBase
 {
  
/// 0 记录的主键ID
  public string ID;   //
  /// 1 链接地址,用于静态页或者URL重写
  public string URL;   //
  /// 2 全部标题
  public string AllTitle;  //
  /// 2 限制字数的标题
  public string Title;  //
  /// 3 发表时间
  public string AddedDate; //
  /// 4 简介内容
  public string Introduction; //
  /// 5 备用1。人气
  public string Other1;     //
  /// 6 备用2。图片路径
  public string Other2;     //
  /// 7 备用3。
  public string Other3;     //
 }


     这是一个固定的“实体类”,他的属性名称是固定的,这样做有什么优点呢?

 

1、只需要定义一个实体类就可以了,实体类的数量不会根据网站(列表页面)的扩展而扩展。
2、给实体类赋值的函数只写一个就可以了,不同的列表只需要修改SQL语句即可。
3、由于实体类的属性名称是固定的,这样如果只是字段名称修改了,那么只需要改一下SQL语句即可,其他的代码都不需要修改。

 

缺点:有优点就会带来点缺点。


1、有浪费的嫌疑,由于属性的数量是固定的,有的时候并不需要这么多,那么多出来属性的就浪费了。
2、需要写一个属性名和字段名的对应关系的说明(约定),各个部分按照这个约定行事。这个应该属于文档的一部分吧。
3、SQL语句的编写有一定的要求:SQL语句里的字段数必须是8个,而且字段的顺序必须要对好。
4、只适合网站的列表型数据的显示,因为一般这样的数据字段比较类似,字段数量也比较少,8个属性可以应对。

 

代码实现

 

定义实体类,

实现填充数据的help
定义数据层
定义业务逻辑层
定义UI层

 

 

Code

 

 

简化的写法

 

 

private void MyLoadData()
        {
            TitleBase[] Group_topic ;

            
//                            ID            URL      Title   AddedDate Introduction  Other1  Other2  Other3
            string sql = "select top 10 TopicURL,GroupURL,Topic,  SendDate,  Group,       View,   Author, AuthorURL from cmt_Group_topic order by SendDate desc "//SQL语句略
            
            Group_topic 
=  LoadTitleBase(10, sql);

            
//逻辑处理

            
//数据绑定

        }

 

 

直接在.aspx.cs文件里面写上面的代码。

 

 SQL语句的处理。
SQL语句可以放在一个单独的类里面统一管理,也可以放在xml文件里面动态加载。这样是不是变成了传说中的依赖注入呢?

 

 

 

2