- 浏览: 10724 次
- 性别:
- 来自: 深圳
文章分类
最新评论
面向对象的思维方法 (转)
<p>我是从学习Java编程开始接触OOP(面向对象编程),刚开始使用Java编写程序的时候感觉很别扭,因为我早以习惯用C来编写程序,很欣赏C的简洁性
和高效性,喜欢C简练而表达能力丰富的风格,特别忍受不了Java运行起来慢吞吞的速度,相对冗长的代码,而且一个很简单的事情,要写好多类,一个类调用
一个类,心里的抵触情绪很强。
<br><br>
我对Java的面向对象的特性琢磨良久,自认为有所领悟,也开始有意识的运用OOP风格来写程序,然而还是经常会觉得不知道应该怎样提炼类,面对一个具体的问题的时候,会觉得脑子里千头万绪的,不知道怎么下手,一不小心,又会回到原来的思路上去。
<br><br>
举个例子,要发广告邮件,广告邮件列表存在数据库里面。倘若用C来写的话,一般会这样思考,先把邮件内容读入,然后连接数据库,循环取邮件地址,调用本机的qmail的sendmail命令发送。
<br><br>
然后考虑用Java来实现,既然是OOP,就不能什么代码都塞到main过程里面,于是就设计了三个类:
<br><br>
一个类是负责读取数据库,取邮件地址,调用qmail的sendmail命令发送;
<br>
一个类是读邮件内容,MIME编码成HTML格式的,再加上邮件头;
<br>
一个主类负责从命令读参数,处理命令行参数,调用发email的类。
<br><br>
把一件工作按照功能划分为3个模块分别处理,每个类完成一件模块任务。
<br><br>
仔细的分析一下,就会发现这样的设计完全是从程序员实现程序功能的角度来设计的,或者说,设计类的时候,是自低向上的,从机器的角度到现实世界的
角度来分析问题的。因此在设计的时候,就已经把程序编程实现的细节都考虑进去了,企图从底层实现程序这样的出发点来达到满足现实世界的软件需求的目标。
<br><br>
这样的分析方法其实是不适用于Java这样面向对象的编程语言,因为,如果改用C语言,封装两个C函数,都会比Java实现起来轻松的多,逻辑上也清楚的多。
<br><br>
我觉得面向对象的精髓在于考虑问题的思路是从现实世界的人类思维习惯出发的,只要领会了这一点,就领会了面向对象的思维方法。
<br><br>
举一个非常简单的例子:假使现在需要写一个网页计数器,客户访问一次页面,网页计数器加1,计数器是这样来访问的
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span>http:</span>
<span class="comment">//hostname/count.cgi?id=xxx</span>
<span> </span>
</span>
</li>
</ol>
</div>
<p>
<br><br>
后台有一个数据库表,保存每个id(一个id对应一个被统计访问次数的页面)的计数器当前值,请求页面一次,对应id的计数器的字段加1(这里我们忽略并发更新数据库表,出现的表锁定的问题)。
<br><br>
如果按照一般从程序实现的角度来分析,我们会这样考虑:首先是从HTTP GET请求取到id,然后按照id查数据库表,获得某id对应的访问计数值,然后加1,更新数据库,最后向页面显示访问计数。
<br><br>
现在假设一个没有程序设计经验的人,他会怎样来思考这个问题的呢?他会提出什么样的需求呢?他很可能会这样想:
<br><br>
我需要有一个计数器,这个计数器应该有这样的功能,刷新一次页面,访问量就会加1,另外最好还有一个计数器清0的功能,当然计数器如果有一个可以设为任意值的功能的话。
<br><br>
做为一个没有程序设计经验的人来说,他完全不会想到对数据库应该如何操作,对于HTTP变量该如何传递,他考虑问题的角度就是我有什么需求,我的业务逻辑是什么,软件应该有什么功能。
<br><br>
按照这样的思路(请注意,他的思路其实就是我们平时在生活中习惯的思维方式),我们知道需要有一个计数器类 Counter,有一个必须的和两个可选的方法:
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span>getCount(); </span>
<span class="comment">// 取计数器值方法</span>
<span> </span>
</span>
</li>
<li>
<span>resetCounter(); <span class="comment">// 计数器清0方法</span>
<span> </span>
</span>
</li>
<li>
<span>setCount(); <span class="comment">// 设计数器为相应的值方法</span>
<span> </span>
</span>
</li>
</ol>
</div>
<p>
<br><br>
把Counter类完整的定义如下:
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span class="keyword">public</span>
<span> </span>
<span class="keyword">class</span>
<span> Counter { </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">int</span>
<span> getCount(</span>
<span class="keyword">int</span>
<span> id); {} </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">void</span>
<span> resetCounter(</span>
<span class="keyword">int</span>
<span> id); {} </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">void</span>
<span> setCount(</span>
<span class="keyword">int</span>
<span> id, </span>
<span class="keyword">int</span>
<span> currentCount); {} </span>
</span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>
<br><br>
解决问题的框架已经有了,来看一下如何使用Counter。 在count.cgi里面调用Counter来计数,程序片断如下:
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span class="comment">// 这里从HTTP环境里面取id值</span>
<span> </span>
</span>
</li>
<li>
<span> ... </span>
</li>
<li>
<span>Counter myCounter = <span class="keyword">new</span>
<span> Counter();; </span>
<span class="comment">// 获得计数器</span>
<span> </span>
</span>
</li>
<li>
<span><span class="keyword">int</span>
<span> currentCount = myCounter.getCount(id);; </span>
<span class="comment">// 从计数器中取计数</span>
<span> </span>
</span>
</li>
<li>
<span><span class="comment">// 这里向客户浏览器输出</span>
<span> </span>
</span>
</li>
<li>
<span> ... </span>
</li>
</ol>
</div>
<p>
<br><br>
程序的框架全都写好了,剩下的就是实现Counter类方法里面具体的代码了,此时才去考虑具体的程序语言实现的细节,比如,在getCount()方法里面访问数据库,更新计数值。
<br><br>
从上面的例子中看到,面向对象的思维方法其实就是我们在现实生活中习惯的思维方式,是从人类考虑问题的角度出发,把人类解决问题的思维方式逐步翻译成程序能够理解的思维方式的过程,在这个翻译的过程中,软件也就逐步被设计好了。
<br><br>
在运用面向对象的思维方法进行软件设计的过程中,最容易犯的错误就是开始分析的时候,就想到了程序代码实现的细节,因此封装的类完全是基于程序实现逻辑,而不是基于解决问题的业务逻辑。
<br><br>
学习JDBC编程的经典错误问法是:“我怎样封装对数据库的select操作?”
<br><br>
面向对象的设计是基于解决业务问题的设计,而不是基于具体编程技术的设计。我不会去封装select语句的,我只封装解决问题的业务逻辑,对数据库的读取是在业务逻辑的编码实现阶段才去考虑的问题。
<br><br>
回过头看上面那个发广告邮件的例子,应该如何应用面向对象的思维方法呢?
<br><br>
对于一个邮件来说,有邮件头,邮件体,和邮件地址这三个属性,发送邮件,需要一个发送的方法,另外还需要一个能把所有邮件地址列出来的方法。所以应该如下设计:
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span>类JunkMail </span>
</span>
</li>
<li>
<span> </span>
</li>
<li>
<span>属性: </span>
</li>
<li>
<span> head </span>
</li>
<li>
<span> body </span>
</li>
<li>
<span> address </span>
</li>
<li>
<span>方法: </span>
</li>
<li>
<span> sendMail(); <span class="comment">// 发送邮件</span>
<span> </span>
</span>
</li>
<li>
<span> listAllMail(); <span class="comment">// 列邮件地址</span>
<span> </span>
</span>
</li>
<li>
<span> </span>
</li>
<li>
<span>用Java来表示: </span>
</li>
<li>
<span> </span>
</li>
<li>
<span><span class="keyword">public</span>
<span> </span>
<span class="keyword">class</span>
<span> JunkMail { </span>
</span>
</li>
<li>
<span> <span class="keyword">private</span>
<span> String head; </span>
</span>
</li>
<li>
<span> <span class="keyword">private</span>
<span> String body; </span>
</span>
</li>
<li>
<span> <span class="keyword">private</span>
<span> String address; </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> JunkMain(); { </span>
<span class="comment">// 默认的类构造器</span>
<span> </span>
</span>
</li>
<li>
<span> <span class="comment">// 从外部配置文件读邮件头和邮件体</span>
<span> </span>
</span>
</li>
<li>
<span> <span class="keyword">this</span>
<span>.head=...; </span>
</span>
</li>
<li>
<span> <span class="keyword">this</span>
<span>.body=...; </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span> </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">static</span>
<span> </span>
<span class="keyword">boolean</span>
<span> sendMail(String address); { </span>
</span>
</li>
<li>
<span> <span class="comment">// 调用qmail,发送email</span>
<span> </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span> </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">static</span>
<span> Collection listAllMail(); { </span>
</span>
</li>
<li>
<span> <span class="comment">// 访问数据库,返回一个邮件地址集合</span>
<span> </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>
<br>
当把JunkMail设计好了以后,再调用JunkMail类完成邮件的发送,将是非常轻松的事情。
<br><br>
如果说传统的面向过程的编程是符合机器运行指令的流程的话,那么面向对象的思维方法就是符合现实生活中人类解决问题的思维过程。
<br><br>
在面向对象的软件分析和设计的时候,要提醒自己,不要一上来就去想程序代码的实现,应该抛开具体编程语言的束缚,集中精力分析我们要实现的软件的业务逻辑,分析软件的业务流程,思考应该如何去描述和实现软件的业务。毕竟软件只是一个载体,业务才是我们真正要实现的目标。
<br><br>
但是在设计过程中,心里却往往在担心,如果我完全不去考虑程序代码的实现的话,那么我怎么知道我的设计一定合理呢?我怎么知道我设计的类、接口一定可以实现呢?所以经常可以看到的现象就是:
<br><br>
在设计过程中,虽然知道不能过早考虑代码实现,但是每设计一个类,一个接口,心里都要不知不觉的用自己熟悉的编程语言大概的评估一下,看看能否编出来,因此,一不小心,就会又回到按照程序功能实现的思路进行设计的老路上去了。
<br><br>
举个例子来说明,在做Web程序设计的时候,经常要遇到分页显示数据的情况。比如说需要把系统中所有的用户都列出来这样的功能。假设使用User
类来表示用户,增加用户addUser(),删除用户deleteUser(),查询所有用户listUsers()方法。而数据库中有一个user表,
一条记录是一个用户的信息。下面考虑一下User类的方法的实现:
<br><br>
addUser()和deleteUser()方法都好实现,就是对数据库增加记录和删除记录。对于listUsers()方法,其实就是对user表的select,取出一个记录集。但是该怎么从listUsers()方法中得到所有用户的列表呢?
<br><br>
一个方法调用的返回值只有一个,没有多个,所以很多情况下采用的办法就是返回值定义为集合类型,比如Vector。这样就可以在
listUsers()方法的具体代码实现的时候,从数据库依次取出一个个记录,插入到Vector里面来。在主程序里面,调用listUsers()方
法可以返回一个Vector,然后再对Vector遍历操作,就可以得到用户列表了。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span class="keyword">public</span>
<span> </span>
<span class="keyword">class</span>
<span> User { </span>
</span>
</li>
<li>
<span> </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">static</span>
<span> </span>
<span class="keyword">void</span>
<span> addUser(...); { </span>
</span>
</li>
<li>
<span> <span class="comment">// 数据库insert一条记录</span>
<span> </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span> </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">static</span>
<span> </span>
<span class="keyword">void</span>
<span> deleteUser(...); { </span>
</span>
</li>
<li>
<span> <span class="comment">// 数据库delete一条记录</span>
<span> </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span> </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> Vector listUsers(...); { </span>
</span>
</li>
<li>
<span> <span class="comment">// 数据库select结果放到一个集合里面</span>
<span> </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>这
样的设计基本合理,但是仍然有点小问题。因为在设计的时候,就考虑到了用Java的集合类Vector来实现对不定长数据集的存放,因而违反了面向对象设
计的一个原则:在设计的时候不应过早的考虑具体程序语言的实现。所以必须用抽象的方法,和具体实现无关的方法来表达业务逻辑。
<br><br>
我们知道,通常对具有集合特征的数据结构进行遍历通常可以使用next和hasNext方法,next实现取下一个用户,hasNext判断是否还有元素。 因此我们定义一个接口Iterator,这个接口中定义两个方法next和hasNext:</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span class="keyword">public</span>
<span> </span>
<span class="keyword">interface</span>
<span> Iterator { </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">boolean</span>
<span> hasNext(); {} </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> Object next(); {} </span>
</span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>
<br>
而User类的listUses方法返回值改为Iterator接口的实现类:
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span class="keyword">public</span>
<span> </span>
<span class="keyword">class</span>
<span> User { </span>
</span>
</li>
<li>
<span> ... </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> Iterator listUsers(); { </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span> ... </span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>
<br>
这样就把User类的设计和具体的实现方法分离开了,因为此时任何实现了next()和hasNext()方法的类都可以做为listUsers
的返回值,都可以被用来表达“用户列表”,而不仅仅可以使用Vector而已。比如,我可以用ArrayList来表达用户列表,因为ArrayList
也实现了Iterator,当然我也可以自己专门写一个类来存放用户列表,只要实现next()和hasNext()方法就行了。
<br><br>
这样在具体的编写代码的时候,程序员具有了最大的灵活性,可以根据具体的情况,采用不同的编程方法来存放用户列表。特别是降低了程序的耦合度,提高了程序的可移植性。对于上面那个JunkMail的listAllMail()方法也同样应该改为接口类型。
<br><br>
然后,在主程序里面就这样来使用User类的listUsers方法:</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span>User myUser = </span>
<span class="keyword">new</span>
<span> User();; </span>
</span>
</li>
<li>
<span>Iterator iterator = myUser.listUsers();; </span>
</li>
<li>
<span><span class="keyword">while</span>
<span> (iterator.hasNext();); { </span>
</span>
</li>
<li>
<span> iterator.next();; </span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>
<br>
这样就可以完全不用考虑程序代码实现了,从高层次上把功能抽象出来,定义成为接口,同时又可以把系统设计的很合理,完全根据业务的需求来进行设计。
<br><br>
结语
<br><br>
通过上面的几个例子的设计说明,使用面向对象的思维方法,其实是一个把业务逻辑从具体的编程技术当中抽象出来的过程,而这个抽象的过程是自上而下
的,非常符合人类的思维习惯,也就是先不考虑问题解决的细节,把问题的最主要的方面抽象成为一个简单的框架,集中精力思考如何解决主要矛盾,然后在解决问
题的过程中,再把问题的细节分割成一个一个小问题,再专门去解决细节问题。
<br><br>
因而一旦牢牢的抓住了这一点,你就会发现在软件设计和开发过程中,你自己总是会不知不觉的运用面向对象的思维方法来设计和编写程序,并且程序的设计和开发也变得不再那么枯燥,而一个合理运用面向对象技术进行设计和架构的软件,更是具备了思维的艺术美感。
</p>
和高效性,喜欢C简练而表达能力丰富的风格,特别忍受不了Java运行起来慢吞吞的速度,相对冗长的代码,而且一个很简单的事情,要写好多类,一个类调用
一个类,心里的抵触情绪很强。
<br><br>
我对Java的面向对象的特性琢磨良久,自认为有所领悟,也开始有意识的运用OOP风格来写程序,然而还是经常会觉得不知道应该怎样提炼类,面对一个具体的问题的时候,会觉得脑子里千头万绪的,不知道怎么下手,一不小心,又会回到原来的思路上去。
<br><br>
举个例子,要发广告邮件,广告邮件列表存在数据库里面。倘若用C来写的话,一般会这样思考,先把邮件内容读入,然后连接数据库,循环取邮件地址,调用本机的qmail的sendmail命令发送。
<br><br>
然后考虑用Java来实现,既然是OOP,就不能什么代码都塞到main过程里面,于是就设计了三个类:
<br><br>
一个类是负责读取数据库,取邮件地址,调用qmail的sendmail命令发送;
<br>
一个类是读邮件内容,MIME编码成HTML格式的,再加上邮件头;
<br>
一个主类负责从命令读参数,处理命令行参数,调用发email的类。
<br><br>
把一件工作按照功能划分为3个模块分别处理,每个类完成一件模块任务。
<br><br>
仔细的分析一下,就会发现这样的设计完全是从程序员实现程序功能的角度来设计的,或者说,设计类的时候,是自低向上的,从机器的角度到现实世界的
角度来分析问题的。因此在设计的时候,就已经把程序编程实现的细节都考虑进去了,企图从底层实现程序这样的出发点来达到满足现实世界的软件需求的目标。
<br><br>
这样的分析方法其实是不适用于Java这样面向对象的编程语言,因为,如果改用C语言,封装两个C函数,都会比Java实现起来轻松的多,逻辑上也清楚的多。
<br><br>
我觉得面向对象的精髓在于考虑问题的思路是从现实世界的人类思维习惯出发的,只要领会了这一点,就领会了面向对象的思维方法。
<br><br>
举一个非常简单的例子:假使现在需要写一个网页计数器,客户访问一次页面,网页计数器加1,计数器是这样来访问的
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span>http:</span>
<span class="comment">//hostname/count.cgi?id=xxx</span>
<span> </span>
</span>
</li>
</ol>
</div>
<p>
<br><br>
后台有一个数据库表,保存每个id(一个id对应一个被统计访问次数的页面)的计数器当前值,请求页面一次,对应id的计数器的字段加1(这里我们忽略并发更新数据库表,出现的表锁定的问题)。
<br><br>
如果按照一般从程序实现的角度来分析,我们会这样考虑:首先是从HTTP GET请求取到id,然后按照id查数据库表,获得某id对应的访问计数值,然后加1,更新数据库,最后向页面显示访问计数。
<br><br>
现在假设一个没有程序设计经验的人,他会怎样来思考这个问题的呢?他会提出什么样的需求呢?他很可能会这样想:
<br><br>
我需要有一个计数器,这个计数器应该有这样的功能,刷新一次页面,访问量就会加1,另外最好还有一个计数器清0的功能,当然计数器如果有一个可以设为任意值的功能的话。
<br><br>
做为一个没有程序设计经验的人来说,他完全不会想到对数据库应该如何操作,对于HTTP变量该如何传递,他考虑问题的角度就是我有什么需求,我的业务逻辑是什么,软件应该有什么功能。
<br><br>
按照这样的思路(请注意,他的思路其实就是我们平时在生活中习惯的思维方式),我们知道需要有一个计数器类 Counter,有一个必须的和两个可选的方法:
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span>getCount(); </span>
<span class="comment">// 取计数器值方法</span>
<span> </span>
</span>
</li>
<li>
<span>resetCounter(); <span class="comment">// 计数器清0方法</span>
<span> </span>
</span>
</li>
<li>
<span>setCount(); <span class="comment">// 设计数器为相应的值方法</span>
<span> </span>
</span>
</li>
</ol>
</div>
<p>
<br><br>
把Counter类完整的定义如下:
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span class="keyword">public</span>
<span> </span>
<span class="keyword">class</span>
<span> Counter { </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">int</span>
<span> getCount(</span>
<span class="keyword">int</span>
<span> id); {} </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">void</span>
<span> resetCounter(</span>
<span class="keyword">int</span>
<span> id); {} </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">void</span>
<span> setCount(</span>
<span class="keyword">int</span>
<span> id, </span>
<span class="keyword">int</span>
<span> currentCount); {} </span>
</span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>
<br><br>
解决问题的框架已经有了,来看一下如何使用Counter。 在count.cgi里面调用Counter来计数,程序片断如下:
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span class="comment">// 这里从HTTP环境里面取id值</span>
<span> </span>
</span>
</li>
<li>
<span> ... </span>
</li>
<li>
<span>Counter myCounter = <span class="keyword">new</span>
<span> Counter();; </span>
<span class="comment">// 获得计数器</span>
<span> </span>
</span>
</li>
<li>
<span><span class="keyword">int</span>
<span> currentCount = myCounter.getCount(id);; </span>
<span class="comment">// 从计数器中取计数</span>
<span> </span>
</span>
</li>
<li>
<span><span class="comment">// 这里向客户浏览器输出</span>
<span> </span>
</span>
</li>
<li>
<span> ... </span>
</li>
</ol>
</div>
<p>
<br><br>
程序的框架全都写好了,剩下的就是实现Counter类方法里面具体的代码了,此时才去考虑具体的程序语言实现的细节,比如,在getCount()方法里面访问数据库,更新计数值。
<br><br>
从上面的例子中看到,面向对象的思维方法其实就是我们在现实生活中习惯的思维方式,是从人类考虑问题的角度出发,把人类解决问题的思维方式逐步翻译成程序能够理解的思维方式的过程,在这个翻译的过程中,软件也就逐步被设计好了。
<br><br>
在运用面向对象的思维方法进行软件设计的过程中,最容易犯的错误就是开始分析的时候,就想到了程序代码实现的细节,因此封装的类完全是基于程序实现逻辑,而不是基于解决问题的业务逻辑。
<br><br>
学习JDBC编程的经典错误问法是:“我怎样封装对数据库的select操作?”
<br><br>
面向对象的设计是基于解决业务问题的设计,而不是基于具体编程技术的设计。我不会去封装select语句的,我只封装解决问题的业务逻辑,对数据库的读取是在业务逻辑的编码实现阶段才去考虑的问题。
<br><br>
回过头看上面那个发广告邮件的例子,应该如何应用面向对象的思维方法呢?
<br><br>
对于一个邮件来说,有邮件头,邮件体,和邮件地址这三个属性,发送邮件,需要一个发送的方法,另外还需要一个能把所有邮件地址列出来的方法。所以应该如下设计:
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span>类JunkMail </span>
</span>
</li>
<li>
<span> </span>
</li>
<li>
<span>属性: </span>
</li>
<li>
<span> head </span>
</li>
<li>
<span> body </span>
</li>
<li>
<span> address </span>
</li>
<li>
<span>方法: </span>
</li>
<li>
<span> sendMail(); <span class="comment">// 发送邮件</span>
<span> </span>
</span>
</li>
<li>
<span> listAllMail(); <span class="comment">// 列邮件地址</span>
<span> </span>
</span>
</li>
<li>
<span> </span>
</li>
<li>
<span>用Java来表示: </span>
</li>
<li>
<span> </span>
</li>
<li>
<span><span class="keyword">public</span>
<span> </span>
<span class="keyword">class</span>
<span> JunkMail { </span>
</span>
</li>
<li>
<span> <span class="keyword">private</span>
<span> String head; </span>
</span>
</li>
<li>
<span> <span class="keyword">private</span>
<span> String body; </span>
</span>
</li>
<li>
<span> <span class="keyword">private</span>
<span> String address; </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> JunkMain(); { </span>
<span class="comment">// 默认的类构造器</span>
<span> </span>
</span>
</li>
<li>
<span> <span class="comment">// 从外部配置文件读邮件头和邮件体</span>
<span> </span>
</span>
</li>
<li>
<span> <span class="keyword">this</span>
<span>.head=...; </span>
</span>
</li>
<li>
<span> <span class="keyword">this</span>
<span>.body=...; </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span> </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">static</span>
<span> </span>
<span class="keyword">boolean</span>
<span> sendMail(String address); { </span>
</span>
</li>
<li>
<span> <span class="comment">// 调用qmail,发送email</span>
<span> </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span> </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">static</span>
<span> Collection listAllMail(); { </span>
</span>
</li>
<li>
<span> <span class="comment">// 访问数据库,返回一个邮件地址集合</span>
<span> </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>
<br>
当把JunkMail设计好了以后,再调用JunkMail类完成邮件的发送,将是非常轻松的事情。
<br><br>
如果说传统的面向过程的编程是符合机器运行指令的流程的话,那么面向对象的思维方法就是符合现实生活中人类解决问题的思维过程。
<br><br>
在面向对象的软件分析和设计的时候,要提醒自己,不要一上来就去想程序代码的实现,应该抛开具体编程语言的束缚,集中精力分析我们要实现的软件的业务逻辑,分析软件的业务流程,思考应该如何去描述和实现软件的业务。毕竟软件只是一个载体,业务才是我们真正要实现的目标。
<br><br>
但是在设计过程中,心里却往往在担心,如果我完全不去考虑程序代码的实现的话,那么我怎么知道我的设计一定合理呢?我怎么知道我设计的类、接口一定可以实现呢?所以经常可以看到的现象就是:
<br><br>
在设计过程中,虽然知道不能过早考虑代码实现,但是每设计一个类,一个接口,心里都要不知不觉的用自己熟悉的编程语言大概的评估一下,看看能否编出来,因此,一不小心,就会又回到按照程序功能实现的思路进行设计的老路上去了。
<br><br>
举个例子来说明,在做Web程序设计的时候,经常要遇到分页显示数据的情况。比如说需要把系统中所有的用户都列出来这样的功能。假设使用User
类来表示用户,增加用户addUser(),删除用户deleteUser(),查询所有用户listUsers()方法。而数据库中有一个user表,
一条记录是一个用户的信息。下面考虑一下User类的方法的实现:
<br><br>
addUser()和deleteUser()方法都好实现,就是对数据库增加记录和删除记录。对于listUsers()方法,其实就是对user表的select,取出一个记录集。但是该怎么从listUsers()方法中得到所有用户的列表呢?
<br><br>
一个方法调用的返回值只有一个,没有多个,所以很多情况下采用的办法就是返回值定义为集合类型,比如Vector。这样就可以在
listUsers()方法的具体代码实现的时候,从数据库依次取出一个个记录,插入到Vector里面来。在主程序里面,调用listUsers()方
法可以返回一个Vector,然后再对Vector遍历操作,就可以得到用户列表了。</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span class="keyword">public</span>
<span> </span>
<span class="keyword">class</span>
<span> User { </span>
</span>
</li>
<li>
<span> </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">static</span>
<span> </span>
<span class="keyword">void</span>
<span> addUser(...); { </span>
</span>
</li>
<li>
<span> <span class="comment">// 数据库insert一条记录</span>
<span> </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span> </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">static</span>
<span> </span>
<span class="keyword">void</span>
<span> deleteUser(...); { </span>
</span>
</li>
<li>
<span> <span class="comment">// 数据库delete一条记录</span>
<span> </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span> </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> Vector listUsers(...); { </span>
</span>
</li>
<li>
<span> <span class="comment">// 数据库select结果放到一个集合里面</span>
<span> </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>这
样的设计基本合理,但是仍然有点小问题。因为在设计的时候,就考虑到了用Java的集合类Vector来实现对不定长数据集的存放,因而违反了面向对象设
计的一个原则:在设计的时候不应过早的考虑具体程序语言的实现。所以必须用抽象的方法,和具体实现无关的方法来表达业务逻辑。
<br><br>
我们知道,通常对具有集合特征的数据结构进行遍历通常可以使用next和hasNext方法,next实现取下一个用户,hasNext判断是否还有元素。 因此我们定义一个接口Iterator,这个接口中定义两个方法next和hasNext:</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span class="keyword">public</span>
<span> </span>
<span class="keyword">interface</span>
<span> Iterator { </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> </span>
<span class="keyword">boolean</span>
<span> hasNext(); {} </span>
</span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> Object next(); {} </span>
</span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>
<br>
而User类的listUses方法返回值改为Iterator接口的实现类:
<br></p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span class="keyword">public</span>
<span> </span>
<span class="keyword">class</span>
<span> User { </span>
</span>
</li>
<li>
<span> ... </span>
</li>
<li>
<span> <span class="keyword">public</span>
<span> Iterator listUsers(); { </span>
</span>
</li>
<li>
<span> } </span>
</li>
<li>
<span> ... </span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>
<br>
这样就把User类的设计和具体的实现方法分离开了,因为此时任何实现了next()和hasNext()方法的类都可以做为listUsers
的返回值,都可以被用来表达“用户列表”,而不仅仅可以使用Vector而已。比如,我可以用ArrayList来表达用户列表,因为ArrayList
也实现了Iterator,当然我也可以自己专门写一个类来存放用户列表,只要实现next()和hasNext()方法就行了。
<br><br>
这样在具体的编写代码的时候,程序员具有了最大的灵活性,可以根据具体的情况,采用不同的编程方法来存放用户列表。特别是降低了程序的耦合度,提高了程序的可移植性。对于上面那个JunkMail的listAllMail()方法也同样应该改为接口类型。
<br><br>
然后,在主程序里面就这样来使用User类的listUsers方法:</p>
<div class="dp-highlighter">
<div class="bar">
<div class="tools">Java代码 <a title="收藏这段代码"><img class="star" src="http://zohan.group.iteye.com/images/icon_star.png" alt="收藏代码"></a>
</div>
</div>
<ol class="dp-j">
<li>
<span><span>User myUser = </span>
<span class="keyword">new</span>
<span> User();; </span>
</span>
</li>
<li>
<span>Iterator iterator = myUser.listUsers();; </span>
</li>
<li>
<span><span class="keyword">while</span>
<span> (iterator.hasNext();); { </span>
</span>
</li>
<li>
<span> iterator.next();; </span>
</li>
<li>
<span>} </span>
</li>
</ol>
</div>
<p>
<br>
这样就可以完全不用考虑程序代码实现了,从高层次上把功能抽象出来,定义成为接口,同时又可以把系统设计的很合理,完全根据业务的需求来进行设计。
<br><br>
结语
<br><br>
通过上面的几个例子的设计说明,使用面向对象的思维方法,其实是一个把业务逻辑从具体的编程技术当中抽象出来的过程,而这个抽象的过程是自上而下
的,非常符合人类的思维习惯,也就是先不考虑问题解决的细节,把问题的最主要的方面抽象成为一个简单的框架,集中精力思考如何解决主要矛盾,然后在解决问
题的过程中,再把问题的细节分割成一个一个小问题,再专门去解决细节问题。
<br><br>
因而一旦牢牢的抓住了这一点,你就会发现在软件设计和开发过程中,你自己总是会不知不觉的运用面向对象的思维方法来设计和编写程序,并且程序的设计和开发也变得不再那么枯燥,而一个合理运用面向对象技术进行设计和架构的软件,更是具备了思维的艺术美感。
</p>
相关推荐
面向对象编程不仅仅是语法层面的,更是一种思维方式。在软件开发过程中,它模仿现实世界的实体和它们之间的关系。面向对象方法通常包括分析、设计和实施三个阶段,就像制造飞机的过程一样。类是概念上的描述,而对象...
在探讨“Java编程思想面向对象逻辑思维方法”这一主题时,我们深入剖析了面向对象编程(Object-Oriented Programming,OOP)的核心理念及其在Java中的应用方式。面向对象编程是一种编程范式,它将数据和操作数据的...
当你精通一种面向对象语言后,转而学习其他语言会更加容易,因为你已经掌握了编程的核心思想,剩下的只是适应新的语法和库。 总的来说,面向对象编程不仅仅是学习一种编程语言,更重要的是理解如何通过类和对象来...
面向对象方法与结构化方法是两种在软件工程领域内广泛应用的软件开发范式,它们各自拥有独特的设计理念和方法论,同时也存在着一定的内在联系和一致性。本文将深入探讨这两种方法的异同点,以及如何在软件开发实践中...
面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,它...从面向过程到面向对象的转变,意味着思维方式的转变,需要我们更加关注数据结构和数据之间的关系,以及如何通过对象来表达和操作这些关系。
面向对象方法的基本理念是模仿现实世界中的思维方式来构建问题的计算机模型。对象是事物的抽象,用于存储信息和执行操作;消息传递是对象间通信的方式;类是对象的模板,定义了一组共享属性和行为;继承允许子类从...
这个课题旨在让学生深入理解并掌握面向对象的思想体系、方法和技术,以及它们在实际项目中的应用。 首先,面向对象(Object-Oriented,简称OO)的思想体系基于几个核心概念:封装、继承、多态和抽象。这些概念使得...
在本课程中,我们将深入探讨这一关键概念,通过原版课件的学习,帮助你掌握面向对象的思维方式和技术工具。 首先,我们要理解面向对象的基本概念。面向对象编程(Object-Oriented Programming,OOP)是一种编程范式...
本资源“深入浅出面向对象分析与设计(中文版)”涵盖了这些关键概念,通过实例解析和清晰的讲解,帮助初学者建立起面向对象思维,掌握分析和设计的技巧。阅读此书,你将能够更好地理解和运用面向对象编程,为软件开发...
面向对象方法从内部结构上模拟客观世界,它更加注重人类逻辑思维方式,如抽象、分类、继承、聚合、封装等。这种方法不仅是一种软件开发技术,还是一种关于如何看待软件系统与现实世界关系的哲学。 - **面向对象分析...
通过这种方式,可以构建类和对象的模型,实现面向对象的编程思维。 8. 选择器、动态链接和多态性 选择器是面向对象中的一个概念,用于标识特定的方法或操作。在C中,可以通过函数指针实现选择器的效果,结合动态...
面向对象的系统开发是现代软件工程中的核心方法论,它基于面向对象的思维方式来理解和建模复杂的系统。在管理信息系统领域,面向对象方法被广泛应用于系统分析、设计和实施过程中,以提高软件的可维护性、可扩展性和...
本思维导图涵盖了以下几个关键面向对象的方面: 1. **类与对象**:在Java中,类是对象的蓝图,定义了对象的状态(属性)和行为(方法)。对象则是类的实例,具有类所定义的特性。 2. **封装**:封装是面向对象编程...
面向对象方法相对于传统软件开发方法,强调了数据和过程的一体化,更符合人类的思维方式。它通过对象的封装、继承和多态性提高了代码的重用性,降低了系统的复杂性和维护成本。面向对象分析作为软件生命周期的早期...
面向对象编程是一种编程范式,它基于“对象”的概念,每个对象都包含数据(属性)和操作这些数据的方法(函数)。在Java中,类是创建对象的蓝图,它们定义了对象的属性和方法。在贪吃蛇游戏中,我们可以定义几个关键...
随着信息技术的发展,软件项目变得越来越庞大和复杂,面向对象方法论应运而生,它模仿人类的自然思维,通过分解、抽象和模块化来创建软件模型。面向对象分析(OOA)关注于识别和定义问题域中的对象,而面向对象设计...
学习和掌握面向对象编程需要思维方式的转变,从关注解决问题的步骤转向关注如何将问题领域抽象为对象和它们之间的关系。 总之,Java的面向对象编程提供了一种强大的工具,能够帮助开发者构建复杂、模块化的系统,它...