对于一个多类型的用户的应用,我们首先想到的是对用户进行抽象。
但是抽象的根据是什么? 行为!我们要根据不同类型的用户的行为来进行抽象。
我们仔细想想在一个多用户系统的应用,不同的用户之间的区别在于什么?
1. 具有共同行为,但做法不同 (学生,老师都可以查看成绩,但学生只能看到自己的)
2. A有的行为 B 没有 (老师可以修改成绩,学生不能)
看到了学生老师这些字样,很容易就让我们去建立一个又一个Class,如下图所示:
对象不是名词。 如何建立和区分一个对象,最根本的在于它的行为,为什么你要建立一个老师类型?什么叫老师?老师和学生的区别在什么?这是依赖于User的具体行为的。
那么这个对象的体系如何建立?
现在我们有以下的需求:
1. User有一系列的行为。 (查成绩,改成绩)
2. User的某些行为根据角色的不同,做法也有所不同。 (查成绩)
3. 不同角色的User可能有其他角色所没有的行为。 (改成绩)
4. 最终我们要创建并使用User
那么我们就需要在满足上面需求的情况下建立User的体系结构。下面我们一点一点的来说明。
第一点说明不是一个行为,如果只有一个行为,那么我们从User继承老师学生类,通过简单的多态就可以实现他们的区分。也就是说如上图所示的类结构就已经满足我们的需求。但是通常情况下User是不会只具有一个行为的。
第二点说明对应于一个行为可以有两种做法,那么我们将这个行为单独抽成一个类,通过这个类来实施这个行为,然后不同的角色通过继承这个类来实现多态。
第三点和第二点相似,是它的一个特殊情况。在继承的时候我们只要给一个空的实现就可以了。这其中包含了(null object模式)
如果以上的话让你不是很清楚的话,就看一下下面的UML图吧:
实现代码如下
namespace TestStack
{
public class User
{
public void SetUpdateImpl(IUpdateImpl iu)
{
updateImpl=iu;
}
public void SetQuertImpl(IQueryImpl iq)
{
queryImpl=iq;
}
protected void Update()
{
updateImpl.Update();
}
protected void Query()
{
queryImpl.Query();
}
///
///<link>aggregationByValue</link>
private IQueryImpl queryImpl;
///
///<link>aggregationByValue</link>
private IUpdateImpl updateImpl;
}
Query
Update
}
类结构图已经做出来了,那么第四点需求如何实现呢?目前的结构为第四点需求的实现,打下了良好的基础.
在建立好多用户的类结构之后,自然要解决最后的问题:创建并使用User。
在随风的随笔中提到了注册,但是显然我们不应该将注册作为用户的主要行为,用户的责任不应该是为了注册吧。既然如此,那么IRegister这个抽象就显的不太合理。
Wayfarer在他的评论中提到了用一个wrapper来为用户实现IRegister,似乎是一个解决方法。你可以在看完本文后,再来比较一下我的方案和wayfarer的方案。
其实注册这个问题是一个创建User对象的问题,创建自己的工作当然应该交给别人来完成比较好。所以实现IRegister之类的方法并不是一个好的选择,那么交给谁来完成呢?既然想不到,就交给一个创建对象吧。
这个对象的任务就是创建一个User。你想到了什么?其实这就是一个Command对象。将一个请求封装成一个对象,这是Command模式的基本特征。而且在这里command模式还可以让我们实现事务处理。
那么我们如何创建一个User,比如我们要创建一个具有老师权限的User。用代码说明问题:
namespace TestStack
{
public interface ICommand
{
void Execute();
}
public class RegisterUserCommand:ICommand
{
public void Execute()
{
User user=new User();
IUpdateImpl iu=GetUpdateImpl();
IQueryImpl iq=GetQueryImpl();
user.SetQuertImpl(iq);
user.SetUpdateImpl(iu);
//put this user into database
}
protected virtual IUpdateImpl GetUpdateImpl()
{
return new StudentUpdate(); // create a student default
}
protected virtual IQueryImpl GetQueryImpl()
{
return new StudentQuery(); // create a student default
}
}
public class RegisterTeacherCommand :RegisterUserCommand
{
protected override IUpdateImpl GetUpdateImpl()
{
return new TeacherUpdate(); // create a teacher
}
protected override IQueryImpl GetQueryImpl()
{
return new TeacherQuery(); // create a teacher
}
}
}
如果想创建一个可以查看所以成绩而不能修改成绩的用户呢(比如校长)?很简单
{
protected override IUpdateImpl GetUpdateImpl()
{
return new StudentQuery(); // create a student
}
protected override IQueryImpl GetQueryImpl()
{
return new TeacherQuery(); // create a teacher
}
}
在前面的类结构的基础上,只要复写两个Get方法你就可以任意组合出不同权限的User。
由此我们得到了一个很容易扩展的用户结构。
这个例子是从现实的问题中派生出来的,其实在不知不觉中已经运用到了很多的模式,设计模式就是用来解决问题的,知道如何使用它们才是学习模式的关键,希望本文可以让你有所体会。