当前位置: 代码迷 >> C# >> 面向对象设计类的偏题
  详细解决方案

面向对象设计类的偏题

热度:61   发布时间:2016-05-05 05:02:39.0
面向对象设计类的难点?
例:一个Task可以分配多个Employee,Employee有很多派生类:Manager、Assistant等。
       那么此时的Task类该如何面向对象设计,Task类是包含Employee集合吗?
       如果包含Employee集合,那么查询各派生类的信息来自不同的表,又该如何查询呢?
------解决思路----------------------
可以为Employee设计一个接口,Task中包含接口,和实际的Employee具体类分离
------解决思路----------------------
如何查询取决与你如何填充。当然你的问题和面对对象设计没有什么关系!!这是ORM滴职责
------解决思路----------------------
如果你使用传统的关系型数据库来保存数据,那么基本上没有任何一款关系数据库能够像NoSQL 那样去表示记录里边有一个集合字段的概念,所以你就真的需要建立一个“任务执行者”数据库表,它有两个外键分别引用到Task的主键跟Employee的主键。

关于查询,这本身就生一个业务操作程序。一个业务查询可能设计5个数据库表的10个关系运算,甚至对查询中间结果数据在内存中也做一些计算操作,最后才返回调用者需要的数据实体集合,这也完全是正常的。比如说你查询某“单个”流程的所有执行情况,返回为多个“节点”层叠链接数据结构(一个节点能并发地触发多个子节点,子节点再并发地触发多个子节点),此节点信息不但是有Task数据库表记录的信息,而且在每一个节点中都直接有一个属性是Employee[]数组类型的表示任务执行者的详细信息,这种数据实体是完全可以的,这是在你进行查询之后,在内存中再产生和填充完整的数据实体。

但是查询结果数据结构,在设计数据库结构时通常并不能傻傻地直接对应。如果为了对应而过度冗余,那么当你保存和修改数据时就需要花费大量时间去维护冗余的数据关联与索引的一致性。所以数据库表的结构跟数据实体定义往往是不能照搬的。所以 EF 之类的非常高级和强大的 DAL 只是一个比较低级的建模和查询工具,它只能帮你解决“数据库表与内存对象的转换”问题,而如果你的业务查询结果所需要的数据结构高于数据库的表达能力范围(例如你希望Task包含Employee,同时这也不产生冗余),那么就需要自己写查询程序,而 EF 等工具都帮不了你。
------解决思路----------------------
用代码来说比较简单,类似于这样的查询
节点 查询工作流节点(string 工作流编号)
{
    节点  x = ExecuteQuery("select top 1 * from [table 工作流] where sid ='" + 工作流编号 + ' and [是顶级节点]=1");
    填充子节点(new 节点[]{x});
    return x;    //一个工作流只有一个顶部节点,返回这个节点
)

void 填充子节点(节点[] nodes)
{  
    foreach(var x in nodes)
    {
      Employee[] u= ExecuteQueryEmployee("select e.* from Employee as e " +
                   "inner join [执行者] as p on e.id=p.employee where p.task='"+ y.Id +"'");
      x.执行人= u;
      节点[] y = ExecuteQuery任务("select * from [table 任务] where parent_id='" + x.Id +"'");
     if(y !=null && y.Length>0)
     {
           填充子节点(y);      
     }
    x.Children = y;
  }
}


一个业务查询,并非简单的数据库查询,你需要在内存中使用一定的算法来生成需要返回的业务实体对象。各种生成算法,才是你所关心的“查询”问题。而只会sql语句之类的低级的语法,只能完成课堂作业,而往往不能完成真正开发需求。


------解决思路----------------------
代码中变量写错了,另外假设你的sql数据库没有对inner join自动索引优化机制的话也需要更明确地指明“哪一个表优先”:

节点 查询工作流节点(string 工作流编号)
{
    节点  x = ExecuteQuery("select top 1 * from [table 工作流] where sid ='" + 工作流编号 + ' and [是顶级节点]=1");
    填充子节点(new 节点[]{x});
    return x;    //一个工作流只有一个顶部节点,返回这个节点
)
 
void 填充子节点(节点[] nodes)
{  
    foreach(var x in nodes)
    {
        Employee[] u= ExecuteQueryEmployee("select e.* from [执行者] as p inner join Employee as e " +
                   "on p.employee=e.id where p.task='"+ x.Id +"'");
      x.执行人= u;
      节点[] y = ExecuteQuery任务("select * from [table 任务] where parent_id='" + x.Id +"'");
      x.Children = y;
      if(y !=null && y.Length>0)
      {
           填充子节点(y);      
      }
   }
}