在亚历山大同学的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
public TitleBase[] LoadTitleBase(int Count,string sql)
{
//int Count = 10 ; //显示最新的10条小组话题
//string sql = "";
TitleBase[] tmp = new TitleBase[Count];
SqlConnection cn = new SqlConnection();
SqlCommand cm = new SqlCommand (sql,cn);
cn.Open();
SqlDataReader dr = cm.ExecuteReader();
int i = 0;
while (dr.Read())
{
tmp[i].ID = dr[0].ToString();
tmp[i].URL = dr[1].ToString();
tmp[i].Title = dr[2].ToString();
tmp[i].AddedDate = dr[3].ToString();
tmp[i].Introduction = dr[4].ToString();
tmp[i].Other1 = dr[5].ToString();
tmp[i].Other2 = dr[6].ToString();
tmp[i].Other3 = dr[7].ToString();
i++;
}
return tmp;
}
/// <summary>
/// 数据层
/// </summary>
/// <returns></returns>
public TitleBase[] Get_top10_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语句略
return LoadTitleBase(10, sql);
}
/// <summary>
/// 逻辑层
/// </summary>
/// <returns></returns>
public TitleBase[] Top10_Group_topic()
{
TitleBase[] tmp;
tmp = Get_top10_Group_topic();
//逻辑处理
return tmp;
}
/// <summary>
/// UI
/// </summary>
private void LoadData()
{
TitleBase[] Group_topic ;
Group_topic = Top10_Group_topic();
//数据绑定
}
}
简化的写法
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文件里面动态加载。这样是不是变成了传说中的依赖注入呢?