﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-我爱oo,我爱java</title><link>http://www.blogjava.net/oofrank/</link><description>交流blog    QQ:421057986  &lt;a href="http://www.donews.net/oofrank"&gt;oofrank@donews&lt;/a&gt;</description><language>zh-cn</language><lastBuildDate>Sun, 03 May 2026 04:37:37 GMT</lastBuildDate><pubDate>Sun, 03 May 2026 04:37:37 GMT</pubDate><ttl>60</ttl><item><title>DAO-持久层-领域对象-贫血模型</title><link>http://www.blogjava.net/oofrank/archive/2006/04/10/40337.html</link><dc:creator>兼听则明</dc:creator><author>兼听则明</author><pubDate>Mon, 10 Apr 2006 14:21:00 GMT</pubDate><guid>http://www.blogjava.net/oofrank/archive/2006/04/10/40337.html</guid><wfw:comment>http://www.blogjava.net/oofrank/comments/40337.html</wfw:comment><comments>http://www.blogjava.net/oofrank/archive/2006/04/10/40337.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/oofrank/comments/commentRss/40337.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/oofrank/services/trackbacks/40337.html</trackback:ping><description><![CDATA[
		<a href="http://blog.donews.com/ooFrank/archive/2006/04/10/821788.aspx">原文</a>
		<br />
		<br />
		<br />关于"贫血模型"的讨论几乎没有停止过,在openfans.org的开发过程中,我们也讨论了很久,我觉的有很多东西应该记下来:<br />明确一下意思先:<br />DAO:数据操作对象,会操作数据库<br />持久层:能提供对象持久化服务的一系列组件或服务<br />领域对象:描述领域模型的对象,是通过业务分析进行系统建模的产物<br />贫
血模型:就是domain
object只有属性的getter/setter方法的纯数据类，所有的业务逻辑完全由一个所谓的Manager来完成(又称
TransactionScript)，这种模型下的domain object被Martin Fowler称之为“贫血的domain
object”<br />常见的类基本结构如下:<br />一个业务数据类叫做Item，<br />一个DAO接口类叫做ItemDao<br />一个DAO接口实现类叫做ItemDaoHibernateImpl<br />一个业务逻辑类叫做ItemManager(或者叫做ItemService).<br /><br />观察上面的几个类很容易发现问题:<br />1:Item和ItemManager实际是操作与数据的关系,实际完成的就是经典OO中的一个对象的能力;<br />2:当有许多Item时 类组变得很庞大,产生很多 xxxDao xxxImpl xxxManager 其中包含大量重复代码;<br />按&lt;&lt;重构&gt;&gt;的观点,上述代码存在以下臭味:<br />1:重复的代码   xxxDao xxxImpl xxxManager(通常)<br />2:霰弹式修改，一个变化影响多个类,类之间不够高内聚 item变化--&gt;Dao,Impl,Manager均要变动<br />3:依恋情结,两个类之间互相作用过多 item&lt;-&gt;Manager<br />4:平行继承体系,当增加一个新类时总是要增加另一个类<br />5:夸夸其谈未来性,在没有任何暗示的情况下考虑扩展  Dao,实际HibernateImpl可能n年内是唯一的Dao实现<br />6:纯稚的数据类，只有数据的类  item<br /><br />我觉的 贫血模型 是系统分析设计方向性错误的产物:<br />1:没有进行领域建模---以数据表结构为中心,而不是业务模型为中心的思考方式,使设计人员选择Item为考虑问题的出发点<br />2:将DAO与持久层混淆---我们需要的一种持久化服务,DAO紧紧是提供数据操作能力而已,Hibernate是一种高级的服务(他已经包含了DAO,而不是相反),已经完成了所有的持久层服务.<br />3:过于强调低偶合---将一些本来一些提供单一职责的内容分散在多个单元中使 客户端 依赖更多的接口,而忘记了高内聚原则.<br />4:Spring的能力限制---由于Spring现阶段不支持对于领域模型的服务注入,使设计人员将操作和数据分开,并将领域变为DataOnly的.<br />  (Spring2.0将在很大程度上解决这个问题)<br />  <br />我认为良好的解决方案:<br />  首先领域建模,建立领域模型--&gt;合并前面所说的Item和ItemManager成为 domainItem;对于数据库服务,<br />  1:如果考虑领域层包含数据操作能力,则建立DAO并选择其它好的DAO方案比如IBATIS或Hibernate之类的组件;<br />  2:如果考虑将数据库(或其他存储界质)存储考虑在领域之外成为持久层,<br />      a:则或者对持久层框架同时建模,同时选择合适的组件为持久层服务提供存储服务(包括DAO--亦可选择IBATIS/Hibernate组件),<br />      b:或者直接使用Hibernate/JDO等框架实现持久化服务,领域层直接使用持久层服务,对领域对象进行持久化和反持久化(从持久层获取以持久化的对象).<br />  <br />其他:<br /> 
实际上,作为一种解决方案,所谓"贫血模型"的具体使用,并不会有太大的问题,尤其是使用一些代码生成工具或已经做好相应的基本框架时,很多软件的核心价
值都在于对客户提供的服务,而其内部则成为黑盒,我们只要合理的解决业务问题,就是"王道"了,对于代码的臭味,可以慢慢重构--这也需要成本呀.  <br /> <br />再其他:<br />有人说,我们的业务就是CRUD,领域模型只有数据类就足够了.我觉的这是搞错了方向------只有CRUD时,只有处理CRUD的那些类才有必要进行建模(他们才是领域模型),而所谓的User\Item等数据类则完全没有必要进行建模,更不要谈领域了.<br /><br />贫血之外:<br />实际上,软件\OO方法的外延大的很,更多问题与数据库存储无关(但也有贫血问题),所以建模才是根本,OO方法的原则才是我们必须掌握的.<br /><img src ="http://www.blogjava.net/oofrank/aggbug/40337.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/oofrank/" target="_blank">兼听则明</a> 2006-04-10 22:21 <a href="http://www.blogjava.net/oofrank/archive/2006/04/10/40337.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQLServer的一个bug</title><link>http://www.blogjava.net/oofrank/archive/2006/02/05/29643.html</link><dc:creator>兼听则明</dc:creator><author>兼听则明</author><pubDate>Sun, 05 Feb 2006 14:37:00 GMT</pubDate><guid>http://www.blogjava.net/oofrank/archive/2006/02/05/29643.html</guid><wfw:comment>http://www.blogjava.net/oofrank/comments/29643.html</wfw:comment><comments>http://www.blogjava.net/oofrank/archive/2006/02/05/29643.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/oofrank/comments/commentRss/29643.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/oofrank/services/trackbacks/29643.html</trackback:ping><description><![CDATA[SQLServer一个bug终于被我碰上了<br><br>我有一个表使用字符类型存储数字值,想进行汇总计算：<br><br>sum(case when isnumeric(FieldName)=0 then 0 else cast&nbsp; (FieldName as numeric) end)<br>简单试了一下没有问题，可是今天数据中有一个 ’2.1234567E7‘&nbsp; isnumeric返回1 cast  返回错误<br><br>呜呜。。。。<br>怎么办......<img src ="http://www.blogjava.net/oofrank/aggbug/29643.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/oofrank/" target="_blank">兼听则明</a> 2006-02-05 22:37 <a href="http://www.blogjava.net/oofrank/archive/2006/02/05/29643.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Quartz要注意的一个问题</title><link>http://www.blogjava.net/oofrank/archive/2006/01/23/28979.html</link><dc:creator>兼听则明</dc:creator><author>兼听则明</author><pubDate>Sun, 22 Jan 2006 16:52:00 GMT</pubDate><guid>http://www.blogjava.net/oofrank/archive/2006/01/23/28979.html</guid><wfw:comment>http://www.blogjava.net/oofrank/comments/28979.html</wfw:comment><comments>http://www.blogjava.net/oofrank/archive/2006/01/23/28979.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/oofrank/comments/commentRss/28979.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/oofrank/services/trackbacks/28979.html</trackback:ping><description><![CDATA[<br>当设置一个Schedule的startDate早于 new Date()，并且调度周期又触发于startDate和new Date()之间时，就会立即触发当前job。简单的解决方式是将startDate设为new Date().<br><img src ="http://www.blogjava.net/oofrank/aggbug/28979.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/oofrank/" target="_blank">兼听则明</a> 2006-01-23 00:52 <a href="http://www.blogjava.net/oofrank/archive/2006/01/23/28979.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用abator自动生成ibatis代码的经验  及碰到的问题的解决方案</title><link>http://www.blogjava.net/oofrank/archive/2006/01/21/28874.html</link><dc:creator>兼听则明</dc:creator><author>兼听则明</author><pubDate>Sat, 21 Jan 2006 05:51:00 GMT</pubDate><guid>http://www.blogjava.net/oofrank/archive/2006/01/21/28874.html</guid><wfw:comment>http://www.blogjava.net/oofrank/comments/28874.html</wfw:comment><comments>http://www.blogjava.net/oofrank/archive/2006/01/21/28874.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.blogjava.net/oofrank/comments/commentRss/28874.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/oofrank/services/trackbacks/28874.html</trackback:ping><description><![CDATA[<font style="font-family: courier new;" size="3"><br>1:abator下载:<a href="http://ibatis.apache.org/abator.html">http://ibatis.apache.org/abator.html</a><br>2:将abator安装到eclipse中<br>3:此时可以新建一种文件类型：Abator for iBATIS Configuration File,建立一个<br>4:在　jdbcConnection 中设置要mapping的数据库的jdbc连接<br>&nbsp; classPathEntry 是你的jdbc driver类路径<br>5:javaModelGenerator,sqlMapGenerator,daoGenerator
分别设置　java dataObject、sql mapping文件和 DAO 接口、实现类的生成位置:targetPackage
目标包，targetProject：eclipse项目<br>6:daoGenerator 中可以设置属性　　type: ibatis 或　spring 指定生成的dao实现类是使用com.ibatis.dao.client.template.SqlMapDaoTemplate<br>还是<br>org.springframework.orm.ibatis.support.SqlMapClientDaoSupport<br>7: table 中 tableName　指定要处理的表名<br>　　可以有多个table<br>8:table中可以包含子元素　generatedKey: 使Insert方法可以返回值－－由指定的column　mapping<br>9:generatedKey中的sqlStatement属性可以是获取sequence的SQL，也可以是获取自增值的SQL<br>&nbsp; 比如:Oracle的 select theSequence.nextVal from dual<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLServer的 SELECT @@IDENTITY as&nbsp; column_name<br>10:保存文件，选中文件，右键菜单选择Generate iBATIS Artifacts! ok...<br></font><font size="3"><br style="font-family: courier new;"><br></font><span style="text-decoration: underline;"><span style="font-weight: bold;"><br><u>使用abtor生成的iBatis代码出现xml解析错误的解决方案</u><br></span></span>如果按上述方式生成的代码有xml解析错误：&nbsp; 请下载<a href="http://oofrank.bokee.com/inc/abator.rar.txt">这个</a><br><br>注意，该文件名为Abator.rar.txt实际是一个rar文件，只是上传服务器有文件类型限制 所以只好加了扩展名txt。<br>请去掉.txt后解压。<br><br>使用<br>org.apache.ibatis.abator.core_0.5.1.jar<br>替换调你的 eclipse\plugins 的同名文件 即可。<br><br>然后重新生成代码。 OK 应该可以咯....<br><br>我改了一点代码，需要可以留言。<img src ="http://www.blogjava.net/oofrank/aggbug/28874.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/oofrank/" target="_blank">兼听则明</a> 2006-01-21 13:51 <a href="http://www.blogjava.net/oofrank/archive/2006/01/21/28874.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在eclipse－plugin开发中碰到的怪问题：(eclipse 3.1.1 + wtp1.0)</title><link>http://www.blogjava.net/oofrank/archive/2006/01/20/28819.html</link><dc:creator>兼听则明</dc:creator><author>兼听则明</author><pubDate>Fri, 20 Jan 2006 10:45:00 GMT</pubDate><guid>http://www.blogjava.net/oofrank/archive/2006/01/20/28819.html</guid><wfw:comment>http://www.blogjava.net/oofrank/comments/28819.html</wfw:comment><comments>http://www.blogjava.net/oofrank/archive/2006/01/20/28819.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/oofrank/comments/commentRss/28819.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/oofrank/services/trackbacks/28819.html</trackback:ping><description><![CDATA[一个popupMenus Extensions，<br>objectContribution：objectClass*: org.eclipse.core.resources.IFile<br><br>在action的Class代码中：<br>public void selectionChanged(IAction action, ISelection selection) {<br>&nbsp;&nbsp; &nbsp;StructuredSelection ss = (StructuredSelection) selection;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.selectedFile==(IFile)ss.getFirstElement(); //此处抛出异常<br>}<br><br>上述代码的异常非常奇怪：<br>根据的的跟踪，ss.getFirstElement()返回值是File，该类实现了IFile接口，<br>而且我用 ss.getFirstElement().getClass().isAssignableFrom(IFile.class)返回是false；<br>真是奇怪！－－－有人知道为什么吗？<br><br>另外在实践eclipse plugin开发过程中也有几个心得：(肯定能用，但未必最佳)<br><br>1、如果开发plugin，所有的依赖库都要包含到 Plug-in Dependencies 中；而不能只是引入到工程中。<br>2、如何输出到console：<br>MessageConsole mc=new MessageConsole("****",null);<br>IConsole[] cs=new IConsole[1];<br>cs[0]=mc;<br>ConsolePlugin.getDefault().getConsoleManager().addConsoles(cs);<br>mc.activate();<br>PrintStream out=new PrintStream( mc.newOutputStream());<br>out.println("*******.");<br>3、如何获取依赖工程的输出路径：<br>selectedProject：当前工程－－－由用户选择<br>String[] ps= selectedProject.getRequiredProjectNames();&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br>IWorkspace w= selectedProject.getProject().getWorkspace();<br>for(int i=0;i&lt;ps.length;i++){<br>IResource r=w.getRoot().findMember(ps[i]);<br>try{<br>&nbsp;&nbsp; &nbsp;IJavaProject jp=new JavaProject((IProject)r,null);&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br>&nbsp;&nbsp; &nbsp;File source=new File(jp.getProject().getLocation().append(jp.getOutputLocation().removeFirstSegments(1)).toOSString());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //作你的事情.....<br>}catch(Exception e){<br>&nbsp; &nbsp;&nbsp; &nbsp;//不是javaProject&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br>&nbsp;&nbsp; &nbsp;e.printStackTrace();&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br>}&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br>4、如何使用进度Dialog：<br>Shell shell = new Shell();<br>ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell);&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br>IRunnableWithProgress thread = new SomeRunner(shell);&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<br>dialog.run(true, false, thread);<br>//=============================<br>private class SomeRunner implements IRunnableWithProgress {<br>&nbsp;&nbsp;&nbsp; public void run(IProgressMonitor monitor)throws InvocationTargetException, InterruptedException {<br>&nbsp;&nbsp; &nbsp;monitor.beginTask("一些信息", 数值-总工作量);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(;;){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 一些工作<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monitor.worked(数值-已完成工作量); //实际中，我得情况不太相符，不明白，但差不多 :(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monitor.setTaskName("一些信息");<br>&nbsp;&nbsp;&nbsp; &nbsp;// 一些工作&nbsp;&nbsp; &nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monitor.done();<br>&nbsp;&nbsp;&nbsp; }<br>}<br><br><br><img src ="http://www.blogjava.net/oofrank/aggbug/28819.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/oofrank/" target="_blank">兼听则明</a> 2006-01-20 18:45 <a href="http://www.blogjava.net/oofrank/archive/2006/01/20/28819.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring 中的异常悖论</title><link>http://www.blogjava.net/oofrank/archive/2006/01/19/28702.html</link><dc:creator>兼听则明</dc:creator><author>兼听则明</author><pubDate>Thu, 19 Jan 2006 13:45:00 GMT</pubDate><guid>http://www.blogjava.net/oofrank/archive/2006/01/19/28702.html</guid><wfw:comment>http://www.blogjava.net/oofrank/comments/28702.html</wfw:comment><comments>http://www.blogjava.net/oofrank/archive/2006/01/19/28702.html#Feedback</comments><slash:comments>19</slash:comments><wfw:commentRss>http://www.blogjava.net/oofrank/comments/commentRss/28702.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/oofrank/services/trackbacks/28702.html</trackback:ping><description><![CDATA[这个题目可能有些危言悚听,使用Spring的同行不用害怕，只是因为工作中碰到一些问题，才偶然想到这个问题。<br><br>首先要明确两个问题：<br>1、系统隔离原则：<br>&nbsp;&nbsp;&nbsp; 即系统间依赖应该是清晰的，不因为一个系统的故障影响其他系统，甚至整个系统。<br>2、简单应用习惯：<br>&nbsp;&nbsp;&nbsp; 普通程序员只会处理checked Exception,负责任的程序员会处理被调用的函数可能抛出的异常(根据源码或javadoc)<br>&nbsp; <br><br>我们碰到一个情况：<br>使用Spring的 init 特性初试化一个bean--一个使用Qutarz的调度程序。初试化过程中会抛出RuntimeException，从而造成Spring容器的整个初试化失败。首先我们修改了程序，捕获了所有异常，随后在编码指南中加入了一句话："所有使用Spring-init机制初试化的类必须在init中捕获所有异常：Exception"。因为只有如此才能保证整个系统不会因为局部问题而完全瘫痪。<br><br>我觉得这是Spring对异常处理的一个悖论：<br>正方：底层异常都封装成Runtime的,经过几次包装---&gt;简单应用习惯---&gt;异常被抛出领域层(init场景下:由Spring处理)<br>反方: 一个类的失败可以造成整个系统的失败----&gt;Spring被RuntimeException宕掉----&gt;不符合系统隔离原则<br><br><br>当然充分的测试可能可以解决这个问题：但是要注意，测试只能证明系统有bug，不能证明系统没有bug。<br><br>解决方案：<br>1、好的设计习惯：将应该隔离的系统隔离开--&gt;使用不同的Spring配置文件.<br>2、接口层错误处理：在接口层应该尽量对可以处理的异常进行处理,然后以合理的方式传递给上层.<br><br><br>PS:在与人交互的系统中都应该给最终用户合理的错误提示，所以表现层应该尽量捕获非业务的RuntimeException给最终用户更好的操作感受。<br><img src ="http://www.blogjava.net/oofrank/aggbug/28702.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/oofrank/" target="_blank">兼听则明</a> 2006-01-19 21:45 <a href="http://www.blogjava.net/oofrank/archive/2006/01/19/28702.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>白马非马的面向对象分析</title><link>http://www.blogjava.net/oofrank/archive/2006/01/19/28649.html</link><dc:creator>兼听则明</dc:creator><author>兼听则明</author><pubDate>Thu, 19 Jan 2006 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/oofrank/archive/2006/01/19/28649.html</guid><wfw:comment>http://www.blogjava.net/oofrank/comments/28649.html</wfw:comment><comments>http://www.blogjava.net/oofrank/archive/2006/01/19/28649.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/oofrank/comments/commentRss/28649.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/oofrank/services/trackbacks/28649.html</trackback:ping><description><![CDATA[&nbsp;公孙龙，六国时辩士也。疾名实之散乱，因资材之所长，为“守白”之论。假物取譬，以“守白”辩，谓白马为非马也。<BR><BR>&nbsp; 以马作为进行问题域进行建模，已知存在白马这种类型。显然存在马的超类，并且马类包含一个属性－颜色，是否需要建立白马的子类呢？显而易见的是，当马的颜色属性是白色时，马的一些实例表达了一个白马的特殊实例群(由此我们可以得知：白马显然是马)，根据里氏替换原则，子类型必须能够替换掉它们的基类型，显然在分析了马的行为模式以后，我们可以得出结论：白马可以替换马。－－－－！难道真的要建立白马、黑马、X马的子类吗？<BR>&nbsp;&nbsp; 我认为可以从以下几方面进行分析。<BR><BR>1、类的职责(很大程度上等同于服务能力，操作方式):<BR>&nbsp;&nbsp;&nbsp; 设计一个类，首先要从类职责的分析入手，一个类要承担响应的职责，反过来说同样的职责应该由同样的类承担，否则会造成类泛滥，实例孤单的状况。如果领域内马和白马承担同样的职责，应该只建立马一个类，不应该只见树不见林，造成不抽象的类的产生。<BR><BR>2、类的行为模式(当类承担响应职责时，如何扩展)：<BR>&nbsp;&nbsp; 分析一个类要从类的行为模式入手，既然一个类要承担责任，其承担的责任表现方式是否一样呢呢？这就是她的行为模式，这也是里氏替换原则主要起作用的地方，如果两个类的职责相当，但行为模式不同则不能成为超类和子类的关系(比如"著名"的正方形不是长方形问题)。马类，作为超类，基于一些特殊的行为方式：吃草，跑...，对于白马她的行为模式和马是一样的，并没有不同，所以白马是马而非马的同根继承子类。对于长方形和正方形都是具有相同计算面积算法(职责)的四方形的子类。<BR><BR>－－－－－－－以下是关于此事的一些扩展分析－－－－－－－－－－－<BR>3、子类的产生：<BR>&nbsp;&nbsp;&nbsp; 何时需要产生子类呢：是对其父类的职责进行扩展，白马没有对父类的职责进行扩展，所以不是马的子类。首先子类要扩展超类，其次子类不能重写或废除超类的职责。<BR><BR>4、属性，状态的区别(类的域)<BR>&nbsp;&nbsp; 对于一些类，在状态不同时，会有不同的表现(状态机模式),所以，类的getter，setter的部分包含两种不同的特性，对于属于状态的部分，是我们要仔细分析的，而"白"马则属于属性类(非状态)的域, 一般来讲，一个类的实例要能提供相应的差异服务(由于状态不同)最好使用不变模式[生存周期状态不变]或状态机[生存周期有状态,但状态不由调用者控制]来实现。<BR><BR>5、抽象类和接口<BR>&nbsp;&nbsp; 由于java的单根继承特性，很多设计人员不敢定义抽象类为继承树根，一定要先定义马的接口，在建立抽象马，作为一种"准规范"无可厚非，但我认为这是不愿承担责任的表现，有行为的基类应该可以(必须?)从类定义开始，避免白马类(一旦马成为接口，白马的产生就更加"名正言顺"了)的出现.将来如果发生变化可以通过重构(导出接口和使用委托)，解决问题。 <BR><BR>6、对象的创建(组装)和使用应该分开<BR>&nbsp;&nbsp; 既然对象的状态如此重要，属性有有很大程度的不变性(白马在构造时就用该是白的，并且一生不变)，而骑马的人不必要求马的属性(!),所以，我们应该将马的构造和使用分开，使领域模型更清晰。使用一些Ioc容器，比如Spring就能很好的解决这些问题。<BR>&nbsp;<BR>7、分析问题的领域<BR>&nbsp;&nbsp; 说了这么多，有一个问题；如果有一个马的研究机构，专门对不同颜色的马进行专题研究，马的颜色可能会对马的行为有很大影响，例如战马如果是黄色(绿色，哈哈)更利于伪装，此时"白"可能是一个很关键的问题，颜色会影响到不同的伪装策略，此时将白马作为马的一个子类则是必须的！所以问题域不同，类的设计就不同，生活中的问题域比较清晰(生物学家和厨师对马的理解不同)，而软件建模时往往问题域混杂，这也是OO设计时比较困难的问题，所以分析问题域也是非常重要的设计问题。<BR><img src ="http://www.blogjava.net/oofrank/aggbug/28649.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/oofrank/" target="_blank">兼听则明</a> 2006-01-19 14:16 <a href="http://www.blogjava.net/oofrank/archive/2006/01/19/28649.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>我来也</title><link>http://www.blogjava.net/oofrank/archive/2006/01/19/28646.html</link><dc:creator>兼听则明</dc:creator><author>兼听则明</author><pubDate>Thu, 19 Jan 2006 06:05:00 GMT</pubDate><guid>http://www.blogjava.net/oofrank/archive/2006/01/19/28646.html</guid><wfw:comment>http://www.blogjava.net/oofrank/comments/28646.html</wfw:comment><comments>http://www.blogjava.net/oofrank/archive/2006/01/19/28646.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/oofrank/comments/commentRss/28646.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/oofrank/services/trackbacks/28646.html</trackback:ping><description><![CDATA[过去一直在donews混迹，听朋友推荐，此处高手云集，我也来凑凑热闹。:-)<img src ="http://www.blogjava.net/oofrank/aggbug/28646.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/oofrank/" target="_blank">兼听则明</a> 2006-01-19 14:05 <a href="http://www.blogjava.net/oofrank/archive/2006/01/19/28646.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>