JWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JWorld@TW » Java 新手區 » Object/物件導向  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友    訂閱主題
reply to topicthreaded modego to previous topicgo to next topic
本主題所含的標籤
無標籤
作者 面向对象的思维方法 [精華]
robbin





發文: 23
積分: 5
於 2003-09-19 22:54 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
面向对象的思维方法

作者:robbin
网址:http://www.fankai.com/

我是从学习Java编程开始接触OOP(面向对象编程),刚开始使用Java编写程序的时候感觉很别扭,因为我早以习惯用C来编写程序,很欣赏C的简洁性和高效性,喜欢C简练而表达能力丰富的风格,特别忍受不了Java运行起来慢吞吞的速度,相对冗长的代码,而且一个很简单的事情,要写好多类,一个类调用一个类,心里的抵触情绪很强。

我对Java的面向对象的特性琢磨良久,自认为有所领悟,也开始有意识的运用OOP风格来写程序,然而还是经常会觉得不知道应该怎样提炼类,面对一个具体的问题的时候,会觉得脑子里千头万绪的,不知道怎么下手,一不小心,又会回到原来的思路上去。

举个例子,要发广告邮件,广告邮件列表存在数据库里面。倘若用C来写的话,一般会这样思考,先把邮件内容读入,然后连接数据库,循环取邮件地址,调用本机的qmail的sendmail命令发送。

然后考虑用Java来实现,既然是OOP,就不能什么代码都塞到main过程里面,于是就设计了三个类:

一个类是负责读取数据库,取邮件地址,调用qmail的sendmail命令发送;
一个类是读邮件内容,MIME编码成HTML格式的,再加上邮件头;
一个主类负责从命令读参数,处理命令行参数,调用发email的类。

把一件工作按照功能划分为3个模块分别处理,每个类完成一件模块任务。

仔细的分析一下,就会发现这样的设计完全是从程序员实现程序功能的角度来设计的,或者说,设计类的时候,是自低向上的,从机器的角度到现实世界的角度来分析问题的。因此在设计的时候,就已经把程序编程实现的细节都考虑进去了,企图从底层实现程序这样的出发点来达到满足现实世界的软件需求的目标。

这样的分析方法其实是不适用于Java这样面向对象的编程语言,因为,如果改用C语言,封装两个C函数,都会比Java实现起来轻松的多,逻辑上也清楚的多。

我觉得面向对象的精髓在于考虑问题的思路是从现实世界的人类思维习惯出发的,只要领会了这一点,就领会了面向对象的思维方法。

举一个非常简单的例子:假使现在需要写一个网页计数器,客户访问一次页面,网页计数器加1,计数器是这样来访问的

1
http://hostname/count.cgi?id=xxx


后台有一个数据库表,保存每个id(一个id对应一个被统计访问次数的页面)的计数器当前值,请求页面一次,对应id的计数器的字段加1(这里我们忽略并发更新数据库表,出现的表锁定的问题Wink

如果按照一般从程序实现的角度来分析,我们会这样考虑:首先是从HTTP GET请求取到id,然后按照id查数据库表,获得某id对应的访问计数值,然后加1,更新数据库,最后向页面显示访问计数。

现在假设一个没有程序设计经验的人,他会怎样来思考这个问题的呢?他会提出什么样的需求呢?他很可能会这样想:

我需要有一个计数器,这个计数器应该有这样的功能,刷新一次页面,访问量就会加1,另外最好还有一个计数器清0的功能,当然计数器如果有一个可以设为任意值的功能的话,我就可以作弊了。

做为一个没有程序设计经验的人来说,他完全不会想到对数据库应该如何操作,对于HTTP变量该如何传递,他考虑问题的角度就是我有什么需求,我的业务逻辑是什么,软件应该有什么功能。

按照这样的思路(请注意,他的思路其实就是我们平时在生活中习惯的思维方式),我们知道需要有一个计数器类 Counter,有一个必须的和两个可选的方法:

1
2
3
getCount()   // 取计数器值方法
resetCounter()   // 计数器清0方法
setCount()   // 设计数器为相应的值方法


把Counter类完整的定义如下:

1
2
3
4
5
public class Counter {
  public int getCount(int id) {}
  public void resetCounter(int id) {}
  public void setCount(int id, int currentCount) {}
}


解决问题的框架已经有了,来看一下如何使用Counter。 在count.cgi里面调用Counter来计数,程序片断如下:

1
2
3
4
5
6
  //  这里从HTTP环境里面取id值
   ...
  Counter myCounter = new Counter();  // 获得计数器
  int currentCount = myCounter.getCount(id);  // 从计数器中取计数
  //  这里向客户浏览器输出
   ...


程序的框架全都写好了,剩下的就是实现Counter类方法里面具体的代码了,此时才去考虑具体的程序语言实现的细节,比如,在getCount()方法里面访问数据库,更新计数值。

从上面的例子中看到,面向对象的思维方法其实就是我们在现实生活中习惯的思维方式,是从人类考虑问题的角度出发,把人类解决问题的思维方式逐步翻译成程序能够理解的思维方式的过程,在这个翻译的过程中,软件也就逐步被设计好了。

在运用面向对象的思维方法进行软件设计的过程中,最容易犯的错误就是开始分析的时候,就想到了程序代码实现的细节,因此封装的类完全是基于程序实现逻辑,而不是基于解决问题的业务逻辑。

学习JDBC编程的经典错误问法是:“我怎样封装对数据库的select操作?”

面向对象的设计是基于解决业务问题的设计,而不是基于具体编程技术的设计。我不会去封装select语句的,我只封装解决问题的业务逻辑,对数据库的读取是在业务逻辑的编码实现阶段才去考虑的问题。

回过头看上面那个发广告邮件的例子,应该如何应用面向对象的思维方法呢?

对于一个邮件来说,有邮件头,邮件体,和邮件地址这三个属性,发送邮件,需要一个发送的方法,另外还需要一个能把所有邮件地址列出来的方法。所以应该如下设计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
类JunkMail
 
属性:
  head
  body
  address
方法:
  sendMail()    // 发送邮件
  listAllMail() // 列邮件地址
 
用Java来表示:
 
public class JunkMail {
  private String head;
  private String body;
  private String address;
  public JunkMain() {   // 默认的类构造器
     // 从外部配置文件读邮件头和邮件体
     this.head=...;
     this.body=...;
  }
 
  public static boolean sendMail(String address) {
     //  调用qmail,发送email
  }
 
  public static Collection listAllMail() {
     //  访问数据库,返回一个邮件地址集合
  }
}

当把JunkMail设计好了以后,再调用JunkMail类完成邮件的发送,将是非常轻松的事情。

如果说传统的面向过程的编程是符合机器运行指令的流程的话,那么面向对象的思维方法就是符合现实生活中人类解决问题的思维过程。

在面向对象的软件分析和设计的时候,要提醒自己,不要一上来就去想程序代码的实现,应该抛开具体编程语言的束缚,集中精力分析我们要实现的软件的业务逻辑,分析软件的业务流程,思考应该如何去描述和实现软件的业务。毕竟软件只是一个载体,业务才是我们真正要实现的目标。

但是在设计过程中,心里却往往在担心,如果我完全不去考虑程序代码的实现的话,那么我怎么知道我的设计一定合理呢?我怎么知道我设计的类、接口一定可以实现呢?所以经常可以看到的现象就是:

在设计过程中,虽然知道不能过早考虑代码实现,但是每设计一个类,一个接口,心里都要不知不觉的用自己熟悉的编程语言大概的评估一下,看看能否编出来,因此,一不小心,就会又回到按照程序功能实现的思路进行设计的老路上去了。

举个例子来说明,在做Web程序设计的时候,经常要遇到分页显示数据的情况。比如说需要把系统中所有的用户都列出来这样的功能。假设使用User类来表示用户,增加用户addUser(),删除用户deleteUser(),查询所有用户listUsers()方法。而数据库中有一个user表,一条记录是一个用户的信息。下面考虑一下User类的方法的实现:

addUser()和deleteUser()方法都好实现,就是对数据库增加记录和删除记录。对于listUsers()方法,其实就是对user表的select,取出一个记录集。但是该怎么从listUsers()方法中得到所有用户的列表呢?

一个方法调用的返回值只有一个,没有多个,所以很多情况下采用的办法就是返回值定义为集合类型,比如Vector。这样就可以在listUsers()方法的具体代码实现的时候,从数据库依次取出一个个记录,插入到Vector里面来。在主程序里面,调用listUsers()方法可以返回一个Vector,然后再对Vector遍历操作,就可以得到用户列表了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class User {
 
  public static void addUser(...) {
    //  数据库insert一条记录
  }
 
  public static void deleteUser(...) {
    //  数据库delete一条记录
  }
 
  public Vector listUsers(...) {
    //  数据库select结果放到一个集合里面
  }
}
这样的设计基本合理,但是仍然有点小问题。因为在设计的时候,就考虑到了用Java的集合类Vector来实现对不定长数据集的存放,因而违反了面向对象设计的一个原则:在设计的时候不应过早的考虑具体程序语言的实现。所以必须用抽象的方法,和具体实现无关的方法来表达业务逻辑。

我们知道,通常对具有集合特征的数据结构进行遍历通常可以使用next和hasNext方法,next实现取下一个用户,hasNext判断是否还有元素。 因此我们定义一个接口Iterator,这个接口中定义两个方法next和hasNext:
1
2
3
4
public interface Iterator {
  public boolean hasNext() {}
  public Object next()  {}
}

而User类的listUses方法返回值改为Iterator接口的实现类:

1
2
3
4
5
6
public class User {
  ...
  public Iterator listUsers() {
  }
  ...
}

这样就把User类的设计和具体的实现方法分离开了,因为此时任何实现了next()和hasNext()方法的类都可以做为listUsers的返回值,都可以被用来表达“用户列表”,而不仅仅可以使用Vector而已。比如,我可以用ArrayList来表达用户列表,因为ArrayList也实现了Iterator,当然我也可以自己专门写一个类来存放用户列表,只要实现next()和hasNext()方法就行了。

这样在具体的编写代码的时候,程序员具有了最大的灵活性,可以根据具体的情况,采用不同的编程方法来存放用户列表。特别是降低了程序的耦合度,提高了程序的可移植性。对于上面那个JunkMail的listAllMail()方法也同样应该改为接口类型。

然后,在主程序里面就这样来使用User类的listUsers方法:
1
2
3
4
5
User myUser = new User();
Iterator iterator = myUser.listUsers();
while (iterator.hasNext()) {
  iterator.next();
}

这样就可以完全不用考虑程序代码实现了,从高层次上把功能抽象出来,定义成为接口,同时又可以把系统设计的很合理,完全根据业务的需求来进行设计。

结语

通过上面的几个例子的设计说明,使用面向对象的思维方法,其实是一个把业务逻辑从具体的编程技术当中抽象出来的过程,而这个抽象的过程是自上而下的,非常符合人类的思维习惯,也就是先不考虑问题解决的细节,把问题的最主要的方面抽象成为一个简单的框架,集中精力思考如何解决主要矛盾,然后在解决问题的过程中,再把问题的细节分割成一个一个小问题,再专门去解决细节问题。

因而一旦牢牢的抓住了这一点,你就会发现在软件设计和开发过程中,你自己总是会不知不觉的运用面向对象的思维方法来设计和编写程序,并且程序的设计和开发也变得不再那么枯燥,而一个合理运用面向对象技术进行设计和架构的软件,更是具备了思维的艺术美感。

最后,愿面向对象的思维方法也能给您的程序设计之路带来创作的乐趣。

面向对象的思维方法.doc (46.5k)


reply to postreply to post
作者 Re:面向对象的思维方法 [Re:robbin]
Duncan

還隱隱作痛

版主

發文: 7816
積分: 39
於 2003-09-20 00:06 user profilesend a private message to usersend email to Duncanreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
robbin wrote:
面向对象的思维方法

作者:robbin
网址:http://www.fankai.com/

写的很好,很清晰。Thumbs up

不过我想挑个骨头:

这样就把User类的设计和具体的实现方法分离开了,因为此时任何实现了next()和hasNext()方法的类都可以做为listUsers的返回值,都可以被用来表达“用户列表”,而不仅仅可以使用Vector而已。比如,我可以用ArrayList来表达用户列表,因为ArrayList也实现了Iterator,当然我也可以自己专门写一个类来存放用户列表,只要实现next()和hasNext()方法就行了。

不知是不是两岸的用语不同,『ArrayList 也"实现"了 Iterator』中的实现义同洋文 implement 一词吗?
----------------------------------------------------------------------------------------------------
寫的很好,很清晰。Thumbs up

不過我想挑個骨頭:

這樣就把User類的設計和具體的實現方法分離開了,因爲此時任何實現了next()和hasNext()方法的類都可以做爲listUsers的返回值,都可以被用來表達“用戶列表”,而不僅僅可以使用Vector而已。比如,我可以用ArrayList來表達用戶列表,因爲ArrayList也實現了Iterator,當然我也可以自己專門寫一個類來存放用戶列表,只要實現next()和hasNext()方法就行了。

不知是不是兩岸的用語不同,『ArrayList 也"實現"了 Iterator』中的實現義同洋文 implement 一詞嗎?


Duncan edited on 2003-09-20 00:12
reply to postreply to post

給我
辣味豆腐 其餘免談
作者 Re:面向对象的思维方法 [Re:Duncan]
robbin





發文: 23
積分: 5
於 2003-09-20 00:32 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
呵呵,不是这个意思,我的意思是

ArrayList.iterator();

也就是说可以在方法里面用ArrayList,最后返回:

return ArrayList.iterator();


reply to postreply to post
作者 Re:面向对象的思维方法 [Re:robbin]
UndeadJ

UX & UI Design



發文: 384
積分: 3
於 2003-09-20 00:55 user profilesend a private message to usersend email to UndeadJreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
寫的很好Thumbs up

底下這句話,對我而言真是一針見血,我有時候就因為這樣想了大半天的時間Sad
robbin wrote:
但是在設計過程中,心裡卻往往在擔心,如果我完全不去考慮程序代碼的實現的話,那麼我怎麼知道我的設計一定合理呢?我怎麼知道我設計的類、接口一定可以實現呢?所以經常可以看到的現象就是:

在設計過程中,雖然知道不能過早考慮代碼實現,但是每設計一個類,一個接口,心裡都要不知不覺的用自己熟悉的編程語言大概的評估一下,看看能否編出來,因此,一不小心,就會又回到按照程序功能實現的思路進行設計的老路上去了。

--
coding花費的時間並不是最多
最多的時間是在如何將程式寫成OOP、思考如何解決問題


UndeadJ edited on 2003-09-20 00:57
reply to postreply to post
我們是懷抱各自的夢想,買了車票的乘客,不過就像列車一定會有終點,人生有時會碰到必須換車的時候...

我們站在名為「今天」的車站,在名為「昨天」的列車下車,轉搭為「明天」的列車

不過,若沒及時搭上的話....
作者 Re:面向对象的思维方法 [Re:robbin]
zbbert





發文: 5
積分: 0
於 2005-06-17 14:47 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
通俗易懂。对我有很大的帮助。3q

reply to postreply to post
作者 Re:面向对象的思维方法 [Re:robbin]
jencce





發文: 6
積分: 0
於 2005-11-05 00:08 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
我对这句话还是不大理解 很有可能是我太弱了因为我刚刚学习java

“ 也就是说可以在方法里面用ArrayList,最后返回:

return ArrayList.iterator(); ”


ArrayList实现了iterator接口,那它怎么调用了iterator了?iterator是个接口呢
怎么也想不明白 实在忍不住问了


reply to postreply to post
作者 Re:面向对象的思维方法 [Re:robbin]
winddknight





發文: 162
積分: 0
於 2009-02-28 00:53 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
看了很多文章,記得有這麼一句話:「對介面撰碼,不要對實作撰碼」
看完大大的文章,突然有同感,或許就跟文章說明了同一件事,以業務的角度去設計功能

看完這篇文章,思路通明很多,感謝~


reply to postreply to post
作者 Re:面向对象的思维方法 [Re:robbin]
aa061672116





發文: 5
積分: 0
於 2009-03-22 23:44 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
寫的真是不錯呢!!有幫到我的忙,謝謝大大的分享!!
ps:順便轉成<繁體中文>po上來分享給需要的人。

物件導向的思維方法

作者:範凱
E-mail: robbin_fan@yahoo.com.cn

我是從學習Java程式設計開始接觸OOP(物件導向程式設計),剛開始使用Java編寫程式的時候感覺很彆扭,因為我早以習慣用C來編寫程式,很欣賞C的簡潔性和高效性,喜歡C簡練而表達能力豐富的風格,特別忍受不了Java運行起來慢吞吞的速度,相對冗長的代碼,而且一個很簡單的事情,要寫好多類,一個類調用一個類,心裡的抵觸情緒很強。

我對Java的物件導向的特性琢磨良久,自認為有所領悟,也開始有意識的運用OOP風格來寫程式,然而還是經常會覺得不知道應該怎樣提煉類,面對一個具體的問題的時候,會覺得腦子裡千頭萬緒的,不知道怎麼下手,一不小心,又會回到原來的思路上去。

舉個例子,要發廣告郵件,廣告郵寄清單存在資料庫裡面。倘若用C來寫的話,一般會這樣思考,先把郵件內容讀入,然後連接資料庫,迴圈取郵寄地址,調用本機的qmail的sendmail命令發送。

然後考慮用Java來實現,既然是OOP,就不能什麼代碼都塞到main過程裡面,於是就設計了三個類:

一個類是負責讀取資料庫,取郵寄地址,調用qmail的sendmail命令發送;
一個類是讀郵件內容,MIME編碼成HTML格式的,再加上郵件頭;
一個主類負責從命令讀參數,處理命令列參數,調用發email的類。

把一件工作按照功能劃分為3個模組分別處理,每個類完成一件模組任務。

仔細的分析一下,就會發現這樣的設計完全是從程式師實現程式功能的角度來設計的,或者說,設計類的時候,是自低向上的,從機器的角度到現實世界的角度來分析問題的。因此在設計的時候,就已經把程式程式設計實現的細節都考慮進去了,企圖從底層實現程式這樣的出發點來達到滿足現實世界的軟體需求的目標。

這樣的分析方法其實是不適用於Java這樣物件導向的程式設計語言,因為,如果改用C語言,封裝兩個C函數,都會比Java實現起來輕鬆的多,邏輯上也清楚的多。

我覺得物件導向的精髓在於考慮問題的思路是從現實世界的人類思維習慣出發的,只要領會了這一點,就領會了物件導向的思維方法。

舉一個非常簡單的例子:假使現在需要寫一個網頁計數器,客戶訪問一次頁面,網頁計數器加1,計數器是這樣來訪問的

http://hostname/count.cgi?id=xxx

後臺有一個資料庫表,保存每個id(一個id對應一個被統計訪問次數的頁面)的計數器當前值,請求頁面一次,對應id的計數器的欄位加1(這裡我們忽略併發更新資料庫表,出現的表鎖定的問題)。

如果按照一般從程式實現的角度來分析,我們會這樣考慮:首先是從HTTP GET請求取到id,然後按照id查資料庫表,獲得某id對應的訪問計數值,然後加1,更新資料庫,最後向頁面顯示訪問計數。

現在假設一個沒有程式設計經驗的人,他會怎樣來思考這個問題的呢?他會提出什麼樣的需求呢?他很可能會這樣想:

我需要有一個計數器,這個計數器應該有這樣的功能,刷新一次頁面,訪問量就會加1,另外最好還有一個計數器清0的功能,當然計數器如果有一個可以設為任意值的功能的話,我就可以作弊了。

做為一個沒有程式設計經驗的人來說,他完全不會想到對資料庫應該如何操作,對於HTTP變數該如何傳遞,他考慮問題的角度就是我有什麼需求,我的業務邏輯是什麼,軟體應該有什麼功能。

按照這樣的思路(請注意,他的思路其實就是我們平時在生活中習慣的思維方式),我們知道需要有一個計數器類 Counter,有一個必須的和兩個可選的方法:

getCount() // 取計數器值方法
resetCounter() // 計數器清0方法
setCount() // 設計數器為相應的值方法

把Counter類完整的定義如下:

public class Counter {
public int getCount(int id) {}
public void resetCounter(int id) {}
public void setCount(int id, int currentCount) {}
}

解決問題的框架已經有了,來看一下如何使用Counter。 在count.cgi裡面調用Counter來計數,程式片斷如下:

// 這裡從HTTP環境裡面取id值
...
Counter myCounter = new Counter(); // 獲得計數器
int currentCount = myCounter.getCount(id); // 從計數器中取計數
// 這裡向客戶流覽器輸出
...

程式的框架全都寫好了,剩下的就是實現Counter類方法裡面具體的代碼了,此時才去考慮具體的程式語言實現的細節,比如,在getCount()方法裡面訪問資料庫,更新計數值。

從上面的例子中看到,物件導向的思維方法其實就是我們在現實生活中習慣的思維方式,是從人類考慮問題的角度出發,把人類解決問題的思維方式逐步翻譯成程式能夠理解的思維方式的過程,在這個翻譯的過程中,軟體也就逐步被設計好了。

在運用物件導向的思維方法進行軟體設計的過程中,最容易犯的錯誤就是開始分析的時候,就想到了程式碼實現的細節,因此封裝的類完全是基於程式實現邏輯,而不是基於解決問題的業務邏輯。

學習JDBC程式設計的經典錯誤問法是:“我怎樣封裝對資料庫的select操作?”

物件導向的設計是基於解決業務問題的設計,而不是基於具體程式設計技術的設計。我不會去封裝select語句的,我只封裝解決問題的業務邏輯,對資料庫的讀取是在業務邏輯的編碼實現階段才去考慮的問題。

回過頭看上面那個發廣告郵件的例子,應該如何應用物件導向的思維方法呢?

對於一個郵件來說,有郵件頭,郵件體,和郵寄地址這三個屬性,發送郵件,需要一個發送的方法,另外還需要一個能把所有郵寄地址列出來的方法。所以應該如下設計:

類JunkMail

屬性:
head
body
address
方法:
sendMail() // 發送郵件
listAllMail() // 列郵寄地址

用Java來表示:

public class JunkMail {
private String head;
private String body;
private String address;
public JunkMain() { // 默認的類構造器
// 從外部設定檔讀郵件頭和郵件體
this.head=...;
this.body=...;
}

public static boolean sendMail(String address) {
// 調用qmail,發送email
}

public static Collection listAllMail() {
// 訪問資料庫,返回一個郵寄地址集合
}
}

當把JunkMail設計好了以後,再調用JunkMail類完成郵件的發送,將是非常輕鬆的事情。

如果說傳統的面向過程的程式設計是符合機器運行指令的流程的話,那麼物件導向的思維方法就是符合現實生活中人類解決問題的思維過程。

在物件導向的軟體分析和設計的時候,要提醒自己,不要一上來就去想程式碼的實現,應該拋開具體程式設計語言的束縛,集中精力分析我們要實現的軟體的業務邏輯,分析軟體的業務流程,思考應該如何去描述和實現軟體的業務。畢竟軟體只是一個載體,業務才是我們真正要實現的目標。

但是在設計過程中,心裡卻往往在擔心,如果我完全不去考慮程式碼的實現的話,那麼我怎麼知道我的設計一定合理呢?我怎麼知道我設計的類、介面一定可以實現呢?所以經常可以看到的現象就是:

在設計過程中,雖然知道不能過早考慮代碼實現,但是每設計一個類,一個介面,心裡都要不知不覺的用自己熟悉的程式設計語言大概的評估一下,看看能否編出來,因此,一不小心,就會又回到按照程式功能實現的思路進行設計的老路上去了。

舉個例子來說明,在做Web程式設計的時候,經常要遇到分頁顯示資料的情況。比如說需要把系統中所有的使用者都列出來這樣的功能。假設使用User類來表示用戶,增加用戶addUser(),刪除用戶deleteUser(),查詢所有用戶listUsers()方法。而資料庫中有一個user表,一條記錄是一個使用者的資訊。下面考慮一下User類的方法的實現:

addUser()和deleteUser()方法都好實現,就是對資料庫增加記錄和刪除記錄。對於listUsers()方法,其實就是對user表的select,取出一個記錄集。但是該怎麼從listUsers()方法中得到所有用戶的列表呢?

一個方法調用的返回值只有一個,沒有多個,所以很多情況下採用的辦法就是返回值定義為集合類型,比如Vector。這樣就可以在listUsers()方法的具體代碼實現的時候,從資料庫依次取出一個個記錄,插入到Vector裡面來。在主程序裡面,調用listUsers()方法可以返回一個Vector,然後再對Vector遍歷操作,就可以得到用戶列表了。

public class User {

public static void addUser(...) {
// 資料庫insert一條記錄
}

public static void deleteUser(...) {
// 資料庫delete一條記錄
}

public Vector listUsers(...) {
// 資料庫select結果放到一個集合裡面
}
}

這樣的設計基本合理,但是仍然有點小問題。因為在設計的時候,就考慮到了用Java的集合類Vector來實現對不定長資料集的存放,因而違反了物件導向設計的一個原則:在設計的時候不應過早的考慮具體程式語言的實現。所以必須用抽象的方法,和具體實現無關的方法來表達業務邏輯。

我們知道,通常對具有集合特徵的資料結構進行遍歷通常可以使用next和hasNext方法,next實現取下一個用戶,hasNext判斷是否還有元素。 因此我們定義一個介面Iterator,這個介面中定義兩個方法next和hasNext:

public interface Iterator {
public boolean hasNext() {}
public Object next() {}
}

而User類的listUses方法返回值改為Iterator介面的實現類:

public class User {
...
public Iterator listUsers() {
}
...
}

這樣就把User類的設計和具體的實現方法分離開了,因為此時任何實現了next()和hasNext()方法的類都可以做為listUsers的返回值,都可以被用來表達“用戶列表”,而不僅僅可以使用Vector而已。比如,我可以用ArrayList來表達用戶列表,因為ArrayList也實現了Iterator,當然我也可以自己專門寫一個類來存放用戶列表,只要實現next()和hasNext()方法就行了。

這樣在具體的編寫代碼的時候,程式師具有了最大的靈活性,可以根據具體的情況,採用不同的程式設計方法來存放用戶列表。特別是降低了程式的耦合度,提高了程式的可攜性。對於上面那個JunkMail的listAllMail()方法也同樣應該改為介面類別型。

然後,在主程序裡面就這樣來使用User類的listUsers方法:

User myUser = new User();
Iterator iterator = myUser.listUsers();
while (iterator.hasNext()) {
iterator.next();
}

這樣就可以完全不用考慮程式碼實現了,從高層次上把功能抽象出來,定義成為介面,同時又可以把系統設計的很合理,完全根據業務的需求來進行設計。

結語

通過上面的幾個例子的設計說明,使用物件導向的思維方法,其實是一個把業務邏輯從具體的程式設計技術當中抽象出來的過程,而這個抽象的過程是自上而下的,非常符合人類的思維習慣,也就是先不考慮問題解決的細節,把問題的最主要的方面抽象成為一個簡單的框架,集中精力思考如何解決主要矛盾,然後在解決問題的過程中,再把問題的細節分割成一個一個小問題,再專門去解決細節問題。

因而一旦牢牢的抓住了這一點,你就會發現在軟體設計和開發過程中,你自己總是會不知不覺的運用物件導向的思維方法來設計和編寫程式,並且程式的設計和開發也變得不再那麼枯燥,而一個合理運用物件導向技術進行設計和架構的軟體,更是具備了思維的藝術美感。

最後,願物件導向的思維方法也能給您的程式設計之路帶來創作的樂趣。


reply to postreply to post
作者 Re:面向对象的思维方法 [Re:robbin]
bearangel





發文: 1
積分: 0
於 2009-03-28 13:21 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
文章写得很好,对我这样的新手很有用

reply to postreply to post
作者 Re:面向对象的思维方法 [Re:robbin]
netower





發文: 18
積分: 0
於 2009-05-20 14:37 user profilesend a private message to usersend email to netowerreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
学习,思考,致意————面向对象的思维方法其实就是我们在现实生活中习惯的思维方式。

reply to postreply to post
一壶浊码喜相逢
古今多少事
都付程序中
» JWorld@TW »  Java 新手區 » Object/物件導向

reply to topicthreaded modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JWorld@TW 本站商標資訊

Powered by Powerful JuteForum® Version Jute 1.5.8