• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            CG@CPPBLOG

            /*=========================================*/
            隨筆 - 76, 文章 - 39, 評(píng)論 - 137, 引用 - 0
            數(shù)據(jù)加載中……

            (ZT)敲響OO時(shí)代的喪鐘 —— 不全,部分

            !——懷念失落的世界(2)

                值得懷念的世界,卻不值得回去!
                ——莊表偉

             接著昨天的話題說下去,那個(gè)面向過程的世界是完整的,統(tǒng)一的,也是容易理解的——對(duì)于程序員來說——或者說他只需要一種理解能力。這個(gè)世界雖然值得懷念,卻不值得再回去。因?yàn)椋覀儾辉傧癞?dāng)年的程序員那樣,只開發(fā)那些簡單的軟件了。很多人崇拜那些早起的“大牛”,其實(shí)平心而論,我們現(xiàn)在面對(duì)的問題的復(fù)雜程度,在他們當(dāng)年可以說幾乎無法解決。需求的復(fù)雜程度也不是他們當(dāng)年能夠設(shè)想到的。

            我們來看幾張照片:
            http://forum.cul.sina.com.cn/cgi-bin/view.cgi?gid=43&fid=1352&thread=1206&date=20040622&page=1

             這是在秘魯發(fā)現(xiàn)的神秘的納斯卡巨畫,這樣巨大的地面藝術(shù),可以給我們對(duì)于面向過程的編程的結(jié)論一個(gè)可視化的比喻。面向過程的編程,只有一個(gè)統(tǒng)一的世界,他對(duì)于軟件的理解,始終不會(huì)離開計(jì)算機(jī)的操作與運(yùn)算本質(zhì),這就像在平地上作畫那樣,我們需要的一根長1米的直線,非常容易,兩點(diǎn)一線,一拉就出來了。但是當(dāng)我們需要在地面上畫一根5000米甚至更長的直線時(shí),如何保證畫出一條直線,就成為一個(gè)巨大的挑戰(zhàn)。當(dāng)視角無法升到足夠的高度時(shí),如此復(fù)雜的圖案幾乎是無法把握的。僅僅依靠結(jié)構(gòu)化的劃分,并不能完全的隔離復(fù)雜度的交互影響。單步跟蹤一個(gè)1000行代碼的程序并不困難,但是如果是100萬行代碼,甚至更多呢?

            再看一張照片:

            這是世界上最大的“埃及胡夫金字塔”。我們假設(shè),如果當(dāng)年法老在工程進(jìn)行到80%的時(shí)候,提出需求變更,希望金字塔尖能夠向右移動(dòng)10米。情況會(huì)如何?——會(huì)死好多勞動(dòng)人民的!如果希望向右移動(dòng)100米呢?如果希望有四個(gè)塔尖各在一個(gè)方向呢?如果。。。還好這一切都沒有發(fā)生,否則我們就不可能看到一個(gè)真正完工的金字塔。然而在軟件開發(fā)領(lǐng)域,當(dāng)“結(jié)構(gòu)化編程”面對(duì)“移動(dòng)金字塔”的需求變更時(shí),它只能破產(chǎn)!

            可以得出一個(gè)比較關(guān)鍵性的結(jié)論是:

                僅僅從計(jì)算機(jī)的角度出發(fā),對(duì)于更為復(fù)雜的需求,描述力不足。對(duì)于巨大的需求變更,應(yīng)變力不足。而這正是對(duì)于的軟件需求的必然發(fā)展趨勢。

            所以,那個(gè)世界不值得回去,但是,OO真的幫到我們了嗎?
            敲響OO時(shí)代的喪鐘!——OO時(shí)代的來臨(1)

            三、OO時(shí)代的來臨

            多年以后的今天,我們依然在思考這樣一個(gè)問題:“OO怎么就流行起來了呢?”學(xué)術(shù)一點(diǎn)分析,這個(gè)問題可以分為以下幾個(gè)部分:

                1、OO之前的軟件開發(fā)困境何在?
                2、當(dāng)時(shí)的開發(fā)人員如何解決那些困境?
                3、那些解決困境的努力,為何會(huì)匯入OO的名下?
                4、OO這個(gè)概念,從何而來?
                5、OO的核心內(nèi)容是什么?
                6、OO的實(shí)際目的是什么?
                7、OO的理想目標(biāo)是什么?
                ......

            在今后的幾天里,我將詳細(xì)分析這些問題,得出的結(jié)論,說不定會(huì)出乎你的意料之外

            !——OO時(shí)代的來臨(2)

              1、困境

              上一章在分析的關(guān)于面向過程編程的思維所面臨的困境,還有一篇文章可以給大家參考。在去年三月份的時(shí)候,我就打算寫一些對(duì)于OO的反思,但是覺得自己思考尚且不夠成熟,因此沒有勉強(qiáng),當(dāng)時(shí)的文章現(xiàn)在可以在Javaeye看到。思考軟件開發(fā)(1)——面向?qū)ο蟮那扒昂蠛?/span>

              這篇文章其實(shí)只分析到了面向過程的困境,可以和前面我寫的那兩個(gè)比喻相互參照。而兩篇文字要表達(dá)的含義是一致的:“需要一個(gè)超越于機(jī)器執(zhí)行層面的的認(rèn)識(shí)。”或者說,不能僅僅以“解空間”的語言描述解決方案,最好能夠以“問題空間”的語言描述解決方案。這是OO得以流行的真正動(dòng)力,因?yàn)镺O宣稱自己能夠更好的描述“真實(shí)世界”。

              注意我要區(qū)分的幾個(gè)概念:“解決困境的努力”、“困境的根本原因”、“OO所宣稱的目標(biāo)”、“OO實(shí)際達(dá)到的效果”。因?yàn)樵谝酝腛O的宣傳中,這些概念是一個(gè)有機(jī)的整體,而卻認(rèn)為,其中有諸多“斷裂破碎”之處。

              面向過程的編程,面對(duì)的困境其實(shí)相當(dāng)多,最根本的原因前面也已經(jīng)指出了。但是在當(dāng)時(shí),在具體的項(xiàng)目中,在特定的人看來,他們碰的,是各自不同的問題。在人工智能領(lǐng)域,在圖形化界面領(lǐng)域,面對(duì)的是模擬的問題。在企業(yè)應(yīng)用領(lǐng)域,面對(duì)的是數(shù)據(jù)訪問與保護(hù)的問題。從共同的困境來看,適應(yīng)變更,方便重用,系統(tǒng)健壯之類的要求,也是需要考慮的。

              2、概念的發(fā)展歷程

              首先聲明,這是一個(gè)假想的歷程,并非真實(shí)的歷史。真實(shí)的歷史,可以參考以下URL中的介紹:

            http://www.student.chula.ac.th/~46829016/history.htm
            http://heim.ifi.uio.no/~kristen/FORSKNINGSDOK_MAPPE/F_OO_start.html
            http://tech.csai.cn/uml/rjkf.htm

              模擬:模擬的概念由來已久,但是如何模擬卻是一個(gè)大問題。
              抽象數(shù)據(jù)類型(ADT):在對(duì)結(jié)構(gòu)(structure)進(jìn)行簡單的擴(kuò)展之后,ADT就順理成章的出現(xiàn)了。
              封裝:對(duì)于ADT的理論總結(jié)可以表述為“封裝帶來了數(shù)據(jù)的安全性”。
              繼承:一堆不能重用的代碼是“罪惡”的。繼承首先出來試圖解決這個(gè)問題。
              多態(tài):是一個(gè)意外的收獲,如果各個(gè)對(duì)象都提供同一個(gè)名字的方法,似乎就可以不去管他們的區(qū)別。

              在這些努力與嘗試之后,面向?qū)ο髾M空出世,從哲學(xué)的高度總結(jié)了這些努力:“你們都是正確努力的一部分,你們都在試圖更好的描述真實(shí)世界,真實(shí)的世界里一切都是對(duì)象,所有的對(duì)象在一個(gè)分類系統(tǒng)里各安其位,對(duì)象之間通過消息相互‘招呼’。應(yīng)用OO思想,描述真實(shí)世界的運(yùn)行,就是編程的主要工作。”

              但是事實(shí)上呢?編程并不是描述真實(shí)世界!而是描述需求世界。不同的需求世界,需要不同的“世界觀”。這一點(diǎn),面向?qū)ο蟛]有考慮到。當(dāng)時(shí)流行的思想是通用編程語言,使用一種語言解決世界上所有的開發(fā)難題。而要整體解決各不相同的開發(fā)難題,只能將目光投向“真實(shí)世界”,那是各個(gè)差異巨大的“問題空間”的唯一一致的背景。



            四、面向?qū)ο蟮恼軐W(xué)破綻

                在此特別感謝徐昊,這一部分該如何寫,我始終沒有想好,在與他討論之后,我基本理出了一個(gè)思路。

              面向?qū)ο笥袃蓚€(gè)哲學(xué)基礎(chǔ),原子論與形而上學(xué)。這兩大基礎(chǔ),在哲學(xué)的發(fā)展歷程中,曾經(jīng)如日中天,無可置疑(在古希臘那時(shí)候),如果說這樣的哲學(xué)不偉大,那就是我太狂妄了,但是如果有人說:“西方哲學(xué)在之后的幾千年里沒有進(jìn)步,古希臘哲學(xué)就是西方哲學(xué)的頂點(diǎn),因此面向?qū)ο罄硭?dāng)然的應(yīng)該建立于這兩大哲學(xué)之上!”你會(huì)相信嗎?

              1、原子論

                西方哲學(xué)的發(fā)展,經(jīng)歷了兩次變革,一次是認(rèn)識(shí)論轉(zhuǎn)向;一次是語言轉(zhuǎn)向;第一次轉(zhuǎn)向使哲學(xué)的基礎(chǔ)從本體論和形而上學(xué)變?yōu)檎J(rèn)識(shí)論,從研究超驗(yàn)的存在物轉(zhuǎn)到研究認(rèn)識(shí)的主體和主客體關(guān)系;第二次轉(zhuǎn)向把對(duì)主客體的關(guān)系的研究變成了對(duì)主體間的交流和傳達(dá)問題的研究。把對(duì)主體的研究從觀念和思想的領(lǐng)域轉(zhuǎn)到了語言的領(lǐng)域(語句及其意義);這兩次轉(zhuǎn)向的代表人物分別是笛卡爾和維特跟斯坦。

                        ————《OO, OO以后, 及其極限》waterbird

              看來我可能是比較淺陋了,在我看了waterbird的《OO, OO以后, 及其極限》之后,曾經(jīng)深深的自責(zé)過。看來OO沒有我想的那么土,不是直接來自古希臘哲學(xué),而是從維特根斯坦繼承而來的。waterbird的引用而后總結(jié)的一段維特根斯坦的話,使我對(duì)維特根斯坦大為佩服。

                小結(jié)2: 2 主要說明 --- 事實(shí)(facts)由原子事實(shí)(atomic facts)所組成;原子事實(shí)(atomic facts)由更基本的對(duì)象(objects)所組成;我們的關(guān)于外部世界的主觀描述圖畫,與它所描述的外部世界具有相同的邏輯結(jié)構(gòu);注: (這即是相當(dāng)于軟件開發(fā)中的"建模")

              還好,在我昨天列出了閱讀書目之后,gigix提醒我看了另外一篇文章:《維特根斯坦早期思想及其轉(zhuǎn)變》,這是一個(gè)正兒八經(jīng)的哲學(xué)家的文章,總算讓我見識(shí)到了軟件開發(fā)這個(gè)行當(dāng)里,頗有些不懂哲學(xué)的家伙,拿著哲學(xué)來唬人的。

                原子事實(shí)是最簡單的事實(shí),無法再從中分析出其他事實(shí),分析的結(jié)果只能是對(duì)象。因此,原子事實(shí)是對(duì)象的結(jié)合或配置。“對(duì)象是簡單的”〔2.02〕,不可再加以分析,所以,對(duì)象就是簡單對(duì)象,不過,為清楚起見,維特根斯坦還是經(jīng)常采用“簡單對(duì)象”這個(gè)說法。簡單對(duì)象這個(gè)概念引起很多困惑和爭論,其實(shí)維特根斯坦自己也很猶豫,他在筆記中寫道:“我們的困難是,我們總說到簡單對(duì)象,卻舉不出一個(gè)實(shí)例來。” 他曾考慮過關(guān)系、性質(zhì)、視域上的小片、物理學(xué)里的物質(zhì)點(diǎn)。他還說個(gè)體如蘇格拉底、這本書等,“恰恰起著簡單對(duì)象的作用”。一條可能的思路是把簡單對(duì)象理解為一種邏輯要求,一個(gè)邏輯終點(diǎn):“簡單對(duì)象的存在是一種先天的邏輯的必然性。” 在《邏輯哲學(xué)論》中,維特根斯坦大致采用了這條路線,這本書里從未舉例說明什么是簡單對(duì)象。

              維特根斯坦說的對(duì)象,是OO中的對(duì)象嗎?一個(gè)正兒八經(jīng)的現(xiàn)代哲學(xué)家的困惑,OO大師們考慮到了嗎?只有樸素、甚至可以說是幼稚的原子論觀點(diǎn),才會(huì)輕松的混淆:事實(shí)、原子事實(shí)、對(duì)象和具體的物質(zhì),物體。對(duì)于OO來說,對(duì)象非常容易被發(fā)現(xiàn),幾乎隨手一指,都能點(diǎn)到一個(gè)對(duì)象。

              從語言哲學(xué)來說,最為困難的是:“有沒有一種語言,可以清晰地、完整地描述這個(gè)世界?”邏輯原子論原本認(rèn)為有可能。但是,維特根斯坦的后期哲學(xué)轉(zhuǎn)向,恰恰指出了一個(gè)困境,而這個(gè)困境即時(shí)是人類歷史上最為天才的頭腦,也無法“走出”的。德國人最具有理性的思維能力,而其中最為天才的頭腦卻碰上了理性思維的天花板。維特根斯坦很難理解,越是德國天才,他的語言越是晦澀。倒是從中國哲學(xué)的角度,往往能夠看透其中的掙扎。老子在幾千年前就說:“道可道,非常道,名可名,非常名”。因?yàn)樵噲D準(zhǔn)確、無誤、無失漏的命名、描述世界的努力,是不可能成功的。

              因此,我現(xiàn)在可以斷言,面向?qū)ο蟊澈蟮脑诱摚贿^是直接師承古希臘哲學(xué)的簡單、樸素、幼稚的原子論,這樣的原子論哲學(xué),早已破產(chǎn)。作為哲學(xué)史的研究對(duì)象,自有其價(jià)值,而作為指導(dǎo)軟件開發(fā)那么現(xiàn)代活動(dòng)的哲學(xué)理論,實(shí)在是太不適用了。

            !——面向?qū)ο蟮恼軐W(xué)破綻(2)

              2、形而上學(xué)

              當(dāng)我寫下這個(gè)標(biāo)題的時(shí)候,內(nèi)心無比惶恐。這么大個(gè)題目,是我這個(gè)半路出家,Google成才的家伙能夠談?wù)摰膯幔慷嗌僬軐W(xué)家一輩子“皓首窮經(jīng)”,也不過就是研究個(gè)形而上學(xué)啊。

              當(dāng)初,維特根斯坦去找羅素,問到:“你看我是不是一個(gè)十足的白癡?”羅素不知他為什么這樣問,維特根斯坦說:“如果我是,我就去當(dāng)一個(gè)飛艇駕駛員,但如果我不是,我將成為一個(gè)哲學(xué)家”。可見哲學(xué)這東西,只有真正的天才才有能力去研究它。

              還好,我并不是要研究形而上學(xué),我只是要研究面向?qū)ο蟊澈蟮男味蠈W(xué)哲學(xué)基礎(chǔ)。
              我也不是要證實(shí)這個(gè)哲學(xué)基礎(chǔ)的正確性與適用性。我只需要證明“面向?qū)ο蟊澈蟮哪莻€(gè)形而上學(xué)基礎(chǔ)是不正確的、是不適用于軟件開發(fā)的。”

              面向?qū)ο蟮膬纱蠛诵母拍钍牵?#8220;對(duì)象”與“類”。“一切皆是對(duì)象”是由樸素原子論而來的。“萬物皆有類屬”就是由亞里斯多德的形而上學(xué)來的。

              對(duì)于亞里斯多德的形而上學(xué)理論不熟悉的朋友,可以即時(shí)補(bǔ)課,中國人民大學(xué)哲學(xué)系的《西方哲學(xué)史》有好幾節(jié)專門講這個(gè)方面:《亞里斯多德的實(shí)體論I》、《亞里斯多德的實(shí)體論III》。還有就是到Google上去專門搜一下亞里斯多德的邏輯學(xué)說,看完以后,咱們回來接著說。

              咱們用自己的話說一下:“種”、“屬”、“屬差”以及“定義”這幾個(gè)概念。

              種:是一個(gè)大的概念,假設(shè)已經(jīng)預(yù)先定義好了的。
              屬:所有屬于某一種的概念,都是那一種下面的屬。
              屬差:同屬一種的、同一級(jí)別的屬之間的差別,或者說個(gè)性。
              定義:通過種加屬差,可以定義一個(gè)屬的概念。

              舉例說明:人是二足直立動(dòng)物。人是一個(gè)需要被定義的屬,動(dòng)物是人之所屬的種,二足直立是人作為動(dòng)物的共性之外,擁有的個(gè)性,也就是屬差。

              懂得初步的面向?qū)ο缶幊痰耐緜儯銈兌伎闯鰜砹税桑蠖鄶?shù)OO語言也是這么定義類的。你定義一個(gè)Animal,再用Person去繼承Animal。在Animal里有一些屬性與方法,在Person里再加一些人所特有的。很多很多的面向?qū)ο蟮慕炭茣铮踔辆褪侵苯佑眠@個(gè)定義來舉的例子。

              問題出在哪里?或者有人會(huì)問:“這樣有什么不對(duì)嗎?”

              我們可以通過“種+屬差”來定義一個(gè)新的屬嗎?定義成立的前提是什么?先要有種的定義。然后才可能有屬的定義。種的定義又是哪里來的呢?在一個(gè)種的概念之上,必然存在一個(gè)更普遍的種,一個(gè)更大的范疇。在亞里斯多德來說,在所有的種之上的種是“存在”,而存在是無法被定義的。而在面向?qū)ο蟮恼軐W(xué)里,即使是這一個(gè)最基本的哲學(xué)困境也被忽略了,無法被定義的概念,被代換為無需由程序員定義的概念(Object)。屬差的區(qū)別在哲學(xué)家看來,是本質(zhì)的,是基于深刻認(rèn)識(shí)才能提出的。而在面向?qū)ο蟮恼軐W(xué)里,種的共性就是基類所定義的“屬性與方法”,而屬的個(gè)性,就是對(duì)于基類的擴(kuò)展。“種+屬差”變成了“公用代碼+擴(kuò)展代碼”。

              當(dāng)概念定義這樣一個(gè)“問題域的描述手段”,演變成“減少重復(fù)代碼原則”之后。Class繼承的概念就越發(fā)的模糊不清了。我們來總結(jié)一下:

              1、面向?qū)ο笤韭暦Q的描述真實(shí)世界的目標(biāo),采用的工具卻是樸素的“種加屬差”的方式。
              2、面向?qū)ο蠓治鲋校l(fā)現(xiàn)具體的對(duì)象還算是容易的,發(fā)現(xiàn)“種”的概念卻是困難的。
              3、在實(shí)際應(yīng)用中,種概念的發(fā)現(xiàn)與定義,被偷換為公共代碼的抽取。
              4、由于基類的定義的隨意性,導(dǎo)致子類不但可以擴(kuò)展基類的行為與特性,還可以覆蓋(改變)基類的行為與特性。
              5、由于哲學(xué)概念的與開發(fā)概念的混淆,使得在OO領(lǐng)域IS-A、Has-A、Like-A成為最為繞人的概念。

            !——面向?qū)ο蟮陌l(fā)展歷程

              在寫完了哲學(xué)分析部分之后,我總算是喘了一口氣,仿佛穿越了最幽暗的深谷,終于走出了自己最不擅長的領(lǐng)域了。

              后來在MSN上和曹曉鋼聊了挺長時(shí)間,對(duì)于OO的批判,他認(rèn)為有點(diǎn)過頭了。經(jīng)過我的解釋,他提出了一個(gè)更好的建議,清楚的說明自己批判的OO,究竟是哪一個(gè)階段的OO,然后才不至于誤傷到已經(jīng)改善過后的OO。所以我打算整理一下對(duì)于OO發(fā)展階段的看法,寫在下面:

              1、面向?qū)ο蟮恼Z言:先有語言
              2、面向?qū)ο蟮姆治雠c設(shè)計(jì)理論:再有理論
              3、面向?qū)ο蟮脑O(shè)計(jì)原則的全面總結(jié):再有原則
              4、設(shè)計(jì)模式的初步提出:然后才有了真實(shí)的經(jīng)驗(yàn)總結(jié)
              5、重構(gòu)方法的提出:然后才考慮到代碼設(shè)計(jì)的細(xì)節(jié)上的改善
              6、AOP概念的提出:打破OO封裝的“封印”
              7、新語言的出現(xiàn):Python、Ruby之類面向?qū)ο蟮膭?dòng)態(tài)語言:更加方便的語言?
              8、ASM、CGLIB、Mixin之類技術(shù)的出現(xiàn):OO喪鐘的先聲

              具體的對(duì)于各個(gè)階段的分析,將在隨后展開,目前對(duì)于OO的哲學(xué)分析,基本上是針對(duì)原始的OO概念的。隨后的OO技術(shù)的發(fā)展,也在試圖解決由于OO的哲學(xué)基礎(chǔ)假設(shè)帶來的問題,當(dāng)然,越是解決問題,也就離OO的本意越遠(yuǎn),現(xiàn)在有人還以為OO在不斷發(fā)展,而事實(shí)上,OO早就盛極而衰,目前已經(jīng)處在破產(chǎn)的前夜了,我的這篇文章,就是打算使這一天,早日到來!

            敲響OO時(shí)代的喪鐘!——炮彈外面的糖衣

            在javaeye與ajoo結(jié)結(jié)實(shí)實(shí)的吵了一下。是關(guān)于OO的定義的。

            我的總結(jié)陳詞如下:

            ajoo 寫道: 引用: 面向?qū)ο蟊澈蟮哪莻€(gè)形而上學(xué)基礎(chǔ)是不正確的、是不適用于軟件開發(fā)的
            這句話難道不隱含著你知道對(duì)什么是“面向?qū)ο?#8221;這個(gè)前提?
            請(qǐng)問,如果你沒有一個(gè)面向?qū)ο蟮亩x,那么上面的這個(gè)"背后的形而上"是什么意思?
            既然沒有定義,那么怎么會(huì)存在"背后的形而上學(xué)"?以至于你需要證明它不正確?

            要證明一個(gè)沒有定義的東西不正確,這只能是一個(gè)玩笑。
            undefined就是undefined,根本無所謂正確不正確,也談不上背后的形而上學(xué)。



            這句話并不隱含著對(duì)于“面向?qū)ο?#8221;的定義。
            某種需要被明確指出的哲學(xué)作為一種推動(dòng)的力量,導(dǎo)致OO的出現(xiàn)。
            導(dǎo)致OO這個(gè)時(shí)代的到來。

            要分析OO時(shí)代背后的哲學(xué)推動(dòng)力量,需要某種思想史意義上的猜測,這個(gè)猜測不是以對(duì)于OO的嚴(yán)謹(jǐn)定義為前提的。

            可以稱之為:“復(fù)雜現(xiàn)象背后的哲學(xué)動(dòng)力猜測”,對(duì)于這樣一個(gè)OO時(shí)代中出現(xiàn)的復(fù)雜現(xiàn)象,我們需要有一個(gè)“刀刀見血”的“現(xiàn)象批判”!而不是“勉為其難”給OO一個(gè)定義。

            ajoo 寫道: 老莊別急.研究問題嘛.

            你如果這么批判我當(dāng)然沒異議.

            不過你的文章中不是這樣的呀.你根本沒提概念混亂不清這碼事.而是上來就直接給"面向?qū)ο?和哲學(xué)拉組織關(guān)系了.



            姑且說一個(gè)OO的定義:

            OO是一種哲學(xué):這是OO所屬種的定義
            OO是一種指導(dǎo)軟件開發(fā)的哲學(xué):這是種加屬差的標(biāo)準(zhǔn)定義。
            OO作為一種哲學(xué)自然包括世界觀和方法論。
            OO的世界觀是:“軟件開發(fā)面對(duì)的世界中一切皆是對(duì)象”;
            OO的方法論是:“將世界分解為具有類屬關(guān)系的大大小小的類,是軟件開發(fā)的分析、設(shè)計(jì)與編程的指導(dǎo)方法”

            OOP就是在這種哲學(xué)指導(dǎo)之下的開發(fā)方法
            OOA就是在這種哲學(xué)指導(dǎo)之下的分析方法
            OOD就是在這種哲學(xué)指導(dǎo)之下的設(shè)計(jì)方法

            總結(jié)而言,這樣的定義實(shí)際上毫無疑義,不過是炮彈外面的糖衣而已。無論是對(duì)于我還是我的讀者,這樣的定義都沒有給我和他們帶來任何有價(jià)值的東西!

            ajoo,你覺得這樣的定義有意思嗎?

            敲響OO時(shí)代的喪鐘!——類型系統(tǒng)

              多謝Trustno1的幫忙,他因?yàn)槿滩蛔×耍源蛩銕臀野杨}破了

                寫文章,不破不立。我說這句話還不針對(duì)你即將要發(fā)表的解決方案,只是針對(duì)你前5頁的"破",本來以為你能說清楚的,但是你破了5頁都破不了,看著肚子都癢,索性就用EL這樣前人的觀點(diǎn)幫你破了,你就快點(diǎn)立論吧。

              而他引用的兩段話,一段正好可以用來承上“Elminster的那段關(guān)于面向?qū)ο笈c本體論的論述”,另外一段正好可以用來啟下“事實(shí)上,我們猜想是,如果沒有知識(shí)表示和自動(dòng)推力工作的幫助,這些問題(指類,繼承)是無法僅僅通過計(jì)算機(jī)語言設(shè)計(jì)的方式來處理的。”——SICP 2.5,中文版136頁,角注118。

              Elminster那篇論述,正好和我的文章形成一個(gè)互補(bǔ)關(guān)系,他以極為清晰的表達(dá)語言,說明了OO打算以類型化方式描述真實(shí)世界,所面臨的難題。這也使我不必再次動(dòng)腦子思考如何回答JavaCup的哲學(xué)方面的疑問了。而下面這一段話我想特別再次引用一下:

                就我個(gè)人來說,比較傾向于認(rèn)為這條最終是走不通的死路,人是從事物的特征與屬性歸納出它的“類型”,而不是相反。某種意義上說,“類型”是為了節(jié)省描述時(shí)間而產(chǎn)生的 …… 唔,這個(gè)太遠(yuǎn)了,所以就此打住。

              大家記住這段話中的,特征、屬性、類型這幾個(gè)關(guān)鍵字。我先繞個(gè)小彎再回到這個(gè)話題上來。

              我之前分析的面向?qū)ο蟮恼軐W(xué)漏洞時(shí),也有不少朋友認(rèn)為,說:“面向?qū)ο蟛荒芎芎玫拿枋稣鎸?shí)世界,并非一個(gè)有意義的指控。OOA、OOD本來就是用來對(duì)需求建模的。也就是打算描述需求世界。”

              其實(shí)我的指控分為兩個(gè)階段,一方面,OO所依據(jù)的哲學(xué)導(dǎo)致了軟件開發(fā)的苦難,而且至今余毒未清。另一方面,即使是指打算對(duì)需求建模,OO的技術(shù)手段也是有缺陷的。

              就這么說吧:OO的類型系統(tǒng),原本是從ADT來的。一個(gè)抽象數(shù)據(jù)類型,將數(shù)據(jù)與操作封裝在一起,是出于對(duì)于數(shù)據(jù)被“莫名其妙的修改”的擔(dān)心。但是,結(jié)果呢,一個(gè)ADT如果不支持繼承,那么它的代碼就無法被重用。所以O(shè)O就在ADT的基礎(chǔ)上增加的一個(gè)繼承,通過繼承的方式來復(fù)用以有的代碼。這樣的思路原本沒有太大的問題,如果它僅僅只想表達(dá)各種自定義數(shù)據(jù)類型的話。

              但是在OO的哲學(xué)提出之后,一切皆是對(duì)象,所以一切出于類型體系之內(nèi),對(duì)于數(shù)據(jù)類型的定義,擴(kuò)展到了對(duì)于現(xiàn)實(shí)世界中各種實(shí)體的類型定義,整個(gè)一個(gè)類型系統(tǒng),其內(nèi)在的語義大大擴(kuò)展復(fù)雜化了。更糟糕的是——引用Elminster的話是從事物的特征與屬性歸納出它的“類型”——而因?yàn)镺O封裝也就是隱藏了內(nèi)部數(shù)據(jù),事物的特征與屬性,從其本質(zhì)屬性,被轉(zhuǎn)義為對(duì)外的提供的操作接口。但是,要分析一個(gè)實(shí)體的本質(zhì),而不是實(shí)體的外部表現(xiàn),更不僅僅是“我能對(duì)他做什么”。這才是實(shí)體分析有可能成功的關(guān)鍵,而在OO的語言設(shè)定中,這卻是難以做到的。

              我們來看兩張圖片

              這是在SICP里討論類型系統(tǒng)的第一張圖片,我稱之為“OO成功案例”。

              這是在SICP里討論類型系統(tǒng)的第二張圖片,我稱之為“OO失敗案例”。

              為什么一個(gè)能夠成功,而另一個(gè)卻會(huì)失敗?以往的解釋其實(shí)比較“直覺”。看著這個(gè)圖,就想當(dāng)然的以為:“這是因?yàn)槎嘀乩^承導(dǎo)致的!”事實(shí)上呢?

              第一張圖中所顯示的成功,很多人會(huì)認(rèn)為這是由于這一個(gè)對(duì)象塔中的每一個(gè)對(duì)象都能夠支持加減乘除運(yùn)算。而在幾何圖形中,這樣一致的操作接口不存在了。而事實(shí)上,正是因?yàn)閺?fù)數(shù)、實(shí)數(shù)、有理數(shù)、整數(shù),在本質(zhì)屬性上有一致之處,他們才能表現(xiàn)出一致的“可加減乘除性”。而不是相反。當(dāng)我們畫出第二張對(duì)象關(guān)系圖的時(shí)候,也不是根據(jù)幾何圖形可以接受的操作類型來進(jìn)行分類與顯示繼承關(guān)系的,而是根據(jù)不同的幾何圖形的本質(zhì)屬性的相近程度來劃分類型體系的。多邊形的本質(zhì)是: “多條有限長直線組成了一個(gè)封閉圖形”,而三角形與四邊形的本質(zhì)則是,邊的數(shù)量分別為三和四。等腰三角形的本質(zhì)是,不但邊的數(shù)量為三,而且其中有兩條邊的長度相等,直角三角形的本質(zhì)是不但邊的數(shù)量為三,而且其中有一個(gè)直角。如此等等......

              各位,請(qǐng)?jiān)俅嗡伎歼@樣的分類體系的內(nèi)涵。

             

             

              我的結(jié)論是:“一個(gè)類型,是由其本質(zhì)決定了所能表現(xiàn)出的可操作性,而不是有其所能接受的操作決定了其本質(zhì)。然而,OO正好把這個(gè)問題搞反了!”

            敲響OO時(shí)代的喪鐘!——繼承、重用、多態(tài)

              OO的核心思想就是繼承,那么,OO為什么要繼承呢?對(duì)于這個(gè)問題,OO的理論大師們有好多個(gè)版本的解釋:

              1、“這是OO的類型系統(tǒng)自然的要求。設(shè)想一下生物學(xué)的分類系統(tǒng):動(dòng)物——>哺乳動(dòng)物——>靈長類動(dòng)物——>人類。或者設(shè)想一下我們的概念系統(tǒng):機(jī)器——>交通工具——>汽車——>小轎車。這樣的現(xiàn)象在你的生活中難道不是隨處可見嗎?”

              2、“如果你有一個(gè)類,叫做車輛,這個(gè)車輛類能夠移動(dòng),現(xiàn)在你要建立一個(gè)子類,叫做家庭型轎車,你就可以直接繼承車輛這個(gè)類,而不需從頭再寫移動(dòng)部分的代碼了呀!”

              3、“如果你有三個(gè)類,三角形、四邊形、正方形,他們都需要顯示在屏幕上,那么你就可以建立一個(gè)基類叫多邊形,提供一個(gè)draw()方法,然后讓那個(gè)三個(gè)類都繼承這個(gè)多邊形類并且覆蓋那個(gè)draw()方法,這樣,你就可以在繪圖的時(shí)候,統(tǒng)一對(duì)一種多邊形類進(jìn)行操作,不用去管那個(gè)對(duì)象究竟是哪一種多邊形。”

              這三種解釋,是最為典型的OO繼承的好處的解釋了。但是你如果仔細(xì)的看,就能發(fā)現(xiàn)這三種好處,分別描述的是:“概念的特化”、“代碼的重用”以及“接口的重用”。或者可以分別命名為:“繼承”、“重用”、“多態(tài)”。

              “這樣有什么問題嗎?”,也許有人會(huì)問。問題就出在這三個(gè)好處是用一種方法提供,而這三個(gè)好處相互之間有時(shí)是相通的,有時(shí)又是矛盾的!當(dāng)我們運(yùn)用OO語言,來寫這樣的繼承的語句時(shí),一切都是“攪和在一起的”!

              假設(shè)Class A有兩個(gè)屬性和兩個(gè)方法:String a1;int i;void f1();void f2();當(dāng)我們另外寫一個(gè)Class B去繼承Class A的時(shí)候,我們可以繼續(xù)使用某些屬性,而覆蓋另一些屬性,還可以繼續(xù)使用某些方法,而重寫另一些方法。還可以添加一些新的屬性,還可以添加一些新的方法。如果在加上各種訪問控制符的限定與修正。誰能夠告訴我:“這個(gè)Class B究竟想干什么?!”

              也許有人會(huì)繼續(xù)為這樣的現(xiàn)象辯解:“這是對(duì)于繼承的誤用,正確的OO程序不會(huì)這樣的!”
              但是,這個(gè)辯解并不成立,幾乎所有的OO的編程語言,都沒有在繼承問題上做出太多“非此即彼”的限制,原因只有一個(gè),那就是,在某些特定的場合,這樣的“拼盤”是相對(duì)最合理的編碼方式。

              我們前面還沒有提到多重繼承,一個(gè)允許多重繼承的語言,會(huì)讓這個(gè)問題更為復(fù)雜,也可以說會(huì)使得場面越發(fā)的混亂。讓我們舉一個(gè)例子,這是Eiffel語言的繼承語法,讓我們看一看面對(duì)繼承這樣一件事情,一個(gè)程序員,究竟需要考慮多少問題。來源是《對(duì)象揭密》,我就一邊抄,一邊直接翻成中文了。

              繼承    :inherit 父類列表
              父類列表  :{父類 ";" ... }
              父類    :類名[特性適配說明]
              特性適配說明:[Rename]    :重命名以消除名字沖突
                     [New_exports] :重新設(shè)定特性導(dǎo)出的分組
                     [Undefine]   :撤銷定義
                     [Redefine]   :重定義以取代虛函數(shù)
                     [Select]    :更加高級(jí)的功能
                     end

              最值得看的就是這個(gè)特性適配說明,更加深入的說明還是各位自己去找書看吧。這就是號(hào)稱優(yōu)雅的解決了OO繼承問題的Eiffel語言。他所謂的優(yōu)雅,可以不客氣的說,就是把所有的麻煩都明確的告訴你,而不是像C++和Java那樣,假裝什么事情都沒有發(fā)生過。但是,麻煩依然在那里,并沒有減少,根本的解決方法,是應(yīng)該不讓這樣的麻煩出現(xiàn)呀!可是OO確實(shí)無法做到這一點(diǎn)。

              因?yàn)樗褦?shù)據(jù)和操作封裝在了一起,然后又偷換了實(shí)體本質(zhì)的概念,在這樣的情況下的OO,他的繼承是肯定搞不好的!

            敲響OO時(shí)代的喪鐘——接口、泛型與重用(1)
             先說點(diǎn)提外話,我從小學(xué)開始學(xué)習(xí)BASIC和LOGO,到后來學(xué)習(xí)了FoxBase、FoxPro、C/C++、Visual Basic、VBScript、JavaScript、PHP,之后才開Java編程,之后也沒有再換過語言。誠實(shí)的說,只有Java語言,是我認(rèn)認(rèn)真真的學(xué)習(xí)和研究的。對(duì)于面向?qū)ο蟮睦斫猓彩窃趯W(xué)習(xí)和使用Java之后,才真的開始深入思考這方面的問題,在此之前,我甚至認(rèn)為所有的語言都沒有什么本質(zhì)的差別,面向某某和面向某某之間也沒有什么大不了的差別。
             
            所以當(dāng)我想要寫這篇文章的時(shí)候,其實(shí)內(nèi)心是相當(dāng)惶恐的,我對(duì)于面向?qū)ο蟮牧私猓鋵?shí)只來自于一種語言,那就是Java,而Java是不是就等于是面向?qū)ο竽兀慌率遣荒苓@么說的吧。
             
            JavaEye有人留言:“不要到時(shí)候說不圓影響了一世英名。”;“討論這個(gè)問題,我還是建議去看 SICP,你會(huì)發(fā)現(xiàn)所有OO具有的思想SICP都講到了”;“實(shí)際上我很懷疑莊某最后還是跑不出SICP的框架,如果是這樣,那么其理論的價(jià)值就要打折扣了。”我那個(gè)慌啊,趕緊到書店去買了SICP回來仔仔細(xì)細(xì)的啃,然后再在MSN上向T1好好的請(qǐng)教過幾次,最后總算放心了,我的思路,不在SICP的框架內(nèi),或者說,不在SICP作者的思考局限之內(nèi)。
             
            還有人留言,提到了C++:“OO門檻較高是不爭的事實(shí)。的確很多人并沒有進(jìn)入。有句話可以套
            用,沒有三年的功底,最好不要說懂C++。幸運(yùn)的是這門東西的回報(bào),會(huì)告訴你所付出的是完全值得的。”我又慌了,C++背后的面向?qū)ο螅蔚雀呱睿覅s從來沒有用C++做過哪怕1000行以上的程序,這等門檻都沒有入的人,有資格評(píng)價(jià)面向?qū)ο竽敲创蟮氖虑椋s緊的,我又到書店去買了一本《對(duì)象揭秘》,我對(duì)于當(dāng)年gigix的一篇介紹《編程語言的宗教狂熱和十字軍東征》始終記憶猶新,里面提到了面向?qū)ο螅岬搅薈++的無數(shù)缺點(diǎn),還提到了Eiffel,一個(gè)據(jù)說是比C++要好無數(shù)倍的面向?qū)ο蟮恼Z言。如果我要想加快速度,又想保證質(zhì)量的話,從《對(duì)象揭秘》里面應(yīng)該可以找出很多現(xiàn)成的彈藥吧。
             
            抱著急于求成的功利主義目的,我開始仔細(xì)看這本《對(duì)象揭秘》,一看之下,真是大有收獲:
            *C++果然毛病多多,而且作為第一個(gè)大面積流行的OO語言,OO的實(shí)際含義更多的來自于C++。
            *Java的毛病少了很多,因?yàn)樗氲囊恍└拍睿辉偈褂玫囊恍└拍睿蟠蟮臏p少了C++式OO編程的陷阱,只是這樣一來,在復(fù)雜問題上使用Java語言,往往會(huì)寫出很丑陋的程序。
            *Eiffel同樣也是反思C++缺點(diǎn)的語言,但是它的改進(jìn)基本上是表面的,Java是使問題簡化,哪怕犧牲語言的表達(dá)能力,而Eiffel是使問題表面化、集中化,陷阱雖然沒有了,但是問題一個(gè)都沒有減少,反而因?yàn)?#8220;讓人眼花繚亂的復(fù)雜語法”,讓人望而卻步。
            *《對(duì)象揭秘》是一本很一般的書,作者花了十多年的時(shí)間攢出一本書來,實(shí)質(zhì)上還是BBS里一段一段討論的水平。
             
            ————————————————————————————————————————
             
            好了,題外話結(jié)束,接下來討論正題,今天主要研究OO的概念中兩個(gè)較為邊緣的概念“接口”與“泛型”,以及探討一個(gè)實(shí)際上最為重要的誤用——“重用”。
             
            1、關(guān)于“接口”
             
            接口是什么東西?接口是一個(gè)很奇怪的東西!接口之所以奇怪,因?yàn)樗膩睚埲ッ}實(shí)在是讓人看不懂。基礎(chǔ)的OO概念中,并沒有接口的位置,因?yàn)榘凑?#8220;經(jīng)典的”面向?qū)ο笏季S,一個(gè)沒有代碼、沒有內(nèi)部數(shù)據(jù),只有方法說明的東西,是無法理解的。
             
            追根溯源的說,首先是在C++的面向?qū)ο髮?shí)踐中,發(fā)現(xiàn)了對(duì)于“抽象類”的需要,為什么需要抽象類呢?因?yàn)榇a重用的需要,比如一個(gè)基類,其實(shí)就是一堆公用代碼,有一個(gè)名字把它框起來叫一個(gè)類,但是完全沒有道理把它實(shí)例化。像這種“發(fā)育不完全的類”,該拿它怎么辦呢?OK,就讓它不能“出生到這人世間來”。抽象類的本質(zhì)就是這個(gè)樣子的。
             
            到了Java,因?yàn)閷?duì)于多繼承的恐懼,Java完全擯棄了多重繼承,這是Java被攻擊得最多的地方,因?yàn)檫@樣的單根繼承,實(shí)在是因噎廢食——因“怕被繼承體系搞亂”廢“更加方便道代碼重用”。于是Java就說了:我們有一個(gè)“安全的”多重繼承,叫做“接口”,這個(gè)接口,完全沒有代碼,只有說明,所以絕對(duì)安全,但是由能夠?qū)崿F(xiàn)多重繼承的好處云云。
             
            而事實(shí)上呢?多重繼承的根本目的,并不是像Java所宣稱的那樣為了“同時(shí)繼承多種類型”,而是為了“同時(shí)重用多組代碼”。接口這一發(fā)明,完全不能達(dá)到多重繼承的代碼重用效果,卻被宣稱為多重繼承的替代品。其實(shí)質(zhì)是:“從一個(gè)發(fā)育不完全的實(shí)體,變成了一張徹底沒有發(fā)育的皮”。
             
            最為令人感到奇怪的,還不是“接口的出現(xiàn)”,而是“面向接口編程”的出現(xiàn),Java被冠以“面向接口的語言”的美名,“面向接口設(shè)計(jì)”成了OO的設(shè)計(jì)原則之一,“針對(duì)抽象,不要針對(duì)具體”,成了OO名言之一。實(shí)在是......
            關(guān)于OO的設(shè)計(jì)原則,我下面還會(huì)專門討論,這里先指出一個(gè)大漏洞:“抽象類的那個(gè)抽象,和與具體相對(duì)的那個(gè)抽象,根本就不是一回事!”

             寫技術(shù)文章,例子其實(shí)很難舉,特別是找到有殺傷力的,決定性的例子,相當(dāng)困難。昨天我接著看《對(duì)象揭密》,總算被我找到一個(gè),當(dāng)然,它那上面的解說,實(shí)在是比較模糊,因此我決定用自己的話重新敘述一遍,代碼示例用Java的泛型語法,但是要表達(dá)的意思,確實(shí)所有的具有泛型的OO語言都需要面對(duì)的。

            java代碼:

            public class X {
                    protected int i;
                    public void add(int a){
                            i=i+a;
                    }
            }

            public class Y1 extends X {
                    public void add(int a){
                            i=i+a*2;
                    }
            }


            public class Y2 extends X {
                    public void add(int a){
                            i=i+a*3;
                    }
            }

              這是三個(gè)最簡單的類,Y1和Y2都繼承了X,并且重寫了add函數(shù)。當(dāng)然,這只是舉例,實(shí)際上這三個(gè)add中,有兩個(gè)是不合理的。

            java代碼:

            ArrayList<X> listx=new ArrayList<X>();
            ArrayList<Y1> listy1=new ArrayList<Y1>();
            ArrayList<Y2> listy2=new ArrayList<Y2>();
                          
            listx.add(new X());
            listx.add(new Y1());
            listx.add(new Y2());

            listy1.add(new Y1());
            listy2.add(new Y2());

              這幾行代碼都非常簡單,只是為了說明一個(gè)道理,在ArrayList<Y1>和ArrayList<Y2>中,能夠放的就只有Y1和Y2了,而在以X為泛型的ArrayList中,就可以放X、Y1、Y2了。而當(dāng)然了,這樣的用法,只怕是不合泛型的目標(biāo)的,本來就是希望能有一個(gè)類型的自動(dòng)檢查與轉(zhuǎn)換,都放在ArrayList<X>中,幾乎就等于都放在ArrayList<Object>中了。

             

              現(xiàn)在我們有這樣一個(gè)需求,對(duì)于得到的ArryaList,能夠一一調(diào)用里面的對(duì)象的add(int a)方法,當(dāng)然了,只要這個(gè)ArrayList里的對(duì)象都是X或者X的子類就行了。我們可以寫出這樣的代碼:

            java代碼:

            public void addListX(ArrayList<X> listx){
                    for(int i=0;i<listx.size();i++){
                            X x=listx.get(i);
                            x.add(1);
                    }
            }

              是不是很簡單?且慢,這個(gè)addListX函數(shù),我們能夠把listx傳遞給它,但是能不能把listy1和listy2

            也傳遞給它呢?如果我們能夠把listy1和listy2傳遞給它,就相當(dāng)于執(zhí)行了如下的類型轉(zhuǎn)換代碼:

            java代碼:

            ArrayList<Y1> listy1=new ArrayList<Y1>();
            ArrayList<X> listx=listy1;

              這樣做行不行呢?在Java和C++中,是不行的。也就是說,如果我們要想只寫一遍addListX這樣的函數(shù),而不用再多寫兩遍 addListY1();addListY2();這樣的函數(shù),就需要把所有的X,Y1,Y2這樣的類型都放到ArrayList<X>這樣的容器里,否則,addListX函數(shù),是不接受ArrayList<Y1>和ArrayList<Y2>類型的。即使Y1和 Y2是X的子類型,ArrayList<X>與ArrayList<Y1>也毫不相干。不能相互轉(zhuǎn)換。

             

              有人也許會(huì)說,為什么這么限制嚴(yán)格呢?Eiffel就沒有這么這么嚴(yán)格的限制,他允許ArrayList<Y1>自動(dòng)轉(zhuǎn)型為 ArrayList<X>,這樣是好事情嗎?如果listy能夠被轉(zhuǎn)型為ArrayList<X>,那么就可以往里面添加Y2類型的對(duì)象了,這又是原來的泛型ArrayList<Y1>不允許的。也就是說:除非addListX能夠保證只對(duì)listy1做只讀操作,否則,類型安全性這個(gè)泛型原本要追求的目標(biāo)就不能實(shí)現(xiàn)了。而如果要追求絕對(duì)的類型安全性,像C++和Java那樣,那么代碼要么就得寫三遍,要么X、Y1、 Y2類型的對(duì)象就得都放到ArrayList<X>這樣的泛型容器里去。

             

              注意看這其中的左右為難的狀況,繼承、多態(tài)、泛型,并不是真正正交的、互不干擾的,而是在一個(gè)相當(dāng)普通的目標(biāo)面前,他們就起了沖突了。

            敲響OO時(shí)代的喪鐘——重用為什么那么難?
            先說句提外話,由于徐昊的推薦,我在網(wǎng)上找到了《Modern C++ Design》繁體中文版的前四章PDF文件。果然不出我所料,Loki的設(shè)計(jì)思路與我的隨后將會(huì)介紹的自己的設(shè)計(jì)實(shí)現(xiàn),實(shí)有異曲同工之妙。對(duì)于C++的熟悉程度超過Java的某同學(xué),可以先去看看這本書,如果能夠同意書中的觀點(diǎn),再來與我討論,相信會(huì)得到更多的收獲。
            程序員都是聰明人,沒有誰愿意干重復(fù)勞動(dòng)這樣的傻事,因此,程序中出現(xiàn)重復(fù)代碼是程序員的恥辱。就算不能消除重復(fù)代碼,至少也可以對(duì)于相同的功能,用不同的代碼來實(shí)現(xiàn)所以發(fā)明新輪子的程序員才會(huì)那么多。
             
            面向?qū)ο笞鳛橐环N橫空出世的新技術(shù),首先承諾的就是“更好的重用性”,而“重用性”這樣一個(gè)閃閃發(fā)光的詞,也的確能夠吸引程序員的實(shí)現(xiàn),那么多新的理論、新的技術(shù)、新的方法、新的框架、新的思想,用來說服別人接受的一個(gè)最大的理由,就是“更好的重用性”。然而,OO以及一直以來不斷發(fā)展的 OO相關(guān)技術(shù),對(duì)于重用性的提高,作出了多大的貢獻(xiàn)呢?
             
            JavaEye的age0有一段話特別讓我佩服:“我還是得反復(fù)強(qiáng)調(diào),OO設(shè)計(jì)的價(jià)值并不在于所謂的“代碼重用”或者“接口重用”,任何一種設(shè)計(jì)方法都能夠獲得“重用”的能力,就算是寫匯編一樣可以“重用”。”一個(gè)同志能夠如此決絕的對(duì)于重用不屑一顧,真是了不起。我們還是來面向大多數(shù)希望更好的重用的程序員,分析一下在OO出現(xiàn)之后程序員是如何追求重用這一目標(biāo)的吧。
             
            在面向過程的時(shí)代,重用很簡單,就是代碼的重用,函數(shù)、函數(shù)庫、模板、模板庫。如此而已。在ADT出現(xiàn)之后,世界分裂了。代碼重用的需求,現(xiàn)在分裂為三個(gè)部分:數(shù)據(jù)類型的重用、ADT內(nèi)部代碼的重用、操作ADT的代碼的重用。
             
            這句話特別關(guān)鍵,讓我再仔細(xì)分析給大家看看:ADT=抽象數(shù)據(jù)類型。就是封裝了操作和數(shù)據(jù)的一種用戶自定義數(shù)據(jù)類型。
             
            1、如果僅僅是ADT,那么相近的用戶自定義數(shù)據(jù)類型就無法重用,因此出現(xiàn)了一個(gè)數(shù)據(jù)類型的重用需求;
            2、因?yàn)锳DT封裝了對(duì)于數(shù)據(jù)的操作,對(duì)于A類數(shù)據(jù)的操作,就無法被B類數(shù)據(jù)重用,因此出現(xiàn)了一個(gè)ADT內(nèi)部代碼的重用需求;
            3、因?yàn)閷?duì)于ADT的操作是借助ADT的外部界面執(zhí)行的,也就是說,對(duì)于接近但是不同的類型的操作,必須寫出不同的代碼,因此出現(xiàn)了對(duì)于操作ADT的代碼的重用需求。
             
            這樣的分裂的三個(gè)需求,在隨后的OO的發(fā)展歷史中,分別被兩種方法來初步滿足。第一、第二種需求,被繼承這樣的技術(shù)來試圖滿足;第三種技術(shù)被泛型類試圖滿足。這兩個(gè)技術(shù)滿足了三種重用的需求了嗎?沒有!不但沒有滿足,而且還帶來的諸多麻煩的問題,更在分別滿足三種需求的時(shí)候,起了沖突。(前面已經(jīng)討論過的內(nèi)容,可以回頭再看一看,我將來再改寫這篇文章的時(shí)候,會(huì)將封裝 VS. 重用性的分析,作為一根主線貫穿OO分析的始終,現(xiàn)在就不重新組織結(jié)構(gòu)了。)
             
            由于封裝與重用性之間,存在著本質(zhì)性的沖突,因此,OO的分析、設(shè)計(jì)、編程方法就始終處于一個(gè)難學(xué)、難用、難懂的狀態(tài)。我們說給OO下定義非常困難,但是大家都應(yīng)該承認(rèn),ADT是OO的根。數(shù)據(jù)與操作的封裝是一切OO思想的基礎(chǔ),也是所有OO信奉者從來沒有懷疑的“前提”!
             
            在繼承與泛型不能解決重用難題之后,OO大師們提出了OO設(shè)計(jì)原則,提出了OO設(shè)計(jì)模式,這是我接下來的文章里將要細(xì)細(xì)批駁的兩大“貢獻(xiàn)”。但是OO的原則、模式,依然沒有解決重用難題。在此之后,又有人提出了AOP、IoC這樣的概念,還有人真正開始和我一樣懷疑封裝的意義,而開發(fā)了 CGLib,Mixin這樣的動(dòng)態(tài)改變對(duì)象行為與結(jié)構(gòu)的技術(shù),這也是我將要批判的“最新進(jìn)展”。到了這個(gè)時(shí)候,真正理解OO本質(zhì)的人,應(yīng)該已經(jīng)看出來了, OO時(shí)代即將結(jié)束,因OO而帶來的混亂也該結(jié)束了。現(xiàn)在唯一的問題是:“什么樣的技術(shù),才是可行的、替代的方案呢?”

            敲響OO時(shí)代的喪鐘——OO設(shè)計(jì)原則批判

            OO設(shè)計(jì)原則!

              這是很多開發(fā)資源網(wǎng)站必備的一個(gè)欄目、專題、至少也要轉(zhuǎn)載一篇放在自己的網(wǎng)站上的東西。所有的程序員,如果你不開發(fā)面向?qū)ο蟮某绦蛞簿土T了—— 反正你已經(jīng)落伍很久了,如果你要想開發(fā)OO程序,而竟然沒有把那些OO設(shè)計(jì)原則熟讀背誦,搞得滾瓜爛熟。那么你就完了,一個(gè)公司面試你的時(shí)候,問你:“你對(duì)SRP的理解是怎么樣的?”,而你居然不知道SRP是什么,那么這家公司你也就別想進(jìn)去了。作為OO程序員的《舊約圣經(jīng)》(設(shè)計(jì)模式自然是《新約圣經(jīng)》)他怎么就會(huì)那么神圣呢?

              介紹OO設(shè)計(jì)原則的文章很多,我在google上搜索了一下:“約有58,200項(xiàng)符合OO設(shè)計(jì)原則的查詢結(jié)果”。真正能夠介紹得透徹的,還真是沒幾個(gè)。正好我手邊有一本Bob大叔的《UML for JAVA Programmers》那上面的介紹,在我看來,是最好的OO設(shè)計(jì)原則介紹之一了。另外一本不在手邊的《敏捷軟件開發(fā) 原則、模式與實(shí)踐》也是Bob大叔的,還要詳盡一些。如果要批判,自然要找這樣的靶子來練!

             

              1、單一職責(zé)原則(SRP)

            一個(gè)類只能因?yàn)橐粋€(gè)原因而改變。

              “A class should have one, and only one, reason to change.”

               這個(gè)原則何等的簡單,又是何等的模糊呢?什么叫做一個(gè)原因呢?我們來看下面這個(gè)類:

            java代碼:

            class User{
                private String name;
                private int age;
                public void setName(String name){
                    this.name=name;
                }
                public void setAge(int age){
                    this.age=age;
                }
            }

            請(qǐng)問,這個(gè)類是不是違反了SRP原則呢?設(shè)置用戶的名字與設(shè)置用戶的年齡,是一個(gè)原因,還是兩個(gè)原因呢?Bob大叔在自己的書里舉了一個(gè)例子,說明了違反SRP原則的情況,一個(gè)Employee類,包含了計(jì)算工資和扣稅金額、在磁盤上讀寫自己、進(jìn)行XML格式的相互轉(zhuǎn)換、并且能夠打印自己到各種報(bào)表。我說拜托啊大叔!一個(gè)類里的方法多到如此驚人的程度,自然是違反了SRP原則,但是我們要為它瘦身,該瘦到什么程度呢?按照大叔繼續(xù)給出的自己的答案,它把計(jì)算工資和扣稅金額的兩個(gè)功能留給了Employee,其他的分離了出去。這個(gè)答案正確嗎?員工的工資和稅收是自己算的?還是有一個(gè)“財(cái)務(wù)部”對(duì)象來計(jì)算的呢?且不說那么掃興的事情,就看看那個(gè)類圖里分離出來的那幾個(gè)類:

            EmployeeXMLConverter、EmployeeDatabase、TaxReport、EmployeeReport、 PayrollReport。這些類還需要有自己的內(nèi)部數(shù)據(jù)嗎?請(qǐng)注意,他們事實(shí)上都是通過接受Employee對(duì)象的內(nèi)部數(shù)據(jù)而工作的,換句話說,這些所謂的類,根本就不是什么類,只不過是一個(gè)個(gè)用Class關(guān)鍵字包裹起來的函數(shù)庫!當(dāng)我們看到一個(gè)臃腫的Employee類,被拆成6個(gè)各不相同的類之后,內(nèi)心自然升起了“房子打掃干凈之后的喜悅”。但是,且慢!灰塵到哪里去了呢?當(dāng)我們把一個(gè)類拆成6個(gè)類之后,那個(gè)原本的類自然已經(jīng)遵守了SRP原則,然后新誕生的5個(gè)類,是不是也該遵守SRP原則呢?如果我們不能將一個(gè)原則應(yīng)用于整個(gè)系統(tǒng)設(shè)計(jì)中的所有的對(duì)象,僅僅像小孩打掃衛(wèi)生一樣,把灰塵掃到隔壁房間,這剩下的事情,誰來處理呢?

            好吧,我們不要這么嚴(yán)厲,畢竟這只是一個(gè)原則,追問太深似乎并不合適。我只想再搞清楚幾個(gè)問題:按照SRP原則,C++中是不是一律不應(yīng)該出現(xiàn)多重繼承呢?按照SPR原則,Java中的一個(gè)類是不是一律不應(yīng)該既繼承一個(gè)類,又實(shí)現(xiàn)一個(gè)對(duì)象呢?一個(gè)簡單的POJO,被動(dòng)態(tài)增強(qiáng)之類的辦法,添加出來的新的持久化能力,是不是也是違反SRP原則的呢?歸根結(jié)蒂,我的問題是:按照SPR原則,我那些剩下的,但是又必須要找地方寫的代碼,究竟應(yīng)該寫在哪里呢?

             

              2、開放-封閉原則(OCP)

             軟件實(shí)體(類、模塊、方法等)應(yīng)該允許擴(kuò)展,不允許修改。

              “Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”

              這個(gè)原則倒是非常的清楚明白,你不能改已經(jīng)寫好的代碼,而應(yīng)該擴(kuò)展已有的代碼!如何做到這一點(diǎn)呢?Bob大叔舉了一個(gè)經(jīng)典的例子:個(gè)人認(rèn)為這個(gè)例子說明的是一個(gè)使用接口,隔離相互耦合的類的通常做法。而且這個(gè)做法不應(yīng)叫做OCP,而應(yīng)該叫做DIP。查了一下c2.com里的OCP的解釋:

             

              In other words, (in an ideal world...) you should never need to change existing code or classes: All new functionality can be added by adding new subclasses and overriding methods, or by reusing existing code through delegation.

             

              但是在Bob大叔的OCP解釋中,這個(gè)原則的具體實(shí)現(xiàn)被偷換了概念,從“鼓勵(lì)多使用繼承”,變成了“鼓勵(lì)面向接口編程”。為什么?因?yàn)槔^承式OCP實(shí)踐已經(jīng)被證明會(huì)帶來相當(dāng)多的副作用,而面向接口編程又如何呢?我們?cè)谟懻揇IP的時(shí)候再詳細(xì)討論吧。

             

              有一個(gè)在JavaEye的討論的連接可以參考:對(duì)于OCP原則的困惑

             

              3、里斯科夫替換原則(LSP)

              子類型必須能夠替代他們的基本類型。

              “Subtype must be substitutable for their base types.”

              對(duì)于這個(gè)問題,我都不用多說什么,只引用Bob大叔在c2上的一句話,以作為我的支持。

              “I believe that LSP is falsely believed by some few to be a principle of OO design, when really it isn't.”

             

              4、依賴關(guān)系倒置原則(DIP)

              A.上層模塊應(yīng)該不依賴于下層模塊,它們都依賴于抽象。

              B.抽象不應(yīng)該依賴于細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴于抽象。

              “A. High level modules should not depend upon low level modules. Both should depend upon abstractions. ”

              “B. Abstractions should not depend upon details. Details should depend upon abstractions.”

              Bob大叔還補(bǔ)充說明了一下,這個(gè)原則的意思就又變了:更確切的說,不要依賴易變的具體類。也就是說,不容易變的具體類,還是可以依賴的。那么,當(dāng)我們開始一次系統(tǒng)開放的時(shí)候,那些類是易變的具體類呢?那些類是不易變的具體類呢?怎么才算是易變、怎么才算是不易變呢?我們來看看代碼吧:

            java代碼:

            class A{
                public void doA(){
                }
            }

            class B{
                A a=new A();
                a.doA();
            }

               按照DIP原則,Class B依賴于一個(gè)具體實(shí)現(xiàn)類Class A,因此是可恥的!最起碼你應(yīng)該寫成這樣:

            java代碼:

            interface A{
                public void doA(){
                }
            }

            class AImpl implements A{
                public void doA(){
                }
            }

            class B{
                A a=new AImpl();
                a.doA();
            }

              這樣,AImpl和B都依賴于Interface A,很漂亮吧。還不夠好,A a=new AImpl();還是很丑陋的,應(yīng)該進(jìn)一步隔離!A a=AFactory.createA();在AFactory里,再寫那些見不得人的new AImpl(); 代碼。然后呢?這還沒完,更加Perfect的辦法,是采用XML配置+動(dòng)態(tài)IOC裝配來得到一個(gè)A,這樣,B就能夠保證只知道這個(gè)世界上有一個(gè) Interface A,而不知道這個(gè)Interface A是從哪里來的了。這么做的理由是什么呢?有一個(gè)很嚇人的理由:“如果A被B直接使用,那么對(duì)于A的任何改動(dòng),就會(huì)影響到B了。這就叫依賴,而這樣的依賴會(huì)很危險(xiǎn)!”

             

              我們來看看這頗有說服力的話,漏洞何在?A的變化有兩種情況,一種是只修改A中的方法的內(nèi)部實(shí)現(xiàn),無論是直接使用A還是使用Interface A的某一個(gè)實(shí)現(xiàn),這時(shí)候B的代碼都不用改。另一種是修改了方法的調(diào)用接口,如果是直接使用A的Class B,就需要修改相關(guān)的調(diào)用代碼,而如果是使用接口的呢?就需要同時(shí)修改Class B和Interface A。這樣看來,采用接口方式,反而需要修改更多的代碼!這使用接口的好處何在?

             

              5、接口隔離原則(ISP)

              客戶端不應(yīng)該依賴于自己不用的方法。

              “The dependency of one class to another one should depend on the smallest possible interface.”

             

              這個(gè)我就不說了!因?yàn)檫@個(gè)原則和SPR原則是矛盾的!就像合成復(fù)用原則(CRP)與LSP原則矛盾一樣。

             

              關(guān)于這個(gè)批判,我昨天晚上只寫了一半,今天算是虎頭蛇尾的寫完了。最后錄一段Bob大叔的話,作為結(jié)尾:

             

              什么時(shí)候應(yīng)該運(yùn)用這些原則呢?首先要給您一個(gè)痛苦的告誡,讓所有系統(tǒng)在任何時(shí)候都完全遵循所有原則是不明智的。

              運(yùn)用這些原則的最好方式是有的放矢,而不是主動(dòng)出擊。在您第一次發(fā)現(xiàn)代碼中有結(jié)構(gòu)性的問題。或者第一次意識(shí)到某個(gè)模塊受到另一個(gè)模塊的改變的影響時(shí),才應(yīng)該來看看這些原則中是否有一條或者多條可以用來解決問題。

              ......

              找到得分點(diǎn)的最佳辦法是大量寫單元測試。如果能夠先寫測試,再寫要測試的代碼,效果會(huì)更好。

              讓我來翻譯一下上面的告誡。原則不是你可以用來預(yù)防問題的,而是當(dāng)你已經(jīng)陷入麻煩的時(shí)候,你可以停下來悔恨一下。至于解決之道,依然不是很清楚,因此,你需要寫大量的單元測試。而且,大量的單元測試并不是幫你檢查你的設(shè)計(jì)漏洞,而是幫你更真切的感受自己的設(shè)計(jì)是否正確。至于他究竟是不是正確,這就看個(gè)人自己的感覺了。更為驚人的是,在測試驅(qū)動(dòng)開發(fā)的建議中,如何驅(qū)動(dòng)開發(fā)的準(zhǔn)則,竟然是循環(huán)的來自于OO設(shè)計(jì)原則的。

             

              這樣的OO設(shè)計(jì)原則,就像老爸老媽給我們的人生教誨:“你要做好人啊”,別的什么都沒說。而且我們還遇到了話都說不清的糊涂爹娘,怎么才算好人,不清楚,怎么才算壞人呢?被警察抓了,肯定就是壞人了。至于如何才能做得更好?自己體會(huì)吧。

            敲響OO時(shí)代的喪鐘——設(shè)計(jì)模式批判(1)
             為什么要批判設(shè)計(jì)模式?設(shè)計(jì)模式不是OO開發(fā)的救星嗎?作為“可復(fù)用的面向?qū)ο?#8221;的基礎(chǔ)設(shè)施,設(shè)計(jì)模式大大的超越了OO設(shè)計(jì)原則給予我們的承諾,還記得我們前面的分析嗎?OO設(shè)計(jì)原則并不擔(dān)保你在設(shè)計(jì)之前就能避免錯(cuò)誤,相反的,你往往需要在屢屢受傷之后,才會(huì)明白設(shè)計(jì)原則的真諦。而設(shè)計(jì)模式是如此的偉大,他甚至可以幫你提前避免問題,只要你可能遇到的問題,符合設(shè)計(jì)模式手冊(cè)中,所描述的那種場景,基本上你就可以直接采用相應(yīng)的設(shè)計(jì)方案了。如果找不到正好合適的,你也可以改造自己面對(duì)的問題,使得他看起來究就像設(shè)計(jì)模式手冊(cè)中描述的那樣,然后你就可以放心使用相應(yīng)的設(shè)計(jì)方案了。如果你無法在那23個(gè)模式中找到合適的答案——你可真是太挑剔了——那么你只能自己想法組合一下那23個(gè)中的2~3模式,總之,一切盡在其中
             
            好吧,事實(shí)其實(shí)沒有那么夸張,“GoF”從來沒有宣稱“設(shè)計(jì)模式”能夠包治百病,更沒有說過使用“設(shè)計(jì)模式”可以預(yù)防疾病,他們也的確謙虛的承認(rèn),設(shè)計(jì)模式肯定不止23個(gè)。但是,GoF也必須承認(rèn)的一點(diǎn)就是:“Design Patterns原本是用來指導(dǎo)設(shè)計(jì)的。大多數(shù)時(shí)候,都是在實(shí)際開發(fā)之前完成的。”而且,按照設(shè)計(jì)模式原本的思維模式,應(yīng)該把一個(gè)系統(tǒng)中的各個(gè)類,按照他們所說的一堆模式組織起來,其根本目的,就是不讓后來的改動(dòng),再去修改以前的代碼,所謂OCP原則在設(shè)計(jì)模式中的實(shí)際體現(xiàn),就是對(duì)擴(kuò)展開放、對(duì)修改封閉。
             
            In other words, (in an ideal world...) you should never need to change existing code or classes: All new functionality can be added by adding new subclasses and overriding methods, or by reusing existing code through delegation.
             
            再強(qiáng)調(diào)一遍:“設(shè)計(jì)模式認(rèn)為,靈活性、可復(fù)用性是設(shè)計(jì)出來的”,而在此之后的發(fā)展我們可以看到,新的大師們又偷換了概念,將“設(shè)計(jì)——實(shí)施”的兩階段過程,變成了一個(gè)“TDD+重構(gòu)”的持續(xù)改進(jìn)過程,他們不但提倡改以有的代碼,而且要大改特改,持續(xù)不斷的改,唯一還帶著的帽子是:“重構(gòu)的目標(biāo)是得到設(shè)計(jì)模式。”重構(gòu)真的能以設(shè)計(jì)模式為目標(biāo)嗎?我們下一篇再討論。
             
             請(qǐng)?jiān)试S我先借力打力,利用重構(gòu)這一新生事物,攻擊一下設(shè)計(jì)模式這個(gè)老東西。為什么靈活性、可復(fù)用性不能是設(shè)計(jì)出來的?
             
            軟件開發(fā),一個(gè)很重要的工作,就是對(duì)付“需求變更”,軟件工程的辦法是盡可能的抵擋、或者有效控制變更的發(fā)生。技術(shù)人員的目標(biāo),則是努力開發(fā)出更加靈活的技術(shù),以適應(yīng)可能的變化。值得一提的是,現(xiàn)在軟件開發(fā)的管理者,也開始相信,擁抱變化比限制變更,是更為可取的手段了。
             
            更加靈活的技術(shù),更加容易理解,方便描述事實(shí)的語言,設(shè)計(jì)模式等等等等,都是用來適應(yīng)可能的變化的。對(duì)于技術(shù)人員來說,如果能夠預(yù)測可能的變化,并在系統(tǒng)設(shè)計(jì)期就將其考慮進(jìn)去,那么變化就成為計(jì)劃的一部分,注意這句話,因?yàn)閷?shí)際的情況往往是:“計(jì)劃趕不上變化”。越是自信的技術(shù)人員,越是自以為能夠預(yù)測可能的變化,將變化提前設(shè)計(jì)進(jìn)入自己的系統(tǒng)。所以,濫用設(shè)計(jì)模式的人,往往是那些自以為水平很高的半桶水。而重構(gòu)這一思路的出現(xiàn),就是對(duì)于設(shè)計(jì)模式這種“企圖預(yù)測變化”的否定。事實(shí)上,即使是重構(gòu),也是危險(xiǎn)的,因?yàn)閺脑紶顟B(tài),到第一個(gè)變化發(fā)生時(shí),我們能夠得到的只有兩個(gè)狀態(tài)點(diǎn),這兩個(gè)點(diǎn)聯(lián)成直線所指向的一個(gè)方向,并不一定就是變化的方向,因此,重構(gòu)是一個(gè)好辦法,而將得到設(shè)計(jì)模式作為重構(gòu)的目標(biāo),卻相當(dāng)危險(xiǎn)。
             
            設(shè)計(jì)模式背后的思路非常清楚,就是將可能變化納入設(shè)計(jì)模式的考慮之中,所以我們看到設(shè)計(jì)模式的目標(biāo)“可復(fù)用”,其實(shí)是一個(gè)轉(zhuǎn)了一個(gè)彎以后的目標(biāo),“在盡可能重用已有代碼的前提下,適應(yīng)變化”。我的觀點(diǎn)是:“首先需要滿足的不是復(fù)用,而是快速修改”,這個(gè)問題太大以后有機(jī)會(huì)再討論吧。
             
            這篇關(guān)于設(shè)計(jì)模式的批判,我寫了好幾天,始終感覺難以下手。今天和徐昊討論,他的話我認(rèn)為非常有道理:“設(shè)計(jì)模式的成功,正好證明了OO的失敗”。這個(gè)思路相當(dāng)有用,我決定就按這個(gè)調(diào)子來寫。當(dāng)然,設(shè)計(jì)模式并不是只有一個(gè),而是有很多很多個(gè),作為一種“專家經(jīng)驗(yàn)交流的規(guī)范描述格式”,設(shè)計(jì)模式已經(jīng)非常多了。我們今天也不批判更多的模式,僅僅對(duì)GoF的23個(gè)模式下手吧。
             
            GoF的23個(gè)設(shè)計(jì)模式,主要分為三類:創(chuàng)建型模式、結(jié)構(gòu)型模式、行為型模式。我們就分別批判這三種模式吧。
             
            創(chuàng)建型模式之所以需要,其實(shí)正好證明了OO的失敗。因?yàn)樵贠O的思想中,創(chuàng)建對(duì)象這種事情,天然就應(yīng)該是對(duì)象自己處理的。一個(gè)類在定義時(shí),可以定義一個(gè)以上的構(gòu)造函數(shù),來決定如何得到自己的實(shí)例,那為什么還需要把創(chuàng)建對(duì)象這個(gè)事情,交到別人手里?按照SRP原則,無論出于什么原因,創(chuàng)建自己總是自己的職責(zé)吧!所以按照SRP原則,所有的創(chuàng)建型模式都不應(yīng)該出現(xiàn),出現(xiàn)了也是錯(cuò)誤的。但是為什么我們需要?jiǎng)?chuàng)建型模式呢?先看看GoF的解釋:“隨著系統(tǒng)演化得越來越依賴于對(duì)象復(fù)合而不是類繼承,創(chuàng)建型模式變得更為重要。當(dāng)這種情況發(fā)生時(shí),重心從對(duì)一組固定行為的硬編碼,轉(zhuǎn)移為定義一個(gè)較小的基本行為集,這些行為可以被組合成任意數(shù)目的復(fù)雜的行為,這樣創(chuàng)建有特定行為的對(duì)象要求的不僅僅是實(shí)例化一個(gè)類。”
             
            這樣的解釋,有一定的道理,但是卻局限于“用組合取代繼承”這樣一個(gè)“當(dāng)年的熱門話題”。在我看來,根本的原因在于:“在OO的經(jīng)典思想中,對(duì)象之外的環(huán)境是不存在的,因此,所有的對(duì)象,都要考慮如何產(chǎn)生自己,如何銷毀自己,如何保存自己,總之,自己的事情自己做。”Java的一個(gè)巨大進(jìn)步就在于,銷毀自己這件事情,不再強(qiáng)求對(duì)象自己去考慮了,因?yàn)檫@種事情過于瑣碎,而且復(fù)雜易錯(cuò)。但是創(chuàng)建自己這件事情,java依然沒有考慮到也不該交給對(duì)象自己考慮的。于是設(shè)計(jì)模式中的種種創(chuàng)建模式被發(fā)明出來,用以滿足復(fù)雜多變的創(chuàng)建需求。這個(gè)根本原因同時(shí)也解釋了為什么單例模式會(huì)被發(fā)明,按照GoF的解釋,是無法說明為什么我們會(huì)需要單例模式地。而當(dāng)我們有了對(duì)象環(huán)境的概念之后,各種創(chuàng)建自然有“容器環(huán)境”來完成,“單例”模式也只需要在環(huán)境中配置,有了OO容器之后,所有的創(chuàng)建模式都被一個(gè)概念所代替了。在沒有這樣的概念之前,我們需要用各種模式技巧,來實(shí)現(xiàn)“支離破碎”的環(huán)境。而在真正的容器環(huán)境出現(xiàn)之后,我們就不再需要這些設(shè)計(jì)模式了。
             
            讓我再說一遍:“如果你能夠理解為什么現(xiàn)在會(huì)出現(xiàn)那么多容器,就能理解設(shè)計(jì)模式中的創(chuàng)建模式,只不過是用來解決OO概念欠缺的一種不完善的手段了。”
             
            再來看結(jié)構(gòu)型模式,個(gè)人認(rèn)為將“Adapter、Bridge、Composite、Decorator、Facade、 Flyweight、Proxy”統(tǒng)統(tǒng)歸入結(jié)構(gòu)型模式,是不恰當(dāng)?shù)摹3薈omposite模式算是結(jié)構(gòu)模式,F(xiàn)lyweight算是一種“節(jié)約內(nèi)存的技術(shù)手段”之外,其他的模式,都屬于打破OO靜態(tài)封裝的技巧。我們知道,按照OO的設(shè)定,一個(gè)類,就是一種類型,它在代碼寫下的時(shí)候,就已經(jīng)決定了自己的內(nèi)部數(shù)據(jù)與外部行為。怎么還能在運(yùn)行的時(shí)候再改變呢?事實(shí)證明,這樣需求不但存在,而且重要,設(shè)計(jì)模式之所以被大家欣賞,一個(gè)重要的原因,就是他能夠幫助程序員部分?jǐn)[脫“靜態(tài)封裝屬性與行為”的限制。

            敲響OO時(shí)代的喪鐘——寫作計(jì)劃更改
             原本這一篇我是打算寫《設(shè)計(jì)模式批判(2)》的,但是經(jīng)過權(quán)衡,我決定放棄原本的進(jìn)度計(jì)劃,將攻擊已有技術(shù)成果的文字大大縮短,盡快開始寫我的設(shè)計(jì)方案部分。因此,設(shè)計(jì)模式的批判我將改變批判的方式,不是直接空對(duì)空的批判,而是在提出了自己的設(shè)計(jì)方案之后,通過對(duì)比,向大家表明,使用新的思維模式與技術(shù)手段,原有的設(shè)計(jì)模式需要解決的問題,現(xiàn)在能夠得到更加靈活、易懂的解決。
             
            同樣的,對(duì)于AOP、CGLIB、Mixin之類的新技術(shù),我也將通過實(shí)際的例子證明,在我的新方案內(nèi),這些技術(shù)所帶來的靈活性,同樣能夠自然、安全、方便的得到。
             
            在JavaEye的討論中,JavaCup給了我一堆非常好的地址,我會(huì)仔細(xì)去看的。初步看下來的結(jié)果是,攻擊OO的語氣似乎比我的還要激烈,而改進(jìn)的方案基本上還是從LISP這樣的角度出發(fā)的。因此我的版本應(yīng)該還是有一定的新意的。
             
            關(guān)于JavaCup所同意的age0的觀點(diǎn),我不敢茍同,因?yàn)榘凑账倪壿嫞磺屑夹g(shù)的優(yōu)劣都是不存在的,有區(qū)別的,只是使用者的水平高低不同。而且,似乎firebody也持類似的觀點(diǎn)。一種技術(shù)如果你用得不好,就該自己反省!那么,為什么我就不能也假設(shè)“OO可以被改進(jìn)”呢?任何大家看得到的技術(shù)進(jìn)步,不都是來自于對(duì)于已有技術(shù)的不滿嗎?如果人人都只知道提高自己的武功,卻不去改進(jìn)武器,那么大家現(xiàn)在為什么不是還在用“機(jī)器碼”編程呢?
             
            我明天會(huì)簡單分析一下“關(guān)系型數(shù)據(jù)庫對(duì)于OO的啟示”,后天就打算正式開始介紹自己的新的語言設(shè)計(jì)方案。希望大家沒有等得太生氣

            敲響OO時(shí)代的喪鐘——OO能從關(guān)系型數(shù)據(jù)庫借鑒些什么?
            今天這篇是關(guān)于OO VS. RDB的,OO作為一種編程范型,主要是集中于處理“操作”,而RDBMS作為一種數(shù)據(jù)管理工具,主要是集中于“數(shù)據(jù)”。但是,在一個(gè)需要數(shù)據(jù)庫的系統(tǒng)中,必然的情況是:操作的對(duì)象自然是各種各樣的數(shù)據(jù),而數(shù)據(jù)的管理,自然要通過操作。因此,OO與RDB,從最初淺的角度來理解,雖然分別位于“業(yè)務(wù)邏輯層”與“數(shù)據(jù)層”,但是相互之間卻又有著非常緊密的聯(lián)系。在OO與RDB之間存在著的緊張關(guān)系,其根源在于:“OO世界中的數(shù)據(jù)是被封裝的孤立數(shù)據(jù);RDB世界中的操作是針對(duì)行集的。”
             
            因此,一個(gè)對(duì)象實(shí)例內(nèi)部的數(shù)據(jù),應(yīng)該保存到數(shù)據(jù)庫中的哪些表、哪些行、哪些列,是一個(gè)非常復(fù)雜的問題。反過來說,當(dāng)我們要在內(nèi)存中恢復(fù)一個(gè)對(duì)象實(shí)例時(shí),要從多少表、多少行、多少列中采集數(shù)據(jù),并正確轉(zhuǎn)化為對(duì)象實(shí)例的內(nèi)部數(shù)據(jù),也是相當(dāng)?shù)膹?fù)雜。O/R Mapping,需要考慮的問題還不止于此,在RDB中自然存在的“關(guān)系”這一概念,在OO當(dāng)中,卻沒有明確的對(duì)應(yīng)概念。在OO的世界里,對(duì)象之間存在各種各樣的關(guān)系,卻非常難以進(jìn)行規(guī)范化的描述。再加上:“添加、修改、刪除、查詢”等等情況的O/R映射,以及與“關(guān)系”相關(guān)的“級(jí)聯(lián)操作”——“級(jí)聯(lián)添加、級(jí)聯(lián)修改、級(jí)聯(lián)刪除、級(jí)聯(lián)查詢”,一個(gè)好的O/R Mapping工具,要做出來真是千難萬難。
             
            很多人都意識(shí)到了這一點(diǎn)!是的,問題的確存在。但是,為什么呢?該怎么辦呢?幾乎沒有人會(huì)反思OO的不是,而是想當(dāng)然的認(rèn)為:“關(guān)系數(shù)據(jù)庫技術(shù)太老了,跟不上OO技術(shù)的進(jìn)步,因此,我們需要OODB這樣的能夠直接支持OO的數(shù)據(jù)庫。”
             
            “以其昏昏,使人昭昭”的事情,從來沒有發(fā)生過。依著我前面的分析,在OO這樣的基礎(chǔ)薄弱的理論上,怎么可能搞出有實(shí)用價(jià)值的數(shù)據(jù)庫呢?
             
            在看到了徐昊的《關(guān)于面向?qū)ο笤O(shè)計(jì)與數(shù)據(jù)模型的精彩論述》之后,我相信自己找到了知音。他說:“OO在數(shù)據(jù)模型設(shè)計(jì)上不具有思維簡潔性。”并且提出了一個(gè)重要的詞匯:“邊語義”!這使我相信,和我有類似想法的同志,是存在的。后來又現(xiàn)場聽到了曹曉鋼同志的《ORM時(shí)代的數(shù)據(jù)訪問技術(shù)》的演講,并且在他的筆記本里看到了他做的一些代碼,居然與我的很多想法不謀而合!再加上后來與徐昊的幾次MSN交流,終于使我敢于開始寫這樣一篇“OO喪鐘”的文章,因?yàn)椋蚁嘈抛约翰⒉皇枪陋?dú)的反對(duì)者。
             
            OO可以從關(guān)系型數(shù)據(jù)庫那里借鑒些什么呢?
             
            1、關(guān)系:也就是徐昊所說的邊語義。在 OO中,對(duì)象與對(duì)象之間是否存在關(guān)系,在對(duì)象之外是不知道的。當(dāng)一個(gè)對(duì)象被封裝起來以后,他內(nèi)部是否使用、關(guān)聯(lián)、組合了其他的對(duì)象,是不可知的。因此,我們看到的通常的OO圖,只能說是Object被剖開了以后的對(duì)象圖。事實(shí)上,關(guān)系是被隱藏起來的。而在RDB中,關(guān)系非常明確的被定義與標(biāo)識(shí)出來,一目了然。這將帶來巨大的描述效果。相比起UML Class圖,E-R要容易理解得多。
             
            2、Primary Key:這是RDB 特有的概念,在OO中沒有對(duì)應(yīng)概念。因此,我們要判斷兩個(gè)對(duì)象是否相等,就相當(dāng)困難。如果每個(gè)對(duì)象都有一個(gè)“一次設(shè)置,終身不變的Primary Key”,那么對(duì)象之間的比較語義,就能夠被清楚的區(qū)分為:IS和LIKE。IS就是Primary Key相同的兩個(gè)對(duì)象,他們應(yīng)該完全一致,甚至在內(nèi)存中,也只應(yīng)該保存一份。LIKE,就是成員數(shù)據(jù)相同的兩個(gè)對(duì)象,他們不是一個(gè)東西,僅僅是像而已。
             
            3、SQL:這也是RDB特有的語言,而在OO的世界里,查找一個(gè)對(duì)象的工作,從來沒有被規(guī)范過。
             
            目前就先想到這些,可能還需要再補(bǔ)充。
             

            posted on 2008-06-14 14:22 cuigang 閱讀(491) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 轉(zhuǎn)帖

            国产99久久久久久免费看| 色8久久人人97超碰香蕉987| 99精品久久久久久久婷婷| 热综合一本伊人久久精品 | 亚洲精品无码久久久久久| 久久只有这里有精品4| 伊人久久成人成综合网222| 久久强奷乱码老熟女网站| 美女久久久久久| 久久人人爽人人爽人人片AV不| 欧美国产成人久久精品| 亚洲成色www久久网站夜月| 2020久久精品国产免费| 99久久精品免费观看国产| 欧美国产精品久久高清| 精产国品久久一二三产区区别| 久久婷婷五月综合97色一本一本| 国内精品久久久久影院优 | 久久精品无码一区二区app| 性做久久久久久久久老女人| 亚洲精品第一综合99久久| 久久99精品久久久久婷婷| 久久福利片| 亚洲国产精品成人久久| 国产精品免费久久久久久久久| 国产精品久久新婚兰兰| 青青青伊人色综合久久| 综合久久久久久中文字幕亚洲国产国产综合一区首 | 人妻精品久久久久中文字幕一冢本| 久久亚洲精品视频| 天天做夜夜做久久做狠狠| 狠狠88综合久久久久综合网| 久久乐国产精品亚洲综合| 国产情侣久久久久aⅴ免费| 亚洲国产精品综合久久一线| 996久久国产精品线观看| 久久青青草视频| 久久精品国产色蜜蜜麻豆| 久久久久国产精品熟女影院| 久久久精品国产亚洲成人满18免费网站| 精品国产99久久久久久麻豆|