反思以前对“多对多”关系处理的设计
很多公司的笔试题都喜欢考这一道:有n个供货商和m种货品,每个供货商可能供应多种货品,每种货品也有可能由多个供货商供应,建立相应的数据库表维护供应商和货品以及他们的供货关系。
这类多对多问题其实在真实的系统中很常见,比如用户--角色,角色--权限,用户--用户组等等。对接触过类似的系统的人这当然不是问题,三个表,套路来的嘛。
当然有其他方式,比如供货商表中添一个货品列表字段,用xml或者约定的格式保存货品列表,但是这样一是查询更新效率很低(查询的时候要解析数据,更新的时候要解析并重新组合数据,反向查找的时候更低效),二是没有办法建立外键约束。对第反向查找的问题,如果我们允许数据冗余并且有信心维护好数据一置性的话可以同时在货品表中添加一个供货商列表字段。但是除了效率、约束(以及冗余,假如它也算问题的话)之外,还有个最重要的问题我们忽略了:这样的设计不优雅。
但是在数据库表之外,我们做系统的时候是怎么处理类对应关系的呢?我没有看到过别人的设计,但是在我的系统中使用的其实是类似于上面说的第二种方式,比如在用户对象中有一个角色列表字段,存储了与用户关联的角色。当然解析数据带来的效率问题不存在了,因为角色信息是以原始方式保存的;数据一置性问题也由数据库解决。系统工作的很稳定也很快,但是我们一直忽略了这个问题:这样的设计优雅吗?我认为我们可以做的更好。
假如我们象设计数据库表一样的设计系统,系统本来可以做成这样子的:
(Database)
tb_user tb_user_role tb_role
------------------------------------------------------------------------------------
UserDAO UserRoleDAO RoleDAO
UserMng UserRoleMng RoleMng
这样user的model对象里面就不需要维护role列表了。我们需要访问关联关系的时候可以通过UserRoleMng来获得列表,如果我们需要访问role对应的user列表也一样的简单(在原来的设计中要遍历全部user)。
更进一步,我们把对这种多对多关联关系的处理抽取出来,做一个基类:
abstract class AbstractRelationManager{
protected void init();
protected void addRelation(id1,id2){
...
}
protected void deleteRelation(id1,id2)
...
}
protected List getId1ListById2(id2){
...
}
protected List getId2ListById1(id1){
...
}
}
然后UserRoleMng、RolePermissionMng或者其他类似的关系管理器都可以继承自AbstractRelationManager并由超类处理关联查询的逻辑:
class UserRoleMng{
public void addRelation(userId,roleId){
super.addRelation(userId,roleId);
}
...
}
如果高兴还可以这样包装
class UserRoleMng{
public void addRelation(user,role){
super.addRelation(user.getId,role.getId);
}
...
}
这样的设计看起来比原来的要好一点了,但是有一个问题是,按我的经验AbstractRelationManager很可能需要使用模版方法模式(templet method)来实现,这样不可避免的会违反依赖倒易原则(DIP)并降低代码的可读性,与我们的初衷有些背离。当然templet method也可以用代理类来代替,但是这样的实现在我看来不但复杂而且更不优雅。另一种代替的方式是不采用templet method而把对模版方法的调用实现在每个子类中,这样要求每个子类严格的符合编码约定,而且带来了拷贝代码的臭味。相比之下,我还是宁可选择在必要的时候templet method。
最后,我们有可能需要处理更复杂的关系,比如:
group------group-------user
| |---------user
| group-------group------user
| | |---------user
| |---------user
|---------user
user-group关系的一方,group是具有递归的层次结构的对象。这中情况下我们大概还需要从AbstractRelationManager继承一个新的抽象类
abstract AbstractRecurveRelationManager AbstractRelationManager{
protected List getId1ListById2Recurve(id2){
//递归方法id2对应的对象的子树并获得所有树节点的id1列表。
}
}
分享到:
相关推荐
总结部分,作者可能对整个项目的完成情况、遇到的问题及其解决方案、系统的优势和局限性进行了反思。参考文献列出了项目开发过程中引用的相关资料,致谢则表达了对指导老师和相关人员的感谢。 总的来说,这个基于...
- **求生存阶段/生存关注阶段**:初任教师主要关注如何适应新环境,处理好与学生、同事之间的关系。 - **入职阶段/生涯前阶段**:这个阶段的教师正在逐渐熟悉教育教学的基本要求和规范,努力提高自己的教学能力。 ...
2. **代沟的阶段特征**:课程提到了8岁以前的崇拜期,8岁至21岁的逆反期,以及21岁以后的理解期,这三个阶段反映了孩子对父母态度的变化,这有助于解释为何青少年时期与父母的冲突较多。 3. **代沟产生的直接原因**...
- 情感态度和价值观目标:培养对父母的尊重和理解,对自我行为的反思,以及对正确行为的欣赏和错误行为的悔改。 4. **教学重难点**: - 教学重点:正确理解并接受父母的关爱和教育方式。 - 教学难点:理解代沟的...
LCA是对一个产品(或服务)从原料获取到废弃处理的全过程进行环境影响评估的方法,它关注建筑在整个生命周期中的环境足迹,推动了"CO2减排"和"生物多样性设计"等全面环保设计理念的发展。 绿色建筑的发展历程反映了...
内容中提到,以前的工作速度过快,缺乏深思熟虑,导致细节处理不足。改进方法是放慢步伐,追求完美,同时不断学习和实践,寻找个人独特的设计风格。这提示我们在制作PPT时,不仅要有创新思维,还要注重细节和逻辑性...
初创时期(20世纪20年代以前)的特点是教育心理学多以普通心理学的原理解释实际的教育问题。实验教育心理学时期(20世纪20年代至40年代)则强调科学的实验方法来解决教育问题,如美国教育心理学家桑代克提出的条件...
别心急,设计C++的class和函数时,多考虑可复用性、可扩展性、可维护性 - **解析**:良好的设计是写出高质量代码的基础,应从长远的角度思考问题。 以上是关于C++编程开发学习的50条建议的详细解析,希望能帮助您...
【教育理论综合知识】是教师招聘考试中的一个重要科目,涵盖了教育法律法规、教育...以上是对教师招聘考试题库中部分题目的详细解答,涵盖了教育法律、心理、教育方法等多个方面,展现了教育理论综合知识的广度和深度。
试卷的设计旨在考察学生对于道德规范、家庭关系、社会公德、传统文化以及资源节约等核心知识点的理解和应用。 在填空题部分,涉及到的知识点包括: 1. 勤俭节约是中华民族的传统美德,学生需要明白这一价值观,并...
把C++当成一门新的语言学习(和C没啥关系!) - **解析**:虽然C++是从C语言发展而来的,但两者之间有着本质的区别。C++引入了类、模板、异常处理等面向对象特性,这些特性在C语言中是没有的。因此,在学习C++时应...
教育心理学的初创时期大致可以追溯到20世纪20年代以前,随着心理学作为一门独立学科在1879年由冯特建立,教育心理学逐渐形成和发展。成熟时期则是在20世纪60年代到70年代末,这一时期,教育心理学开始注重实际教育...
文章编号:1672-5913(2019)09-0042-09 中图分类号:G642 周傲英:新工科的影响及重新审视和反思 相比新工科的发展,当前出现了新农科、新文科、 新医科、新商科等,现在讲这么多,但最早讲的都是 新工科。...
- 定量研究利用数学模型和统计分析等方法,通过对数据的收集和分析来研究事物的数量关系。 17. **学前教育科研方法的学科归属**: - 作为教育科学的一个分支,学前教育科研方法不仅关注学前教育领域的具体问题,...