对于领域模型这个概念,以前没有系统性的认识,只是根据经验,在设计系统时自发的在使用.尤其是O'R
Mapping技术成熟并且逐渐成为主流以后,这种模型化的设计方法在项目应用中体现得非常之多.
http://forum.javaeye.com/viewtopic.php?t=17579
在JavaEye的这个帖子中,大牛robbin总结了4种常见的领域模型,并分析了它们的优缺点.
1、失血模型
2、贫血模型
Service(业务逻辑,事务封装) --> DAO ---> domain object
3、充血模型
Service(事务封装) ---> domain object <---> DAO
4、胀血模型
domain object(事务封装,业务逻辑) <---> DAO
其中失血模型与贫血模型的主要区别在于“domain ojbect包含了不依赖于持久化的领域逻辑,而那些依赖持久化的领域逻辑被分离到Service层”。
好理解点的表达就是:如果某个逻辑不依赖于DAO来实现,就把它放到domain object里面,否则就在Service层实现。
讨论的结果,robbin倾向于使用基于良好设计和实现技术下的贫血模型。
这是以前做过的一个Java项目,典型的失血模型,db->bean里面是按照模块划分的实体bean,也就是只含有getter和setter的domain
ojbect。manager就是DAO,使用Hibernate做持久化。做业务逻辑和事务封装的Service封装在Action里面,使用Struts做MVC控制。
在使用里面,这种模型的domain ojbect和DAO实现基本都是靠工具来生成了。程序员可以更关注具体的Action实现。
另外,在最近的一个项目里面,我使用了一种比较畸形的设计模型,
domain ojbect的实现:
Domain
Object示例
1/**//// <summary>
2 /// This code is generated by PersistentObjectCodeGenerator
3 /// </summary>
4 [Table("Company_Info")]
5 public class Company_Info : DBO.Persistence.PersistentObject
6 {
7 [PrimaryKey(DbType.Int32, "CompanyId", AutoGenerated=true)]
8 private int _CompanyId;
9 [Column(DbType.AnsiString, "CompanyName", Nullable=false)]
10 private string _CompanyName;
11 [Column(DbType.AnsiString, "CompanyLogo")]
12 private string _CompanyLogo;
13 [Column(DbType.AnsiString, "CompanyArea")]
14 private string _CompanyArea;
15 [Column(DbType.String, "CompanyInfo")]
16 private string _CompanyInfo;
17 [Column(DbType.Int32, "CompanyAddedBy")]
18 private int _CompanyAddedBy;
19 [Column(DbType.Boolean, "IfChecked", Nullable=false)]
20 private bool _IfChecked;
21 [Column(DbType.Date, "CompanyPubDate", Nullable=false)]
22 private System.DateTime _CompanyPubDate;
23 [Column(DbType.Int32, "NewsCount")]
24 private int _NewsCount;
25 [Column(DbType.Int32, "BSCount")]
26 private int _BSCount;
27 [Column(DbType.Int32, "MSCount")]
28 private int _MSCount;
29 [Column(DbType.Int32, "BBSCount")]
30 private int _BBSCount;
31 public virtual int CompanyId
32 {
33 get
34 {
35 return _CompanyId;
36 }
37 set
38 {
39 // Use SetFieldValue to assign a column field, not the "=" operator
40 this.SetFieldValue("_CompanyId", value);
41 }
42 }
43 public virtual string CompanyName
44 {
45 get
46 {
47 return _CompanyName;
48 }
49 set
50 {
51 // Use SetFieldValue to assign a column field, not the "=" operator
52 this.SetFieldValue("_CompanyName", value);
53 }
54 }
55 public virtual string CompanyLogo
56 {
57 get
58 {
59 return _CompanyLogo;
60 }
61 set
62 {
63 // Use SetFieldValue to assign a column field, not the "=" operator
64 this.SetFieldValue("_CompanyLogo", value);
65 }
66 }
67 public virtual string CompanyArea
68 {
69 get
70 {
71 return _CompanyArea;
72 }
73 set
74 {
75 // Use SetFieldValue to assign a column field, not the "=" operator
76 this.SetFieldValue("_CompanyArea", value);
77 }
78 }
79 public virtual string CompanyInfo
80 {
81 get
82 {
83 return _CompanyInfo;
84 }
85 set
86 {
87 // Use SetFieldValue to assign a column field, not the "=" operator
88 this.SetFieldValue("_CompanyInfo", value);
89 }
90 }
91 public virtual int CompanyAddedBy
92 {
93 get
94 {
95 return _CompanyAddedBy;
96 }
97 set
98 {
99 // Use SetFieldValue to assign a column field, not the "=" operator
100 this.SetFieldValue("_CompanyAddedBy", value);
101 }
102 }
103 public virtual bool IfChecked
104 {
105 get
106 {
107 return _IfChecked;
108 }
109 set
110 {
111 // Use SetFieldValue to assign a column field, not the "=" operator
112 this.SetFieldValue("_IfChecked", value);
113 }
114 }
115 public virtual System.DateTime CompanyPubDate
116 {
117 get
118 {
119 return _CompanyPubDate;
120 }
121 set
122 {
123 // Use SetFieldValue to assign a column field, not the "=" operator
124 this.SetFieldValue("_CompanyPubDate", value);
125 }
126 }
127 public virtual int NewsCount
128 {
129 get
130 {
131 return _NewsCount;
132 }
133 set
134 {
135 // Use SetFieldValue to assign a column field, not the "=" operator
136 this.SetFieldValue("_NewsCount", value);
137 }
138 }
139 public virtual int BSCount
140 {
141 get
142 {
143 return _BSCount;
144 }
145 set
146 {
147 // Use SetFieldValue to assign a column field, not the "=" operator
148 this.SetFieldValue("_BSCount", value);
149 }
150 }
151 public virtual int MSCount
152 {
153 get
154 {
155 return _MSCount;
156 }
157 set
158 {
159 // Use SetFieldValue to assign a column field, not the "=" operator
160 this.SetFieldValue("_MSCount", value);
161 }
162 }
163 public virtual int BBSCount
164 {
165 get
166 {
167 return _BBSCount;
168 }
169 set
170 {
171 // Use SetFieldValue to assign a column field, not the "=" operator
172 this.SetFieldValue("_BBSCount", value);
173 }
174 }
175 }
这个是靠工具实现的实体类的代码,其中使用的Attribute是由于我使用的持久化实现不是从配置文件而是通过反射从元数据中读取的。
DAO实现:
1public class DBManager
2 {
3 public static bool Add(PersistentObject obj,string ConnStr)
4 {
5
6 }
7
8 public static PersistentObjectCollection Select(string sql,string ConnStr,System.Type objType)
9 {
10
11 }
12
13 public static bool Delete(PersistentObject obj,string ConnStr)
14 {
15
16 }
17
18 public static bool Update(PersistentObject obj,string ConnStr)
19 {
20
21 }
22
23 public static bool RunSql(string sql,string ConnStr)
24 {
25
26 }
27 }
这其实是个通用的DAO类,只是实现了通用的ADSU方法,外加一个RunSql实现。没有设计针对具体的domain
ojbect 实现具体业务逻辑的Service Objects,而是把剩下的事情,交给客户(web层代码消费者)了。
先别骂我,先看看这样的设计,是基于一种什么背景:
1、这是个所谓的web2.0网站,需要以半天为周期的进行系统更新与功能升级。
2、底层的基本数据结构不会发生变化。
3、在系统升级过程中,需要尽可能多的减少升级时间成本。
4、开发团队人员很少(1-2人),web层开发人员本身就参与底层库编码,因此,向web层开发人员隐藏底层对象没有意义。
5、新的用户功能是难以在设计时考虑周全的,甚至可能是每天都有增加或者变更。
个人认为在这种情况下,这种设计模型基本可以满足项目的需要了。需要注意的是:DAO和domain ojbect,都是独立的编译单元,在系统更新时,是无需考虑它们的。
结合我的项目经验,在认真读了JavaEye上的讨论之后,我感觉有些地方也有一些其它思路:
所有这些设计中,可能胀血模型是最符合OO的了。但是胀血模型应该也是实现成本最高,风险最大的一个模型。原因很简单,domain ojbect模型的不稳定带来的成本会非常痛苦。
因此,一个项目设计的好坏,是否不应该单纯看其设计思路是否符合OO原则,而应该考虑这个设计给项目带来的整体实施成本。相比之下,失血模型和贫血模型的domain
ojbect层非常稳定(基本可以使用工具生成了),而且编写相对很简单,复杂的业务逻辑交给Service去实现,大家分工明确,不同编码水平的coder可以很好的配合起来,完成项目的高质量实现。而胀血模型中,对domain
ojbect的编写显得非常重要,因为domain ojbect本身就包含了事务封装和业务逻辑。
另外大胆说一句,是否OO有时并不取决于我们的设计,而是要受技术条件的限制的。假设数据库本身可以提供的不是关系型数据而是对象集,假如我们不需要DAO编码,设计本身,会更加漂亮。
以上是我的一些体会和思考,记录下来。
另外向诸位交流几个问题:
1、在DAO实现中,有没有在DAO接口与DAO实现设计方面比较有经验的兄弟,介绍一下这样设计的好处?
我在项目中基本没有过DAO接口设计的经验,都是直接做实现,虽然使用接口可以使系统不依赖于某种具体的持久化实现方法,但是目前为止,还没有遇到过需要在项目中做更换持久化方法这种大手术的情况。况且额外增加一个接口层,还是蛮麻烦的。
2、在.net里面,大家常用的持久化工具有哪些?除了NHibernate?
|