对领域类的添加、删除、修改是每个系统的主要任务。下面介绍系统是如何在实现这些操作,以及如何利用泛型来减少重复代码。根据前面说到的业务层和数据访问层的关系。要添加,删除,修改信息,必需先在业务层定义业务层用到的数据访问层接口。比如要操作Student、Teacher对象,应该在业务层定义两个Dao接口。IStudentDao、ITeacherDao.下图4-1说明了这两个Dao接口的部分持久化操作的方法。
图4-1 IStudentDao,ITeacherDao类图
我们发现这些Dao接口都有类似的方法。只是参数和返回值不一样。如果要定义所有领域类的Dao接口。不得不到处的粘贴复制然后在修改每个方法的参数。为了减少代码的重复我们要充分利用.net 2.0的泛型机制。利用泛型我们首先定义一个泛型的接口。参数和返回值都现用泛型参数。然后在让具体的Dao接口继承泛型接口并给出类型参数就可以了。下图4-2是泛型接口的定义。
图4-2 IDao泛型接口类图
这样我们就不用到处粘贴修改Dao接口代码了。所有的具体Dao接口只要继承泛型的IDao并给出类型就自动有了基本的持久化操作方法定义。定义完业务层所需要的接口。接下来要在数据访问层实现这些接口。下面给出StudentDao和TeacherDao的代码。其中用两个持久化方法的代码来说明数据访问层是如何利用NHibernate来实现对Student、Teacher的持久化操作的。namespace ExaminationSystem.DAL
{
public class StudentDao:IStudentDao
{
private static ISessionFactory sessionFactory =
new Configuration().Configure().BuildSessionFactory();
public Student GetById(long id)
{
ISession session = sessionFactory.OpenSession();
return session.Get<Student>(id);
session.Close();
}
public void Save(Student student)
{
ISession session = sessionFactory.OpenSession();
ITransaction transation = session.BeginTransaction();
session.Save(student);
transation.Commit();
session.Close();
}
}
}
2.TeacherDao
namespace ExaminationSystem.DAL
{
public class TeacherDao:IStudentDao
{
private static ISessionFactory sessionFactory =
new Configuration().Configure().BuildSessionFactory();
public Teacher GetById(long id)
{ ISession session = sessionFactory.OpenSession();
return session.Get<Teacher>(id);
session.Close();
}
public void Save(Teacher teacher)
{ ISession session = sessionFactory.OpenSession();
ITransaction transation = session.BeginTransaction();
session.Save(teacher);
transation.Commit();
session.Close();
}
}
}
以上两个Dao实现方法只是为了简单的说明不是真实代码。我们发现两个Dao的具体实现也是如此的相似以至于很轻易就想到粘贴复制就可以写其他的Dao实现代码。但是一旦我们到处粘贴复制我们的代码时候我们就应该意识到我们应该来抽象。用抽象来消除重复。这里依然用泛型来解决问题。先定义一个抽象类来实现上面提到的泛型接口。并在抽象类中给出泛型版的Dao实现。最后具体的Dao继承抽象的Dao实现类并继承具体的Dao接口就可以自动实现所有的数据库操作了。下面分别用类图4-3和代码来说明是如何来实现的。
图4-3 数据库接口类图
下面是AbstractDao类实现。{
public abstract class AbstractDao<T>:IDao<T>
{
private static ISessionFactory sessionFactory =
new Configuration().Configure().BuildSessionFactory();
public T GetById(long id)
{
ISession session = sessionFactory.OpenSession();
return session.Get<T>(id);
session.Close();
}
public void Save(T obj)
{
ISession session = sessionFactory.OpenSession();
ITransaction transation = session.BeginTransaction();
session.Save(obj);
transation.Commit();
session.Close();
}
}
}
下面是更改后的StudentDao和TeacherDao
public class StudentDao : AbstractDao<Student>, IStudentDao { }
public class TeacherDao : AbstractDao<Teacher>, ITeacherDao{ }
这样数据访问层常见的逻辑全部都可以由AbstractDao一个类来实现。这样大大简化了测试和开发速度。
下面简要的说明系统的80%逻辑—CRUD。为什么说80%的逻辑呢?我们知道对于一个信息系统来说最多的操作就是对系统信息的添加,删除,修改,和查找。为了避免重复的介绍系统这方面的功能。我们这里就用学生用户信息的添加,删除,修改,查找来概括说明系统中其他的信息的CRUD。系统中每一个领域类都有一个Dao对象。和一个Service对象。领域类负责业务逻辑。Dao对象的职责就是对领域类对象的数据库操作。而Service对象则是对业务层的封装。三者之间的关系如下图4-4所示。
图4-4 各层主要类的类图
为什么要多出来一个Service那?因为Domain是系统最核心的东西。是业务逻辑的所在。所以Domain不应该依赖与其他的系统对象。但是有时候调用过Domain的业务逻辑后希望信息能够更新到数据库,所以必需要使用Dao来保存Domain,但是Domain不能依赖Dao所以就有了Service。我们可以在Service里包含与Domain相同的业务逻辑方法。然后将真正的业务逻辑委托给Domain。最后Service再调用Dao来更新数据库。又因为我们不想让表示层的UI直接依赖我们的数据访问层。Service的另一个作用就是包装Dao的方法。总之Service是业务层的外观类。它自己没有任何实现。它将业务逻辑委托给Domain,将数据操作委托给Dao.并协调所有的操作。下面是伪码说明
public class Domain
{
public void DoSomething()
{
//执行业务逻辑
}
}
2、Dao
public class Dao:IDao
{
public void Update(Domain obj)
{
//更新Domain对象到数据库
}
public Domain GetById(long id)
{
//查询数据库返回Domain对象
}
}
3、Service
public class Service
{
IDao dao=new Dao();
public void DoSomething(Domain obj)
{
obj.DoSomething();
dao.Update(obj);
}
public Domain GetById(long id)
{
return dao.GetById(id);
}
}
非常遗憾的事情是系统绝大多数的逻辑就是对Domain的CRUD操作。因为大部分Domain几乎都是数据类没有什么有趣的逻辑。所以本系统的Service类多数都是简单的对Dao对象的调用。
下面用伪码说明系统是如何操作Student对象的。
StudentService service=new StudentService();
//获取指定id学生
Student student=service.GetById(id);
//获取姓名是hanjie的学生
Student exmple=new Student();
example.Name=”hanjie”;
Student student=service.GetUniqueByExample (example);
//获得所有学生
IList<Student> students=service.GetAll();
//更新学生
service.Update(student);
//删除学生
service.Delete(student);
其他领域对象的操作和Student类的操作类似。