所有的程序都需要經(jīng)過嚴(yán)謹(jǐn)?shù)脑O(shè)計(jì)。沒有人可以坐在電腦前憑空將一些復(fù)雜的邏輯寫成程序。軟件工程這門學(xué)科就是研究程序結(jié)構(gòu)的正確性,能否正常工作以及書寫規(guī)范的。軟件工程師的工作就是使用已被證明正確的方法去分析問題,然后設(shè)計(jì)一個(gè)程序來解決這個(gè)問題。 在19世紀(jì)70至80年代間,結(jié)構(gòu)化編程是主要的程序設(shè)計(jì)方法。結(jié)構(gòu)化編程的大體思路是:將待解決的問題分解成幾個(gè)小問題,然后再對小問題進(jìn)行分解,直到問題可以直接解決為止。這就是自上而下的編程方法。 自上而下的程序設(shè)計(jì)方法很常用,具備一定價(jià)值,但卻有待完善。比如,用這種方法編程基本上只寫解決某一問題的必要指令。不久人們就發(fā)現(xiàn),數(shù)據(jù)結(jié)構(gòu)和子程序設(shè)計(jì)及流程控制同樣重要。自上而下程序設(shè)計(jì)并沒有充分考慮對數(shù)據(jù)的操作。 這種方法的另一個(gè)弊端就是代碼難以復(fù)用。從分解問題的方法出發(fā),自上而下編程傾向于針對特定問題來設(shè)計(jì)。這樣,你就很難在別的大型工程中復(fù)用這些代碼,即使勉強(qiáng)可以使用, 也要進(jìn)行大量修改。寫高質(zhì)量的程序代碼費(fèi)時(shí)費(fèi)力,所以程序員和工程師都盡可能的復(fù)用以前的工作。 所以,在實(shí)際的程序開發(fā)中,自上而下的程序設(shè)計(jì)通常與自下而上的設(shè)計(jì)相結(jié)合。在自下而上的設(shè)計(jì)方法中,如果你對某個(gè)問題已經(jīng)有了已知的解決方法,或者手頭有可復(fù)用的工作,就可以從這里開始,逐步向上工作,來解決整個(gè)問題。 復(fù)用部分的組件應(yīng)該盡可能的模塊化。一個(gè)模塊能夠和系統(tǒng)的其他部分進(jìn)行交互,并遵循簡潔,規(guī)范的設(shè)計(jì)原則。就像是模塊通過「插頭」插入系統(tǒng),模塊內(nèi)部是如何運(yùn)作的,對系統(tǒng)來說并不重要,只要這個(gè)模塊的功能運(yùn)作良好就行了。這就是信息隱藏,在軟件工程中是一中重要的思想。 軟件模塊都要包含一些數(shù)據(jù),以及這些數(shù)據(jù)的處理方法。例如,一個(gè)郵件列表模塊就得包含一些名字、地址的信息,和這些信息的處理方法,像是添加新名字,顯示地址之類的方法等等。在這樣的模塊中,數(shù)據(jù)對模塊外是不可見的,以保證只有模塊中的方法可以以正確的方式處理這些數(shù)據(jù)。這也使得對模塊編程的時(shí)候更加方便,只要調(diào)用這些方法就可以了,并不需要知道實(shí)現(xiàn)的細(xì)節(jié)。 支持模塊的信息隱藏的編程語言從80年代開始流行。那時(shí)候起,一種類似的更加先進(jìn)的編程方法統(tǒng)治了軟件開發(fā)行業(yè)——面向?qū)ο缶幊蹋╫bject-priented programming,縮寫 OOP)。 面向?qū)ο缶幊谈拍畹暮诵氖菍ο?,對象就是?nèi)含數(shù)據(jù)和數(shù)據(jù)的處理方法的模塊。在面向?qū)ο蟮母拍钪校瑢ο笫且粋€(gè)自我實(shí)現(xiàn)的實(shí)體,有一個(gè)內(nèi)部狀態(tài)(就是存儲(chǔ)的數(shù)據(jù)),能夠?qū)ν饨绲男畔⒆鞒龌貞?yīng)(調(diào)用內(nèi)部的方法)。用郵件列表對象舉例,它內(nèi)部有個(gè)包含了名字、地址信息的狀態(tài),如果你向它發(fā)送信息,要添加一個(gè)名字,他就會(huì)通過修改自身的狀態(tài)來回應(yīng)你。如果你發(fā)送打印自己的信息,它就會(huì)打印內(nèi)部保存的名字和地址。 面向?qū)ο蟮某绦蛟O(shè)計(jì)就是通過修改一個(gè)對象開始,使它能對傳入的信息作出正確回應(yīng)來解決問題。編程的最終工作結(jié)果是一個(gè)對象集合,每一個(gè)都存有一些數(shù)據(jù)及方法,對象之間通過傳送信息來交互。這種編程的大規(guī)模設(shè)計(jì)上自上而下的思想并不是很多。習(xí)慣了傳統(tǒng)編程方法的人對這種面向?qū)ο缶幊虝?huì)不適應(yīng)。但是,使用面向?qū)ο缶幊痰娜送耆梢赃@樣宣稱:這種編程方法更加符合自然規(guī)律——事物自己工作。更不用說這種編程方法更易寫、易懂、正確率高。 你可以這樣想:對象就是知道對于不同的信息做出怎樣的回應(yīng)。對相同的信息來說,不同的對象可能做出不同的回應(yīng)。例如,不同的對象對于「打印」信息做出的回應(yīng)可能截然不同。不同的對象對相同的信息做出不同的回應(yīng),這種特性就叫做多態(tài)。 一個(gè)對象可以和其他對象相似。存儲(chǔ)的信息類型相同,對于相同的信息能做出相同的回應(yīng)的對象屬于同一個(gè)類。(在實(shí)際的編程工作中,類的概念是主要的。這是因?yàn)?,我們首先?chuàng)建了類,然后在以這個(gè)類為模板創(chuàng)建了對象。)但是不屬于同一個(gè)類的對象可以相似。 例如,假設(shè)有一個(gè)程序可以讓用戶在評不上畫直線、三角、橢圓、多邊形和曲線。在這個(gè)程序中,每一個(gè)可視的物體都有一個(gè)對應(yīng)的對象。那么在這個(gè)程序中就會(huì)存在5個(gè)類,用以產(chǎn)生5個(gè)不同的可視對象。所有的直線對象屬于同一個(gè)類,三角形屬于另一個(gè)類,以此類推。顯然,所有的這些類都是相關(guān)的,都代表了可繪制對象,都會(huì)對「繪制圖形」的信息做出回應(yīng)。另一種分組方式,建立在他們保存的數(shù)據(jù)所代表的圖形上,這些關(guān)系不很明顯,但是在編程中很有用:可以將多邊形和曲線作為「多點(diǎn)對象」一組,直線、矩形、橢圓作為「兩點(diǎn)對象」一組。(這是因?yàn)?,直線通過兩個(gè)點(diǎn)可以確定,矩形通過兩對點(diǎn)可以確定,橢圓通過兩個(gè)焦點(diǎn)可以確定。這里所說的矩形指的是所有邊都垂直或者水平的矩形,編程中用到的矩形大多都是這樣的。)我們可以用下面這個(gè)圖標(biāo)來表示他們的關(guān)系。 DrawableObject、MultipointObject、TwoPointObject 在程序中屬于不同的類。MultipointObject 和 TwoPointObject 屬于 DrawableObject 的子類。Line 屬于 TwoPointObject 和 DrawableObject(非直接)的子類。子類繼承了父類的特性,子類也可以寫入新的特性來覆蓋掉原來父類的特性(通過定義一個(gè)對信息的不同回應(yīng)方式)。不過,Line Rectangle 這些對象都是可繪制對象,DrawableObject 對象表達(dá)了這一關(guān)系。 繼承在程序設(shè)計(jì)中大有用處,因?yàn)檫@涉及到代碼復(fù)用。而類就是代碼復(fù)用的最終形式。如果已經(jīng)有一個(gè)類滿足你的需求,就可以直接來使用這個(gè)類。如果有和你的需求類似的類,你就可以通過定義一個(gè)子類,稍作修改來使用。 綜上,面向?qū)ο蟛粌H是一種先進(jìn)的編程方式,還是復(fù)用代碼的一種手段。在接下來的章節(jié)中,對象、類和面向?qū)ο缶幊虒?huì)成為重要主題。下一章你將會(huì)開始在 Java 語言中使用對象。在第五章中你將會(huì)創(chuàng)建自己的類和對象。 |
|