新手編程導論


第一部分

前 言

By Chenyi
眼前這本書充分體現了作者的所思、所想、所感,他用自己獨特的眼光審視着計算機技術的世界,也用自己獨特的思維邏輯對技術進行解讀,並用自己特有的,呵呵,偶爾帶有“四個逗號=一個逗號”這樣的語言風格,進行着自己的詮釋。創新是一種美,獨立思考也是:)

學習是一件因人而異的事情,因爲每個人的生活經歷、教育背景、年齡、認知模型等等,都是不盡相同的,也就是每個人所處的“維度”不同,而作者有一種“建立更高層抽象的能力”,用一種特有的方法嘗試着給大家建立一個學習計算機的、相對高層的構架,這樣,可以在一定程度上突破個人的“維度”,使大家從與周圍事物建立聯繫開始,一步一步的走向計算機的世界。不識廬山真面目,只緣身在此山中。確實的,在學習技術的過程中,橫看成嶺側成峯,遠近高低各不同,但是作者卻盡力想讓這高低或是遠近都不同的山峯,能在我們面前呈現出一種規律、共性來,這是難能可貴的,因爲這個架構的過程對思維的要求是比較高的:)

哲語有云,動身的時候到了,有的人去生,有的人去死,只有上帝知道,於是這個問題被迴歸到“ To Be ? OrNot To Be ”的問題,是生,是死,只有上帝知道。

但是,人類對真理的探索和對知識的追求,卻從來沒有因爲“生死”的維度而停止過,是的,一顆崇尚真理、探尋真理的海洋之心,將從來不會因爲泰坦尼克號的沉沉而消沉,它將永遠綻放在人們的心中,激勵着我們向更廣闊、更深髓的世界,一路前行、風雨無阻:)

在這個意義上,鼓勵作者的寫作和思路,也是對我們自身追尋真理的一種鼓勵、一種迴路。是爲一點小感想:)與作者分享!!
By Minlearn
對思想的認識和界定是重要的!!因爲我們需要一個知識體系才能不致於困惑!!(而身處編程界,紛繁複雜的術語和概念足以讓一個初學者卻步)

我抓住了哪些轉瞬就在我腦中消失的思想,,因爲它們遠比一切成書的東西都讓我感到它的珍貴!而更玄的是,他們竟然真的能夠被文字描述出來!!這整本書就是小說式的教學。它力求呈現出一個精緻化了的術語集。以使初學者能真正理解至關重要的那些概念。

正如Chenyi所說,每個人都是某個維度上的人,有他自己的年齡和認知,具體到某個歷史時刻,我們的人生閱歷已然被格定,而這決定了你接受新事物的能力和眼界,人生在世,已經不可能脫離某種信念(也異或某種阻力和障礙)而活,當我們開始學習編程,我們永遠都是用外行的眼光去看待某樣東西,而當你佔在巨人的肩膀上成爲一個專家之後,你就需要用全局的眼光去看待曾經陌生的知識,此時你不再是個學習者,而會批評產生你自己的認知,但那畢竟是要過的第二道檻,而初學者就是那些連第一道檻都難以過去的羣體。

這其中最大的攔路虎就是對術語的理解,很多書並不切合初學者的實際,從他們的角度呈現一條清楚可見的理解路線,而只是一些大部頭衍生下的反覆抄襲品。

給你一個術語或道理,這個道理有什麼用?沒用,是的,因爲要給你一個情景,你才能理解它,僅僅讓你去學一個知識,而知識和衆多其它知識之間相似而微有不同,如果不給出它被產生時的歷史和它所處的架構(這本書不但給你思想,而且給你對應的細節),那麼我們就會迅速迷惑,更遑論運用它,因爲我們不是泛化主義者,形而上學者(但是的確存在超前主義學說,只是爲了創立一種學說,後來才慢慢與實踐相結合),我們需要一種與自身相聯繫點去理解它,我們只是生活的人,我們不是高高在上的學院派高手。

一個高手必定是與常人有不同的思想級深層的東西和他自己特有的體會,因爲他也走過初學者才走過來的路,可是往往人們都忘了歸納那些至關重要的經驗,那會是什麼經驗呢,那些是不會出現在任何描述具體技術細節的書裏的思想級的東西,那麼這本書嘗試的正是記錄那些祕訣,如果真的想當高手,請你不要錯過這本書裏任何一個字眼!!如果你是高手,這本書一定與你內心深處的某些想法偶合。

本書過後,再輔於其它教科書(比如你手頭上的一本C++教材,本書後面列舉了一些與本書能很好答配的推薦參考書)你應該會具備基本的編程能力和編程理解能力。本書前半部分是對思想和認知的導論,後半部分註定實踐和技能能力的形成。

知識是事物之間的聯繫,那麼實踐就是強化或深入這些聯繫的方法,我常想,到底是什麼重要,是認知還是技能,人們普遍認爲實踐應在任何情況下都高於認識,事實是:可能有技能但是沒有認知,但卻不可能有認知但沒有技能,就拿學習英語來說吧,看英語報紙也是一種實踐,因爲它也能夠加強你實際使用英語的能力,(我不是在模糊這二者之間的區別,我只是企圖站在這二者之上求得一種更泛化的認識),實踐不過更側重動手能力而已民,而認知跟它並不矛盾,知識的獲得與能否運用知識本身無必然因果,擁有足夠的知識,再加上泛型的思維,,你就會快速得以實踐,一切都是一種格物致知的過程,只有格物至知,先格物,認識到了一定程序後就會產生對事物本質的認識,也可先認識事物本質再在指導下去發展技能,但是認知可以直接傳遞給你(至此只是一個你所能想象得到的淺層和大概,而且除非實踐,這個大概形象你也不知道它到底是正確的還是錯誤的,更深層的你想象不到的抽象以及關於這些認識的正確性要求實踐),相比之下一本書不可能傳遞很多實踐的東西。本書前一部分正是力求讓初學者完成從認知到實踐的有效過渡。

所以說實踐和認知都是重要的,沒有誰比誰更重要的說法,然而對於初學者來說淺層認知的重要性要高於實踐,一開始就有一個好的思想和基礎顯然可以爲未來的實踐掃清障礙,這是因爲學習是一個層次上升階段,在擁有一定知識後,理解基於這些知識之上的更高層知識會很快,, 即掌握了基礎再加上一定勤奮的博物廣識,知識量是幾何級上升的,因此一種很好的學習方法是,學習應該先吞,(在一定知識量的前提下儘可量地博物廣識,即使看不懂也要瀏覽完,以獲得淺層的認知繼續下一步學習),這是學習中自然而痛苦的過程。(不是提倡光談和光看理論,而是把理論整理成一個架構也是一項重要的工作,不是不能直接把這個認知傳遞給你,而是需要再找一個與你的結合點來讓你認識它,因此它是一本同時講解到認知與實踐的書, 不是提倡導光談理論,而是如果事先有理論的指導,那麼學習中就會少走很多彎路,學習中最怕不能理解細節,更怕以爲細節就是一切,所謂一葉屏目不見泰山,更有人把學習語言作爲編程的終極目標,而如果事先有人給你指導,你就會少走很多彎路)

在學習方法上面,有一個問題是關於細節和思想的。

我們鼓勵在實踐基礎上去學習,也提倡速成,但大多數人顯然不會擁有正規的實踐教育,我認爲學習不應該提倡逐步深入,人的生命有限,染啓超在渡日的般上一夜之間學會日語,這就是說他掌握了思想,細節的東西永遠是後來的,只要思想是重要的,(瞭解足夠多的細節才能泛思,,在學習編程中,除了一些對至關重要概念集的理解之外,,從來都不是大思想決定一切,而只是小細節,這就要求你作很多的實踐)

掌握了思想和基礎後,每天寫小程序,編程能力就會日漸提高,而當你寫過和分析過很多程序之後,你就會具備一眼看出的本事,過程的最後你發現自己蛹變蝶飛了

學習應首先理解框架(這是泛讀),然後是細節(這就是對某些內容的精讀),就好像在一個大型應用中,編譯跟解釋並不會走二個極端一樣(低層用編譯碼,而高層用腳本),學習往往是混合了這二個過程的過程,,,矛盾和老子的不可絕對在這裏起作用

所以說思想和基礎永遠是重要的(人月神話的作者固然精通很多細節,但是他寫出來的卻是一本思想書),,知識和思想自然是越多越好(泛讀可以無限進行,花再多人年都無礙,人年是人月神話裏面的概念),但是有一些知識不必深入(精讀卻需限於自己的開發領域),但一定要知其然

本書主體中的二部分就是認知和實踐,思想和細節的結合,所以你要做的就是在認識的基礎上作大量實踐。這就是我在前言的後半部分推薦給你的看書方法。

如果說一些知識僅僅知其然就夠了的話(僅僅是不致於迷惑和建立知識結構),那麼有一些知識卻是要精通的,因爲不但要知其然而且要實際拿來應用

要成爲某領域能實際勝任某份工作的程序員,就要做到精通四個“Idioms”(注意這是精通)
1. 你要用到的語言和IDE的“Idioms”(一門語言,一種開發庫)---編程首先就是會用一門語言和它的庫
2. 數據上的”Idioms”(數據結構-數據的內存模式,數據庫-數據的外存模式)---編程第一個就是數據,想起DOS下的編程了嗎,一是數據,二是代碼
3. 設計上的”Idioms”(面向對象,設計模式)-----編程第二個就是代碼或代碼框架
4. 以上三條都是前提,那麼這第四條就是最終的你要實際涉入的業務領域的”Idioms”---編程最終是爲了爲這個領域服務
以上四條是主幹(最好按1-4的順序精讀),而其它的都是支節。比如工具的使用啊,XML啊,UML啊,XP方法啊,ANT部署發佈知識啊等等

對於計算機專業來說,爲什麼也才那麼幾門課程(高數線代離散,編譯原理,C與算法,Java),,因爲這些學科是最重要的(真正掌握了這些基礎,你會發現再多後面的技術用語及其背景都是支節),這就相當於前面提出的四個Idioms

對語言細節的學習和深刻理解永遠都是學習編程的重頭戲,但決不是一切,比如拿編程語言來說,只要越過語言的表達這一層,我們才能用語言表達和理解事物(語言跟你要說的話就像用C++語言去表達一個算法,方案領域跟應用領域的對應,就像穿鞋去上海, 穿上鞋只是開始,真正你要去的目標-上海還遠着呢),就像口才,一個好口才的人說話時絕對不會想到措詞,因爲語言已經成爲一種意象,只要把一樣東西思想化,才能超越這個東西而去想到別的東西而長足發展,比如面向對象,這本書將幫你解釋爲什麼面向對象是一種科學的機制,解釋的過後你甚至會覺得這是一種本來就該存在的很親切的機制,只要超越了面向對象我們編程時,再加上一定設計模式,才能真正不會一動手編程就考慮什麼是面向對象之類。。(而這些,好像都是一個高手所能做的事了。)。

編程時碰到的信息量永遠是巨大的,有生之年我們不可能掌握這些信息的來龍去脈,對於程序員來說,提供一個關於它的編程參考文檔可以說是掌握了此信息,因爲這個文檔就是這個信息的大概,實際上我們編程大部分情況下都只是用第三方的代碼庫來編程,這個信息用於編程所需的全部東西,對於編程來說只要掌握這些東西就行),換句話說,一些知識如果不能理解就得先放(這本書並不適合於趕考使用), 在這個信息的社會,至於信息,是撞事學事!一個程序員並不全知全能,它只提取和了解事物對於編程方面的信息。對於事物的邏輯認識,只能在對它的編程中不斷掌握它,抽象是慣穿這本書的重要的思想,維度也是,我們是從學習編程的眼光來組織這本書的。也是站在初學者的角度來解釋諸多概念及其關係的。

問題隨之而來,既然存在這個界限,又如何突破,否則這就是一個空談

多走彎路,學習是認識事物間聯繫的過程,而記憶或實踐是加強這個聯繫的過程,,能夠認識到事物之間的聯繫,即便是自想的聯繫也可加深對事實的記憶(一個程序員有他自己的知識體系是重要的),這就是知識

一切東西,我們應該查本究源,深入其原子世界(任何一個術語都不會簡單,有它自己產生的環境與其它知識的聯繫,但也正是因爲這樣,這也決定了它的有域性,任何知識只要放在它自己的領域內去理解才能更容易被理解),, 翻譯過很多文章你就知道要措詞,措詞跟概念有關,二個稍微相差不大的措詞都會讓讀者摸不頭腦或讓他們恍然大悟。

我們高中做到了英語的相似名詞不同分析,本書也打算在一定程序上這樣做(雖然計算機技術領域一個概念可以自成一書這種現實不允許我們這樣做),咬文嚼字在這裏起作用,,在某些情況下,它是一種好習慣!

然而千萬不要走入另外一種極端, ,知識用詞和技術用語沒有一個標準,比如方法和函數指的是同一個東西

什麼是線性(一次就是線性),什麼是離散(離散了的量),這都是仁者見伍,智者見,但人們對此的理解都不會差到那裏去,並且也不會影響後來的學習,這裏有一個描述或形式的概念,相信大家都還記得初中學過的集合,是描述性概念,但集合其實還有一個形式概念,給定了形式就可以框死

而且,要知道,即使是《虛擬機的原理與設計》這本書的作者也會對別人書裏的進程概念感到不解。

我注意到程員序考試中多了一項標準化,的確,知識的傳達也需進入標準化時代了

最後,如果說任何行爲都是功利的,那麼我寫這本書的目的只爲博你一笑。

書中錯誤再所難免,望不吝賜教!!

l 別懷疑,這正是一本同時可作爲入門和進階級的書(更偏重入門)!然而真正的高手和真正的初學者都將從中有所得。
l 你還在爲學不懂大量術語而煩惱嗎?如果你真有這種體會,那麼你可能先要看這本書再看你正在看的C++的書,因爲你僅僅缺少一根主線, 而它是能讓你少走很多彎路的柺棍
l 對架構的學習纔是真正的學習,知識也有它的架構,然而在這本書內有架構也有細節(高手固然知道細節,然而高手也有精神空洞,因爲你還需要懂得一些細節之外的架構級的東西)!
l NotWar3的從零到尾的實現,讓你知道其實你一個人就可以做出類War3的遊戲!!(本書作者也自稱是一個菜鳥,但是這個程序的確是他自己寫的)

導 讀



任何語言都是有門檻的
C用最初級的封裝了彙編的邏輯寫應用,這也就是C代碼看起來難的原因,如果你看過位操作(比如oopc實現的RTTI),看過用C表達的數據結構,就會發現,有時看出來樣子普通的一堆語句,你根本不知道它想表達什麼上下文邏輯(因爲C代碼本身跟現實問題走了二個極端,你看懂了代碼卻看不明白代碼後面的東西,這是因爲有設計在作梗,而C跟設計和現實問題走了二個極端),除非你真一開始就明白它想寫什麼(有文檔的情況下),但Java這樣的語言,能夠整體上看起來是一個一個的類,顯得意義明瞭,但實際上語言越接近應用問題反而代碼看起來更復雜,,因爲現實生活是很難的你根本做不到絕對靠近除非你爲每一個問題寫一個dsl(C表達的向系統問題接近和Java表達的向現實靠近各有各的難處,作爲一門工業語言,要求它看起來便於程序員理解和複用,Java這方面是做得不錯的而C肯定只是專家語言),一堆有機類有時反而難於讓人看出它想表達什麼,而且另外一方面,類裏同也是函數,也是Java的語句邏輯而不是封裝上的類邏輯,這些語句邏輯,同樣用Java中的庫來形成,Java的庫是重新經過設過的,相比C語言,比如它規範了流的概念,Java的OO和規範的庫是人們說Java比其它語言易的二個方面。。

但是雖然熟悉編程的人可以拿來用,但是對於沒有編程經驗的人來說,它照樣跟C一樣難。

因此JAVA的所謂易,是相對用過C的這樣的熟悉編程者來說的。至少使用JAVA,我們照樣得學好跟C一樣的數據結構知識。。

看不懂一套源程序,主要是

1)你不知道普通的語句是體現什麼樣的數據結構

2)你不知道普通的語句是體現什麼樣的算法

3).........抽象慣用法

4)。。。。。如何向現實問題靠近抽象並設計的

5)現實問題的複雜性,跟語言邏輯的簡單性,非dsl之間的矛盾(語言從來被設計成通用的)..

因此要學好一門語言解決問題,不但要學精語言的語法和庫,而且學習的重點還就在於:

數據結構和算法,現實問題,,抽象和設計。。。

什麼是語言級和語言外要學習的(數據結構與代碼結構)
設計最終要被體會到源程序,如果把設計作爲整個軟工過程,那麼源程序是這個工程最好的結果最終證明,(參見《源程序就是設計》一文,你可以google得到它)
我認爲這樣的說法是對的,因爲它綜合了我在下面要談到的1,2二個過程。(但因爲它沒有顯式提到下面我列出的二個過程,所以我認爲該文在一定程序上是一篇含糊其詞的文章)

1是脫離了語言的那些映射,即人們通常說到的設計一詞的意義(請參照我的《什麼是設計》一文,這裏的設計實際上是指大設計,指領域抽象,語言選擇,這些東西,包括數據結構),2是結合了語言的實現映射。即人們通常說到的實現,不過人們通常把1作爲架構師的工作,而把2作爲程序員的工作而已。如果架構師的工作深入到類的類部,深入到詳細設計,那麼他實際上擔當了一部分程序員的工作。但在人類的工作中,2是受1控制的,

這裏面其實有一個代碼抽象模式和數據抽象模式的區別。

類實際上是一種數據抽象,而不是一種數據結構,,因爲它將代碼抽象爲一個一個的“數據模式”,即將C++這樣的通用語言增加DSL詞彙,讓它成爲DSL,可以表達class cat,class pig,豬貓,諸如這樣的領域詞彙,所以類是一種數據(把詞彙抽象爲語言的一級first class數據,即UDT,ADT這裏面D的意義)抽象模式,也就是代碼模式,而數據結構學是一種實現模式,而不是代碼模式。

數據結構學與代碼結構學的區別,是解決問題的問題和解決語言映射問題的區別,兩者在不同抽象層次,這就是爲什麼數據結構可以用任何語言可以用基本流程實現也可以C++的類來實現,因爲數據結構學跟它如何用一種代碼結構來抽象是沒有直接關聯的,前者是如何解決問題的抽象(是一種脫離了語言的映射,即我在1中談到的設計),後者是代碼問題(如何面向用了類的可複用目的來進行對具體語言的映射,即我在2中談到的實現)。

在編程的實現領域,從來最終都是過程編程即Core C的那些內核邏輯〔類體裏面也是〕,所以,用了類,只是將面向過程抽象到了一個更好被複用的問題,並沒有將如何實現這層抽象到程序員不需要理會,所以對於不懂C的人來說,即使它能很好理解OO,也做不了什麼程序〔類只是將實現套了一殼,從而將面向對象層次的複用維持在這殼外,殼的裏面卻不行,照樣是過程編程〕,OO的強大隻是指它類外的那些代碼抽象模式。OO提供的最本質的意義和作用有二,1,將C++這樣的通用語言“在類層次”變成爲DSL,2,正是因爲1的作用在先,所以普通個人也可以開發複雜的軟件系統。

也是C程序員的能力更多在前者(數據結構),而數據類型屬於後者,設計模式,面向對象都是後者,是C++程序員或架構師的事。

對於2,即實現。跟具體語言有關。下面舉C和C++爲例。

遺憾的是,C++只是對C的增強而非替代,如果他離開了C,就只能有類的代碼模式的複用能力,卻無法保留C的強大而直接的實現能力(如果寫數據結構這樣的東西都要強制用到類,那麼除非有必要爲複用作目的,否則沒有用C++的必要。),所以一個C++程序員(假設他用了OO來實現數據結構)既是一個實現者,也是一個設計者。因爲它既抽象瞭如何能被解決的問題,又抽象瞭如何能被複用的代碼。



程序員對於算法,數據結構,數學實現問題這些細節的實現問題把握最重要,而不僅僅是語言能力設計,代碼抽象能力,如果有架構師的工作在先,C的程序員僅僅需要提供函數過程模塊,(極度求接口化和求極度模塊化,設計是需求驅動的,接口是要提供的功能驅動的,都是不必要的那是架構師的工作)
什麼是語言級要學習的
什麼是編程要學的。首先給出一張圖。圖中描述了編程知識結構。




就學習一門語言比如C來說。
綜觀C語言的語句形式,就只有編譯原理後期翻譯的那些語句形式了,,僅僅從語句形式上來看是這樣的。。

計算機能做的事,就是CPU的取指令,譯指令,執行指令,從內存中取數,CPU用邏輯器和運算器運算,,中斷處理例程,IO設備的功能,
這些是計算機能做的全部事情,由CPU統一控制,,因爲這是所有硬件資源能做的事情。。計算機後來所有的軟件邏輯包括OS的所有的功能都由它決定。。
由OS生成調用這些如上硬件資源的策略。。

template是C++的一種高級語言機制,它建立在C++的類型機制上提供範型,但是它的語法及其古怪,跟C++那些其它的普通語言機制十分不相同,(由於語言是由語法定義的,因此我們稱,類型,表達式,控制語句形式,賦值語句,這樣的東西爲語言的要素,打開任何一門關於程序語言教學的文章,我們都可以發現這一點。。字符串,IO,異常,標準庫,,數組,與Windows的接口,這樣的東西是語言的高級話題,和高級功能,不是語法級規定的,,比如可能是庫提供的)
編程學習方法
只要學習C語言,計算機系統知識,語言實現原理,網絡原理這些系統知識,才能從一個比較大的側面去看待現實問題對於計算機的實現問題(也即編程問題),,只有這樣,只有懂C,懂編譯原理,懂計算機系統,懂網絡,才能從一種大局的高度去看待並計劃你的系統你的應用。。
  
  要學習編程,我覺得C語言是起碼要學的,即使是以後學LUA,RUBY這樣的高階語言,最好的途徑還是先學C.
  
學習編程必定最後要涉及到系統邏輯,那麼是首先學習上述的計算機專業課程的知識,還是先學語言呢,,,,我覺得是先學語言,,因爲理論的知識在沒有實踐的前擔下很難掌握,,而一開始就學習語言(我這裏只指C語言)是最好的辦法,,,因爲在學習的過程中,你需要一門語言來實踐,,,驗證各個小問題的語言實現,來掌握更多的知識

計算機學生專業課程本質講解
RUBY跟C語言還是結合得蠻緊的,,RUBY在數據結構方面還是有C語言影子的,,人們說RUBY偏向於人,但是幾乎所有的語言都脫離不了數據結構,,都脫離不了底層,,RUBY的OO只是指類的那一方面,,RUBY的IO,數據結構,跟其它語言是一樣的,,,複雜,跟底層相關,,數據結構和底層有什麼關係呢?內存地址是線性的啊,這就是數組,,我們的虛擬機是棧式機啊,,所以要堆棧,,我們的運行時把類對象分配到堆中,,,所以要堆,,這就是編程語言處理計算機,解釋器本身的離散形式而導致的複雜性,,所有的語言包括RUBY都沒有很好地用OO封裝數據結構,STL是個例外,,但是無論理解OO的STL還是RUBY的數據結構,都是差不多要理解到底層,即數據結構的原理,每種數結的具體情況,,學習RUBY並不能讓你省這個懶,,問題就出在這,,,要學好RUBY,C的數據結構方面還是要下點功夫的,,任何一門語言,語言庫包括STL都沒有自稱它把數據結構簡化到不需要明白任何具體數據結構的形式就可以拿它來進行編程的程度 ? 其實計算機專業學生的那些課,,數結,C,操統,都是無比珍貴的東西 ,,學校設立這些課而不是C++,不是RUBY,,是因爲那些纔是可以解釋後來一切的底層 ,,而並非一種捨本未未的作法..
語言就是處理三個東西之間的關係,平臺,語言,要解決的問題(解決什麼問題用什麼語言最好),其中,彙編基礎:解釋了硬件平臺,即CPU中內置了控制內存的模塊,因此要涉及到寄存器,內存地址等,操作系統課程,如果說彙編基礎解釋瞭解硬件平臺,那麼這就是解決的軟件平臺的問題,而離散數學,,就是一切離散形式,計算機和程序語言環境,和語言本身都是本質上一些離散形式,比如圖靈機就是程序模型,是個離散東東,在編譯原理中體現就更明顯了,比如函數語言實際上就是一種高次方程的離散,編譯原理的本質是什麼呢,,,如果說硬件和操作系統都是解釋了平臺,那麼編譯原理就解釋了程序本身的本質,那麼C語言課程呢,,這解釋了硬件編程的一些方面(C+大量彙編的形式廣泛用於硬件編程,驅動開發),,而且,C語言這門課程最最重要的意義還在於,它解釋了一切後來的高級語言比如RUBY,比如JAVA,要學習到的跟底層相關的離散形式,,這是尤爲珍貴的,比如進程,比如併發,比如異常,,比如數據結構,而數據結構中的"數據"二字永遠是一個程序的中心因素,,從普通數值,字符串,數值到結構體到OO數據,體現了人們封裝程序開發數據對象的複雜性的要求..而且這種發展是爲了產生一種廣泛深度的開發方法的,這導致了軟工,JAVA就是這樣一種好的語言.
應該如何從C的觀點來看待你的編程語言比如RUBY跟操作系統之間的關係呢?一方面,C本身就是一門硬件編程語言和系統開發語言*(OS一般就用C來開發,很少用第三代語言比如C++,JAVA,RUBY),而且,C語言就是一切語言的基礎,JAVA,RUBY都是用它寫的 ,這導致了一些現實,,下面一一說來,C語言跟OS編寫有關,是OS的直接宿主語言,,其它的比如RUBY,比如C++,比如JAVA,,是C的上層語言,學了C語言,,RUBY,JAVA,OO,都很簡單 而且C語言是系統編程語言,是底硬件編程語言(embed c),,,是使用最多的語方,opengl能用JAVA開發嗎,,不能,那會慢得要死,而且根本也不行,,,驅動程序能用JAVA嗎,,那簡單是笑話,,只有WEB開發,,這些不需要很實時的I/O,這些架在邏輯上的邏輯(TCP,HTTP,WWW,SOAP一層一層而來)不需要很強的反應,,纔可以用JAVA,而且JAVA編程統一,OO複用強,,是軟工理想語言, 一句話,C擴展RUBY簡單,而RUBY調用C卻需要BIND,,一句話,編程三個平臺,1硬件,裸機,,用C好,,2配有OS的桌面,用C好,,用OO的C++也可,,,3,WEB,用JAVA好,用OO好
C語言一般用來作底層編程,比如那些靠近硬件層的,選用普通的C語言(C語言標準)可以在WINDOWS平臺上開發控制底層的編程,而且還存在嵌入式C語言用來開發各種硬件編程,驅動,BSP(主板支持驅序),比如一些智能手機的操作系統開發等,其實C語言開發也就是一種範式,一種編程習慣,它是過程式命令編程範式的代表,世界上用得最大的編程語言不是JAVA,不是RUBY,不是VB,而是C,因爲C用來作系統編程時,它可以提供較快的運行速度,接近底層的更好控制,實現C語言的編譯器是一種將C語言代碼直接編譯成本地碼的工具,不存在任何邏輯中間層次或解釋程序(比如虛擬機)因此運行速度很快而且,C語言提供的指針等,直接與硬件內存地址啊,這些東西掛鉤,而且系統本身大都用C語言開發,比如WINDOWS,這就在你要開發的應用程序和宿主環境中提供了統一性,你可以更好控制和調用系統DLL,不必作語言之間的BIND,而且C語言不必涉及到OO(因爲OO主要是面對人的)而C語言更多地是機器因素,它要求人用機器的思維方式來編程,這句話本身也就說明C語言是靠近機器的,因此它適合用來作系統編程,而RUBY等OO語言用來作面向程序員的高級腳本編程,所謂腳本語言,就是相對系統編程語言來說的,系統編程語言提供了強大的底層編程能力,而RUBY等腳本語言提供了調用這些系統功能的高級應用層的開發..
無論如何,一個程序員是要天天學習新知識的,如果不能快速接受(也即你的基礎知識跟不上來),那麼基本上你會很累 大學只是學習這個基礎知識的階段,你最好把編譯原理,離散數學,彙編程序設計,操作系統,C語言,這些基礎弄得滾瓜爛熟,,並積累一些具體的開發經驗,,等出了社會之後,你會發現社會上的編程知識跟你在大學學習的東西差了去了,這個時候你的這些基礎知識就發揮了很重要的作用,你需要這些基礎來理解軟件開發中的大學問,新思想,比如設計模式,AOP,OOP,DP,DSL,STL,UML,LAMP,REST,COM,J2EE, 總之鑽進去了,也就是一種樂趣..好自爲止,
用C++開發要學到什麼程度
鑑於這樣的話題太有爭執性,本文只給出一個參考意見。

都是無窮無盡複雜的細節讓初學者停滯了他們學習一門語言的腳步,在你開始滿懷喜歡地按書上的例子用C++開始寫HELLO WORLD時卻被IDE少引用了一個文件之類的問題折騰得一頭惱怒浪費不少時間,而書上沒有任何一點關於這些的描述,或者網上也只能找到零碎不成文的信息,即使一門語言內部被宣傳得天花亂墜比如擁有OO有多少語言機制多麼利於複用這種論調氾濫的的今天,在真正動手使用一門語言的時候,作爲新手的他們還是會能遇到輕易就把他們擊敗的細節問題
這些細節問題往往不是大思路大方向(比如什麼是OO,這個邏輯用什麼來設計好),反而是那些日常時常見到的工具上或語言上的陷阱(象其名家一般研究基礎的東西,因爲任何東西都是基礎的東西通過泛化得來的,而很多人就是基礎都搞不明白,,雖然存在大量關於某主題的高級論題,但是基礎的東西永遠是要求精和熟的),往往這些細節問題可以被理解,但就是難於掌握(這就是那些被稱爲語言陷阱的東西,然而這是一個大問題,對於軟工來說它是),,應該被儘量在語言被設計時就避免而不是人腦避免,比如IDE環境,比如語言細節,比如目標領域的問題,當一門語言複雜到你真正需要掌握這些才能編程的時候,編程就演變爲不僅僅是簡單的複用了,雖然我們以爲OO語言就是簡單的複用語言但它們明顯做得不夠.(沒有一門語言,即使RUBY,也沒有把語言細節弄得足夠簡單,使得你可以不管任何語法產生式的機制去直接構建你的應用,,在不損耗邏輯的情況下,而且RUBY的一些語法機制也不是不簡單.需要你有深厚的系統知識去理解他們,比如協程,元編程等技術,在接確過一點C++人的眼裏,絲毫不比C++簡單)
用C++進行開發要學到什麼程度,需要什麼知識最小集呢,當然要根本目標問題的不同決定不同的複雜度,但語言和工具級的複雜度都是一樣的
那麼真正要掌握C++進行開發,你需要掌握那些語言級和庫級的知識呢
1,至少熟練一個IDE,make,install,,調試,等編譯技術,能在編譯期出現錯誤的時候能搞明白基本的I關於DE環境配置的錯誤
?2,在語言級和庫級要明白的就多了出去,比如對STL理念和當中每個LIB的理解,對指針進行理解才能明白諸如函數指針,指針當返回值以返回一個數組等機制,,,當然還有很多很多C++的慣用法,等等,這是主體部分,注意我這裏說的是會用,人們可以不懂STL原理和任何實現卻照樣可以拿來熟練使用的人大有人在.
3,要明白你使用的第三方庫的一些知識,要了解它們OO的架構,一個字,要達到一種能用C++使用它們的接口的能力就夠了,這就是OO語言宣傳它們的資本..又一次,你只會使用就夠了,不必懂得庫的OO架構,,你需要了解它們透出來供你使用的粗略架構模型和接口就行了.
4,在複用方面,要明白設計的一些知識,知道OO,GP這樣基本思想,知道你的應用大家都用OO作了什麼設計,你所使用的庫用了什麼樣的封裝上的設計.OO並不僅僅是封裝,封裝是爲了複用,,因此OO最終就是爲了複用,封裝只是中間過程..就像接口並非用來封裝一樣,而是用來抽象,,一切都是抽象..
5,在開發自己的庫和邏輯方面,要明白應用域的一些知識,這樣在設計時就能知道要構建什麼樣的架構.用什麼模式來設計等等,用什麼語言細節來實現,等等
6,要儘量熟悉以上,多練手,才能快速打出代碼,,,,要記住,這個過程很自然,,就像你學好了英語的語法,再多看了一些英語的文章,那麼你就可以寫出英語文章來了.一切都是習慣用法,和語法遊戲,,,除此之外,編程中其它的一切就是設計問題,而不是編碼問題了,,,設計的問題是其它領域的問題,比如算法設計,,而不是編碼問題(有些書用雞免同籠這樣的問題來放在C語言的書,這對學習C語言本身的語言機制如流程控制有意義,數學問題的複雜性只對研究算法有用,對解釋C語言本身無任何作用,而算法是設計通用的,不跟語言相關)
一切在於多看,多寫!一開始不要寫邏輯和架構過大的邏輯,在懂得設計時就可以接確了.
設計本不存在?當你快速寫代碼時你根本不會覺得設計的存在,,這是因爲編碼就是一種習慣,而設計就是一種關於要寫出什麼樣的邏輯的設想,用什麼編碼方法來體現,,,設計就是對編碼進行控制和計劃,,這裏就是編碼跟設計的關係,,難的不是編碼,因爲所有人都可以學會語法,學會寫作習慣,,,但是設計樣的文章卻千差萬別.;.
?設計不僅是算法設計,而且是複用設計
本書目錄安排
宜交叉來讀本書!!本書的架構是自成體系的,除掉第一部分(書前附錄)和最後一部分(書後附錄)外,中間三部分是主體,這三部分自成體系,獨立且又相互聯繫,其中的每一節也自成體系,既有知識架構,也有學習方法,技術細節的描述,第二到第三部分很多知識點的講解都爲了第四部分服務,比較側重於講解通用的編程知識 (同時兼顧了遊戲編程),因爲我們本書的主要任務之一是產生一個遊戲(本書第四部分《一個例子》),下面一一講解:

第二部分 導論,這一部分主要是關於編程的導論,
(要懂得一點思想具備一點常識)《設計,編碼,,與軟工》(編程與思想)這一章解釋了三種思想,原語,抽象,組合,,和軟件開發的二個重要過程,,軟件工程的相關概念,是編程入門的關鍵
(要懂得一點領域內的數學)《數學與算法》(編程與數學)計算機整個就是架構在數學上的,跟計算機平臺實現,算法設計,,架構密切相關,,真正要深入編程,,,對數學的學習是必須的,,千萬不要相信別人編程不需學數學!!那是膚淺的編程!!
(要懂得所處的環境)《硬件,平臺,網絡》(編程與平臺)三種大的編程環境,,對他們的理解是必須的,才能走出窗口,,在一個更廣闊的思維空間裏進行考慮問題和編程。

第三部分 C,C++代碼閱讀與控制

(要精通所用的語言的語法語義)《語言和語言庫》這一節講解了C++,JAVA和,,JFC言和類庫相關的知識,要實際編程,,掌握一門語言和庫是必須的
(要精通處理什麼樣的數據) 《數據結構與數據庫》(編程與數據)計算機就是用代碼來處理數據,
(要精通寫什麼樣的代碼或框架) 《算法與架構》(編程與設計)寫什麼樣的代碼,,,不再是寫什麼樣的實現,什麼樣的算法的問題了,,,而且還是要體現什麼設計,,構造什麼架構的問題,,如果說面向過程的寫代碼就是注重設計算法,,,那麼在OO這個注重可複用(而且現在這個年代,我們大部分情況下是一個開發者而不再僅僅是一個實現者,我們的確經常是使用外來庫來構建應用)的軟工時代(時代真的不同了!!),,而且要懂得設計架構,在本節的最後講到《J2EE大領域學》J2EE是現在最流行的,,OO以後的範型和問題集,因此對它的學習就代表對大多數計算機技術的學習,因爲第一部分到這裏爲止的基礎,,所以我們這裏可以大致描J2EE的整個模型
(要懂得待處理問題域的細節,爲第四部分作準備)《遊戲編程與虛擬現實》從這一部分開始講解多媒體編程和遊戲引擎開發的細節,,爲下面一部分做好充足準備。。

第1到第3講的都是範型(1講的是語言級的,2,3講的都是非語言級的範型),第4講的是問題

第四部分 一個綜合的例子

這部分就是講解如何用範型來解決實際問題,最終產生一個可玩的遊戲。。注重了架構的學習

《設計----GVWL1.0開發過程》
《編碼-----一個NotWar3核心Demo的實現》
.作業與重構

注意各個小主題前面的標號,,有這麼幾種(組合),,初級和基礎主題,,進階主題,,例子主題,,理論主題,,等等,這些主題都是最新的,一個程序員深入編程或遲或晚都要遇到的(摒棄了一些過時的),,當然進階就是高級主題,,,基礎的主題還是要花大力器的

哲學和基本思想這一節,我會寫跟軟工密切相關的思想和學習IT知識,以及任何一切知識通用的思想,,比如抽象啊,範式啊這對解釋以後OO,都是必須的基礎

領域數學並不打算寫一切近代數學知識,我只寫與計算機領域交集域的那些數學知識,,它對以後一節“機器,平臺,網絡”是必須的,因爲我要在機器平臺網絡 這一節講解程序設計語言原理,也就是專業學習必學的編譯原理 而數學知識其實還可以被用到以後無論什麼時段的編程工作當中,,對機器平臺網絡這一章的寫法中,我並不會突出太多技術細節,我意在只讓初學者明白這一切都是怎麼回事,學習編譯原理爲什麼會對學習程序設計語言有幫助(是因爲它解釋了很多語言被設計出來的原理)

機器,平臺與網絡揭露了軟工所處於的三個環境,WEB編程越來越流行了,這就是網絡上面的一層抽象(internet是internet,web是web),編譯原理其實有很多方面跟彙編有關,這就是跟機器編程有關,,平臺 就是OS,,OS就是一個編程層,就像腳本機一樣,,,,我們native編程是對本地編程,這裏的本地就是指OS,,而虛擬機是可以不需要操作系統就 能運行程序代碼的層,,這是另外一個軟件運行環境 總結起來,,,這機器,平臺與網絡講解了很多“軟件運行環境”,特別是我在編譯原理講解中涉及到的機器部分,是最底層的軟件運行環境,對設計虛擬機也是必須要用到的知識,,對解釋OS的實現又是必須用到的知識,這三個環境是天然不可分割的,,站在軟件的角度來看

應用,設計與軟工,如果說機器,平臺,網絡是講解軟件環境,那麼這一章這就是講解軟件與人的關係了,一言以概之,這就是軟工,軟工是爲了讓軟件產品適合“應用”而出現的人類的活動的統稱,(界面與邏輯分開啊,數據分開啊什麼的),在這個大活動中,涉及到多人合作,,需要考慮軟件作爲產品的可擴展性的時候,,就需要提出設計和架構這二個字眼和關於它們的一切系統非系統的知識,,而這是很多書本都忽略寫出來的 作爲產品的,,注意這幾個字,,本來軟件是不需要寫構架的,,但是如果想讓軟件成爲一個好產品,就需要構架,,這就是說“構架不是功能的要求,但卻是工程的要求”

  以上我說的都是“要懂得的部分”,,,一個程序員只有懂得這些,纔算是理解了軟工的常識,,,否則一味鑽細節,什麼都不知道,處於一種很可憐的未開化狀態,,更要命的是,不知道何爲設計,也就不可能主動領會別人代碼或庫中的設計之巧,,僅滿足於瞭解算法實現的人是不行的,這樣的人,由於一開始不考慮軟件作爲產品的特性,,最後寫出來的東西生命力就不怎麼樣, 事物之間的聯繫很重要,你在學習的時候,意識到了計算機和其它東西的聯絡,然後你構建了這種關係之間的系統,就自成一個學習體系了,這是有價值的地方而且你非瞭解不可!!因爲除非這樣,你纔能有效地學習,,否則迷茫於龐大的細節,,鉅細不分,也就不能界定概念之間是什麼關係,,,這是十分造孽,,比 如平臺網絡機器這一章,“爲什麼要出現編程語言”,,“我們看到的編程語言到底是什麼東西”“它們下面是基於什麼離散數學構建的抽象”我把一切功能的達 成的中間層,,無論是軟件,還是硬件,都看成抽象,我們今天能看到編程語言爲什麼要求我們用語法,,而不是直接在語法樹上寫程序爲什麼Lisp語言就能 直接在抽象語法樹上寫程序,而不需要一個語法,,,爲什麼Yacc是一個沒有語法的詞法編輯器解釋了爲什麼“我們要用流程控制,邏輯轉移”這樣的方式寫程序,而很多人並不知道,他們學習編譯原理,就是爲了學到這個知識,這是死讀書的人多麼大的損失啊,,而我打算擇其要點,,組成一條線,,解釋語言的由來,,淺而不缺地解釋很多很地道的東西,有機地講解,,擇其重點和必要的,去除過時,歷史複雜性
  
  我們要編程(如果說第一部分是要懂得的原理,那麼這裏就談實踐細節,當然要求精通),當然首先用一門語言,我打算舉C++和JAVA作爲例子,因此語言 和語言庫這一部分重點是說到了這二大編程語言,我現在的想法是再增加一個Ruby,,,因爲Ruby是新興的動態腳本語言,是歷史所趨,應用所朝,必須要講解出來否則就是不合理,不完整 當然我也不會講個大而全,我會從某個線上某個維度上作一個系統講解 要明白,學語言只是學語法,我們編程是用語言所帶的庫的,C++有STL,BOOST,JAVA有JDK《J2EE,,所以庫是不能不學的,,因此學一 門語言=這本語言的語法+實現了這個語言的某個開發庫,,而且程序=算法加數結也是不符合現狀的,這也是我接下來要談到的
  
  而且,其實C++和J2EE作爲編譯語言,是靜態系統語言,因此有設計模式的出臺,,,而動態語言就不需要設計模式,因爲它是運行期語言,類作爲數據結構的定義在運行期可以被動態改變,,根本就不需要編譯期cast 這和《C++新思維中》“編譯期策略”是一個道理,C++只能提供“編譯期動態”以實作出設計模式,而動態語言由於它的性質根本不需要設計模式 有人說設計模式是C++程序的“補丁”,,這話說錯也可以,說沒錯也可以,,這是因爲狹隘設計模式之說就是爲C++,JAVA這樣的靜態語言作補丁,, 而廣泛的設計,包括設計模式所提出的那些東西,,你知道範式是什麼東西就明白我爲什麼這麼說了,廣泛的設計是一種意識內你要明白它是廣義設計,,但是實際設計時,你要因地制宜的這因地制宜,經濟學就指明,,你有自己的資源,,要設計出一個適合你自己的框架,在你的能力和時間範圍之內,不要做大而全的超級設計,,雖然大而全正是設計的本質意義所在,,,總而言之,設計的意義是泛義的,但是人的掌握範圍之內就得因地制宜,就是多範型設計
根據我們在前面“平臺網絡OS”那一章提到的,語言的本質就是數據加處理數據的方法寫程序的過程就是操作這二個東西,,手工作坊的寫軟件時代,,就是編程=數據加算法,現在軟工時代,編程=數據加算法加架構,其實算法是比架構更廣的概念,它側重於指"功能",而架構側重指"擴展功能",但這二者都是編碼能實現的"真正功能",,說明白點機器語言也是數據加語句,,,這也是一切高級語言在機器端的實現形式

  一種語言要最終被體現成數據加語句的形式,那麼創造這種形式的語言本身也要提供這二重機制,,這二重機制就是數據加操作數據的方式,數據由原來的數學,字母,這些計算機硬件能直接處理的東西,變成了OO的對象,而處理數據的方法,則變成了架構加算法

比如類形式,,用一種類的形式綜合體現了數據和操作數據的方法,,這就是OO



對你死亡級的提醒,請不斷寫代碼唯手熟而的方式是閱讀代碼,成千上W,並實踐!!!第二部分 基礎:導論

第1章 系統


1.1 何謂PC
計算機俗稱電腦。我們平常談到的計算機就是指PC。

當然,計算機不光指PC,還可以指服務器,大型機,我們無意去爲分別這裏面的區別花費時間,這純粹是一種歷史叫法。在不加特別說明的情況下,本書作爲編程教學書以下部分我談到計算機就是指PC。我們是對PC編程。

PC的概念更多地是一個 歷史概念而非範疇概念,歷史上第一代(注意並非第一臺)PC是指1981年8月ibm推出的第一代個人計算機,稱爲IBM PC,它配備了intel的8088 CPU和Microsoft的MS-DOS,從這裏開始,intel確立了它PC行業的CPU霸主地位,微軟也慢慢成爲了軟件領域的巨無霸。Wintel組合出現。

當然,IBM是賣電腦的,Intel是做CPU的,而微軟是搞軟件的,這三者業務領域不一樣。但電腦就是從這個時候開始進入大衆生活的,在這之前是一些巨型機,科研設備機器,以及個人愛好者研製的雛形所謂“計算機”。正是這樣的發展,帶來了計算機向PC發展的態勢(硬件上的,軟件上的,總體架構上的)。

關於這其中的種種歷史,存在有一些趣聞,比如:

l 【硬件上的】:
第一臺PC其實是由一個叫毛利的人發明的而不是教科書中普遍談到的那個巨大的傢伙。
l 【軟件上的】:
蘋果圖形界面拉開了計算機圖形時代的到來。
50,60年代某段時間一種叫CP/M的操作系統差點取代MSDOS而使它所屬的公司成爲IBM的合作伙伴。
MSDOS實際上只是一層殼。它是改PCDOS來的。
l 【架構上的】:
CPU是有架構的,AMD和Intel主頻之爭從來都沒停過。
1.2 圖靈機與馮氏架構
計算機的產生源於對自動控制(自動機理論)和人工智能(機器人)的研究。

圖靈開始研究人工智能的時候那時計算機尚未產生,他的圖靈機是對計算機領域的貢獻但其實更多的是對人工智能的探測。

圖靈機是一種通用自動機器的模型,這種機器有極其簡單的形式(因爲要能易於實現),然而只要產生足夠豐富的變形它可表達併產生一切邏輯(計算函數,表達其它離散算法邏輯等),圖靈機在理論上是理想的,它有一個二端無限沿伸的紙帶作爲存儲裝置,輸入,輸出和狀態轉移函數是一個機器的三要素,這三要素組合並變形可成爲一切機器的原型,可解決一切圖靈機能解決的問題。因爲它揭示了計算機在抽象層次運行的本質即形式計算。所以圖靈因爲他的這個貢獻被稱爲計算機之父。各種形式的自動機理論隨後在那個時代發展開來。

圖靈機是被嚴格證明的。雖然它是一種抽象機難於直接說明,但正是因爲這種抽象可用一套形式化的東西來表示並證明(圖靈機的運作原理被稱爲形式計算,是離散數學自動機的一部分)..通過測試的機器就叫圖靈完備,,所有通過圖靈完備的機器,這說明它們可以產生等價的算法。。可以解決同樣的問題。

圖靈機的意義是偉大的,因爲它是抽象的,所以它能被實現在很多不同層次的地方,比如大到語言識別器,虛擬機模型,小到自動售貨機等等。。

如果說圖靈機闡述的是一種泛義上的自動機,那麼馮氏模型就是專門針對計算機的自動機理論了,以前的機器(在可編程電腦出現之前),指令是硬化的,要爲某機器編程相當於重置整個機器。

馮氏機的精神在於"指令存儲,順序執行",在馮氏模型下,執行指令的機制和存儲指令的機制是分開的,要換一套程序不需要更換指令系統,因爲程序是被內存存儲的,取指令器是從RAM中隨機取出(某條活動指令總是會在某個內存位置待命,隨機的意思就是直接指定內存地址並從裏面取碼進行執行),並不用預先在內存位置固化程序,然後通過控制和地址總線交互存取到內存,這樣CPU只負責執行,對於不同的程序,它可以只用一套指令系統。這樣的架構就形成了一種執行指令加調用數據(指令數據在內存中的位置,或編碼了的數值和字符)的方式,然而同圖靈機一樣,這種簡單的抽象形式同樣可形成高級的具體邏輯。

這樣一來,我們編程也成了書寫指令和指定供指令操作用的地址,指令內含內存地址爲操作碼,而如何在內存中組織數據又發展出了數據結構,用底層的觀點加數據結構的觀點來解釋現實事物的解法就形成了算法。(當然,計算機的觀點是如何尋址和執行指令,實際上在沒有高級編程語言理論之前,遠遠還沒有算法的概念,因此不能說指令是語句,也不能說地址是個變量,地址裏面的東西更不能稱爲數據類型,CPU能直接執行和認識的只是由0,1表達的指令和編碼的地址以及各種基本數值和字符,只是後來以人的眼光來看可以無窮地把0,1編碼成其它各種邏輯而已,不過在彙編語言級別,這些都不是機器能直接理解的)馮氏機以及它提出的“指令控制存儲並串行順序執行”的理念,這些都深克影響了我們這個時代的編程工作。

l 我們注意到馮氏機主要處理數據和代碼,而指令是控制數據的,這使得我們後來的編程工作的抽象焦點在於數據和語句。這是馮氏模型對編程工作最大的影響。

l 我們注意到指令是被串行執行的,這說明馮氏模型一開始就被設計成爲非並行計算機,因此在後來編程中,要實現併發邏輯需要從軟件上和設計上去突破。

l 其它。。
1.3計算機能幹什麼
我們要首先懂得計算機能幹什麼,它的功能發源主要在哪裏?這樣才能懂得編程的出發點和目的。這就不得不首先談談計算機系統的組成。

首先,CPU是PC中的總控制中心,在前一節中我們談到CPU執行的過程就是一個不斷取指令並譯碼的順序過程(所以本質上它其實並不智能,PC只是一臺高速自動機而已),其實CPU不但處理它自己的事情而且是作爲整個PC的總控制中心存在的,CPU不但接管了內存管理,還接管了其它硬件,比如它把顯存歸入它的管理統一跟主內存編碼,而且把硬件消息歸爲它的中斷並把其中斷處理程序統一跟內存編碼。CPU在執行中(有一個時鐘發生器指導CPU不斷地運行)不斷變換其狀態,輸入輸出則是這些二進制(因此整個PC做的工作都是某種意義上的IO)。總而言之,馮氏架構中,CPU統冶了整個機器,內存二把手,其它硬件則是被它統治的。在CPU眼裏,一切都是內存地址和指令(這就對編碼工作提供了無比便利的條件,使得馮氏下的編碼工作變得無比統一)。

CPU中最主要的二個部件是控制器和運算器,計算機之所以被稱爲計算機,是因爲它提供了形式計算,還是CPU裏面的邏輯和算術運算呢,當然是前者。實際上運算器和邏輯器的地位就像協處理器一樣,是CPU不必要的組件,是CPU擴展它的功能加的電路板。這些硬件功能固然是計算機功能的重要組成部分並影響對其的編程工作,然而並非全部。

計算機邏輯主要發源於二個地方,這決定了他的功能和對於編程的意義所在。

l 首先,計算機各個部件都是有功能的,比如浮點器可以表達浮點數並處理,計算機籍此可以直接表達硬件級的浮點抽象以及用浮點表達更大的編碼了的抽象(實際上也存在軟件的浮點處理器),這就爲編程引入了浮點邏輯。另外一個道理,你不能編程指使PC爲你做“給我泡一杯牛奶”之類的事情,因爲它不是硬件上能提供的功能和邏輯所在(即使是抽象也得不到),只有當PC真正接入了一臺牛奶機之後,你才能編程實現諸如泡牛奶的硬件功能。

l 計算機的功能不但在於表達硬件邏輯,而且更大的地方在於表達廣泛意義上的應用邏輯。我們可以對PC編程創造一個遊戲,雖然圖形功能是來源於顯卡的,但是遊戲世界的邏輯明顯不是硬件的。Web應用就是這個道理。

在機器級別編程,我們無法表達高級的應用邏輯,因爲0,1能直接編碼的邏輯是非常有限的,只有當PC發展到提供了操作系統,進程等高級邏輯之後,我們才能在一個新的工作起點上構造更強大的應用邏輯。

所幸的是,當編程發展到高級語言階段之後,這些機器的細節都被完全抽象化了。
1.4 內存地址
既然馮氏架構就是將執行指令的CPU和存放程序的內存分開的一種架構機制,CPU中集成了對內存的管理,在CPU的眼裏一切都是內存地址,而且這導致了馮氏模型下編程的統一性。那麼CPU是如何與內存發生關係的呢? 學習這個有助於我們理解操作系統諸如這樣的軟件的系統邏輯是如何在硬件基礎上包裝出來的。

CPU與內存如何發生聯繫被稱爲CPU的存儲管理機制,CPU管理內存的硬件是它的地址總線和數據總線(我們知道CPU雖然自己處理數據,但他同時還主要做着一種IO的動作,CPU管理IO的硬件被集成在主板上一塊被稱爲芯片組的地方),其中地址總線是負責尋址機制的通道,而數據總線表示CPU能一次性處理的數據通道,地址線數與數據線性往往長度不一,這就導致了CPU的尋址跟處理產生了一對矛盾,地址線決定了它能看到(CPU能使用到的)和管理到的內存總量(物理地址),而數據線決定了它能一次性處理的數據長度和能表示的地址形式(邏輯地址),,,這就是表示(邏輯地址形式)和實際內存(物理地址)上的矛盾。

撇開其它因素,我們就事論事來討論這個矛盾,20位地址線可以使用2^20個最小內存單元即1MB的內存空間(這就是說使得這種CPU的一個任務理論上可使用至多1MB的線性空間,因爲它只能尋址到這麼大的地兒)但16位CPU中的寄存器只能表示前16位(CPU是用寄存器來表示地址的,這就是說雖然CPU能看到整整1MB的空間,但他一口吃不下其中的最小一個單元,因爲它首先都不能直接表達這個單元)。因此CPU要表達和要用盡這1MB的空間,不能以直接線性對應的方式來表達。除非數據線多於或等於地址線。

間接的方法就是設置另一層抽象。可令16位先表達一個大小爲64KB的段,1MB的內存剛好等於1MB / 64KB倍數個這樣大小的段。在這種情況下,內存就不再是絕對線性的了(當然,實際上所有內存都是線性的,這裏說的是分段之後的邏輯情況下,而且你不用擔心CPU認不認識這樣的劃段法因爲它的確認識,下面會談到一個實模式的東西),而是被劃分成了一個一個的段(在段內纔是線性的),16位用來表示段內的每個最小內存單元,空出的4位不再用來表達內存本身,可以用來表達段本身。

以上討論的情況是8086CPU在實模式運行的存儲管理邏輯,即從邏輯地址(CPU要形成任務用到的地址形式)到真實物理地址的編碼(實際機器有的地址),這中間要經過一個變換,CPU負責這個轉換。無論在多少長度的地址線和多少長度的數據線產生矛盾的情況下,都會由CPU負責這個轉換,不過32位數據線的CPU轉換方式要特別複雜而已(特殊的分段方式再加一個分頁的方式,段寄存器不像實模式那樣用來實際存儲物理地址的線性表示,它只用來實現硬件級計算最終物理地址的一箇中間存儲)。

在32位CPU出現之後,寄存器能表示的邏輯地址早就是4G了,而地址總線超過了32位(除非地址總線少於邏輯能表示的數量才需要實模式那樣的分段,然而32位CPU下並沒有產生這樣的矛盾因此可以以線性地址的直接表示方式來表示邏輯任務線性空間,然而32位CPU照樣也實現了一種轉換機制,這是因爲它需要實現更強大的保護模式而不僅僅是完成尋址。

綜上所述,邏輯表示是寄存器能表示的地址形式,,真實地址是系統裝配的內存量,而線性表示是CPU用來形成任務的任務地址。統稱爲任務空間。不跟硬件地址相關,也不跟邏輯表示形式相關,這完全是一種獨立機制的編碼,32位CPU下,一個任務的線性空間表示總是4G(注意總是這個詞),只是一個轉換機制會負責這邏輯地址到線性地址的轉換,,然後又有一個轉換機制負責線性地址到真實物理地址的轉換,程序員完全不必爲線性地址過少和這中間的轉換而煩惱那是CPU必須管的事,,否則32位的彙編工作幾乎要手動驅動CPU。

明白了以上這三個概念,讓我們再來看下面的章節。
1.5 分段和分頁以及保護模式
32位的CPU利用它的轉換機制可以保證4G空間一直存在內存中(這就是說,實際上並沒有一個實際的4G空間在內存中,只是CPU可以看到一個4G大的線性段,能以它爲基礎形成任務直接供程序使用這纔是CPU關注的)..這樣的話,對於編程來說,只要是在保護模式下,我們都可以擁有一個4G的編程可用空間,不必調進調出的(它本來就一直在內存中爲CPU所看見)。。

上述32位CPU的轉換機制是分段跟分頁的綜合作用。分段機制不管這個4G空間的實際調進調出,因爲它不負責實際分配內存,它只負責邏輯地址到線性地址的轉換過程..實際分配內存的工作由分頁機制來完成,它負責線性地址最終到實際的物理地址的轉換,它把這分段後形成的4G虛擬的空間用來調度真實的內存頁,內存頁就是真實的物理地址的載體,分頁機制只需保證生成的頁面大小總和侷限在這4G空間中即可。。頁面是隨用隨載,調進調出的,以較小的單位而不是段來實際使用內存,,這就加大了內存的使用率(雖然分頁並非百分百做得到如此)。

要深切理解CPU是如何完成分段分頁形成任務可用空間的過程是一個精微的學說,段選擇符,門,LDT,GDT,pae (CPU中有一個pae位可以保證所有CPU都只在概念上只看到這一大小)這些概念之間是如何相互間發生關係並維護這樣一個4G空間。以及這個機制產生的結果和意義所在,需要翻閱相關的書籍。下面試闡述一二:

l CPU首先檢查這個選擇符的ti字段,以決定是選擇到ldt還是gdt中選擇描述符,然後檢查相應的索引以在ldt或gdt(這些都是內存中的數據結構表,表驅動方式)找到最終的描述符。。這些符結構都在4g內存的高端部分,注意是在內存中..找到描述符之後,再以判斷選擇一樣的方式在描述符中判斷各個字段,主要是生成段基和段長,段的三個權cpl,dpl,rpl..

l 比如在如下的一個指令中 mov ds,ax, ax存儲的並不是指向ds的指針,,也就是說它並不實際存儲ds的地址。而是一個選擇符(注意,第一它不是段描述符,第二它段選擇符是一個32位的數據結構,它的各個字段表明了不同的意義,組成整個段選擇符的意義),,,段寄存器大小有限,CPU中的寄存器只用來存儲指針值,或者描述用的數據結構,比如在這裏是一個段選擇符。。

l CPU就是靠以上方式來實現對於段機制的保護的,每一條指令都有內存讀寫邏輯,每一個內存讀寫邏輯都有這樣的尋址,最終要涉及到進入段中。涉及到保護模式的一系列機制。。

l 門是CPU中的一種迂迴機制,有調用門,任務門,中斷門,異常門,這四種門的用途不一樣,但都是爲了實現CPU對內存訪問進行保護的一種迂迴機制(比如數據訪問權限啊,任務安全切換啊,中斷正常返回啊),調用門實現了數據訪問的權限機制,跟四種普通段,段描述符有關,任務門跟tss,tss段選擇符有關,中斷門與異常門跟中斷或異常處理例程有關。。
首先來談調用門。對於一種跨段的段間調用call或跳轉jump,統稱調用和跳轉和轉移,有直接轉移和間接轉移,,但是直接訪問方式不能實現段間調用的同時轉變程序的特權級,調用門的設置就是爲了實現一個間接調用加同時改變程序特權的跳轉方式。。
任務切換有直接切換和間接切換,任務門跟一個tss段有關,跟調用門談到的對於普通段迂迴機制一樣,任務門也是實現間接任務切換的手段。

而對於中斷門與故障門來說,門描述符內存儲的是中斷與異常處理例程的地址。。
總而言之,CPU提供了對保護模式的硬件支持。而所謂保護,是對段,內存,這些保護模式的概念提供迂迴機制,,於是發展出分段,分頁,調用門,任務門,中斷門,異常門這些的CPU機制,更多請自行研究。。


1.7 操作系統
當CPU能夠被用來形成任務控響應中斷時,操作系統這樣的軟件級的邏輯就可以在上面慢慢構建起來了。實際上機器往往並不需要一個操作系統也可以運作,比如電傳打字機和電子打孔機,在裸機層次也可以實現其功能。

但配備了強大CPU的PC絕不是一般的電器,馮氏模式一開始就指出,它是能夠換程序和可編程的,對PC的使用往往源於它抽象出來的強大的軟件功能,而不是僅僅是用CPU來加熱雞蛋(如果實際上CPU真的能煮熟一個雞蛋而且業界流行這樣使用CPU的話)。

操作系統就是這樣的軟件的第一層,它溝通了裸機與使用機器的用戶,對於裸機來說,操作系統可以由它直接抽象出線程,進程,網絡,圖形等系統功能,運行於操作系統下面的各種應用軟件可以使用它們進一步抽象出各種更爲高級的應用邏輯(比如Web,多媒體),對於最終用戶來說,操作系統提供了界面使得各種高級應用成爲可能,而且對於程序員用戶來說,使編程工作脫離直接面向機器編程,,因爲操作系統可爲各種應用軟件提供一個執行路徑,程序員可以面對操作系統編程,這是一個巨大的成就。即操作系統不但是計算機功能新的提供者,而且是開發環境。

這樣計算機系統實際上是硬件系統支持下的軟件系統了。人類要直接面對的就是軟件系統(這就是抽象的能力,它一方面隔斷了人類並不擅長的硬件細節,另一方面提供給人們一個更爲強大的軟件環境)。不論對於最終用戶或開發用戶來說都是如此(當然對於硬件工程師來說不是這樣)。

一句話,操作系統的出現是源於人的需求。

操作系統提供那些對於應用和開發來說最最基礎的功能和邏輯,因爲它直接關聯機器和CPU機制而直接面向初級軟件需求(比如形成過程以運行程序使操作系統具有執行程序的功能),在操作系統內核中,內存管理,進程,文件,網絡,用戶界面,是最先應被抽象出來和最先被解決的問題。我們知道軟件即抽象,所有的計算機系統能呈現和解決的邏輯,第一步是解決系統支持的問題(這導致了對系統編程的意思所在。),第二步纔是應用邏輯的表達和解法(應用編程和領域編程)。
當然,操作系統是一個大的概念,它小到只需要包含一個內核,大到可以包括內核層,開發支持層,調用層,硬件抽象層,應用接口層這樣的操作系統架構(參見Google手機平臺,這本質是因爲軟件是抽象,所謂抽象就是在不同的抽象層次完成不同的工作,操作系統作爲軟件也不例外)。因此討論操作系統我們只需討論操作系統內核便可一窺其端倪,我們只討論巨內核的Linux Core(相比之下有的系統內核是微內核,這種內核基本上只提供對進程,內存管理,圖形,用戶界面,這樣抽象層次的封裝和架構邏輯而不實際實現它們)。

Linux內核實際上主要是一個定製硬件功能爲軟件的邏輯,怎麼說呢,它一方面溝通了硬件驅動和軟件,使硬件可以運作起來,爲軟件邏輯服務,,,這是第一,第二,它對於硬件功能的定製功能,比如進程,,,就是把CPU硬件資源轉化爲軟件可以使用的進程資源,把網卡資源轉化爲socket跟進程一樣是種OS資源,,因此,內核實際上只是初初級的硬件功能抽象,

界面就是表現抽象,它的窗口機制,都是獨立內核的(KERNEL硬件抽象層),,如果稱內核爲操作系統(實際上只是OS的核心一部分)的話,那麼,X協議的實作品,桌面環境,窗口管理器,該桌面上的APP,,,都是作爲應用程序來運行在內核之上的,而不是像WINDOWS一樣直接在內核就集成了GUI,蘋果OS在內核集成了X,因此圖形效果很好(LINXU桌面沒有WINDOWS快), 這就是移殖問題產生的地方之一。,因爲UNBUT邏輯並不是其它邏輯的泛化源而是與其它APP一樣地位的基於內核的普通APP。

1.6 併發與協程
在操作系統和應用程序的邏輯中都會涉及到併發邏輯(特別是數據庫系統中),併發邏輯可以多線程也可多進程,甚至多線程多進程的方式進行,當然,單核環境下的併發永遠是一種對併發的模擬機制。

解決多線程的問題是多解的,,源於解決方法的高層模式各有不同,,(也即,從開發的眼光來看,它首先是一個設計問題),,有不同的相關概念,,比如鎖,,,就不需要用到信號量。

當然,有時你用的語言提供了併發的支持,,而且有時候,操作系統的併發邏輯會影響到你選用一種更有效的相容於操作系統的併發模型

併發問題源於圖靈模型與需要開發處理的現實問題之間的矛盾,圖靈模式是控制主導數據(機器的底層問題都是IO問題),這使得

1.6 CPU與異常
一般談到源程序的跨平臺特性時,總是說在p4加winxp下編譯通過(同時提到了CPU和OS),,CPU與OS的關係該如何理解呢,如果說CPU控制了計算機硬件上的一切,OS就相當於CPU的外層調用策略(shell就相當os的外層調用接口)。它把CPU邏輯和CPU控制下的外設邏輯,存儲邏輯封裝爲軟件可用的東西。os的許多東西,是CPU硬件加速下的結果。

比如說Windows和linux的內存分頁機制和保護模式,,如果CPU沒有提供三種模式(實模式,x86虛擬模式,保護模式)中的保護模式,那麼在OS級雖然可能可以實現一個所謂的保護模式(但卻沒有直接硬件的支持)

在保護模式下,CPU中的段寄存器不用來實際存儲物理地址的線性表示(而在實模式下是這樣),它用來實現硬件級計算最終物理地址的一箇中間存儲。。

中斷是硬件級的消息,中斷和異常是CPU直接相關的東西(CPU有二跟中斷引腳),,,很多編譯器提供了異常處理機制,Windows更是實現了一個SEH,,但是,這些都是抽象了CPU的這方面的邏輯。。

中斷中的硬件中斷是真正意義上的中斷,把它稱爲中斷是爲了跟異常區別開來,二者都指CPU對影響它原來指令流程的意外干預過程。(CPU對這二者都提供了處理例程)。但是中斷是硬件級的,是來自外部的,異常是來自CPU內部的指令執行過程中的一個出錯,,是來自指令執行過程中的(所以,所謂的軟中斷指令其實也是異常)。

而發生中斷或異常時,二者都是靠CPU的跳轉指令來完成跳轉到相應的處理例程的,,這是CPU直接執行指令的結果(Ruby甚至鼓勵用中斷和異常來代替正常跳轉,這是直接用CPU的指令的結果,編譯器控制不好會造成很多隱患),機器級的跳轉指令是程序語言實現它的控制流的一個重要方面

在保護模式下,跳轉應在各任務間正常切換,否則會引起著名的操作系統保護錯誤,處理例程調用完之後通過一定手段返回正常任務),那麼CPU就發展出一些諸如調用門,任務門之類的東西。。用來規範這些跳轉。。

再比如遞歸,由於它每次調用實際上都產生一個新的函數,對於棧式CPU的計算機來說,由於它主要利用內存而不是寄存器來存儲這些新函數的臨時變量和壓參操作,,因此這個遞歸過程不應有過多的調用深度,否則壓參入棧方面的開銷會過大(這是指空間方面的優勢,棧這個段區可用來存儲很多參數但是速度不快,而reg式計算機速度快但是沒有過多的reg供遞歸存參用。因此空間方面是個劣勢。)。

1.7 所謂堆棧
運行時是機器掛鉤的概念, 非語言掛鉤的概念,(但是實作品可以用語言來描述)一門完善的語言的提出,,包括提出一個針對該機器環境關於此語言版本的的代碼運行時環境.(因爲語言實現需要被運行,那麼就需要發展出一個c runtime dll的東西了)稱爲native runtime,因爲是對機器本身,而非類jvm軟件機器,的本地機器運行時邏輯的C語言封裝。(我們將一種機器是屬於reg方式來執行邏輯還是用內存stack方式來執行鍼對它的平臺邏輯,,以及相關的一系列知識稱爲運行時)

我們知道,REG和內存都是語句運行時(不妨改成運行空更好)的存儲空間集散地,但是不同的機器,有的不具備REG,有的側重利用這二者的機會又不盡相同

如果機器是堆棧機(採用內存stack方式的運行邏輯),那麼它就是用內存來作爲主要運行場所的機器,JVM速度很慢,但是很耗內存,從這點就可以看出來了(因爲它的機器中沒有軟件摸擬的reg,,爲了跨平臺,jvm根本不打算將x86的reg機制引入,而我們的x86平臺是硬件的reg,所以稱爲本地平臺,硬件的reg總比軟件的快,軟件的3d render machine總沒有直接硬件訪問的快你可以聯繫這點來理解)

機器如果是寄存器機,那麼主要用寄存器來執行語句邏輯,指令邏輯,完成數據的傳送(語句即指令加數據,爲什麼任何一個邏輯正確的可執行文件爲什麼CPU能執行它併產生結果呢,這是因爲一切pe邏輯都可反彙編譯成指令加數據的語句,它能被執行,因爲它同時包含了代碼加數據,CPU不能執行純data或純text),,因爲寄存器是CPU的而堆棧是內存的,所以reg機明顯比stack機快,但是寄存器器數量和容量有限(比如遞歸不好做),所以針對這類機器設計的單個指令代碼長度一般不長,,在一些方面是比不上stack機的

當然,intel平臺中,,CPU和內存是共同發揮作用來實現尋址的,而且也共同(主要指CPU中的REG,一方面,SP這類REG發揮指針的作用指向內存,一方面,一些REG本身可用來直接存數據)作用構成運行時(時空=CPU時間加內存場所),,那麼intel平臺是stack機還是reg機呢,當然是綜合機

首先,對於x86平臺,是如何執行函數邏輯(比如反彙編的一段C語言的函數的附近得到的一段彙編語句)的呢,,通過探索這個過程分析那些彙編邏輯(其實就是編譯器產生的平臺邏輯,機器碼嘛),我們可以發現這個平臺的運行時底層信息

首先,PE載體邏輯內有一段堆棧段,被映射到內存(載入器和重定位器會完成這個工作)後形成一個堆棧區,寄存器此時高速活動,它的一個活動記錄中,必定保存有棧基棧頂的一個指針(此時它們重合)

堆棧的基礎概念要掌握,它是一段從高地向低地生長的空間,而且是活動空間,因爲棧頂(指示當前出棧入棧位置的指示,它存在sp reg內,永遠只擁有一個是‘’當前“的值,減它就是在低地址更低所以是爲堆棧騰生長空間,加它是讓低地址往高地不低一點,堆棧空間會變小)會因爲頻繁的出入棧動作而變動。這種變動性使得我們掌握堆棧的方式(求某一個成員在此活動堆棧空間的位置)只能用簡單的'bp"+-該成員對應bp的偏移,的方式來指定了,而棧頂的偏移永遠是sp-bp


在進行一段函數調用時,必定是存在調用方和被調用方,一定存在參數間的互傳,怎麼傳參,調用結束後指令權如何在調用方與被調用方之間協調,平臺是如何執行這些運行時邏輯的呢?

對!x86用到了堆棧,對於C語言函數,參數右向左入棧(顯示成彙編邏輯時是push),第一個參數反而在堆棧(此時是堆棧對應這個函數附近活動區域的一個幀,因此稱爲堆棧幀,即堆棧區的某個真正的活動堆棧)的最上面(偏向低地址方向生長)這樣,相對其它參數來說,它就顯得先進後出嘛,後進所以先出嘛,當然,它到底出不出,此時出不出,這跟當前sp沒有一定聯繫,,因爲sp當前位置不只是由壓參影響的(甚至沒有必然系,因爲sp這東西可以人爲用匯編碼指定),還受其它自動變量的影響呢
C語言的這種規則跟pascal的規則完全不同,,這導致的C語言函數可以有變參數數量的好處
1.8 真正的保護模式
8086/8088有20根地址線,寄存器爲16位
寄存器的位數,16位表示決定了計算機的字長,即一次性能處理的數據總長度(16個位,始終記住,位是計算用來表示存儲單元和數據長度的最小單位,無論是外存或內存都一樣),因此在程序中只能定義至多16位長的變量,因爲只能定義16長的數值(變量),故(寄存器大小)它也決定了計算機能表示的數值的大小,即2的16決方(1048576),這種數值至多能用來表示的存儲單位是1048576個,,或者說1048576個位,而這正是一個16位的數值變量所能達到的最大值,因此這種寄存器爲十六位的CPU只能表示至多1024kb個內存位,雖然可能計算機本身不止這麼多內存,雖然有時地址線不止尋址這麼點的空間,但16位的寄存器只能看見並尋址這麼多的內存(因爲地址線是CPU引腳線,是CPU的概念),寄存器的位數理論上決定了CPU能"表示"的最大的內存範圍或外存範圍(當然,連CPU表示都無法表示的內存範圍那就沒有意義了),而地址線決定了計算機實際能存取訪問尋址到的內存範圍,即1M,不包括CPU連看都無法看見的那部分,(當然如果你的計算機都沒有這麼多的內存也是枉說)一個是16位,一個是20位,CPU是怎麼樣產生數值用來表示地址線所能尋址到的1M地址的各個單元的地址的呢???,(這裏以字節來說)1M可以分爲64k個64b,這樣,寄存器用來存放地址,80286有24地址線,寄存器爲32位
因爲寄存器是存在於CPU中的,因此說是CPU的尋址,又爲什麼說CPU對內存的尋址呢,爲什麼我們在這裏口口聲聲地說CPU對內存的尋址呢,這有什麼意義呢?這當然是爲了計算機能正確執行指令而準備的,這是計算機工作的本質啊(而爲了執行指令,CPU能對內存進行尋址是首先必須要辦到的,因爲程序最終從外存到內存中才能被CPU執行,CPU和內存都是靠電工作的,CPU提供執行指令的本領,而內存提供存儲指令的本領,這是馮儀曼說的,成功完全指令必須是CPU和內存一起工作完全的,而外存是靠磁來工作的,CPU只能執行內存RAM內的指令,外存用來永久存放文件,稱爲持久化),程序要從外存被加載到內存中才能形成指令

(指令在程序的CODE段中,EXE文件被加載到其進程空間時------這個過程術語叫映射到其進程空間,
它的代碼段就在物理內存中了,因爲只有代碼段才包含指令,這部分要首先映射到物理內存中來,程序的指令用到的數據------這通常表現爲程序中的變量常量存在data中,數據段部分被開闢成堆或棧的方式被映射到EXE的進程空間中來(分段機制),形成EXE的編譯器不同開闢模式不同,像Delphi的編譯器實現的exe進程空間只有堆沒有棧這種內存模式,堆和棧是系統級運用內存管理器進行分配內存的動作後(是系統級的作用)形成的特殊內存塊,這些內存塊被映射到EXE的進程空間,這有點像EXE的DLL模塊的映射模式,dll文件被映射到其宿主EXE的進程空間時,不像EXE一樣一開始就把代碼段實際加載到物理內存中去了而是在EXE實際調用到它時纔到實際的物理內存中去(分頁機制,只要你的計算機定義了虛擬內存,那麼在執行大程序時,,這個分頁機制就會頻繁用到),跟EXE的DATA段一樣屬於一開始就映射到EXE進程空間而不實際形成內存的部分,EXE,DLL被加載到內存後,它所佔據的內存塊就叫模塊是靜態的,而進程是一個執行體的實例是活動的,線程是一個進程的某個執行單元,所以我們說程序被映射到到其進程空間而不直接說映射到物理內存中,只是需要執行的代碼段(注意此段非實模式彼段,後面會談到)才被進入到物理內存,但不需執行的那部分不需立即加載到內存(就像DATA和DLL)不得不說的是,進程空間並非物理內存,前面一再強調"程序被映射到到其進程空間而不直接說映射到物理內存中",而且更準確地來說,它們二者是完全沒有關係的,4GB虛擬地址空間整個兒壓根兒就是虛擬的(這個道理就像你玩一個客戶端爲2G的遊戲時,你啓動客戶端的時候已經把整個客戶端的2G資源都加載到4GB空間去了,但是隻要這2GB中需要當前調用的那部分資源才進入內存,分段機制開避四GB任務空間,分頁機制把需要用到的數據動態加載到進程空間,任務空間就是進程空間,然後通過這些資源在程序中的自動變量表示離開內存),它只是能表示4GB空間的虛擬地址而已,並不是實際的物理內存,僅僅根據32位寄存器能表示那麼多的內存來設置的那樣一個機制,這種機制成全了將進程空間隔離的好處(所以四GB的說法是進程的一個概念通常說4GB是進程的空間),而不像整個六四KB都可以,Windows的虛擬內存管理器(保護模式下)會負責適當的時候把需要的,映射到進程空間的內存搬到物理內存中去(分頁機制),現在來解釋"注意此段非實模式彼段,後面會談到"這句話,在FLAT下,已經沒有段segment了,,在Flat模式下就無所謖code段跟data段的了,因爲原本實模式CS,DS意義下所指向的段全都在一個4GB大的虛擬地址空間中,實模式下段的意義不存了,是段選擇子,FLAT內存模式下,CS,DS根本就不實際用來指向段,即不實際用來指向一段內存(而是存儲一個稱爲段選擇符的數據結構),FLAT下說的代碼段是指EXE頭定義的段,是RAW FILE(RAW指沒有分配實際內容的內存)裏定義的段而非實模式下CPU段寄存器指向之"段",模擬原本的段取而代之正是EXE頭的節的意義,程序員無須知道這些段的實際的物理內存地址而只須知道虛擬地址,(我們知道在32位寄存器,在RAW FILE裏纔有節section,有了PE文件的頭中的這樣段定義,當EXE被加載到內存中來,就相當一個跟內存數據段一樣的數據結構,雖然平址模式下無所謂代碼段數據段,但PE文件的格式間接實現了它,就像XML文件它本身就像一個數據結構一樣所以它被很多程序語言的IDE用來持久化,被很多程序用來保存信息如3D網格信息也會用xml文件格式)



1.9 異常與流轉
Linux內核實際上只是一個定製硬件功能爲軟件的邏輯,怎麼說呢,它一方面溝通了硬件驅動和軟件,使硬件可以運作起來,爲軟件邏輯服務,,,這是第一,第二,它對於硬件功能的定製功能,比如進程,,,就是把CPU硬件資源轉化爲軟件可以使用的進程資源,socket跟線程一樣是種OS資源,這之後纔出現OS,,因此,內核實際上只是初初級的抽象,而SDL,就是更高一級的抽象..
它的窗口機制,都是獨立內核的(KERNEL硬件抽象層),,如果稱內核爲操作系統(實際上只是OS的核心一部分)的話,那麼,X協議的實作品,桌面環境,窗口管理器,該桌面上的APP,,,都是作爲應用程序來運行在內核之上的,而不是像WINDOWS一樣直接在內核就集成了GUI,蘋果OS在內核集成了X,因此圖形效果很好(LINXU桌面沒有WINDOWS快),,,,我們平常所謂的UBUNTU也只是APP集(在內核上面一點點的邏輯),,,在服務器版中能精簡得到,這就是爲什麼移殖了UBUNTU卻不等於移殖了它上面能進行的一切APP一樣,因爲UNBUT邏輯並不是其它邏輯的泛化源而是與其它APP一樣地位的基於內核的普通APP
運行時是不是移殖的產物?比如apache運行時,是一個抽取最核心的運行庫,比如C庫的運行時,等
異常是什麼呢?異常不是錯誤,,是比錯誤輕一級的概念,當異常(即使被捕獲到了)不能(在程序中預定義的處理塊)被正確處理,就可以拋出一個錯誤(當然,錯誤和異常在程序中可表現爲很多種,編譯型語言如C++就有靜態編譯期語法和動態運行期邏輯錯誤之分)而調試,包括對這二部分進行調試,靜態期的調試要容易得多,而運行動態期的類型在寫代碼時(編譯前和編譯中)顯得易讀,但是調試時十分困難(因爲要從運行中調試)..這就是動,靜的區別所在
(這個過程通常是,我們按照正確的程序流程來處理錯誤,發生錯誤,在程序預測到它,並不嘗試恢復,,比如跳棧,如果能處理則處理之,不能則崩潰)
在傳統編程中(一種語言未引進異常機制前),我們總是用IF,,THEN這樣的判斷跳轉語句來提供處理異常發生時的處理邏輯,,,當一門語言內置異常機制時,此時,我們就可以用新的處理邏輯,此時,通常程序語言會作跳棧和保護現場的動作.因爲大凡大型軟件,需要一種很強大的異常機制,它必須全天侯運行,不能出錯就恢復不了,這種動用了程序語言原理(一切程序即函數調用,如果你反彙編過就知道了)來處理異常的方法就是真正的異常..(異常與跳轉關係如此親密,RUBY甚至鼓勵用異常實現流轉)



1.10 最小,最完美的系統
在操作系統方面,LINUX KERNEL是最小邏輯,GNU LIBC在這個時候就被集成了,很好,內核的架構是重要的,LINUX先是硬件邏輯,再是語言作爲中間層,用戶程序SHELL等,作爲用戶空間,這種架構很模塊很科學
我覺得用語言本身來作爲架構的一部分,這樣來實現移殖,可以解決一切移殖問題,只有該語言的VM被移殖了,那麼該語言之下的邏輯就全部被移殖了
對一個平臺的開發是促使這個平臺能得於流行的基礎,,,我覺得編程語言要儘量靠近自然語言,無論這種編程語言出於什麼理念(函數式,OO式),還是與什麼離散基礎相關(機器結構堆棧式,,這是不是嶄斷了人們對底層進行擴展和編程呢),跳轉啊,數據結構啊 ,這些東西應該跟開發者沒有一點關係,用戶應把他們全部精力放到要解決的問題上而不是系統和語言(比如這門語言的語法都不要掌握)這二個東西上,,這個道理就像EXE跟RPM的關係一樣,只有無聊玩得太深的開發者纔對RPM感興趣
(畫一張架構圖,類andro架構,架構是重要的,當架構不正確,一旦當它發展疇形了,那麼要改動底層是很困難的,就像LINUX桌面被設計成一個外掛於內核的APP一樣,這樣的架構並不適合開發遊戲,內核與桌面的分離一方面讓內核可以脫離GUI移殖另一方面卻導致了一些不好的後果)
加入微內核的多進程多線程支持,不需把線程作爲開發語言的用戶級線程,系統提供相關進程的API
也可提供用戶態的多線程支持
從裸機開始,到驅動程序到OS,最最應該注意可執行文件的載體邏輯(EXE載入邏輯和邏輯承載邏輯),應把它發展爲類Windows下EXE的東西.不要像LINUX下的RPM那樣的東西,讓用戶下載應用程序的體驗足夠好
對於桌面系統,我還覺得WINDOWS的菜單,,窗口機制什麼的都有欠缺,,如何改造呢?我覺得,應儘量避免展開菜單(即使最常用的打開命令都要展開一級才能執行到這很差勁),我們應把最優先能用到能直接點擊到的地方,比如WAR3的操作盤,,當你選定一個對象時,顯示關於這個對象的所有詳細信息,當你圈選一堆對象時,在顯示欄內實時顯示你選取到的對象和概況,這很不錯
這樣的操作系統最好是EMBEDED的,可同時移殖到ARM(手機多使用)和PCX86上,,體積上要小
驅動程序過後,多媒體開發方面,一切用SDL,SDL是用C寫的,不如用它,不如用SDL實現一個GTK的東西,因爲SDL是直接訪問媒體硬件的(DX作爲底層也用了OO的COM,這樣不好吧)
在桌面環境中,可以改造一下X,使得它不僅僅能在服務/客戶環境中畫簡單圖形,而且能畫3D遊戲效果,,這樣網遊就直接跟桌面集成了,哈哈
桌面和SHELL,SHELL就用RUBY SHELL,(儘量讓最白癡的用戶願意用命令行)
在編程語言方面,C是最小邏輯(GNULIBC是不是太單一了,它應該能提過一些模塊擴展出OO功能,動態裝載,在OS KERNEL內不需要集成,在後來向RUBYSHELL時就需要集成這個OO模塊),我想知道有沒有一種類RUBY的C語言,它應該接近底層,爲本地機器作優化而不需要一個虛擬機,沒有太慢的GC機制,而且採用OO,有RUBY庫的那些功能和編程理念(RUBYC?呵呵,就相當於把C++改造成RUBY語法了,徹底去掉編譯期設計模式導致的複雜性)
這樣的RUBYC是不是就是JVM的JIT模式呢,同時擁有對本地機器和JVM進行編譯的功能
C開發理念跟RUBY開發簡直就是二個節然不同的理念(雖然RUBY也並沒有把這種理念做到極限):一個用機器來考慮問題一個用人腦靠近應用本身考慮
像一般的智能手機平臺上都是用C作爲底層開發,而用JAVA作爲架構的上層,作爲高級開發,比如GOOGLE的手機平臺,但是就是不常有C++,開發
傳統上C開發一個應用(比如一個大型應用),那麼就要考慮用什麼庫,用什麼算法,用什麼語言已提供的,,RUBY在這方面雖然也要最終考慮這些問題(這些問題已經被SOA式的邏輯實現了),但是一着手時並不是考慮這些,而是考慮應用問題是什麼思想模型,a比如是一個MVC,那麼就用ROR,,這樣理念更自然,,因爲它的機器支持被提前實現了
編譯器和鏈接器是平臺相關和有關的,,,爲某一個平臺特定服務(因此也可針對某平臺進行優化,速度大爲提升),,而JVM直接提供了執行宿主,JVM中的解釋器就是這個作用.
提供一個極好的關於這個語言的解釋器和IDE,運行環境爲OS而不是虛擬機,因此不需要JVM的HOTSPOT技術(但是要保證這門語言在不同的軟硬件平臺上的移殖能力).
在網絡方面,P2P是最小邏輯(P2P實際上是最科學最好的,一切網絡程序都應是P2P服務模型,當然也兼容CS,即P2P在CS之前),JXTA也是用C寫的,WEB根本不需要用一個IE導出去,網絡應該直接跟桌機集成,不需要定義一個WEB邏輯,這是很差勁的,這樣桌面開發和WEB開發都在一起了,RUBYC成爲桌面語言和WEB語言.同時成爲系統編程語言和應用編程語言.
要開發一個類GOOGLE EARTH那樣的網遊3D桌面環境(虛擬世界),定義一個邏輯上的虛擬世界,
XML成爲一切數據的格式,不需要OODB,文件系統跟壓縮包,跟磁盤完美結合,數據庫可持久(XML邏輯應發展出一門DSL語言嗎?不需要,可以將其邏輯內置到OS KERNEL?或者KENRL之外作爲用戶態的東西?)
在人類的手控方面,我覺得手機式的10個數字鍵纔是最小邏輯,應該成爲一切輸入機器的最終原型.
Flash的SSD固態硬盤卡很好,可別在錢包內,,主板上集成的卡不行,壞了就拿不下來了(這是屬於WEB2.0的個人數據所在,每個人都有身份信息在內)
再發展一個世界邏輯,模擬我們這個地球的世界邏輯,定義


1.11 操作系統與語言的關係
語言有它跟OS直接相關的地方,雖然形式語言的理論不跟任何OS相關,但是具體一個語言實現(編譯系統或IDE)都服務於一個OS。
  最初的第一個編譯器是以很複雜的方法被寫出來的,後來的編譯器是有形式語言這樣的理論指導下發展出來的,因此,解決開發編譯器的角度不同,那麼所付出的努力也會不同,就像GUI 問題,如果你能換一種眼光去看,,那麼或許我們現在所看到GUI就根本不是現在的GUI,,現在GUI的消息機制,也許以另一種思路就能很輕鬆解決(比如我們將界面邏輯看成XML邏輯,在XML中提供消息機制達到的效果,或者根本不需要一個所謂的消息機制,不同思議的不是問題本身,而正是人類的大腦,任何問題都可以只是想象,然後纔有解法),不給平臺任何更多的運行負擔,,因爲沒有平臺相關的邏輯存在,這樣OS的內核就不會越做越大。
  如果能有一種方法,使一種語言機制直接跟OS相應,而且一一對應,,沒有其它任何第二種語言建成在這個OS上,換言之,跨平臺問題將不存在,,因爲只有一種語言產生的邏輯需要考慮移殖,
  編譯器的目的是爲了產生碼,系統語言基於平臺邏輯是爲了開發平臺邏輯,腳本是爲了描述應用
  那麼解決GUI問題有什麼新的方法呢,



1.12 虛擬機與語言
虛擬機是對CPU的模擬(還有模擬中斷機制和IO),,,一般提到硬件的“機”就是指馮氏的構架,這個構架中,CPU是首先要談的東西。。Intel的構架有專門一本書來講解。彙編語言就是CPU的彙編語言,不同的機器有不同的指令集。。因此有不同的彙編語言,這就是移殖問題最初產生的地方。。

如果你玩過軟件的虛擬機就知道了,,市面上存在很多虛擬機..

在設計一種CPU時,總是先實現一個軟件的虛擬機來模擬它,這種軟虛擬機被運行在一個平臺上,運行在這個平臺上但對虛擬機編程的編譯器叫交叉編譯器。在虛擬機設計完全並實現之後,這個編譯環境也要移殖到虛擬機上(此時它被實現到目標硬件平臺上),,只需要用這門語言產生關於這門語言在新硬件環境下的編譯器就可以了。

然後,操作系統就可以在這個基礎上開始設計並實現了。。但用虛擬機方式來驗證此機器,和提供一個編譯器,這是首先要做的事情。。

posix是類unix系統的接口標準,,爲了移殖方便而提出的,因爲OS實際上是一種系統調用例程的抽象,這個系統調用例程規定了“計算機硬件反映到軟件能做的事”,是最初軟件邏輯產生的地方,被經過OS封裝之後形成了進一步的進程邏輯,sokcet邏輯等。。對系統編程就是考慮到硬件邏輯和OS邏輯的一個綜合過程。

C語言作爲系統編程的代表語言,它的標準庫裏面的函數,實際上是站在硬件邏輯和OS平臺邏輯上面的一個通用接口。在Windows和類unix上都是這個抽象接口。支持C標準庫的OS必須聲明它支持這些接口。方能讓標準C語言移殖其上。。

unix被產生時,是用匯編寫的,彙編有移殖問題,因此unix的作者又創始了C語言,並改寫unix,f所以歷史上unix上是產生C之前的,C的移殖性超好,這個作者又寫了一本書,形成了C語言的第一次標準化。幾乎是unix產生之後,類unix系統不斷出現,,爲了規範這些類unix的兼容性,提出了一個posix,對系統調用例程接口進行了規範,因此產生了posix.

最初的C++語言的實現(就是C++作者寫的一個編譯器)實際上本質上是一個C編譯器,他在C編譯器的前端額外加了一個過程,把C++代碼映射爲C語言作爲目標語言。再把此目標語言映射爲機器語言。。



1.13 虛擬機與語言
通常用一門語言寫出來的程序都被用在諸如X86加OS的架構上運行(平臺移殖或編譯通過時,我們總是說,在某某某架構上加某某OS上編譯通過),當一門語言爲了實現跨平臺(一般是跨硬件架構,硬件掌有真正的運算資源即芯,而OS核心將硬件能力供軟件服務,包括進程,等,對一臺裸機編程就是彙編了)考慮時,它首先發展出一個虛擬機(只有這個虛擬機實現了跨軟硬平臺那麼語言代碼就實現了跨平臺了,因爲代碼的運行環竟的根本是硬件架構),這樣所有用這個語言寫出的的代碼就由這個虛擬機來解釋,這個源代碼就不是本地碼了(不是供X86+OS這樣的本地生成機器碼),軟件實現的虛擬機往往是一種高級機器(是一種刻意爲了適應程序的運行而設置的完美環境,實際硬件上的這樣的機器沒有),而往往是一種虛擬機能執行的中間碼,,當然,虛擬機上的語言如果有特定編譯器和編譯技術的支持,同樣可以爲操作系統和硬件生成機器碼

一般情況下,軟件運行環境=硬架構加軟架構
那麼虛擬機就是專門爲了一門語言而出現的高級軟件機器,,它直接在裸機上(加OS?)運行,因此去掉了OS的作用,是語言跟它運行環境的直接結合體(虛擬機是虛擬機,虛擬機裏面的編譯器或解釋器就是它跟語言發生聯繫的地方),是爲了一門語言而出現的優化運行環境
虛擬機的意義遠遠不只是爲程序代碼提供一個可運行環境,它真正的意義是使編程工作完全遠離操作系統跟硬件架的差別(傳統的源程序總是要考慮到移殖,而且要考慮到平臺本身給語言造成的複雜性,比如C庫的I/O),,在JVM和JAVA下編程,我們無視有一個OS和平臺的差別,所有人都面對同樣形式的代碼..JVM有了這個優勢,,JAVA因此成爲了工業語言的原因之一.
通常用一門語言寫出來的程序都被用在諸如X86加OS的架構上運行(平臺移殖或編譯通過時,我們總是說,在某某某架構上加某某OS上編譯通過),當一門語言爲了實現跨平臺(一般是跨硬件架構,硬件掌有真正的運算資源即芯,而OS核心將硬件能力供軟件服務,包括進程,等,對一臺裸機編程就是彙編了)考慮時,它首先發展出一個虛擬機(只有這個虛擬機實現了跨軟硬平臺那麼語言代碼就實現了跨平臺了,因爲代碼的運行環竟的根本是硬件架構),這樣所有用這個語言寫出的的代碼就由這個虛擬機來解釋,這個源代碼就不是本地碼了(不是供X86+OS這樣的本地生成機器碼),軟件實現的虛擬機往往是一種高級機器(是一種刻意爲了適應程序的運行而設置的完美環境,實際硬件上的這樣的機器沒有),而往往是一種虛擬機能執行的中間碼,,當然,虛擬機上的語言如果有特定編譯器和編譯技術的支持,同樣可以爲操作系統和硬件生成機器碼

1.14 調試器與彙編器
虛擬機與語言的關係很密碼,像jvm跟Java語言的關係就很愛味,有人說,jvm彷彿並非一個通用的軟件環境,對jvm編程久了,會覺得像是吃飯的時候去了一家只提供一種口味的冷飲店,因爲有些jvm的機制,比如rmi,,只限於用Java語言去開發。

Java語言屬於高級語言,實際上當構造了一個虛擬機之後,第一件是給他設計一套彙編語言。除非你不打算給這種虛擬機進行開發,一旦有開發的需要,就需要實現一個彙編語言或一套高級語言。這種必須要實現一個彙編器,,,只有這樣,虛擬機才能開始執行,,虛擬機本身並不需要一個彙編器才能開始執行字節碼,你可以直接爲虛擬機手動寫字節碼,,提供彙編器只是爲了開發的需要。。上面說了。

同樣爲了開發的需要,,調試器也是需要的,一般的IDE都有調試器,調試邏輯來源於CPU邏輯,intel中有斷點中斷處理例程,,中斷一般來源硬件動作,,但也可在源程序中人爲地給CPU下一個軟件中斷,這就是int指,interrput的前三個字母,int 3這樣的指令讓CPU產生一個陷阱狀態,讓機器進入調試狀態,此時CPU並不正常執行程序,,而是一步一步地執行程序。。

調試器分硬件級的和軟件級的,但都是爲了讓開發者或機器調試者研究程序在機器內部二進制級別的執行程序,,調試器一般向你顯示CPU寄存器狀態,符號信息,內存上下文等等。。

彙編語言實際上並不圖靈完備,因爲它是對指令的一種命稱指代,彙編語言沒有變量。因此沒有編譯器裏面談到的變量等符號映射爲內存地址的符號化信息,,因爲彙編器畢竟不是編譯器,解決問題時所面向的源語言和目標語言不一樣。。

運行時,從字面上理解爲代碼運行的環境,無論它是機器套個OS執行引擎,,還是虛擬機這樣的軟件模擬的運行時,無論如何,代碼要得於運行,必須需要內存和CPU指令,,JVM就是JAVA字節碼的運行時(並不包括JIT,JAVA的編譯器等部件,這些都不是執行字節碼必須的,只是負責生成字節碼的,而運行時是驅動目標代碼運行的環境),C語言代碼被編譯後到WINTEL上執行時,Windows操作系統加編譯器的runtime支持就是機器代碼的運行時..fs

1.15 平臺之GUI
從C語言的觀點來說明桌面,從C 語言的觀點可以說明一切邏輯,因爲計算機邏輯都是由C開發的,而C本身也最接近計算機的處理邏輯,C抽象了彙編中的一些機器困素,但是它幸好沒有做到全部抽象,比如指針,實際上直接跟內存這個部件相關,因此C語言可用來解釋很多計算機的東西,,,這是C語言作爲教學語方的一個很好的地方.
現在流行的開發一般都分爲桌面開發和WEB開發,但是桌面和WEB應該是被整合的,GOOGLE DESKTOP SEARCH,GNOME的名字中,就有NETWORK,甚至於OS內核中就有網絡支持,在GNOME中也有BELGEL這樣的桌面搜索,,因此網絡跟桌面本來就該在一起,只是WEB和HTTP協議這特定的一塊的邏輯得到了長足發展(因爲跟企業應用緊緊結合),導致了流行的WEB開發,因此,技術都是受商業驅動的..不要以爲技術是很高尚的東西
最初的邏輯都是沒有GUI接口的,一切都是SHELL跟KERNEL,Unix系統下,MIT X Windows是桌面的CORE,歷史上地,SUN公司一直在操作系統方面力圖佔據桌面(它後來的SUN DESKTOP SYSTEM 簡單就是拉及啊),,它的OPENLOOK 後來敗給了MEIRF,桌面最著名的模型,就是B/C消息模型,用類C++寫的桌面系統纔有CLASS,用C寫的比如GNOME沒有窗口類名這樣的概念存在,有的只是關於窗口的指針,句柄(這些邏輯可由QT,GTK這樣的來產生,也可由編程語言來產生,比如GTK,在庫內就實現了"對象"這樣的邏輯,因此它的窗口也有CLASS NAME這樣的邏輯)等.
爲什麼GUI如此重要?
因此,GNOME這樣的圖形平臺標準,或者一個實現,,就影響了桌面應用,,以及桌面上的很多程序的移殖問題,,從瀏覽器、辦公套件、郵件客戶端、音樂/視頻播放器、CD/DVD刻錄工具、BT下載軟件、即時通訊工具以及偏門的音頻抓軌工具都一應俱全,,這本質也就是我們在前面說到的是邏輯的關聯問題,,比如,瀏覽器所用的HTML邏輯,就被封裝在GNOME中,這造成運行在它上面的WEB瀏覽器各不一樣,因此IE核心,跟FOX核心是不一樣的..corba甚至用在GNOME實現內,就像新興的XML一樣也被用在GNOME內



1.16 界面的本質應該是命令行功能支持下的配置描述文件
從更大的範圍講,,任何一種設計都是自蠶自纏,,當然,適合當前應用的設計總是存在的
  設計的合理性只能是相對的,我們只能做一種“目前最科學”的有限設計,而且這種設計方案也受到問題本身的影響,並不是所有所有目前合理的設計都要拿來用在同一個設計上
  你以爲GUI是什麼呢?
  界面GUI這個東西,不是邏輯本身(人們通常以爲CUI纔是,CUI中提供一些支持界面的邏輯,,然後,界面主體的本質應該是命令行功能支持下的配置描述文件,此時GUI不是邏輯,而是配置),但是界面也可當作邏輯本身來看,,此時,它就不應該是配置
  在操作系統的設計中,那些硬件訪問功能,CPU資源轉化成進程,永遠是最底層,最應該被首先解決的邏輯,,那麼界面呢,通常被放到後面,操作系統的設計跟一般稍微中等應用程序的設計(特別是涉及到移殖)時碰到的問題幾乎是一樣的多,,因爲應用程序總有它的關於運行的平臺邏輯(CPU+OS),,而不總是那些建立在業務領域的高層邏輯,,在完成這些底層邏輯的過程時,也要涉及到界面,,涉及到線程,,涉及到圖形,,涉及到SOCKET,,內存模式的數據結構,等,因爲這就是PC的邏輯,除非你不是對PC開發(開發語言本身跟平臺結合導致的一系列底層邏輯也大量出現在開發語言本身的機制和細節中,,也出現在開發語言開發出的可複用庫中,),,因此只要是複用,都跟平臺邏輯相關,,只有像WEB開發+JAVA,這樣的組合,才使程序員徹底(也不是徹底,70%吧)不需要了解系統知識,,而可以進行編程(所以有人說WEB程序不是純正的程序員,因爲它們不懂計算機,只懂他們的工作業務,他們做的不是開發,而是邏輯配置),你說寫XML文件是開發嗎?寫XML機制的實現纔是開發,,因爲涉及到數據結構和內存。。這纔是開發,,,lua 作爲配置語言的腳本語言就是這個意思,它適合寫界面描述和配置數據描述這些高層的東西,不適合描述機器本身..
  可見,所謂程序,就是不同的層完成不同的工作而已,,一個是靠近PC底層的比較難點,,一個是靠近你要做的事的高層,比較容易點,,這一切體現的精神,,就是封裝和接口,,封裝體現的終極精神是複用,,複用體現的終極精神是人。。
  如何爲一個OS設計GUI接口呢(我們以爲OS內核都是命令接口的像LINUX就是這樣,不像WINDOWS內核那樣把GUI集進去)
  首先最底層就是LINUX,然後是SDL邏輯和XML邏輯封裝進linux內核,作爲CUI中爲後來所用的GUI接口,然後在這基礎上發展出一些封裝SDL的高層庫(使得以後的開發不要動不動就訪問硬件),,用這個庫發展出一個桌面環境,比如argr
然後玩遊戲什麼的,就直接使用SDL就可以
其它的應用邏輯(除了玩遊戲等桌面應用),比如網絡應用,因爲內核中有SDL和XML,至少它們的界面已經解決了,其它的問題就是其它的問題了


1.17 命令行下編程實踐
編程爲什麼要在命令行下而不提倡在界面下呢,,這是因爲命令行下有界面,而界面下人們都往往不覺察到有命令行,而其實命令行纔是本質邏輯,界面只是一種GUI,是一種建立在本質邏輯上的用戶接口和配置文件(只是一種不必要的表現抽象)。真正的邏輯可以不需要一個界面接口來展現它,但強加了界面接口的邏輯就限制了邏輯本身的表達。。因爲命令行是觀念裏自由的世界,是C這樣語言開發時所面向的直接世界,是計算機邏輯發源的真實世界,所有計算機邏輯的起點是命令行的(僅需要文字界面這樣的用戶接口就行了),圖形用戶界面邏輯只是後來的後來需要考慮的東西。。所以開發時不免將界面邏輯滯後,而實際上,無論先後順序還是本質特徵,我們始終都要把握一個觀念,即其實命令行比(圖形)界面豐富有趣得多,我們始終要相信,雖然我們在Windows下長大,但其實命令行纔是主流,纔是我們的思想和開發根據地。

UNIX下的那種Shell操作,命令行工具和管道理念,遠遠比Windows菜單有效率(當然,命令行最普遍它的門檻卻很高),因爲本質上,命令行邏輯的一條語句"ProduceMenu(10000)",屏幕再大,也不可能生成10000個菜單供你使用。。而在命令行下,一條命令可搞定這樣的邏輯,因爲命令行是沒有界面限制的觀念世界(圖形沒有文字表意豐富),形成計算機邏輯的一層一層是是命令行的,,C開發時也是命令行的因爲C的庫全是命令行的,Windows API接口是C後來的產生GUI的一種方式,C是面向命令行的,它面面系統底層,並不一開始就將界面邏輯納入所有的邏輯開發中。。

我常用的C開發平臺是CH,一種C解釋器和命令行模式下的交互編程環境,它用瞭如下的Msys工具,我們需要深克掌握這些Linux下的開發工具(它們實際上也是系統工具,因爲Linux比Windows提供了更原始對開發的支持).

make,這是linux下編譯大型程序使用的,很多IDE都集成了它。

vim,,如果能熟練使用VIM,那麼你就會發現,它遠遠比Windows下的記事本,和一切IDE都有用。。

新手編程導論(三)

第2章 語言



2.1 真正的計算模型
下面給出一張圖(計算機抽象結構科學)

計算模型就是用戶跟計算機交流的抽象(如果這個抽象被解決了,我們就完全不管它而去幹其它事,因此你感覺不到它的存在),當然我們希望不僅僅是表層的交流,但是隻要實現了與計算機的交流,,這之後的處理我們就不用管了(因爲這並不屬於計算模型學的內容)

用計算機來識別的語言只能是形式語言而非我們的自然語言,自然語言是一種形式語言的超形式,因爲它可以被解析爲形式語言,並且帶有它作爲自然語言的非機器解析元素

形式語言是嚴格的(不符合條件就出錯),自然語言是活拔的(出錯範圍小並不影響信息的傳達)

計算機作爲一種機器,給計算機設計它能識別的語言就是指明計算機進行識別我們給他的輸入的模型(任何語言都是一個形式上的模型,供識別和基於識別之上的高級功能比如交流用,並且這個語言可用於後來的編程以給計算機下達指令(用語言進行編程給計算機下達命令是跟計算機的一種交流)後來計算機對我們的輸入進行計算(注意這個“計算”的範圍是廣的不只是數值處理)這裏說到的計算機對它能識別的語言表達的輸入(是一些指令)進行處理,處理已經走過語言的範疇了,雖然是基於語言識別之上但不屬於語言的範疇),,

語言和文法
語言分爲語言具體和語言形式,語言具體即最終由終止符構成的串,而語言形式即不一定最終由終止符構成的串(它可以是語言具體和語言形式的雜合體)
那麼什麼叫串呢?
語言有組成它的最小單位即詞(較大的單位有句子,段落,文章,卷等等,這些更大一級的單位又是由詞或其它單位構成的,,包括詞在內,這些語言單位構造的語言具體或語言形式統統稱爲串,語言的最終目的就是得出語言的一個一個串),和語言單位進行組合(產生串)的規則即文法產生式,任何語言最終都是具體語言材料即詞彙和具體語言規則即文法的組合

串是語言材料的模板(串代表一個一個的語言具體或語言形式),,而文法產生式是語言規則的模板(文法產生式產生文法)

語言具體或語言形式的產生是通過串與串的等價替換得出來的(這是串與文法關生聯繫的地方與手段),,如果存在w0->w1,即串w0可以替換w1,這個串間替換的規則就是“文法產生式”,,用來產生文法

以上到底什麼意思?相信現在不難理解了

2.2 開發模型與語言模型
軟件界不存在銀彈,是因爲我們站在老百姓的立場說機器的事。永遠只是模擬。而不會真正得到一棵銀彈。


編譯器的定義中其實少了一個方面,編譯器不僅是翻譯工具,而且更重要的是形成語言規範纔是這個定義的主體。

在我的理解中,至少在圖靈機的層次上,圖靈機不是智能的。在我看來,智能的唯一條件是機器有自主意識。

在我們現用的PC上提倡英語編程(並用傳統的編譯理論知識去實作),那是SB,因爲這是捨近求遠的做法,我們的PC本來就不是語言機。我們需要對PC本身進行改造或重建。

爲什麼機器模型跟開發模型是緊緊聯繫的呢?而且歷史上存在很多失敗的計算機模型(死亡計算機協會),只有馮氏模型活了下來。

因爲語言寫出來的軟件就是爲了系統服務,(比如結構化程序語言符合堆棧機),所以這個系統最好就是基於某種語言的,(但馮氏模型太屈就通用的低層實現而不是高層開發抽象),語言邏輯即系統邏輯,這多好啊(從這方面看,圖靈機既是計算機模型也是語言模型是很容易理解的),,我們的PC設計成非語言直接對應的機器,是因爲開發雖然對於PC很重要,但做其它工作時需要PC提供其它通用機制,因此不能僅僅屈就開發模型(比如還屈就機器實現難度上面談到)。

虛擬機就是在這樣的歷史使命下產生的,它是軟件上實現的機器,是語言模型跟機器模式折中的產物,因此有些人提出它,目的更多地是爲了迎合某種語言的提出(實際上虛擬機並不企圖提出一套機器標準,因爲硬件上很難實現這樣理想的機器,我們的CPU只能是晶體管認識二個狀態),,而且這樣的機器不必屈從硬件實現(可以基於某種高層理論提出),只要給程序執行提供一套執行路徑即可,因此也需要OS設計中的運行時,進程,中斷等等。’’

對於開發來說PC史的發展就是一個一直在走彎路的過程,,這個過程中又提倡什麼抽象(這是由軟件的根本任務決定的因爲軟件即抽象),但是太晚了,PC一開始就被設計成一個遠離人類大腦的東西有它自己的底層機制(再抽象的高級語言開發抽象都受制於馮氏的數據加指令模型)。應該把PC設計成一開始就靠近人的東西,這樣後來的開發中纔不需要太扭曲的抽象。

想象一下,如果我們人也是電器化的東西,,也有控制器等等,比如一個生物硬盤,那麼當人們被接入計算機時,編程的工作,也就變成了用0,1組成代碼,,即,機器語言就是高級語言,編譯原理這樣的知識根本不用去學。因爲用不着編譯器和彙編器這樣的東西。這樣的話,如果我們說話表達中存在銀彈的話,那麼軟件開發必然也存在一種銀彈。

即如果讓CPU一開始就屈從於人,而不是馮氏的屈就底層的模型,那麼我們今天的開發就不會是這樣的數據加語句。

我覺得所謂網絡神經電腦的研究是不必要的(我這裏說不必要僅僅是對於開發來說無多大影響),因爲那是變更底層,而對於軟件和開發者來說,再怎麼樣的底層都沒有用,,我們着重的是能影響開發邏輯的PC的高層模型(如何變更底層來實現這個PC級的高層模型纔是重要的,如果它是能學習的就是真正的AI機了)。即開發中我們只需要那些能根本影響開發的PC高層模型(而不是馮氏屈就底層的模型這導致以後開發需抽象),,,而且這樣的機器不但是“開發機”,而且也是“通用機”。

所以系統必須是基於語言的某種。。二進制,不,,機(必須基於某種低層啊,日,語言數學)

這樣的機器在硬件上不需要內存在軟件上不需要OS

即,面向系統的語言,導致面向語言的系統出現s,,因爲語言邏輯就是系統邏輯,所以這種機器在產生軟件時所需的邏輯很小。軟件很快

界面邏輯不需要硬件支持,聲音也不需要,全是軟件作用。利用空器成像,,光腦是另一個維度上的

這種機器有語法集,詞彙集,可以擴展,,解釋器就是機器本身,不需要編譯後端。

CPU是語法器,,硬件直接支持。編程的理念就是把一切能編程抽象到的問題(當然沒有硬件的支持不能爲你泡一牛奶當然如果接上配備有語法器CPU的牛奶機是可以的)全部用語義直接來描述(而不必經過編譯後端)。。

這樣的CPU可以直接集成到聲卡等硬件上(或者只是一個單片機),這相當於每一個東西接上了人類的語言和思想(當然語言並不等於思想),可以組成更強大的計算機(或者功能系統)
在複用性方面,,CPU本身提供一個最小內核語言,就像只認0,1一樣,它認很多東西(比如圖的節點和圖的動態邊上的信息)。

抽象叫概念--->實例叫功能,軟件叫文章,,我們知道第一,計算機不但能控制硬件做事比如調用硬件的功能接口這需要硬件本身提供對應的功能設施然後由類C語言調用,第二,計算機也能表達人的思想,實現抽象裏的軟體比如你怎麼用語言來表達數學公式的計算這需要軟件提供相關語言設施。(比如C語言中的指針一方面是底層的,一方面是屬於迂迴這個“人的思想”的,,所以將指針的前部分用法看成爲底層開發,而後面部分看成爲抽象開發)。實際上,語言的語義就是一切(計算機的功能),,從語言的語義來看,第一部分是被包含在第二部分的,正是語言,,讓計算機被賦於能計算的能力,,計算機的計算能力就是它運行語言語義的能力,因爲語言邏輯由人產生,書寫,所以計算機被賦於體現人思考的能力,但顯然不是AI機(因爲人的語義經編譯後居然成爲機械的二進制碼,不再是動態的人的語義而只有人才有產生動態語義的智能),只有計算機能自行產生代碼邏輯,(產生語義,而且是動態變化的語義),那麼它就是AI的。如何使計算機成爲真正的AI機,在這個點上思考纔是正確的(即讓計算機成爲理解人和它自己的語義並自行產生語義的機器,)。如果能理解人腦的機能,再具體實現在這樣的計算機上,那麼會有多好啊,我們知道人手,人腳只是人的部件,人腦纔是人相當於PC馮氏模型的東西(而非人的整個構架)。

存儲器是詞彙器

《連價語言識別器one d of brain simalte machineodobs機》開發,,注意這個語言識別器是pc模型,而非CPU模型

我們的工作在於儘量將這個實現過程簡化成電路設計的簡單形式。比如設計成一張電話磁卡(這樣整個PC就是一張32k的卡了哈哈,說真的,我很懷疑現在的PC用得着那麼複雜嗎??,而且可以放入一個手機殼中形成一臺真正的有芯智手機)。

CPU的指令如果可以定製就好了。(虛擬中可)

只能是即時發生的,而無記憶功能的機器。神經元?那麼學習能力呢(混亂狀,無自我意識狀)?看來得去看些大腦科學方面的書。形象思維和邏輯思維?

規則和知識的集合導致的專家系統?
CPU和存儲混成一體的機器,把寄存器當存儲器的機器。少量的寄存器如此記住大量無限信息。因爲大腦鋪開可以鋪滿整個半球。圖與語言??如何在圖節點上分散記憶到的信息??又如何促成學習與自主意識?
因爲語言影響思維,所以語言處理中心是me(自我意識),記憶點是圍繞着me的圖點(但顯然我們的念頭是先於語言的,語言只是念頭的前沿地),那麼me究竟是什麼呢?是物質慾望?

慾望-me-記憶點-語言

一個東西具備了思考能力,就是智能的,具備了語言能力(那麼所有的計算問題能歸原到語言處理問題嗎,因爲計算機所有的問題都是開發問題嗎??這樣的話就是一臺計算機,,用硬件實現這種編程語言??可計算問題不就是圖靈問題嗎?),像是智能的

或者,邏輯->語言,或者me(單純記憶體?可複用庫,在線快速反應庫)-語言(加入了語義發生器的語言,forward到me的語言機制)
這種機器叫,可自學習詞語機,self learn vec m ,slvm,,那麼開發出來的程序就不能用在x86上了。。所以需要一個VM啊。,,,因爲編譯原來只需要幾種數據結構,因此CPU設計中只需要樹,圖等存儲基地,指令只需要操作它們的幾條指令便可。

不需要明白很多背景知識就能編程的語言。。。包括術語。因此語義和符號是寬鬆的。

提供與C的接口。

CPU能學習是什麼意思?它首先不能是封閉的,是跟我們一樣直面這個社會的(所以能學習),
是需要時間成長的,如何實現??



指令設置

樹,圖,字典

數據演化爲,從內存中取詞,REG26個,其實加減X除這些操作可從軟件上模擬到。
語句演化爲,指令全是。。所以這二者可淡化掉

這種機器,指令操作26個寄存器。跟內存沒關係。圖作爲數據結構,需要什麼寄存器?人家是堆棧機,我這是圖機(就跟luatable機一樣),,所以需要base,stack,,圖就需要node ,link,CPU及其簡單,,只有2個寄存器指示器,而且這種語言可自舉,,寫出的語句無上下文環境。因此(跳轉或調用)不需要將狀態入stacck。指令creategraph(V,E),insertvertex(),…,沒有指令指針,指令不保存在ram(在這種語言的眼光裏,ram只是快一些的硬盤), 所有程序是在線執行的(所有的計算不是函數調用,而是圖link),而且維護一個由26個寄存器2^26(有關圖的指令可用來在這裏發揮作用,而且在高級語言的設計中,硬件加速的圖操作可用來實現編譯原理的一些東西,比如樹圖,hash 圖)維護的臨時在線存儲器(用於即時編譯,以及學習,比如用AOE網記住大量的臨時信息,每個邊都可存東西,所以利於debug)。

先來想象一下這種語言具體的語言設施。
用自然語言,欠入一個英語字典到編譯系統中,




無法把這種語言的彙編語言作爲高級語言(機器語言即是它自身,它用高級語言說話就是一方面也可以表達這種CPU的功能)??否則要求低層也是“高級的”,能直接明白高級語言,,否則不行。底層根本不能稱爲智能的。而且由於需要智能化,因此不能通用地像二進制機一樣實現(這樣的機器只能是高級軟件VM?不能被硬件上被實現?)。

語言組合CPU,如何實現,樹圖?把學到的東西寫入bios?àme庫-
論文《一個智能計算機CPU實現====自學習語言導向機》

指令即最小內核語言(這樣高級語言開發時就是機器語言了,並且無上下文環境)。指令中的數據和輸入是庫(me,,即指令可以根據節點升級和動態變化,,非固化指令CPU,而不是跟馮氏一樣從內存中取指令,因爲程序在那,這種CPU的程序直接動態臨時發生在節點構成的大 存儲機制內,只維護調試狀態寄存器最小狀態集)。

英語有多少單詞,這個CPU就有多少指令(要使CPU智能,必須要讓學習用的存儲機制直接存儲在CPU中,,即相當於馮氏架構中的內存數據的東西,以後衡量CPU就不只是速度了還在記憶量,如果不得以要用內存,那麼內存最好不是線性的,而是節點狀的。因此這種機器的內存只存數據,實際數據也是指令,CPU中的寄存器用來構造節點的關係,不過不需要程序員來維護這些節點關係來構造什麼數據結構,,否則不還是馮氏模式開發了??)

那麼類圖靈的抽象作用(可計算問題,嚴格的數學東西,,話如何是一種算法,有沒正確性),如何被這種機器的高級語言體現呢??難道就是編譯原理??我們知道編碼數字,等等東西到這種CPU機是很容易的,就像馮氏用0,1表示數字一樣。

編程時,因爲計算機用跟我們一樣的語言,所以雙方是平等的,,me的庫存儲了計算機CPU的硬件功能調用,表示附有這種語言的硬件功能庫。。。智能網卡也是同樣的道理。


。整個CPU就是一個TRI樹

2.3 正規表達式與有限自動機
有限自動機是一種抽象的識別裝置,,,往往一個文法對應一個有限狀態機(一個編譯器詞法階段,,語法階段不屬於這個階段),所以我們來論討有限狀態機

正則表達式(文法產生式)往往對應一個正則集合,


10.2帶輸出的有限狀態機
許多機器,包括計算機的某些部件都可以用有限狀態機作爲模型,,有限狀態機本身也有一些形式,這是由構成它的定義的某些要素組合而變換到的,,無論如何,,一個基本的有限狀態機必須具備以下幾個基本要素

顧名思義,這種機器必含有一有限的狀態集合S(在其中包含一個特定的初始狀態S0)
一個輸入字母表I
一個轉移函數f(這個轉移函數實現將“狀態與輸入對”轉移到下一個“狀態”)

自動售貨機

首先讓我們來描述這種自動售貨機
這是一種出售飲料的機器,出售桔子汁或蘋果汁,,都是30分一瓶,投幣口只接受5分,10分,25分這三種幣值,如果投入多於30分的幣量,則機器退出超過30分的那部分,保留30分,然後提示按紅色按鈕或按黃色按鈕,,如果按紅色就得到一瓶蘋果汁,如果按黃色就得到一瓶桔子汁

根據以上描述,如果我們要製造這樣一種機器或來描述它的整個工作流程,那麼必須要考慮進“狀態”,“輸入”,“輸出”這幾個要素,
這種機器的狀態有:
s0(未投進任何量的幣) ,,這是初始狀態,,機器在工作前以這個狀態作爲初始狀態
s1(偵察到當前收集了5分),,
s2(偵察到當前收集了10分),,
s3(當前收集了15分),,
s4(當前收集了20分),
s5(當前收集了25分),
s6(當前收集了足額的30分或30分以上,等待用戶按鈕)
這種機器的輸入有:
i0(5分幣) 其實也有“輸入0分幣”的情況,,但是不考慮,我們只以起作用的要素作爲劃分的證據
i1(10分幣)
i2(25分幣)
i3(黃色按鈕O)
i4(紅色按鈕R)
這種機器的輸出有:
o0(不執行任何操作n)
o1(找零5分)
o2(找零10分)
o3(找零15分)
o4(找零20分)
o5(找零25分)
o6(推出一瓶桔子汁)
o7(推出一瓶蘋果汁)
機器工作的過程就是根據“輸入”+“機器的當前狀態”=>“下一個狀態”+“輸出”

舉個例子假設有人投入了10分,,再投入25分,,,機器退出5分後該人按黃色按鈕得到一桔子汁,,

機器的工作過程:
第一步:當前狀態爲s0(未投進任何幣值, 無因輸入產生的狀態變更,無因輸出產生的狀態變更,即爲初始狀態s0)
第二步:當前輸入爲10分(i1),將當前狀態爲s2(偵察到已經投入10分,機器狀態爲“收集了10分”)
第三步:當前輸入爲25分(i2),,將當前狀態改爲S6(“用戶已經投入足夠的幣值,等待按鈕”)
第四步:輸出5分(o1),,狀態不變
第五步:輸入黃色鈕(i3),輸出桔子汁(o6),,狀態復位爲s0

狀態表
即對“輸入和狀態”的每個組合指明“下一個狀態和產生的輸出”的組合
可以無輸入或無輸出,但機器狀態是貫徹始終的
(實際上,無輸入也是一種輸入,無輸出也是一種輸出,,故這三個要素都是貫徹每步的,這裏每步是一個轉移函數的意思)



表示有限狀態機的一種方法就是以上的狀態表,另外一種方法就是有向圖
構造有限狀態機的方法是設計狀態和設計輸入,再畫出其狀態表或狀態圖




10.2.2 帶輸出的有限狀態機
有限狀態機的形式定義

定義1 有限狀態機M=(S,I,O,f,g,s0)由如下部分組成:一個有限的狀態集合S,一個有限的輸入字母表I,一個有限的輸出字母表O,一個轉移函數f,f爲每個狀態和輸入指派一個新狀態,一個輸出函數g,g爲每個狀態和輸入對指派一個輸出,,還有一個初始狀態s0.


例7 在某種編碼方法中,當一個信息出現了3個連續的1,則信息接收器就知道已經發生了一個傳送錯誤,,試構造一個有限狀態機,使得它輸出1,並且僅當它所接收的最後3位都是1

要理解題目的意思,,,當且僅當它所接收的最後三位都是一是什麼意思呢,,這個信息接收器接受零或一這二種輸入,,當最近(也就是最後)接收了三個連續一之後報錯,,因爲一旦輸入了三個連續一就會被信息接收器發現出來並輸出一個一,因此,,“最後接收”這個用語是正確的,,,

注意此機只接收1或其它的數,我們這裏只用0代表“其它的數”,這對討論沒有影響

可設計三個狀態:
s0(前面一個輸入不是1)
s1(前面一個輸入是1,,但再前面一個輸入並不是1)
s2(前面二個輸入都是1,,但再再前面一個輸入並不是1)
爲什麼這麼設計呢,因爲總是考慮最後輸入的那個數,,這個數與先前輸入的前二個數存在什麼關係呢,正是這種關係影響了輸出,所以只需考慮當前輸入之前的二次輸入情況,即,“最後輸入的前面一次不是1”,,“最後輸入的前面一次輸入是1,但再前面一個輸入並不是1”,,“最後輸入的前面二次輸入都是1,,但再再前面一個輸入並不是1”這三種情況就行了


2.4 聯繫編譯原理學語言
寫一個編譯器真正涉及到了設計(把編譯器分階段完成正體現了設計,,編譯器理論本身就是吸取喬姆斯基這個不懂計算機的自然語言研究學者研究出的成果,以及後來的一整套編譯理論,包括,圖靈的機器,正規詞法邏輯這些高層邏輯模型),,編碼(具體編譯器的實現自然離不開編碼),,算法(關於語法的bnf設計就是一個大算法,驗證圖靈機是不是會終止算法),,數據結構(樹與遞歸頻頻出現,這個層次上的數據結構還是離散數學意義的離散結構,樹啊,環啊,圖啊,還沒有到具體用什麼語言編碼的層次,也就是說還是通用的理論層次),,,是一個系統工程,從C語言的眼光來看,開發一套編譯器這所有的過程,是一套算法加數據結構實現的集中體現(當然JAVA也要開發編譯器,只是JAVA有高層設計工具,比如OO等,對於算法加數據結構的體現沒有C深,而JAVA也一般不用作開發編譯器,因爲JAVA對於編譯器後端開始的工作幾乎力不從心)。。

在這個系統工程前期,,編譯器前端(詞法,語法,中間代碼)大部分理論還是基於編譯理論的高級邏輯的,實現方法和圖徑都比較單一,因爲有統一的理論和相關的yacc.lex工具等,在後端時(代碼生成,代碼優化,運行時環境,錯誤處理和調試)就得進入平臺邏輯了。。這裏纔是發生分歧的地方和可以無限深入的難點所在。。

一般來說,編譯原理就是指前端,,因爲後端不再屬於編譯知識了,,而是平臺處理邏輯,,至中間代碼生成時,已經完成高級源程序到代碼的生成了(雖然是中間代碼,雖然還要經過彙編到最終的目標語言,不過這些後來的過程不做也可以,因爲我們也可以發展一個虛擬機內置解釋器執行這些中間代碼,,,後端的動作不屬於編譯原理,因爲視具體平臺不同,,後來的過程不是統一的理論了,,比如代碼優化,,那更是一個沒有定論的過程

正規式和自動機理論統一了詞法分析的過程就像大家都用bnf來描述文法一樣,它讓編寫編計器的工作變得科學化和合理化,有人說,nfa是給機器看的,而正規式是給人看的。。這樣說比較形象,而實際上直接寫語法也是可以的。詞法分析可以一點也不涉及到正規式與自動機這樣對於人的迂迴邏輯模型,代碼控制能力強的人可直接寫此類邏輯。

什麼是詞法分析呢,因爲我們現在是以”寫”作程序的手段的,是面向行(每一行語句都對應中間代碼的一個三元式,每一行都是一個編譯時給定行標號產生內存地址的)的,因此編譯器也是面向文本的,,字符串成爲編譯器唯一面向的東西,,首先,從一段源程序中讀入,,詞法分析的任務在於得出一個一個的lex爲語法分析所有,,,詞法分析直接面向源程序文本,,語法分析面向業已分析好的詞,,故前者僅僅產生詞(語法單元token),只有語法完成才能產生語言(文法產生式),而語義給業已建立的語法和語法元素(即樹和節點)增加屬性等語義信息,,語義分析過程的一個實現方法就是這所謂的語法制導翻譯.語法分析過後,如果是一遍掃描,那麼中間代碼幾乎產生目標代碼,編譯幾乎完成

而語法分析則採取了文法產生式(而非正規產生式,雖然都是產生串集,但正規式本身表明一個匹配模式,匹配式。。文法產生式指明如何爲語言產生一個串,前者產生的語言是詞會集,後者產生的是語句集,,詞法過程遠遠沒有到底語言的意義,,只有到達語法階段了,,才能談得上語言,,因此對於編譯原理的幾個階段來說,詞法處理只是一個跟語言掛不上鉤的很初級的過程),
它接收的輸入是詞,編譯原理語法分析過程產生的輸出是串,串的集合即語言,串即語句,不再是詞了,而是語法一級的單元,,而詞是詞法一級的單元,,

正如關於詞的邏輯有正規邏輯一樣,,關於串的邏輯就是產生式邏輯,關於產生式邏輯有一個就是上下文無關文法(這也就是當今很多語言採取的喬姆斯基的文法了),,而其實存在很多其它文法的

詞法分析和語法分析過程都涉及到圖靈機,有限狀態自動機dfa,無限狀態自動機nfa,是一種名叫圖靈的抽象的機器模型,,它剛好與正規集形成的語言(由一個正規表達式推導出的串集形成語言,這是指詞法意義上的語言)一一對應,,即,對於一個正規語言,有一個dfa能夠處理它(這個一一對應關係在離散數學中存在科學的證明方法)。

圖靈機涉及到圖靈狀態與此狀態相關的處理,,因爲圖靈機是一種關於狀態與處理的機器,有一些圖靈機還提供記憶功能,這就是爲什麼要在lex工具中寫一個C例程對應每一個狀態的原因,,我們說語言是圖靈完備的主要是指文法產生式的圖靈處理過程.

這個過程中所有狀態都可以用一種離散結構(或者稱爲數據結構吧,我認爲數據結構都應該提供數據存儲地,而圖好像可以沒有)圖來表示和加以方便地研究。。對圖靈模型的研究往往藉助這種狀態圖

語法分析產生一段抽象樹,,樹這個數據結構深刻並與生俱來地與遞歸相關,,因此你可以看到LR,LL,自頂向上,,自頂向下這些概念,,語法分析的過程產生抽象語法樹,供下一階段的語義分析和代碼生成過程用。。

語義分析,就是將語法分析過程中產生的符號賦於語言的意義,即語義(比如此符號具有什麼類型,而類型是語義上的東西),語法制導翻譯(以之前分析得到的抽象語法樹作爲推導的翻譯過程,爲它當中的各個節點建立意義)不屬於語法分析階段,,即不屬於上面一段的編譯原理語法分析階段,,注意翻譯二字導致意義的差別。。如果說詞法分析和語法分析僅僅是分析階段的話,,那麼從語法制導翻譯和中間代碼表示開始,,就進入了實際的翻譯的實質階段了(編譯就是把高級源程序轉爲彙編目標語言,這個總過程稱爲編譯,,編譯,編譯,有一個譯字,直到這裏爲止,譯字漸漸明朗,編譯前端完成,編譯完成,因爲進入了代碼的階段,雖然這裏是中間代碼表示),

詞,串,,還有一個符號的概念,,詞,串是編譯原理意義上語言的概念,,符號就是程序意義上的語言本身的概念了,,對應詞法分析中的詞,語法分析中的終結符等概念,,,這些東西(不光是詞,終結符,而且是語句,相關的語句邏輯塊比如一個for過程,我們知道在形成三元式時一切都規範化了,成了一行一行的符號加地址的統一形式,) 在被作爲中間代碼表示時,,就成了實質翻譯成的語言(也許此時也不能稱爲語言,只能稱爲中間形式)的符號,,詞法分析階段和語法分析階段絕不僅僅就是分析,,他們還生成和完善以及維護一個記號系統,,前端是一個所有動作的統稱

這些符號記號在前端被不斷完善它們的屬性值,比如變量類型啊,,關鍵字ID啊,變量編譯後的地址啊,

中間代碼表示則着眼於產生的串,如何用中間形式(源程序到目標語言的中間形式)表示一個串(一個符合語法規則的句子,),,一般採用三地址格式(這是個形式化了的中間形式表示的串),比如對於算符優先的規則,,一般採用波蘭後綴,或逆波蘭形式。如果前面分析階段主要是樹和圖的方式來說明和處理,那麼這裏棧式處理數據的方式在這裏頻頻出現。。、
所謂的棧式機概念在這裏出現

爲什麼一門語言的類型機制如此重要呢,答案就在符號系統。

彙編原理說明了機器語言的邏輯。。編譯原理後端的那些知識說明了語言代碼如何映射到機器。。通過該語言編譯後端學習此語言是學習的比較好的途徑

爲什麼控制結構如此重要呢,因爲它代表語法和語義方面的要求.比如布爾條件式,語義允許短路的語言會直接編譯出條件判斷後的結果。。有時如果語法語義允許,語法上布爾運算也可等同算術運算。。因爲布爾結果用數值可表示。。C語言就是這樣

C語言字符串有大量指針這說明了與C++的抽象字符串不一樣,說明C是用底層來描述問題和設計的。

這些說明,要更好地瞭解一門語言,,最好要上升到他的語法規範。聯繫編譯原理知識來了一解一些東西。以及編譯實現時是如何滿足這些規範的。

2.6 如何理解運行時
編譯後端會對目標平臺產生代碼,這目標平臺包括機器和OS,最主要還是OS,因爲OS是封裝了系統調用(BIOS中原子基本的幾條IO功能接口,可供OS和系統進行以後的抽象)的,在語言看來,封裝了的OS和語言的其它庫一樣,也是一種語言邏輯,有接口就可以調用(語言跟OS並非一種雞生蛋生雞的關係,往往是OS出來了,然後有一種特定運行於這種OS下的語言實現即編譯器,tiny甚至可以自編譯自身),

那麼如何在OS下運行這種語言寫成的代碼呢,它如何從OS中獲得空間等運行資源,如何獲得system call IO操作文件(比如cstdio中的文件操作實際是封裝了system call io邏輯來的)呢,把運行時想象成語言後端邏輯(編譯後端代碼生成器及其生成的代碼)跟OS的邏輯接口就行了。對於OS來講,它解決的是OS如何運行它,對於代碼來說。它解決的是如何從OS中獲取資源進行運行,和提供main()這樣的接口,,當然特定編譯器商甚至也包括一些語言實現std lib dll,因爲這畢竟算是開發完整一套編譯器嘛。。一般會把std lib dll當成crt,這是不對的和不完整的。

其它語言是沒有crt的只有c有,因爲C有標準編譯器實現必須實現它,這個意義上run time是語言的庫邏輯,run time是編譯後端,是每種語言包括無標準庫的語言都需要的,這個意義上它是系統軟件是語言對於系統的接口是編譯器後端一套完整編譯器實現必須的,(我們知道解釋器一般是系統軟件,作爲編譯後端的意義的crt是相當於解釋器對於一門語言的意義)

從解釋器的角度我們來看一下,的確是這樣的,解釋器不爲特定目標生成代碼,它只負責運行語言源代碼(雖然這其中會有一箇中間代碼,但解釋器不運行目標代碼,它的目標就是解釋器自己,目標代碼就是中間代碼),,解釋器就相當於機器和OS,它沒有c runtime,,編譯環境下的crt相當於額外的中間層。。


再者比如.net的通用runtime,也是這個道理.。
2.7 運行時環境
XML其實是先是面向解決數據異構問題出現的,,如果想理解XML,不理解它這個歷史背景的話,那麼你得到的認識會以爲XML僅是一種新的理論加實踐,而不知道它產生的根本和存在的本質。
  
  運行時其實不如叫做運行空,就是程序運行的物質環境,比如CPU架構(裏面的寄存器,就是專門爲通俗意義上-不只是一門語言的程序,,,的程序而設置的最終運行時,裏面的部件全是程序專用的),,當然,運行時,也可以是虛擬機這些軟件邏輯上的東西,,其實程序不可能純粹以硬件作爲運行環境(除非電器化的微指令,或者裸體指令,運行時不是純粹指CPU構架,因爲單純一個CPU提出來,只有指令,並沒有程序的概念,必須等OS機制,高級語言的彙編原理出來,然後程序機制出來,才發展出運行時這個說法),還是有OS邏輯來封裝CPU硬件邏輯,然後爲運行時所用。
2.7 運行時
Dfdsfsdf

6.3 語言的類型系統
高級語言引進類型機制,其意義是重大的,一方面,類型直接抽象了現實生活,比如數值,字符,這使得計算機可直接用來作科學運算,另一方面,廣義的類型是程序員用來操作內存的規則,變量是程序員用來操作內存的手段,而變量是建立在類型機制上面的。構建在類型上的數據結構就抽象了計算機存儲機制和它能表達的現實事物。程序員可用這種抽象來產生更深層的邏輯和解決計算機能解決的問題(算法是針對數據結構的撒,如果說數據結構是計算機的結構,那麼算法就是用來解決計算機問題的,數據結構和算法是計算機的而不是編程語言的科學,這個說法就來源於這裏)。

對類型的進一步抽象是很多高級語言在做的事情,比如動態類型語言,比如類,範型,數據結構等。。特別是類的提出,是一種通用類型,這使得類型不僅僅只表示數值和字符,還可以表示更抽象的概念,
編譯原理告訴我們,類型機制是編譯期的一切,,關係到變量表示這種類型,關係到編譯期類型安全和轉換機制,關係到一門語言組織更大數據抽象的能力。關係到與它相關的表達式的屬性。
動態類型語言不是沒有類型,而是其變量在運行時可以自由轉型。範型是建立在爲一個通用類型上的操作的手段。類是一種定義類型的手段UDT。。。因此除了簡單類型之外,還有自定義類型。。否則僅僅靠建立在基礎類型之上的複合類型還是不夠(C++提供Class這種UDT並導致了基於對象和麪向對象,這是C++區別於其它語言最尤爲可以拿來類比的特徵)。

我們可以從一個很初級的邏輯來講解類型與變量,在沒有計算機出現之前,是用算盤這樣的工具來計算,但算盤不能處理,一個世界跟一個豬如何交互,它只能處理1加1等於2。。這就是類型產生的必要。。因爲計算機不僅能抽象數值,還能處理人類想象出來並能通過計算機表達的其它抽象。。當然,這一切都是抽象了的。。但算盤根本無法抽象。。所以在計算機開發領域,對類型的抽象是必要的。

1,動態語言是指語言的運行時,其運行環境是動態的,,新的函數可以被引進,,等等
  2,動態類型語言,是指類型可以運行期被改變的語言,,一般來說,類型系統是一個語言的特徵之一,如果它都可以是在運行期是動態的,那麼該語言就是在運行期動態的
  3,弱類型語言,,,語言有類型,,但是類型之間的轉換並非嚴格,,,字符串指定可以轉型後用作數值型
4,無類型,,,無類型是指類型是不需要顯式在寫代碼時聲明給它一個類型的語言,,在運行時視給它的值確定類型,而且還可以再變動,即所謂的dukingtype,,,因爲沒有類型,,所以就沒有變量(因爲變量就是類型的代表撒),...沒有變量只有值,,,值決定一切,,給它一個鴨子走的動作,,如果它像,,那麼它就是一隻duck

數據結構與算法是屬於計算機的,而不是程序設計語言的。更多在出現在計算機實現上。比如計算機圖形上。
2.8 編譯與解釋
CPU+OS的本地給了我們開發的空間,,也就是說,雲計算實際上是個老概念,雲計算就是=web 2.0,web x.x + nc,我們日常用的計算機會縮水成nc,不必爲一個沒有CPU,沒有OS的“本地”編程和開發軟件,,所有的軟件都被開發在“遠程”的WEB中去了,以及其它服務器,這就是雲計算,,以後所有的雲計算機纔有OS和CPU,,雲計算機說將不必爲本地開發軟件,這除非本地計算機成爲一個NC空殼,,否則,如果我們現在在用的計算機,,它有強大的CPU,也有OS,,那麼對它的開發工作就會一直存在,,在雲計算下,以後所有的雲計算機纔有OS和CPU這些雲計算機僅是服務器,我們人手一臺的PC將嚴格不能成爲服務器,比如,因爲我們的工作站沒有NT操作系統,所以只能作爲上網用的broswer這樣的簡單終端而不能成爲提供服務的雲計算機,NC很多年前就被提出了,而web2.0是最新的,所以雲計算就是不新不老的結合體
CPU+OS的環境給了我們什麼樣的"本地"編程空間和限制呢,而WEB"遠程"計算又給了我們什麼樣的編程空間和編程限制呢,,,這首先要理解這二者的抽象基礎
CPU提供了馮氏架構,因此要有IO,要有寄存器,OS提供了進程,內存管理,API,進而提供瞭解釋器,而編譯則是直接面向硬件的,當然無絕對的編譯語言或解釋語言
編譯跟解釋
編譯可以爲特定平臺作優化,而解釋不行
2.9 運行期與編譯期
先說一句題外話..用了Openoffice,我才發現微軟技術的封閉性,那種憑空吃硬盤空間和內存空間的歷史複雜性,實在是不必要
開始今天的講解,運行期與編譯期
就像有些知識是爲了跟人結合而產生的一樣(OO這個東西不僅是技術問題而是軟工問題,是爲了解決編程方式跟人的關係結合產生的),是爲了探索一個領域的東西卻總是無法不避象涉及到另外領域的事物一樣..
我們現在的編程方式,需要以什麼方式寫流程,,需要處理什麼樣的異常,不純是編程的知識,,而是跟程序運行環境有關的,必須先明白編譯原理抽象領域的知識,才能用一門編譯語言作編程領域的工作
範式就是編程習慣,比如函數編程法,,OO編程法,AOP編程法
這些編程法是由計算機處理數據的方法和內部邏輯所決定的,,比如函數語言就是由lamda演算得來的,函數語言可以用一種其它範式的語言不能用的eavl()過程
計算的原理是一種圖靈機的離散形式,,,因此是命令式的,,,只需一個入口,和一堆要處理的數據,就能串行得到另一個結果(這就是串行形式算法,計算機的功能就是狀態的改變,未來出現的並行計算和多核會導致編程出現並行範式),這個結果可以被另一個串行處理作爲中間結果,,,計算機(堆棧機)或虛擬機(JVM的軟件機器)是構建在這上面的另一層抽象,在這種開發方式下,要注意很多算法,,,離散的算法,和數學的算法,拿遞歸跟迭代來說,迭代就是計算機處理的方式,,,是行動導向的,而遞歸偏重於目標,,偏重於人的思維,,比如函數語言中,,就用迭代比較好, 而OOP中的語言中,,就用遞歸比較好因爲計算機離散形式處理迭代好,迭代需要一個循環變量而遞歸不需要

2.9 編譯與運行期分開的本質與抽象
其實字符串邏輯,數據結構邏輯都可以完全不跟內存有關,語言都可以站在一個比較高的角度來抽象,甚至如果可以,一門語言可以把字符串抽象爲rope(當然還可以是其它東西),因爲語法級是設計抽象,可以不跟運行期有任何關係,,編譯期後端才負責運行的事,纔有運行效益的說法,所以語法級,也就是設計期可以站在一個完全脫離運行邏輯的抽象高度上進行對字符串的抽象,因爲語法級是C語言,C++,JAVA這樣的高級語言它只負責這方面的高級邏輯的事,而編譯前端跟後端是可以分開的,只有到編譯後端時才需要映射到彙編語言這樣的機器邏輯,

C用底層來表達語法級的設計抽象,,比如它將字符串看待是指針數組,這就是C語言跟其它所有語言不一樣的地方,,因此它產生的運行期抽象跟語言級抽象最接近,雖然是運行抽象但幾乎等同設計,因此可用在內存有限的地方,因爲它抽象小,比如手機等特殊平臺上。。

但即使是這樣,C的抽象能力也是巨大的,它可以抽象OOP,抽象結構,抽象。。。

而相比之下,C++直接在語法級支持更大的抽象,它沿襲了C的core(流程控制什麼的,C被稱爲最小內核語言),但在比較大一點的抽象上它沿襲了C的指針和預編譯這二大抽象模塊,並自己發展出一個運行期的OO,接着面向運行期的模板可以產生STL這樣的泛型抽象集,面向受限編譯期的模板可以產生BOOST這樣的元編程抽象集,而其它的第四代語言比如JAVA,RUBY,LUA,PYTHON,它們在語法級直接支持的設計抽象就更多了,因爲它們不需要像C一樣處處屈就運行期,,而是屈就人,想怎麼樣設計就怎麼樣設計,至於運行期,,完全是虛擬機的性能問題。。


2.10 腳本語言
net的虛擬機就是把所有的語言不直接放到CPU+OS這個二次本地上了(純CPU是一次),而是放到了同一個虛擬機內這個三次平臺之上.因此各種語言規範寫出來的代碼一者相對另一者來說是native的.可以共用一個類庫,而Ruby,Java由於是從C發展而來的,它們只視C爲native,如果是其它代碼要爲它們所用,必須改變別的語言中的函數入棧壓棧規則,,,通過一種bind的技術改造..IT界的整合與分離無處不在
要知道什麼是腳本語言,就必須知道什麼是腳本程序,腳本程序簡稱腳本,是一種程序代碼,它是由某個系統(如操作系統)或服務器(如WebServer)或應用程序(如AutoCAD,MS Office等)環境提供的能自動化操作該環境的功能的指令執行序列.而爲了合法地編寫出腳本程序所制定的形式語法和語義的規範,原則,定義等等總和形成了腳本語言.腳本語言實質上其實是一種用戶接口.現在用戶接口這個詞,多指圖形用戶接口(GUI),即指軟件的視覺和使用設計必須符合用戶的習慣.但用戶是有層次的,一部分是普通用戶,他們通過軟件提供的類似菜單選項,工具條選項,以及使用說明和簡單配置等功能,來完成他們的日常工作和任務的,這種用戶也被認爲是最終用戶.而另一部分的用戶不僅使用這些一般的功能,而且希望軟件隨時能按照他們的意願定製使用在菜單工具條選項中不能提供的功能,或者他們能在這個軟件平臺上做二次開發,將開發後的產品再出售給自己的客戶或者自己內部的其他部門.爲了能夠滿足這部分用戶的需求,軟件開發商就推出一種編程語言,讓這部分用戶通過編程的方式來使用軟件內部提供的功能.這種語言就被稱作腳本語言.使用它的這些用戶被稱爲高級用戶.
????腳本語言賴以生存的軟件環境被稱作是宿主環境(host environment).宿主環境可以是操作系統,服務器程序,應用程序,而開發這些宿主環境的程序語言(如開發操作系統一般使用c語言,開發WebServer一般使用c或Java語言,開發應用程序一般使用C++/Java/c#語言)被稱作系統開發語言,或用一個更貼切的說法是---宿主語言(Host Language).在軟件產業發展初期,軟件沒有GUI接口,軟件供應商提供一些使用該軟件的API(應用程序接口),而這些接口一般採用的編程語言是宿主語言.由於宿主語言是功能強大但也複雜的語言,因此使用該軟件的用戶也是專業性較強的用戶.但隨着硬件的快速發展,軟件業逐漸滲透到其他產業以及用戶羣體的不斷擴大和GUI的出現,逼迫軟件開發商必須提供一種比宿主語言功能較弱,但使用簡潔,方便的語言來給一些非專業程序員用戶使用.這就是腳本語言產生最根本的原因.目前世界上有數以千計的腳本語言形式.在操作系統領域,Linux上有bash, Windows上有WSH(Windows script host),而web上有perl,jsp, php, asp, VBscript, JavaScript. 在應用程序領域, AutoCAD上是AutoLisp, MS Office上有VBA. 3ds MAx上有MAXScript.各種各樣的腳本語言極大地豐富了其宿主程序的功能,使宿主程序能滿足不同客戶的個性化需求.
????因此,目前大多數相當出名的軟件都提供有腳本支持,我們國內的軟件開發商是否也能考慮以這種形式來提供二次開發的接口呢?因爲腳本語言的簡單性能夠降低二次開發的成本.如果軟件是使用Java開發的,提供的二次開發語言也是Java或乾脆直接提供源碼進行二次開發,會極大地增加開發和維護的成本.而且當今軟件行業的競爭日趨激烈,誰能快速滿足不同客戶的個性化需求,誰就能在競爭中佔據有利位置,因此腳本語言的地位也日益突出.可以這樣下一個定論,如果軟件產品中不提供腳本支持,該軟件產品必死無疑.
????腳本語言有多種分類方法,但按照使用方式來劃分,腳本語言可被分爲兩種,一種是獨立型(或稱宿主型)(stand-alone)腳本語言,另一種被稱爲嵌入型(embedded)腳本語言(也稱作嵌入式腳本語言).獨立型腳本語言顧名思義,是指所構建的應用程序的主體程序全部或絕大部分是由腳本語言來編寫的,即使用到了系統設計語言,也是非常少的(主要存在於庫中),腳本語言與宿主語言的接口也只是由腳本語言調用宿主語言編寫功能的單方向調用,極少反過來由宿主語言調用腳本語言功能.由於完全是用腳本語言來編寫,因此就可以擺脫宿主語言的束縛,定義出符合需求的腳本類型,如腳本語言中所定義的對象的內存佈局不必要與宿主語言中所定義的對象相同(比如Python對象和C++對象在內存佈局方面就是不同的,兩者要進行通訊必須經過一定地轉換).甚至可以忽略具體的類型,設計弱類型語言.獨立型腳本語言的代表有Python,Ruby,perl等.嵌入型(也稱嵌入式)腳本語言是指構建的應用程序的主體結構由宿主語言(C/C++/C#/Java)來編寫(主要是爲了性能和效率方面的考慮),但爲了增加靈活性和二次開發性,在應用程序內部嵌入一種腳本語言來靈活地操控宿主語言編寫的功能,並且宿主語言功能和腳本語言功能之間的雙向調用是非常頻繁的,而且也是對等的(即指雙向調用的機會是相同的),由於是嵌入到宿主系統中,所以要受到宿主語言的一些約束,無論是數據類型還是內存佈局,應儘量與宿主語言保持一致,如腳本語言的對象和宿主語言的對象最好能夠在內存佈局上保持一致,以便兩種對象能非常直觀快速地相互通訊,而無須進行費時而又冗長的相互轉換.當然獨立型與嵌入型腳本語言也並不是絕對分得非常清楚的,許多腳本語言既是獨立型腳本語言又可作爲嵌入型腳本語言使用.如Python和lua.

2.11 靈活性與安全性
系統編程第一要考慮的問題不是方便性和靈活性,而是安全性,特別是類型的安全性,

C++有運行期多態,這集中體現了它的面向對象,,是用虛表來實現的,這就是面向對象的精粹,,

在一門dukingtype語言如RUBY,LUA這樣的應用語言而非系統語言中,C++那樣的虛表機制實現運行期多態是不需的,因爲duking type這字眼已經是多態了,

基於對象是什麼意思呢,,這個基於對象不是指"不能從中定義出對象的類本身這種ADT",,,這裏的基於對象,,就是指通過運態語言中通過"接口"實現的對象多態這種編程泛式,是動態語言中除了OO範式之外的另一種更爲高級的編程泛式..

什麼是基於原型呢,,就是說,事物是什麼樣的就是什麼樣的,如果它動起來像一隻Y子,那麼它就是一個Y子,,於是我們就提出一個原型,,比如Y子動的動作,(這就是二進制級的接口,對象行爲集,而通常情況下,基於類的OO語言中都是單根繼承,語義上的,實際上這種繼承是不符合現實的,因爲“類別”都是人爲加上的概念,class這個字眼與其說是一種ADT,,事物原型,不妨說它更像是一種偏離原型的分類即classification),實際上運行期的行爲集接口實現的原型纔是事物的本質,,,而編譯期事先由單根定好的繼承,這種早就分好類,並在運行期泛出來的對象反而是不符合事物原來本質的。。

類型在運行期可以被動態改變,,像Y子一樣靈活地編程,那麼在編譯期類型就得不到被檢測,,這極大地放寬了一門語言對於類型的檢測機制,要知道如果是對於系統編程的話,類型機制是多麼多麼地重要,雖然動態類型語言編程靈活,但是其安全性沒有一個保障..這就是安全性和靈活的矛盾之處.

上面的基於對象,實際上也就是基於原型,,C++只有運行期的面向對象,卻沒有類似RUBY的基於對象開發泛式了嗎?用C++的模板技術可以辦到!!

"C++模板實現的基於對象"是十分可貴的,第一,它提供了類RUBY的基於對象編程的機制(提供了類型多態,),,保證了編程的靈活性,第二,這種基於對象的多態是在編譯期被栓測的,,一方面又保證了語言的類型安全性

一個是二進制運行時級的多態,一個語義級的多態

那麼這樣說,C++的運行期多型可以被拋棄了(我覺得作爲系統語言的C++只需要基於對象和模板就夠了,雖然C++可同時作爲應用開發語言,但是它的運行期實現的OO實在是給系統問題引入了過多的人類OO思維,使系統問題變得多解,很多時候,我要求去掉C++的OO,當我只是希望用C++開發系統相關的邏輯的時候),,因爲基於對象的方式提供了編譯期多態就夠了..而且同時提供了類型在編譯期多變了靈活性和安全性

世界上最好的編程語言的組合是,底層用基於對象加模板的C++,千萬別用它的運行期虛表實現的OO,,,,,在高層用LUA這樣的快速通用腳本語言(雖然LUA更適合開發遊戲)
2.12 二進制指令與循環
大多一門語言都提供了流程控制,,形成了各自的語言關於流程語句的語法要素,,語法是一種形式(所以稱語言是形式語言),,語義是一種意義,,,
其實循環,跳轉這些東西來自於彙編語言,,,高級語言接納了這些理念(因爲彙編語言幾乎就是機器邏輯,高級語言提供類彙編語言這樣的機制是爲了符合平臺邏輯,,,況且高級語言最終要編譯成彙編語言和平臺邏輯,,循環語言要最終被還原成彙編語言的形式,這些處理速度就可大大加快),,,發展出了它們關於循環,跳轉封裝了的語言機制..
C語言最終要編譯成彙編語言和平臺邏輯,,循環語言要最終被還原成彙編語言的形式,,這就是調試的由來,,,調試分語法級調試和語義級運行期的錯誤.
2.13 所謂函數
函數機制往往被作爲過程式語言的代表,但其實無論是C這種純正的過程式語言,還是RUBY這種OO式語言,,與其說函數它是一種語言機制,倒不如說函數是一種接口,更多地來說是一種過程式非過程式語言通用的機制,一種具體的構造邏輯的接口形式(一種單入口多出口,而且可被其它函數無限調用的接口,因爲它是一種最接近計算機單路離散算法的邏輯,,這也就是C語言採取的自頂向下的形式,,所以實際上這種簡單的形式可構造一切計算機邏輯,就是這種機制,,決定了函數實際上可構造一切邏輯,包括被C++用來構造類機制,注意函數有一特點就是它的返回並不決定函數一定就結束),,雖然這些語言談到的函數都不是一個意思(C的函數是通常意義上我們說的函數,語言直接支持的第一等公民,而RUBY的函數是另一種運行時支持下的函數變形,比如虛函數,成員函數,COM接口,RUBY並不提倡用函數爲主體進行編程)
從源程序到機器碼經過了編譯和彙編,,這二個過程都是單向過程,對於純編譯語言來說,不可能將機器碼還原爲源程序,,對於類Java的半解釋半編譯語言,,有一些工具跟據jvm解釋器的邏輯,可從中間代碼還原出源程序的形式
反彙編的過程是一個模擬還原的過程,這是一個靜態過程(可模擬得出pe載體的機器碼,全部函數級算法邏輯,注意,這個過程並非hex查看,,hex查看是打開pe載體,以十六進制形式查看這個可執體進行修改),,有的工具在反彙編方面做得很好,如果ida內置的反彙編器甚至可得出函數名級的標識,而dumpbin做爲另一個反彙編工具,僅僅做了初級的這方面工作,比較它們對同一段程序的反彙編結果就可看到,,不同的彙編器對同一個東西得出不同的結果說明反彙編是一個模擬的過程,,影響PE邏輯的最終還是hex修改pe本身(PE載體在外存磁盤上會有一個載體,因爲它內部按外存線性地址劃分成了幾大塊,所以被map到內存中時,這些塊的相對偏移還是不變的)
如果說調試器是靜態過程(分析了一次就形成了結果),那麼調試的過程是運態模擬運行的結果(調試器維護一個活動的邏輯過程環境,可根據你的輸入和程序邏輯作出反映),這種方式相比反彙編來說,,是一種更加好和互補的尋求PE邏輯的方式
無論是二種方式中的任何一種,可看到,都用了函數作爲十六進制級反工程邏輯的最高代表,所以說函數是分析一切機器邏輯,計算機語言形式體現的計算機邏輯的最好接口模塊,,當然也是構造的最好方式,如文章開頭所說,,
函數有三個部分,,一原型,二類型,三,參數,對一個函數的把握要從以下幾方面進行中
首先,原型部分,各個編譯器在編譯並彙編相應語言的同一段代碼時,會產生相同的反彙編序列(這就是progo原型,這成爲在彙編邏輯中發現一個函數開頭部分並藉此發現這是一個函數邏輯的方法)
我們知道基本的原型有C的自右向左和pascal的自左向右方式(其中,由於C函數原型方式下,將執行權交由調用代碼來完全,這使得被調用函數中可出現可變參數)
2.14 所謂流程
高級語言在封裝機器彙編邏輯上損失了一定的靈活性(因爲只有彙編纔是與機器邏輯一一對應的)
而高級語言的流程控制,分支邏輯等,,終歸是某種抽象品,,只能提供有限的if else形式,這些封裝了的高級語言關於流程的邏輯(其實判斷,循環都是流程控制邏輯)
這就是封裝,抽象,與原來可獲得到底層靈活性的矛盾所在
第一個if往往是最基本的條件邏輯,else是一種變相的if邏輯,是針對於已提出的if的反面,,是if正好相反的條件,而其它的if,在一個if存在的條件下,,相當於else if
一定要明白這裏的區別,,這些語言邏輯產生的對應的彙編碼的絕對性決定我們得明白這些細微的差別
理解這一類彙編邏輯時,我們得理解intel平臺的邏輯,條件指令邏輯,,和分支邏輯這二大部分
典型的有,高級語言的條件邏輯轉化爲彙編邏輯時是它的倒裝形式,而且else部分是放在所有分支邏輯前面的。
明白這些,將有助我們理解高級語言匯編出來的邏輯,從而更好明白高級語言的這些關於流程的語言機制。。

2.15 爲什麼需要數據類型和數據結構
一切語言機制都是爲了抽象,,,抽象真的有那麼重要嗎??
爲什麼需要數據類型和數據結構
對數據的抽象必要嗎,,,
誠然,用C和機器的觀點來反映應用就夠了,因爲屏幕本來是二維的我們卻要發展出一個三維遊戲,,,有時複雜性僅僅是爲了跟人靠近而不是屈就計算機,因此這個繞的彎子是值得的,是一種計算機邏輯向人類邏輯的變換,但最終要變換成計算機邏輯,我們只取OO的中間變換,,,雖然OO最終要變成計算機離散邏輯
因此簡單類型是機器的類型,但複雜抽象類型是人的類型,,,計算機的東西是死的,但是卻越來越抽象,因爲抽象帶來的不是複雜性,而是靠近人的簡單性,這個要特別明白,,,即,相反的,抽象是簡單化而不是複雜化
什麼是真正的數據,,什麼是抽象了的數據,,,,數據類型就是數據的模板,,計算機和語言用了什麼樣的形式語言的形式去指代和控制他們?
2.16 數據類型和數據結構是二種不一樣的東西
數據類型type是高級語言對彙編直接操作位的“位”的抽象,,而這句話中的“操作”,,也被高級語言抽象爲施加在這些類型上的操作,比如,對於整型數,有相加,但是不能對整型數執行浮點數的運算,,一切都是抽象,數據類型的提出本身就是一種抽象,而至於提出什麼樣簡單類型也是一種抽象,數組,指針,都是C的複合類型,實際上C只有int,float,char這三種類型,高級語言提出這三種類型是因爲這三種類型抽象了我們的現實事物。比如int,float對應數值意義的抽象,char對應字符意義的抽象。。

而算法和數據類型是建立在type和施加在type上操作的更高級抽象。。是語言,大凡具有類型機制的高級語言通用來的,用來設計應用和解決問題的方法。。

算法提供了一種用語言來進行設計的手段,是設計抽象,當你不知道如何實現一個程序時,先找到數據結構,自然就找到了算法,編碼之後程序就實現了,,就是這個道理,所以說數據結構和算法是通用的語言用來進行設計的抽象慣用法。如果說數據結構是非語言級的設計抽象(它也是一種實現相關的設計抽象),那麼高級語法機制就是語言相關的設計抽象(也是實現相關的),而我文章中最後一部分談到的設計就是工程相關的設計抽象(相對實現來說,這是偏向人的)。。

首先,數據結構在type的基礎上進行抽象,,它看不到彙編的位,只是考慮如何將type翻來覆去地變換形式進行使用,而算法,看不到彙編的指令,只是考慮如何用高級語言的語句來操作這些數據結構,“即算法是對如何操作數據結構的抽象”因此數據結構和其上的操作稱爲adt,

一句話,type和type上的操作是抽象彙編的,,那麼,數據結構和其上的操作是高級語言站在type和type操作上的抽象,一者是高級語言面向彙編,一者是高級語言面向高級語言的type.

函數是這裏最能體現這種抽象的機制,首先,函數接納實參或數據結構這些抽象,,函數體內的代碼是“施加在實參上的操作的整合體”這樣一種抽象。。
----------------------
C剛開始是作爲unix的系紡編程語言出現的,計算機的產生源於圖靈的人工智能實驗.
2.17 爲什麼需要變量這些東東
Java中一切對象都是引用,引用即地址引用,操作對象只要操作對它們的引用就可以影響對象本身,因爲引用本來就是對象本身存儲在內存中的物理表示嘛

地址是變動的,因爲一塊內存在不同時刻可以存儲程序中用到的各種數據,因此稱這些數據爲變量,數據的本質是類,構造函數就是實際爲某種數據分配內存的過程,

在程序中我們常常需要作數據的移動,複製,比如函數的形實演繹

實際上,我們根本不需要操作數據本身(在面向對象的範疇裏,類對象就是唯一的數據,比類的成員更能代表數據),因爲往往有時候這樣的代價太大了,傳值的方式就是直接複製一份原來對象的新的對象(需要給產生這個對象的類一個複製的構造函數),這樣就會產生一個臨時的複製體,現代的應用中,一個應用中有成千上萬個對象是很常見的(因爲往往是用某種大型庫寫的),,給系統巨大的時空開銷負擔,而且很不安全,在沒有提供垃圾收集的語言中,涉及到二次刪除的問題

而引用方式就不需要作頻繁的複製,因爲從抽象上來講,,引用的確可以代表對象本身(因爲對象是程序的,而引用是計算機的,引用本質上就是對象嘛),

另外一個方面是,當我們用邏輯操作符比較二個相同的對象時(一個對象和它的複製體),返回的結果居然是False,這是因爲實際比較的是它們的引用,,,由於第二個比較對象是原對象的複製體,因此它的地址即引用是與原來的對象不同的,因此比較結果會是假!!

GameObject& rgo = *pa; // rgo 的靜態型別是GameObject,
// 動態型別是Asteroid。
爲什麼說動態呢,因爲=賦值時就經過了一個隱式的轉換
dymic cast就是對動態型別的轉換
注意此時rgo就是gameobject類型,,而不是asteroid類型,,也即,,一個變量的類型永遠是它的靜態類型而非動態類型
這是什麼呢?rgo被聲明爲一個指向GameObject的引用(引用就是引用,,而非指針,引用就代表對象本身,取地址操作符不是引用,這二個東東根本不一樣,雖然可以通過對一個對象取地址就可以將它變爲指針),,但其實它就是一個對象______即rgo是一個GameObj對象,因此說rgo的靜態型別是gameobj
point to someobj跟ref to someobj是不一樣的
refcount就是實現對引用的計數的,
由於引用是變量的別名,,所以,引用並不是一個地址值,而是變量名,,,,注意
必須對一個即存變量進行它的一個引用聲明,而不能對一個常數進行引用聲明
2.18 面向類型化的設計和麪向無類型泛化的設計-OO不是銀彈
以數據抽象動作爲中心的設計,以類型爲中心的語言。這是馮氏下編程的特點

有沒有注意到呢,編程首先是將概念進行數據化,以至於將那些不是實體的抽象概念都數據化---比如設計stl時將迭代器template class化,,,腳本語言的變量中,數值可直接表示科學運算用到的元素,其它UDT和ADT可表其它高級的dsl抽象表示。。也即類型。

類型就是數據,編譯語言嚴格這個過程,腳本語言提倡類型即數據。我們可由類型定義出子類型,,也是一種數據類型。。比如typedef int myint,,,這在jass2中廣泛使用。

類型即內存中編碼了的“計算機數據”,即設計者眼中的“數據”(UDT),(即“OO設計”實際上=“用UDT表達設計,用數據描述設計”)在很多地方,類型化正是設計的死敵,因爲它規定了目標對象的產生模式,在整個設計中充滿着錯綜複雜的對象頭文件引用。數據間的複合進入設計一旦被固化,用數據表達的設計容易造成偶合。

而這正是範型產生的最佳替代。,範型將設計中出現中的數據默認爲一個空的佔位符UDT。無論是OO還是泛型開發,都是以數據封裝動作爲中心的。而過程化設計是以函數封裝爲模塊爲中心的。

即第一點:OO以數據封裝爲中心,這會造成偶合。

第二點:OO容易產生過度OO化的負影響,因爲極容易將那些不顯式的設計元素封裝爲對象,這反而帶來了負面影響。

第三點:而OO的三重機制中的繼承,更是

在抽象類型作爲設計手段的方法上,OO和模板是差不多的,然而不同的是模板是“泛化”類型OO是純粹只用“類型化”來實現設計(對即邏輯的抽象邏輯,我們知道,實現中也離不開設計,設計庫的設計就更離不開設計了),,所以OO的設計手段不足,而設計模式是一種新的設計理念,語言並不直接支持,,C++用STL作爲語言的數據抽象,,用LOKI作爲語言的設計能力(OO,模板相比來說是小規模的設計手段)。

注意,OO的類型化絕對不能說成對象化,OO定義成面對對象是極其錯誤的譯法,,OO是用於設計的,,所以O這個字眼只能是“類型化”的“類型”,而不能譯成對象,,因爲對象是實現裏面,不是OO設計的原義所在。

新手編程導論(四)


第3章 語言之爭


3.1 學編程之初,語言之爭
最最古老的問題,什麼是計算機,這個問題跟什麼是人生一樣簡單但是難解,但是我們可以站在開發的角度,給出一個足夠簡單的答案,計算機=硬件架構+軟件OS
只要瞭解了硬件架構和軟件OS,那麼我們就可以驕傲地說我們精通計算機了,你就可以進入第二個問題,什麼是編程,編程絕對是個廣泛的話題,但站在,編程的根本在於抽象
在C的時代,我們以爲語言細節和系統知識才是難點,,在提供了VM式OO語言的時代,我們卻發現設計更難.應用更難,如何開發被別人複用更難,如何複用別人的邏輯更難,一個時代有一個時代的問題
在C時代(也就是彙編時代),CPU內置了操作碼指令碼,又提供了對內存和寄存器的控制,因此程序=數據+操作(換言之,計算機再怎麼樣,在其內部都是用操作碼操作數據的機制).以C中對數據還進行了結構體這樣的封裝,後來數據經過了CLASS抽象和封裝,一樣都可以最終解釋到這個機器過程.

給二套代碼,一套過程C式,一套OO式,讓讀者發現他們之間的不同(一般C提供的接口都是函數,,一個庫應儘量提供少的邏輯(一個接口應儘量簡單,提供它自己應提供的邏輯,其它多的都是多餘),這些邏輯都是基本的,比如一個socket庫只需提供諸如transport(int),transport(float)這樣的接口,,那麼所有的邏輯都可以建立在它上面了(比如發送大件數據的後來邏輯只需建立在transport()這樣的函數基礎上就行了),,接口並不總是虛的迂迴接口,,比如C++的抽象函數,,也有幹實事的函數,,作爲可複用的接口應是抽象的,,像上面提到的這些接口都是作爲應用接口的,面向應用的,是不首先考慮作爲複用接口的,如果是面向複用考慮而設計的接口,一般是幹虛事的抽象函數)
第三個問題,什麼是語言,我該選擇什麼語言,又回到可愛的語言之爭了,誰也不想浪費精力花費時間卻發現學了一門不太討好的語言.但是千萬要相信我,沒有通用永遠有前途的語言,永遠沒有最好,只有此時此刻剛剛好,沒有通用,否則就是騙自己,很遺憾給了你一個幾乎沒有用的答案,我們在這裏不宜講解(請在本書進入一個階段之後看p178)

把C++的語言機制分個類,,,有1,指針,間接訪問者實現OO也可,,2,OO,3,訪問控制COSNT,STATIC等
深入分析一下全部的C++語言機制,會發現他們都面向抽象,C++沒有接口,卻輕易能用指針和純虛類實現JAVA的接口
本書將從計算機這個編程環境,語言是什麼
3.2 語言與應用與人(1)
一門語言提供什麼機制,,是由它能表達的應用邏輯來決定的,歷來是語言適應並決定應用,什麼語言開發什麼樣的邏輯(人,應用邏輯,語言及其機制永遠是三個相互依存的矛盾,應用邏輯要求語言提供什麼樣的邏輯,而人開發應用邏輯也要求語言提供靠近人的機制比如OO,,因此用該語言開發的邏輯都可以用這語言本身的機制來解釋),
比如JAVA適合做WEB,LUA適合做遊戲(因爲它的協程機制),XML適合做數據(因爲它提出的元數據理論解決了瀏覽器文本交換的異構,並一度深入到數據庫領域),比如WINDOWS是用C開發的,那麼C的那些語言機制就可以用來解釋WINDOWS原理的一切,,再舉一個例子
我們將語言要面向開發的邏輯稱爲“問題本身”,“邏輯領域”,WEB領域是最能體現這三者關係(稱爲軟工)的領域,因爲這裏面存在關於這三者的諸多矛盾和他們的解決之法。
比如虛擬現實領域,,一個“虛擬世界”這個說法是用OO來表達呢,,還是用LUA的具體語言機制來表達呢還是用語言實現的某個庫邏輯來表達呢(LUA畢竟還是屬於語言,可能在庫級邏輯提供對遊戲的支持,並不直接在語言機制裏,,即它的語法支持裏支持遊戲邏輯,,因爲它還是屬於不同於SQL這樣的高度面向領域的DSL語言的)
那麼“虛擬世界”是用語言語法來表達呢,還是用庫來表達呢,,是用語言的OO機制來表達呢,,,還是由語言的一種更好實現的機制來表達呢??(誠然,OO作爲語言機制能夠表達一切,比如“虛擬世界”,但並不總是直接的好方法,有時對於一個特定應用邏輯,LUA可能提供了一種更好更快速的語言機制,或庫級邏輯,,反正要記住,OO並非一切)這就是語言機制基於應用的要求。。

3.2 語言與應用與人(2)
很多語言機制,當你做到用抽象的眼光去看待它時,它才真正算是被你理解了,即如果你能從現實的應用開發這個高度和需求角度,突然想到你可能會需要用到C++的模板(假設你以前未碰過它或只是聽說過),那麼你纔會真正開始明白關於模板的一些東西而不是從學習C++的語法書開始。比如你的應用邏輯中存在一個循環你需要一個循環,而C語言的語法剛好提供了循環語句,那麼你會主動去翻C的這種流程控制進行學習它,但這是例外的情況,很多情況下,絕大部分的應用邏輯並不直接對應到一套語法機制,可供你直接採用,完成這個應用到語言的映射。因爲語法機制實在有限,更多的邏輯被體現到了這種語言的庫中,或其它第三方庫中。

即應用纔是最終的目的現實問題,我們永遠要先行解呈清和解決它,最難的編程是認識你要解決的問題,在編程解決一個問題的整個過程中。語法問題的解決處在編程的未端,應用開發更多地跟語義直接相關,而非語法。無論你看了哪本語言方面的語法書,負責的都會告訴你只學會了基礎,的確,比起對應用的理解來說,以及如何用語法表現應用,學語言語法永遠都是基礎.明白語法只是第一步,你得明白語義,明白語義抽象到現實應用的那些部分。但是有人會說了,應用問題在一定意義上 — 比如提供了複用庫,就轉化爲語言問題了,此時應用問題本身還是要搞清嗎?要注意,語言會跟應用發生聯繫,但應用絕對不會跟語言發生聯繫,所以應用問題是獨立語言和語言問題的要單獨搞清的,這裏我們談到了語言與應用的關係,那麼我們該如何理解C++的一些常見語法機制呢並聯繫到應用呢?
在C++的諸多語法機制中。類和模板都是代碼結構,是面向人(解決複用和軟工問題,處在人和語言之間)而不是面向機器(像數據結構處在計算機和現實問題之間,

解決的是現實問題而不是人的問題),類提供了一個數據化“代碼”的統一手段,使編程工作複用維持在這個高度上,但是正如上面所說,它沒有解決實現問題。這也就是實現與抽象的區別所在。數據結構解決實現問題,代碼結構解決抽象(到語言)問題。
3.3 C與Ruby
編程語言涉及三個東西,1,平臺,2,語言,3,要解決的問題

設計模式是OO範式剎車的標誌,因爲它拉大了計算機與要解決的問題的溝隔,怎麼說呢?OO以後的OOP範式太多了,有設計模式,框架等,其實計算機內部的離散形式是死的,但用編程語言解決的目標問題纔是巨大的(實際上OO加重了編程的負擔和新手的入門難度,用OO來描述現實世界只能做到"對象"層次,如果向模式,向框架發展就越來越龐大和力不從心了),只能越做越複雜,有了OO就應該適可而止了,不要再發展OO以後的東西了,比如設計模式,比如框架,因爲OO以後的東西其本質還是計算機內部的離散形式,這二者南轅北轍,造成的結果是適配這二者的負擔太重了,疊在裸機上的邏輯太多了,有OS,有虛擬機,有運行時,有PE載體邏輯,有解釋器,這隻能給機器越來越重的負擔,這是第一,第二,計算機底層的離散形式本身就是固定和簡單有限的,自從有了OO之後,編程邏輯發展爲遠離計算機底層(向人靠近)的高級邏輯,而現實問題用OO來解只會越來越複雜,不如用固定的編程範式,,比如C語言規範來表達,這最大的證明就是單例,RUBY可以用極爲簡單的形式來表達單例,而JAVA要用一大堆OO的思想,,
c有全部的離散而RUBY相對來說就少點,沒有指針這個離散因此C產生RUBY,而RUBY只能BindC,RUBY不能寫我們現在能看到的WINDOWS(因爲它關於C的底層用到指針),而C可以出RUBY,
什麼樣的應用用什麼樣的語言,需要達到什麼樣的邏輯就要選擇好語言,因爲C幾乎能控制計算機內部所有離散(而應用就是計算機離散的向後發展),因此OO反而沒有C的直接底層強大(這相對計算機能完成的事來說的,在開發上,OO自然會快點好點)

下面是我提出的一些方法

1,OS用LINUX內核+Ruby shell,比如Ruby,底層開發一律用C,比如驅動程序,遊戲底層硬件渲染,RUBY可方便調用C,而且直接用於硬件編程時十分科學,比如embed c,C的運行也十分快
有人會問了,當RUBYBIND C時,那難道C的模塊不也成非本地代碼了?其實不然,當C模塊被BIND時,它只提供一個接口給RUBY使用(只是在RUBY這邊產生了一個使用邏輯),真正的執行體(被調用者)還在C模塊中,還是nativecode.
2,RUBY與C混然天成,當作高級非系統編程時,應把它變爲一種SHELL加通用編程語言..讓RUBY也同時成立軟工時代標準語言

在學習編程中,我覺得C和RUBY都是要學的(一個是底層,一個是高層,不需要學習C++等中間層的OO語言),,C面向底層,很好地解釋了學習一切C泛化出來的其它語言的底層知識,比如字符串,比如IO,比如進程,線程,對學習是極爲有利的,而RUBY
3.4 你爲什麼需要Ruby
C不能滿足大規模的開發,因此出現了引進了OO的C變體C++,C++幾乎是C的再造,雖然它對C的兼容性是天生的,但是C++又有很多陷阱,這些陷阱一部分來自對OS平臺的邏輯,一部分來自C++語言本身的複雜細節,(C++的庫並沒有把平臺複雜性封裝得讓開發者可以不管它,而且語言細節帶來的複雜性比如指針是用C++這樣的語言進行開發永遠不可避免的)平臺邏輯和語言本身細節帶來的複雜性是阻礙開發者前進的二個攔路虎因此出現了JAVA,JVM使JAVA開發不需要了解任何我們真實的環境(硬件架構加OS).而且JAVA提供的庫已經把JVM的進程資源,SOCKET資源調用邏輯封裝得稍微會點OO的人都可以拿來用了.當然我們並不總是作基於平臺的開發,,比如對抽象WEB的開發就是另外一個遠遠高於平臺邏輯的開發過程
困此我們說C,和C++是一派,因爲它們沒有一個虛擬執行環境,面向真正的計算機環境,C可以做C++做的一切事情,但是C明顯不能大規模開發需求,當面向本地編程時,當邏輯過大時,一定需要C++這樣的語言作輔助.C和C++被禁固在本地上,C++是本地編程的極致了,提倡對本地編程只需要C就夠了而排除C++的這種論調是極端的,而C++也並不是做得完美,除非對C++本身進行改造,增加如RUBY那樣的新語言的新機制,讓C++成爲容納一切範型的多範型,否則它並不能直接實現很多RUBY能幹的事情(RUBY構建在RUBY虛擬機上的OO機制和其它語言機制多了去了,比C++簡單但比C++的那些範式強大,比如C++的模板,雖然可以讓C++帶來GP,但是RUBY的GP要科學和有效得多,還比如JAVA用反映和代理技術去模擬RUBY的動態語言性質),JAVA卻根本不擅長操作本地,因爲這是它的設計目的導致的(JAVA就是爲了獨立平臺,使得開發時可以不管複雜的平臺邏輯,而且即使是對JVM的平臺邏輯,JAVA的那些庫也作了很好的封裝)
那麼你到底需不需要一個VM式的語言呢,首先如果你是作本地開發,雖然VM式的語言可以通過JNDI這樣的本地接口和BIN技術來進行訪問,但終歸是一種繞之又繞的方法,而且BIND和改造技術也不是萬能,有時你並不需要全部的原語言的邏輯,此時你需要定製BIND來調用你需要的部分邏輯,這是第一,第二,帶了VM作執行環境的語言過於寵大,當一旦有這種語言寫成的源程序當被執行時,其執行效立造成的影響不容小觀(比如遊戲開發,在一些實時碰撞和消息互發時要求不要有遲緩,WEB除外,WEB的平勁不在IO)
第三點,要搞清RUBY等這些語言的目標,RUBY被設計成通用編程語言,但是更接近一種DSL,它就比較適合搞WEB,
那麼你到底需不需要一個動態語言呢??動態語言就是在寫代碼的運行前編譯期不需要爲變量指定類型,因此類型寬鬆,寫作起來不需要考慮類型之間的靜態期轉換,只需像對待普通簡單類型變量一樣對待抽象CLASS類型(變量),源程序可讀性也很強(因爲沒有很多關於類型約束的邏輯和信息),但就是因爲類型信息在運行期,因此在編寫時IDE不能獲取到正確的類型信息,這給編寫造成了一定的麻煩.
那麼到底需不需要一個解釋語言呢?解釋語言就是邊翻譯邊運行,程序可以被實時調用,這幾乎是腳本語言的一個通用特徵(一般腳本的意思就是通過一個SHELL調用,SHELL就是解釋器,腳本這個說法相對於系統來說的,實現比較簡單的DSL方面的東西,系統編程語言是通用的,那麼腳本往往實現某個領域的邏輯,比如文本處理,但RUBY和JAVA走的都是通用腳本編程語言的路子),類邏輯可以先存在於持久化狀態,再被實時加載,每一句新寫的源程序都可以被即時執行.
那麼到底需不需要一個類RUBY語法機制的語言呢,如果沒用到協同線程,那麼Ruby中的Coroutine你就用不到.如果這些都剛好對應上你需要的功能,而且上面提到的其它方面都基本滿足,那麼RUBY還是比其它語言要適合你的.
因爲腳本就是要實現一種DSL的東西,所以RUBY即使很強大,Blizaard還是願意用LUA這樣的東西,困爲LUA的一些比如協同線程很適合遊戲開發,而且LUA不必通用,而且遊戲就是要輕量的VM,腳本語言的本質決定了它不好被發展成爲一種通用語言.
沒有一種通用最好的系統編程語言+通用的最好的腳本語言,不要去尋找它,因爲歷來都是應用決定語言,而不是語言成就應用..
切記,切記!

3.5 C++還是Ruby
不要去幹追逐技術的蠢事,,你只是用戶,,只需學會一門工具開發,但是也不要走入另外一個極端,就像我崇沿ubuntu,我並不會動則就把Windows和ubuntu作一個徹底的分離,並一有時間就抵制win並使用ubuntu一樣
邏輯指出,,,當人們需要掌握的形式過多時,,,那麼它造成的門檻也就會過高,(當然如果是C++的高手,那麼這種話可以不說,我只是對編程新手說的)C++終於還是屬於一種複雜的語言,它的機器因素太多了,多得初學C++的人邁不開腳,,所以要去的地方還很遠,,,,用一門語言來描述要解決的問題,C++只完成了最最初級的抽象,雖然它提供了OO,但是它導致的因爲C++的OO機制也很讓人頭痛..
似乎有人嫌Ruby過簡單,而且非C++不能體現他們鑽研到底層的精神,要知道語言級的形式纔是羈絆,現實問題纔是複雜的,如果抱這種習慣久了,就出不來了
3.6 C++與Java
其實就語言來說,沒有C++和JAVA這二個語言之間那個語言更強大一點的說法,大凡用其中一方能實現的功能,用一方都完全能夠抽象得到,JAVA所關注的WEB編程領域,C++完全可以提供同樣的功能實現,但是,JAVA不僅是一門語言,它是一個平臺,這指的是它的JVM,(C++卻不是一個可移殖的語言,如果要移殖,它的庫也不允許,關於C++的庫有太多涉及到本地OS,C++與OS藕斷絲連,這在一方面導致它並不適合WEB開發,C++與OS掛鉤,JAVA代碼與JVM持鉤,而JVM本身是可移殖的,因此這層抽象也就說明JAVA代碼是可移殖的),關於JAVA的WEB應用是發展在這個平臺上的,JAVA變爲WEB開發語言不僅僅是因爲JAVA代碼可以一次運行,到處執行,歷來不是語言成就應用,而是應用成就語言,就像ROR成就RUBY一樣,人們發現J2EE框架庫的出現適合WEB開發於是就導致了JAVA作爲WEB語言的流行,而且關於JAVA的庫和先進的框架越來越適應WEB開發,由於這個歷史,天時地利原因,JAVA最終成爲WEB開發的主流.
後來XML出現了,XML與WEB的結合可謂是無孔不入,XML甚至滲入到桌面開發的OFFICE中(,當然,這是XML不作爲文檔意義上另一個維度上的抽象,其實軟件開發到最後,桌面與INTERNET或者WEB的抽象間隔會越來越小),WSDL被提交給W3C,JAX,AJAX等等,這些都說明現在的WEB開發和布薯都趨向於與XML緊密相聯
XML+J2EE終究太複雜,在證明ROR這種框架比J2EE優秀之後(開發週期短),RUBY語言作爲開發WEB的最佳選手身份就被成全了,RUBY是動態語言,它提供的處理HTML的能力使它優於JSP在網頁中嵌入TAG的方式,然而這些都不是最最重要的方面,PHP也有這個很強大的處理文本的能力
3.7 .NET與JVM
.NET語言的公共語言運行時就相當於JVM,它們爲一種語言或多種語言的代碼提供運行的平臺(比如運行時爲它們分配內存,,普遍認爲在.NET的運行庫支持下可以運行多種語言的代碼,在JVM下可以運行JAVA原生代碼
但是要知道,原生不原生是相對的概念,如果能在JVM上實現一個Ruby的解釋器,那麼Ruby代碼也就是原生的Java代碼,只有抽象完成,整個Windows系統都可以用Java來寫,這就是說,在軟件的抽象裏,任何事情都可以以抽象疊成的方式來完成.但是顯然地,在WINTEL上裝個JVM,再實現個Windows,這是個傻瓜行爲.
OS跟虛擬機的關係,比如用C(更嚴格地說是C的一個編譯器實現比如MSVC,BC,GNUC)寫出來的代碼就是直接在操作系統上運行的(由一個叫運行時的抽象層向OS請求內存時空資源比如CLS的託管內存說法),,這相對OS來說,C代碼就是原生代碼,但是當爲一種語言發明一種虛擬機運行平臺時,這個抽象就大了,我們不再稱這個抽象跨度爲原生,而是過度了的原生,也就是說,不是原生,而是相對虛擬機的原生,比如JAVA代碼之於JVM的關係
實際上編寫虛擬機是編寫OS的技術之一(在一臺裸機上寫出一個虛擬機才能調試代碼和執行代碼),並且直接在一個業已存在的OS上抽象出一個虛擬機實現也是可以的,,,因爲這樣可以獨立很多平臺執行這種代碼,,這樣做的目的(在業已存在一個OS的情況下)就傾向於"爲了語言而創建一個運行平臺"也即一定程序上"JVM是爲了JAVA而出現的",而本來不需要一個JVM就可以直接在OS上寫JAVA語法的代碼的
?那麼JVM與JAVA解釋器的關係又是什麼呢?一門語言的最高級公民(first class)往往存在於棧內,比如函數啊,OO體啊,但是JVM又不是JAVA解釋器,不屬於運行時抽象也不屬於OS抽象,而是編譯原理抽象,學習的過程中,我們必須格定這種"抽象所屬",才能
3.8 你爲什麼需要Ruby
C不能滿足大規模的開發,因此出現了引進了OO的C變體C++,C++幾乎是C的再造,雖然它對C的兼容性是天生的,但是C++又有很多陷阱,這些陷阱一部分來自對OS平臺的邏輯,一部分來自C++語言本身的複雜細節,(C++的庫並沒有把平臺複雜性封裝得讓開發者可以不管它,而且語言細節帶來的複雜性比如指針是用C++這樣的語言進行開發永遠不可避免的)平臺邏輯和語言本身細節帶來的複雜性是阻礙開發者前進的二個攔路虎因此出現了JAVA,JVM使JAVA開發不需要了解任何我們真實的環境(硬件架構加OS).而且JAVA提供的庫已經把JVM的進程資源,SOCKET資源調用邏輯封裝得稍微會點OO的人都可以拿來用了.當然我們並不總是作基於平臺的開發,,比如對抽象WEB的開發就是另外一個遠遠高於平臺邏輯的開發過程
困此我們說C,和C++是一派,因爲它們沒有一個虛擬執行環境,面向真正的計算機環境,C可以做C++做的一切事情,但是C明顯不能大規模開發需求,當面向本地編程時,當邏輯過大時,一定需要C++這樣的語言作輔助.C和C++被禁固在本地上,C++是本地編程的極致了,提倡對本地編程只需要C就夠了而排除C++的這種論調是極端的,而C++也並不是做得完美,除非對C++本身進行改造,增加如RUBY那樣的新語言的新機制,讓C++成爲容納一切範型的多範型,否則它並不能直接實現很多RUBY能幹的事情(RUBY構建在RUBY虛擬機上的OO機制和其它語言機制多了去了,比C++簡單但比C++的那些範式強大,比如C++的模板,雖然可以讓C++帶來GP,但是RUBY的GP要科學和有效得多,還比如JAVA用反映和代理技術去模擬RUBY的動態語言性質),JAVA卻根本不擅長操作本地,因爲這是它的設計目的導致的(JAVA就是爲了獨立平臺,使得開發時可以不管複雜的平臺邏輯,而且即使是對JVM的平臺邏輯,JAVA的那些庫也作了很好的封裝)
那麼你到底需不需要一個VM式的語言呢,首先如果你是作本地開發,雖然VM式的語言可以通過JNDI這樣的本地接口和BIN技術來進行訪問,但終歸是一種繞之又繞的方法,而且BIND和改造技術也不是萬能,有時你並不需要全部的原語言的邏輯,此時你需要定製BIND來調用你需要的部分邏輯,這是第一,第二,帶了VM作執行環境的語言過於寵大,當一旦有這種語言寫成的源程序當被執行時,其執行效立造成的影響不容小觀(比如遊戲開發,在一些實時碰撞和消息互發時要求不要有遲緩,WEB除外,WEB的平勁不在IO)
第三點,要搞清RUBY等這些語言的目標,RUBY被設計成通用編程語言,但是更接近一種DSL,它就比較適合搞WEB,
那麼你到底需不需要一個動態語言呢??動態語言就是在寫代碼的運行前編譯期不需要爲變量指定類型,因此類型寬鬆,寫作起來不需要考慮類型之間的靜態期轉換,只需像對待普通簡單類型變量一樣對待抽象CLASS類型(變量),源程序可讀性也很強(因爲沒有很多關於類型約束的邏輯和信息),但就是因爲類型信息在運行期,因此在編寫時IDE不能獲取到正確的類型信息,這給編寫造成了一定的麻煩.
那麼到底需不需要一個解釋語言呢?解釋語言就是邊翻譯邊運行,程序可以被實時調用,這幾乎是腳本語言的一個通用特徵(一般腳本的意思就是通過一個SHELL調用,SHELL就是解釋器,腳本這個說法相對於系統來說的,實現比較簡單的DSL方面的東西,系統編程語言是通用的,那麼腳本往往實現某個領域的邏輯,比如文本處理,但RUBY和JAVA走的都是通用腳本編程語言的路子),類邏輯可以先存在於持久化狀態,再被實時加載,每一句新寫的源程序都可以被即時執行.
那麼到底需不需要一個類RUBY語法機制的語言呢,如果沒用到協同線程,那麼Ruby中的Coroutine你就用不到.如果這些都剛好對應上你需要的功能,而且上面提到的其它方面都基本滿足,那麼RUBY還是比其它語言要適合你的.
因爲腳本就是要實現一種DSL的東西,所以RUBY即使很強大,Blizaard還是願意用LUA這樣的東西,困爲LUA的一些比如協同線程很適合遊戲開發,而且LUA不必通用,而且遊戲就是要輕量的VM,腳本語言的本質決定了它不好被發展成爲一種通用語言.
沒有一種通用最好的系統編程語言+通用的最好的腳本語言,不要去尋找它,因爲歷來都是應用決定語言,而不是語言成就應用..
3.9 語言功能上的差別
學習時所一開始站的難度和起點不同,後來的成就也就不同,如果不一開始學編譯原理,那麼以後學習編程語言中所碰到的問題都會求助於自己的經驗,就會產生形而上學的錯誤,我們能想到的聯繫都是知識,有一些可以被證明爲正確,另一些不正確或即使正確卻不受人吹捧的漸漸流失而已,,,知識本多維,如果僅想從IT去認識IT,,那麼你會看不到全部,也會不知道很多學習IT需要學到的其它維度的知識(我實際上說,有些IT的問題不純粹是IT問題,並不能直接用IT原有的知識來解決,就比如,JAVA到底比不比其它語言優越,,這是軟件的問題,然而它跟人有關,,跟軟工有關)

也即,我們站在跨度太大的抽象間進行一個抽象到另外一個抽象的理解,因爲這個抽象太大了,在另一個抽象發生時,那個最底層的抽象可以顯得忽略不計,計算機可以記住那麼多抽象,而人不能,,就像這個世界已經發明瞭哲學一樣,難道人行事做人動不動就會求索哲學去理解事情嗎?問題出現了而過不去,你會發現再高深的哲思也不過幾條醜陋的信條,所以哲學有方法論,但是作爲哲學的方法論是大抽象上的,並不會深入到指導每個細節,當方法論脫離細節即脫節時,就沒有太大的意義
離散數學,,就是把數學理論,,形式化爲計算機能處理的東西,什麼是離散??計算機本身就是一個巨大的離散概體,,能讓計算機來處理數學,就必須把純理論數學的一些因素形式化爲計算機部件能理解的東西,因此會有形式語言的出現,因爲很多形式語言的低層知識還是圖等數學知識,等它與計算機結合成爲形式語言時,形式語言這門學問也就成了離散數不的範圍
就像在理解Delphi爲什麼能提供過程內過程一樣,爲什麼函數語言可以避免不用設計模式,爲什麼函數範式的語言可提供eval函數(流程控制,等都是語言間通用的),爲什麼動態語言適用於WEB開發和XML結合,線程和異常.等等,這是因爲語言也是抽象品,在它上面可以構建其它高級抽象,但是如果能瞭解一種語言抽象的得來過程,那麼許多問題也就不問自知了(因爲抽象跨度很小,中間很多的透明抽象現在可見了).學習一門語言的好方法是學習它的抽象由來..雖然這是個巨大的學習過程,涉及到很多其它非程序語言編制領域的抽象..
當然,如果僅僅是想開發一個應用,那麼不必瞭解這麼多,有人不理解STL的設計原理和架構,照樣都可以用STL技術快速開發,有人對JAVA不必作如上我們所涉及到的分析,是因爲每個人都有自己維度上或科學或不科學的理解(有人甚至因爲JAVA是編譯器哈),正確的思想級的理解對開發不是一定必要的,所謂死讀書也是有用的,不一定要理解到那個層次,能學會使用就夠了,細節永遠是重要的(有一句很出名的話:別無它,唯手熟耳),與開發應用最結合緊密,但是思想和學習方法也是重要的,我以前看過一本參考書,因爲把所有的知識來源都理了一遍,抽象達成從不大的跨度說了一遍,比純粹的一堆細節性的教科書更能讓我明白很多東西,也即,細節不是一切,只是關鍵,思想僅僅只是重要的.
3.10 C與C++之爭
C++教父在他的一本書中透露說,1/3的人反對在C++中加入OO(OO引進了編程向人的思維靠近的理念,是一種信仰和理念),,1/3的人積極推薦他向C++中加入這些功能。
  
C和C++的爭論高潮就在這裏,C++讓系統問題都OO化,使得系統問題都多解化,讓人們不習慣,這造成的可復性反而會降低。

可複用不是指修改而用,而是傾向於不用修改也可以用,,因此跟平臺相關的那些邏輯,無法通過僅用拿來就用的方法去複用。而且系統邏輯本來就是固定的,死的離散,而C++的OO對他們作了極爲靈活的封裝變換,因此造成即使對系統的理解,,也變得不夠透明,只有懂C,懂編譯原理,懂計算機系統,懂網絡,才能從一種大局的高度去看待並計劃你的系統你的應用。。而C++之後的OO機制不行,這種原因使C++受到了C粉絲的抵制。

OO是不是真的提高了我們想到的可復性?
  
  而脫離平臺邏輯的那些領域問題,基本上人人都會有一個解法,宜在這裏採用OO,OO最應該出現在腳本語言中,並且面向對象和基於對象也是二種幾乎性質的東西,雖然都是設計期,都是對運行期的設計,但其底層支持下的RTTI有性質上的差別,C++作爲底層開發語言,它提供的“基於對象開發”還是比較接近C“封據抽象”的風格的,但是它的面向對象,就有點脫離系統編程語言向DSL發展了,一般來說像RUBY這樣的語言才提供OO和其它一系列語言機制。因爲這些語言機制都是脫離系統編程的高層問題所需要的。而給底層開發提供一個OO,反而讓人覺得封裝過頭,,抽象得太象了,,太象人的思想了,反而不能夠讓人家瞭解系統編程語言作爲描述底層的透明性要求。。
  
世界上最好的語言是objective C加快一點的Ruby
3.11 我爲什麼選擇C而不是C++及其它語言
我爲什麼選擇學C而不是c++以及其它語言

首先C是最不容易過時的語言。
因爲它跟硬件接近最近,可以向後發展,而其它語言只能向後發展卻不能向前發展。。即使是在馮氏結構過時之後,c語言的地位也是最好的。嵌入式就說明了這個道理。。

C語言算法解決那些對於計算機實現要迫切解決的問題,比如內外排序,當要排序的對象遠遠大於內存時,需要設計一種好的算法來解決其對於計算機的實現問題,,
而Java語言的設計,解決的是站在人類邏輯角度的算法問題,比如如何進行架構設計才能更好複用以利於軟工的開展,這二者所處的維度不同,決定了他們解決問題的方法根本不一樣。。

當要設計機器的時候,Java的那些設計思想根本派不上用處。。

c語言加數據結構算法的方式解釋了計算機產生邏輯的根本,只有深克地理解了這些,那些高級抽象就有被準確理解的可能性了,,這個道理就像接口和抽象、、

產生了抽象,提供了接口,但是接口卻是大口徑的,它隱藏了實現,接口並非刻意去隱藏實現,而是這是它的性質決定的,客戶程序不能通過接口去訪問接口後面的實現,否則只有它是這個接口的客戶,,它就至少無法通過接口向前訪問,,它只能利用接口邏輯,去發展後面的東西。。

要知道,你想要明白stl必須要理解一些adt的東西,這是C語言算法和數據結構的一些東西,,C++並沒有完全做到adt完全的抽象(沒有抽象到你能以一種脫離底層完全不同的眼光去看待adt),,這不是C++的錯,,這是你沒能學習算法跟數據結構的錯。。
不要依賴太多的抽象,因爲它們根本做得不徹底,你依然得學習算法跟數據結構才能理解這樣的adt.

計算機底層=C語言加數據結構,算法,,,,這二門學科剛好完全地歸納並解釋了編程對應於計算機底層的方方面面。。其實C語言很簡單,基礎的指針用法也很簡單,如果學習了C語言版的數據結構,因爲這是在學習C的習慣用法,,因此指針的很多抽象用法也會學到,,也就同時學會了C語言和數據結構,,,因此學數據結構是學習C語言最好的方法。。

深克地理解了算法與數據結構,你學習計算機纔算到了家。。
3.12 類VB,DELPHI類RAD語言分析
vb在Windows平臺上是一個流行的開發環境,在排名上很靠前,VB所體現的理念就是比爾向開發者靠近的態度,一個OS的發展離不開DEVELOPER的支持,比爾算是深刻地做到了這點
並非沒有指針的語言功能就一定很弱,並非沒有OO的語言功能就一定弱,運用VB,你照樣可以用VB開發遊戲,系統軟件等,解釋器並非虛擬機,VB帶了一個解釋器和運行時庫,但是沒有帶一個軟件機器性質的運行時虛擬機,,因此效立也不是很差.
我們關注的是VB的快速開發理念,它的最大特點就是"封裝的可視性"和"複用接口的簡單性",,,可視性是指邏輯單元總是被髮布和開發成一個可視的控件,簡單性是指每個控制都有有限和簡單的幾條屬性和方法,,語言做到這個程度已經十分簡單了,不需要我們掌握學習C++要學習到的一系列其它細節複雜性,VB把這個年代的程序員更多地做的是一種直接使用別人的庫的事情這個理念算是做到家了,只要在上游統一了簡單的形式,那麼在下游就可以用這種簡單性開發一切邏輯,,我們知道,複用就是邏輯組合,邏輯總有接口,對象的屬性和方法都是接口(但一般把數據屬性封裝起來,因爲透露數據的接口是危險的接口),構建應用就是組合接口兼容的邏輯單元,但是這個過程明顯比不得現實生活中的電腦組裝,這種事情.所幸VB也有其另外一種過程式的範式,並不要求用戶一定要用這種方式開發.
因爲應用是複雜的,軟件開發永遠不可能做到比VB還簡單(永遠不可能像現實生活中的找零件配電腦這種過程),,,這是因爲應用永遠是複雜的,而且有它自己的專門性,,,除非別人發佈的庫就是爲你的應用定製的,,又或者你的邏輯不簡單,需要複雜的邏輯(而別人沒有提供類似可拿來用的邏輯,或者即使有相似的,但是爲了適應你的應用而進行改造,那麼複用工作就是純粹的開發工作了(相對來說就不是複用了,而是開發了)..如果改造工作代價過大,或者對邏輯和接口的調整根本不能進行,那麼VB的這種編程理念也是沒用意義的.
這就是爲什麼VB永遠不能跟C,C++這樣的語言功能強大一樣,,畢境,,有限的形式1可視2接口簡單並不能直接產生很多複雜可產生的計算機邏輯(就像其它語言能提供的衆多範式一樣),,,這個道理就像WINDOWS GUI跟UNIX CUI之於開發誰更復雜誰更科學誰更簡單一樣,WINDOWS對GUI做多了文章,,使得在某些人眼中利用GUI IDE開發根本沒有UNIX下SHELL開發簡單和自然,甚至於一個VI就可以當成整個IDE..有時候,提供的形式越少,少得簡單得最最初級的編程者都知道用控件來搭軟件,那麼這2種向"可視化"發展的簡單形式反而不能描述更更復雜的邏輯,只能將邏輯做到控件層

所以VB這樣的東西還是侷限性很強的,,不是一門真正的系統編程語言,,像是一種介於系統語言和WEB語言這樣的DSL之間的語言,,,往往用於RAD,,,通用性不強
因此VB從來都不是標準,,只是像E語言,DELPHI,一樣,只能往RAD的方向發展
正像我所說的,,,如果你想爲你的應用計劃一個科學的設計
最好是理解整個PC邏輯,,硬件的,軟件的,,語言的,,庫的,,應用的,,軟工的
要注意VB並非通用OS編程語言...因此它的侷限性很大
歷來都是應用成就語言,,,有了RAD所有就有了VB
離開了快速開發的需求,沒有人爲VB提供複用控件,VB就沒有太多用了..因爲下不能入底層,上不能跟VB.NET一樣進入高層的WEB開發,是雞肋了.

新手編程導論(五)


第4章 語言最小內核(C)


4.1 C與C++是二種不同的語言
C語言的最初的版本是起源於B語言的,後來經過發展,又經過了多次標準化(第一次民間標準化是C語言的二個創始者K&R寫的一本書,C是最初作爲UNIX專用系統編程語言出現的),分裂成了二支,在C89標準上面加上OO,加上Template,就形成了C++(C++是C89的超集,當你手中拿的C++教材沒講基於過象面向對象和模板並且你需要的不僅是一本C的教材時,你可以現在就扔了它),第二支繼續獨立發展,發展出了C99,目前最新的C標準是C99,支持並實現了它的編譯器市面上比比皆是。
關於C89與C99方面的差異有一些是根本性的,因爲實現的原理根本不一樣(是編譯器方面的差別,而不是庫級邏輯上的差別),比如C++用Template技術實現的數組版本和C99的數組..
所以,C++除去C89的那部分纔是C++的主體(所以說學習C++是學習C語言和C++語言這二個過程,我們知道++是C家族語言中特有的運算符,C++這個表達式表示,第一次的運算過程只首先取C的值,第二次及以後的運算過程纔在保留原值的基礎上開逐次始加1),C++爲了成爲多範型語言,先是雜合了C的很多特性,提供了指針,位,函數,流程這些基於過程的開發支持,爲了成爲基於對象和面嚮對象語言,又實現了一個運行期OO — 其中有運行期多型機制(這是C++之父"發明"的),再後來是在沒有任何原C的編譯技術的支持的情況下發展出了一個Template(也是C++之父所在的研究小組"發明"的),是C++首創的獨立編譯技術(並由此"發現"了C++的編譯期多態和實現了Loki,Boost MPL庫等),STL就是用了C++的Template而出來的東西,STL是面向運行期的,而Boost MPL庫是面向編譯期的。
C99的數組跟STL的數組就是大不相同了,,一個是C上面的,一個是C++的模板技術上面的,有着不同的實現原理,編譯支持基礎..
在C中,數組就是原生Built-in的,,數組就是連續的同類型數據在內存中的分佈,,,注意,1,連續空間2,同類型數據,,如果不是連續的空間就不是數組,,比如用指針link起來的就是鏈表,而不是"數組表",,注意"表這個說法",,數組是一種表,,因爲它用索引下標索引數據對象,是一種key:value對,而鏈表也是2同類型數據,,
在STL中,模擬數組比如Vector,List是用模板來實現的..這二者之間明顯存在差別,靜態數組一般直接用C的數組,,但是正因爲C的原生數組是沒有邊界檢查的,編譯器根本不保證這個栓查全部交給程序員來處理,,而STL版本的模擬數組就會非常嚴格..
這就是二者之間的差別
4.2 C的數組,指針與字符串
(我這裏講的都是C標準)
C只直接支持簡單的數據類型,只有簡單的Int,Float,Char,Void,這樣的數據類型可以直接定義使用(並可作爲函數參數傳送,但數組就不能直接被傳送,需要轉成它的指針形式 — 一個32位長整型數被傳送,,因爲它不是First Class),,其它的高級數據表示比如字符串,數組,都是在簡單數據類型之上模擬得到的,,,是庫級的邏輯,,編譯器並不直接支持..(C編譯器沒有提供對字符串String的Built-in支持,只有Char和Char *,C把它作爲庫級邏輯的String來抽象)
C99在庫級新增了_Image,_Complex,_String這些數據抽象(相對C++來說)..
所以C的指針類型幾乎跟數組,字串這樣的邏輯同胞共母,字串本質是Char * 數組,,數組是內存相續的同型數據,用指針變量來表達數組和字串幾乎渾然天成(數組名就是指向第一個數組元素的指針,類型爲:"(基類型)指針變量名"這樣的形式,,而且字串直接被定義成char[]的形式,,數組大小一定要程序員方面保證容納字串大小+一個/號的大小,字符串函數大量涉及到指針比如strcat,C有一個標準的字串實現,cstring.h,C++有一個STL版的字串,C++還有一個原生版的string.h,特定的編譯器也有特定的字串實現),,,C就這樣把所有的東西統一於簡單的"簡單類型"..
當然C也提供了union,struct這樣的結構體來表達比簡單的類型來抽象一級的類型,其實C也可以表達OO(不過它並不承諾一定要實現運行期多態,繼承等OO語言的標準技術,,有一本<<C支持面向對象技術>>的書講解到了相關的技術)
4.3 C的輸入與輸出流
C++版本有個Iostream(Cin,Cout就在裏面,實際上C++的IO庫很大,是一個複雜的基於對象的模板系統),C也有它的Stdio.h(有Inputchar等和Printf()這樣的函數),這是C語言的標準(庫)獨立於所有的硬件和軟件環境,是語言本身的因素,,,一門語言的IO機制是很重要的,比如,JAVA的I/O封裝得很完善,,它細分成了多個OO表達的"流",極大地方便了程序員。
i/o絕非僅僅輸入輸出這四個字這麼簡單,首先這是一種語言機制(就跟語言提供異常處理,這樣的語言機制的地位一樣重要),是關於這種語言對向它輸入和由它輸出的所有抽象的全部集合,它涉及到一門語言如何處理與系統的交互,如果將用戶輸入轉成該語言寫成的程序所用,,如何看待OS"文件"的概念.並發展出一系統概念,如inputsteam,file steam,等等
在PC的架構中,語言是高於OS的(一般在一個系統的架構中,開發層是高於內核層的),Windows是用C寫的,C有它的I/O,是先於Windows的,一門語言並沒有具體的數據類型,,比如unsigh,sign這樣的區別是來源,起決定作用的還是硬件,,C語言只是對它們提供了一些名稱指代,,比如int在有些機子上是32位在有些機子上是16位,這對考慮一種程序的可移殖性是十分重要的。
在C的眼光中,一些輸入輸出都是某種流,語言接受用戶輸入,或者程序進行輸出,先以內存作爲根據地進行緩種,,緩衝分爲二種,一種是緩衝文件系統(這是C標準的文件系統,,即不是一次性輸入用戶數據到程序中,而是輸入一次緩衝一次),另一種是非緩衝文件系統(每次輸入就完成一次絕對的i/o),這是,C把顯示器,打引機像成標準輸出,把用戶輸入鍵盤,想象成標準輸入,,並把它們看成"標準輸入出流",,,把OS的文件想象成filesteam.
4.4 C的類型系統與表達式
任何語言都有一個“類型系統”,比如C++直接支持類(UDT)爲它的First Class,,Lisp這樣的語言支持直接傳遞函數指針(因爲函數是它的類型),,,就C來講,,它支持primitive types,即普通的Char,Char*,Int,Int *,Float,Float *,Void類型。。
到底什麼是類型呢,C++ template的type跟class有什麼區別呢。。
4.5 二進制指令看循環
大多一門語言都提供了流程控制,,形成了各自的語言關於流程語句的語法要素,,語法是一種形式(所以稱語言是形式語言),,語義是一種意義,,,
其實循環,跳轉這些東西來自於彙編語言,,,高級語言接納了這些理念(因爲彙編語言幾乎就是機器邏輯,高級語言提供類彙編語言這樣的機制是爲了符合平臺邏輯,,,況且高級語言最終要編譯成彙編語言和平臺邏輯,,循環語言要最終被還原成彙編語言的形式,這些處理速度就可大大加快),,,發展出了它們關於循環,跳轉封裝了的語言機制..
C語言最終要編譯成彙編語言和平臺邏輯,,循環語言要最終被還原成彙編語言的形式,,這就是調試的由來,,,調試分語法級調試和語義級運行期的錯誤..
4.6 所謂指針:當指針用於設計居多時
指針是C語言中的一種"語言機制",,它導致的差別在於
如果用得一般,指針就是一種普通的工具,僅僅在給函數傳地址改變實參,數組的定位本質是指針,,這些課題上達到頂點
而如果C語言的指針用得好,,,C語言就會是另外一種語言,
那會是一種什麼語言呢,那會是一種advancedpointer c lanuage(增強型指針C語言,,指針使C變爲設計語言就跟C#高級語言一樣,而不再僅是普通意義上擁有指針作爲底層機制的中間語言)

因爲指針是C語言唯一的"抽象語言機制",我們這裏提出"抽象語言機制",說明可用於設計,,,比如C++有"OO","範型"等等(很多書上講解C++沒有講解這是對的,因爲C++的語言機制中,只有OO和範型是它自己的,而指針幾乎是C語言唯一的抽象語言機制)

指針被用於設計時,,它的用法有哪些?? 這就是學C的最高境界,

首先指針是一種底層實現和設計通吃的語言機制彙編語言中也有指針,比如
mov eax dowrd ptr [某一地址]
mov edx dowrd ptr [某一地址]

這樣的結構,,說明指針在這方面是一種內存地址的指針,,然而當指針發展到C的指針和C++的引用時,,又形成了更高層的邏輯,,,
4.7 指針成就的C語言
幸好你沒有把C跟C++放在一起學,,C是c99,C++是C89,,可喜可賀

學C,,要學透就學指針,,,要學全,就要學C的標準庫

指針不單是控制底層的工具,,,也是C的抽象語言機制
1) 字符串,C的字符串跟指針密切相關.C根本就是用內存來控制字符串的.而C++的Iostream.h你去看看,,全是抽象了的模塊,你根本不需要深入內存,二者根本不可同日而語,,這是因爲C就是站在底層去構造字符串邏輯的,,而C++隱藏了這些,,不讓程序員知道..所以C最適合當系統編程語言,而 C++控制系統的能力卻沒有C強,,,一個很好的例子就是可嵌入開發,C++根本不行,因爲C++更多地是一種應用開發語言
2)數組,C的指針幾乎(我說幾乎)等同數組,前期的C根本不能把數組當參數傳遞,,後來的C標準支持了這個觀點,,因爲數組在C的觀點裏就是內存地址.因此用指針完全可以控制數組,,函數可以返回一個指針以返回一個數組,,,數組也可以用指針而不用索引來定位並操作它的元素
3)位操作,,就是所謂的bitwise了,,這個跟指針的關係就更不用說了

綜上所述,,C是靠指針來進行設計它自身的,,,而且,C是靠指針來解決C能解決的程序問題的..因爲C不僅是一種語言手段,,而且是一種語言抽象...
網上有用指針實現C++的OO的文章,,讀完它,你就發現C通過指針機制還可成爲設計語言,,,什麼是設計語言呢,,C++,VB,RUDY,LUA,DELPHI都是設計語言,而C就是底層語言.

祝好

C是系統編程語言,,一般談到對系統編程,,,就是數據結構加算法(當然,系統編程的真正概念是指:socket,graphics,IO,gui這些平臺支持邏輯相關的層面),,,編譯器前端的構造就是一個大算法,,,運行時就是一個大數據結構,,,操作系統作爲語言運行的環境,,考察它的實現過程,也是數據結構加算法的集中體現,

如果你真要學透C語言,,,,第一要學習C語言是如何來的,,這就是編譯後端了(動態運行期的類型信息),,,第二要學習語言本身的機制,,當然重點是指針..因爲指針不單是內存地址,,,而且是一種語言的抽象機制,,,C語言只有指針這種抽象機制,,就像C++的OO,範型一樣

數據結構加算法是系統的觀點,,所以容易跟C語言結合,,,C語言也因此被用來系統編程

而VB是RAD,,抽象了太多底層的東西,無所謂數據結構加算法,,所以被侷限於代碼組合和快速開發...因爲它的接口就是控件屬性和方法,,,它的功能模塊就是控件..

其實OS不需要一個語言,語言只是爲了開發應用而出現的,OS可以不要它(語言編譯器也屬於系統軟件),更確切來說並不需要一個編譯器,,它只需要一個
編譯器的後端,比如運行時,或者僅僅一個解釋器。有了這二個就可以執行源程序了,這二者之間的接口要明白,因爲解釋器可以直接執行編譯器前端生成的中間代碼


而且VB的運行時面向的根本就不是真實機器,,VB是一種架空的語言..因此它的語言機制也是構空的,,,不是面向系統的.因此無所謂數據結構加算法

4.8 指針是語言的一種抽象機制
指針是一種語言的抽象機制,而不是僅是語言用來控制低層的手段(指針指向變量,而變量是內存地址)
指針有什麼用呢,,指針用"指針本身"來指代"它所指的東西本身"(雖然實質不同, 一個是指針變量一個是指向的變量本身,二者除了意義上指代與被指代這層聯繫之外,其它方面沒有任何聯繫,從變量的三個方面來看,
1.變量的型別,,每個指針變量都是一個指向某種型別的指針變量,它首先是變量,然後是指針變量,再然後是指向某某某型別的指針變量,第四是指向某具體變量的指針變量,被指的變量可以是其它類型,但指針變量都是一個32 int
2,變量的作用域,指針變量只爲索引被指代的變量,如果被指代物被釋放了,而指針本身沒有被釋放,就成了野指針.
3,變量的所佔的空間,不用說了吧
雖然存在極大的不同,但是,但是指針的精神要求你把它們二者建立"他們是相同的"這樣的認識,這是基於抽象的考慮要求的
比如int* pointoaint;????? //pointoaint就是一個關於int類型的指代,,,
那麼這種指代有什麼用呢,(相比之下,引用比指代更能體驗這一點,引用=它所指的對象本身,它所引用的對象本身,雖然這二個概念不等價,但指針這層抽象的意義就是讓我們人腦把指針作這樣的理解,)
在單根繼承OO語言中(就是所有的語言級的first class OO對象都是從一個Tobject這樣的東東繼續而來,用戶定義的OO對象也需要從這個根對象定義而來一樣)實現泛編程的容器中,,比如JAVA的LIST中,一般用指向TOBJECTS的引用再填充這個LIST,這樣容器裏的對象不是對象實體,而是指向他們的索引對象,,,
很顯然,JAVA的LIST中的對象都是索引他們的引用變量,而非對象本身,是假對象.
另一方面,,,人們用索引來看待變量的方法,,這種行爲自身也表明,,引用只是一種靠近人腦的抽象,,,是一種語言的抽象機制,,人們說指針是C語言的靈魂,,說的就是這個道理..

4.9 學C千萬不能走入的一個誤區(其實JAVA比C難)
C語言重點不是爲了來開發應用的,,而是爲了開發跟計算機本身(遠離業務)那一層面的邏輯



C的開發早過時了,,現在是高層邏輯開發時代,,,,

現在的WEB開發,,,提出了一系列新語言,新的框架,,設計哲學,,這些都是遠離計算機底層的(比如數據跟算法),,更像是一種邏輯的配置,,然而邏輯的配置比開發更難,,,,這個道理就像:這個時代GUI遠遠比CUI常見,,但反而CUI纔是主流



這就是說,抽象遠離了計算機底層,,在高層又變得異常靈活和難於掌握,,



其實JAVA這樣的語言因爲面向WEB,面向業務,,,它要學習的東西反而更多更難,,,學習JAVA,如果不學WEB,這幾乎是件可笑的事情,可是JAVA易學,WEB就不易學了

但是C語言要解決的問題永遠是底層,系統永遠是簡單和形式的,大不了把數結與算法學精.學透,把系統本身學透,,C的開發就到家了
但是JAVA後面的應用問題永遠有新的學習需要出現,,因爲JAVA後面的東西是異常靈活的高層應用而非固定的底層...面臨的問題永遠有新的解法,WEB領域永遠有新框架新語言的提出..
所以我說,,,其實JAVA比C難..!!!!!



這就跟C一個道理,,C易學,,但C面向的系統問題涉及到大量數據和算法,又顯得很難,,,如果學C,卻不是爲了解決系統,這又是一件可笑的事情,,,永遠不要以爲學好一門語言就是全部目標了



學好C語言在於將C語言包括指針在內的所有語言機制習慣用法,跟數據結合和算法的結合,,C用於實際問題纔是重點要學習的



懂了C,卻沒懂系統底層一樣沒有用,不懂數據與算法也沒有用,因爲C就是面向並開發這些的



其實,JAVA和C都可以發展很多後來的抽象,,JAVA和C 容易學習,是因爲它們入口處小,(JAVA和C有有限的語言機制和學習成本)學習和控制就很容易了,,但抽象應該長足發展(WEB問題和系統問題是一個不斷可以深入的話題,需要新的語言機制的支持),,JAVA和C解決的問題永遠沒有一個頭,,永遠都可以是新的話題..因爲應用無形而語言有形



應該相信一些簡單的道理,因爲它們是道,道統一所有理



無論是什麼語言,一門語言寫出的程序總是程序=抽象+接口,
(C的抽象在於抽象底層機器因素,提供程序員可見的因素和機制,,這就是抽象,,對於人的靠擾,C的接口在語言級的細微度接口主要還是函數級的接口,,編譯器級有.out文件,,,底層=算法加數據..接口提供了功能模塊如何交互和複用的機制)
再比如JAVA,,提出了很多語言機制,,也是爲了能更好地遠離本地發展抽象,,,它把什麼都OO化了,這很利於對開發人員的抽象,,,,
它提出的OO,,,一方面既是抽象機制,,,一方面也是接口機制..

4.10 C抽象慣用法
誠然,C只有有限的語言要素,它的語法只是稍微抽象了彙編,它適合描述系統底層,而不適用表達過高的抽象,比如它沒有直接站在OO角度上去描述除系統底層問題之外的其它問題,C的那些語法機制是C在語言級能用來表達所有問題的唯一方式(即使這樣,C還不失爲一種系統編程和應用編程的綜合語言,它的表達能力還是巨大的而且C的優點是易於學習進門但進階難,不像別的語言連進門都難比如OO語言,它的OO機制一時半會是很難被初學者理解的)C雖然沒有語言級的OO,不能直接用C的某種語法機制進行OO思維寫OO代碼,但C的一些簡單語法機制同樣可以實現模擬了的OO的抽象(作爲庫級抽象來進行OO)。。這導致“如何用C達成高度抽象語句或程序”這樣的問題的產生(不只是OO,C也可以實現C++的template各種各樣的抽象來間接解決現實問題而不滿足於用C本身來直接解決問題,這裏,間接解決現實問題定義OO等庫邏輯,就是抽象的意思所在)。
這就是說,抽象並不用C的語法機制直接寫應用,而是在進行某種設計,在C模擬的OO中,它企圖先在庫級完成C模擬的OO,再去解決它要解決的最終問題,當然這只是C抽象的一種,C的抽象主要表現在四個方面,1.用C來解釋的數據結構問題本身就是某種抽象和設計,在一些教材講解到的C版本的數據結構教學中,可以大量看到C寫抽象的方式 2,C的指針是C的主要抽象手段(這樣說是因爲一些其它C的語法要素也跟指針一樣用於設計導致高抽象,),C的指針可直接產生很多複雜的思想 3 跟上面一開始談到的一樣,我們想用C構建某種靠近現實問題的抽象,比如我們想用C來實現OO,或者template,這樣C的這些邏輯就成了dsl了。再來解決問題。4,我們不想先實現一個OO,template這樣的通用解決問題的C抽象,這樣的通用抽象稱爲範式,而是直接面對要解決的問題,用C的語法語言要素去產生一個或一套習慣用法的抽象。相比OO,template這些範式是小規模的抽象慣用法,下面我們來討論一下C高抽象的習慣用法。。我們還有另一章專門講解OO範式.
1),,用指針,struct,typedef,,這樣的東西可以聲明一個鏈表的結構本質,這在數據結構中大量看出。指針是“通過地址來操作變量”的抽象,struct是建立在簡單數據類型上覆合數據類型的抽象,typedef是type redefine的抽象。。這些抽象分別使用,或者組合使用可表示很多其它高級抽象。。
2)形參定義爲指針,實參向它傳地址,實質上是傳值然而意義上是傳送地址,這種指針的抽象用法,可用來影響調用函數中的實參。
3)void這種類型可作爲形參,實現一種多態機制,,void函數也是如此.
4)C語言數組的本質是地址,多行數組是按行優先描述的一維數組,因此其元素地址都可由數組名抽象得到.
5)字符串是一種變長字節,操作字符串的那些函數,也可操作內存塊,這就把字符串抽象爲一種數據結構的東西。
6)C用void (foo *)(int)這樣的結構來表示返回void,以int爲參的函數指針foo.這遵守屏看原理.關於函數指針和數組指針還有很多抽象的變形。。來實現回調函數等抽象。
7)C的指針在鏈表中,實現了一種"指針即變量自身"的指代作用,比如stack->next=stack->next->next,這種指針的賦給,實際上就是指針所指變量在意義上的直接替換。。這也就是C++中爲指針新增的引用語法。。
8)typedef實現了換名和子集定義抽象,用舊類型定義出新類型,新類型就是舊類型的別名,或者是舊類型的一個子集,這樣的抽象語義。。特別是define實現了一種變量替換式的函數抽象。
9)用const或雙層const進行防修改。
10)還有很多很多。。
4.11 C的抽象範式之OOP
其實我覺得C++之父不必把它的c前端版本的C++弄成一個獨立的編譯器,因爲不這樣的話,寫出來的OO代碼也是C的,這樣就能極大範圍地做到語法上統一,也能將接口保持在C函數的級別供腳本語言使用,而即使在這些模擬中,不少正是直接對應一模一樣或幾乎一樣的C++的本質。無論如何,下面我們討論一個前端版本的OO模擬,順便學習一下C++面向對象的內容本質。



先來看看面向對象幾種層次抽象
我們知道在C++實現的OO機制中,存在class,class=type,即template中的type,但是我們知道template更像是一種建立在C++類型機制上的腳本語言,其語法古怪,不接近C++本身,,C++用class作爲派生type的類型,用type作爲template中的類型。

class可用來派生子類型也可用來產生對象(我們用措詞上的區別來區分這二者),派生的子類型subclass,此時它也是一個class,,產生的object,我們稱一個對象爲class的instance.class爲object model

這其中存在一些特例,比如純抽象的class(用來實現C++版本的interface),,或者靜態類,,

爲了產生邏輯,我們可以繼承,也可以組合,繼承就是從相對基類中(除了單根繼續的語言,基類都是嚴格相對的)單層繼承或多層繼續,產生新的類,這樣的子類在抽象上繼續了父類的所有屬性,在底層實現上其實是疊加了子類的代碼在基類上的組合代碼,是發生在類間的邏輯。也可以由類產生對象以產生邏輯,是發生在類和對象之間的邏輯,,,注意多層繼續在現代OO中是不受鼓勵的。。

我們將會在下面的RTTI部分討論以上機制在底層的對應實現。。

而發生在對象之間的邏輯呢,是採用將一個對象作爲另一個對象的數據成員來作爲產生邏輯的方式,,對象之間交互的方式就稱爲消息,C開發的函數範式在這裏被消息所代替,實際上我們知道消息也是函數調用,只不過這裏的函數是加了訪問控制和多態的特殊性函數。。

C的模擬OOP主要有以下幾個技術,1,用預處理宏定義keyword和定義結構,2,實現rtti.當然,在C++中寫多態有三種方式,1,是C++擴展了C的普通多態函數(重載只是多態的簡單手段,稍後談到的2,3纔是高級手段),2是OO裏面的多態函數,3是模板的泛型多態,,我們只在這裏討論oo裏面的東西,,並不打算討論其它的機制。比如C++所有的3種多態。純adt的interface機制,也不討論C++中C沒有的異常機制。更不打算討論templaste。我們只模擬OOP的典型和核心特徵。。


首先就class來說,如何模擬呢,
4.12 C的觀點:底層不需要直接抽象
抽象是爲了簡單化,抽象=對機器的簡單性=對人的複雜性
C++在語言語法級集成的抽象太多了,而且它的庫級抽象也太多了,BOOST,編譯期的泛型抽象,運行期的OO泛型抽象等,,OO抽象等,,無一不表示,C++更適合當一門靠近應用的語言(只有高層應用要求抽象,底層開發就要不得太多抽象,因爲機器本身就是機器,C就是對應機器的,無抽象必要),,,這造成的結果是要全面掌握C++要求人們掌握的知識太多了,C++的這麼多因素使C++變成了多範型,完全密封遠離低層(然而你要知道,抽象意味着對底層的迂迴,對人類思維的靠擾,這迂迴多了,對底層的訪問就少了),,,,其實C++也明顯沒有損耗C操作底層的能力(我這裏僅指它OO範型和模板等高級語法機制),,,,但是它提供高級的語言級和庫級的抽象反而對人們要求掌握它的成本變得過高了,這就是說,C++對於開發人員提供的"形式,工具"過多(至少比C多).,提出了很多抽象,.在提供的抽象方面,C只有指針,函數指針,結構體,這些有限的東西,而C++呢,有OO,有TEMPLATE(語法級),有STL,有BOOST(庫級)這麼多
在開發人員這端,C++代碼複雜難解,因爲一個會讀C++源碼的人首先要理解語言本身,才能理解作者要想在代碼中描述的現實事物(機器邏輯通過C++對於應用邏輯和現實事物的變換),,,而這裏面,抽象太多了(甚至過度抽象,過度設計)
用OO表現的現實事物,相對於C用結構和函數表達的現實事物來說,,,後者更接近機器.C本身的那麼有限的語法機制,即使離開了語法級的OO,C也可直接用函數級的第一類型(組成的模塊)和結構體數據抽象,和指針,,這些東西描述應用領域,比如Windows用C表達消息,,jxta用c表達管道,端點這些概念,,,
語言的選擇永遠離不開應用,要開發底層,,C比C++好,WEB開發是那些遠離底層的邏輯,用JAVA可以,但是C其實也是OO的,它的結構體,本身就是一種數據抽象,比如我要描述一個學生,我就選擇性的定義一個struct student,裏面加上學分啊,身高啊這些數據,(這是最好的方法,比C++的類都要好,爲什麼呢,因爲C語言的類型機制就是計算機的類型機制,,這些定義數據抽象做到了機器跟應用域事物的一一對應,非常地好)

而定義關於數據的抽象是馮氏語言模型的根本,我們知道可以用設計數據的方式抽象DSL概念詞彙,系統編程支持詞彙。

JAVA明顯不好用來開發底端,,這就是語言適配應用的道理,,但是計算機終久是計算機,如果你想學習計算機本身而不僅僅是想通過掌握JAVA開發WEB,那麼C+LINUX永遠是最好的選擇,因爲開源的東西一般出現在LINUX下.而且通常用C開發的,世界上開源項目最多的就是C了.
一句話,C比C++簡單,如果想開發圖形啊,網絡啊,這些跟計算機本身有關的東西,這樣的一個想靠掌握語言馬上編程的初學者最好去學習C,而不是C++

4.13 指針:間接操作者
type*
這個形式就表示一種類型,,,,如果說type本身也是一種類型的話,那麼在type後加一個星號也表示一種類型,,這種類型叫“指向這種type的指針類型(重要的是最後的指針類型這四個字)”,,你可以聯想dephi中的ptype類型
所以你就可以這樣定義東西
type* someobj;
(*可向type靠近或someobj靠近,C程序員偏向於向someobj而C++程序員偏向於向type靠近,這樣的話就有二種等價意義,type *someobj表示*someobj是一種type變量,type*someobj表示someobj是一個指向type的指針)
someobj就是一個指針變量,它代表指向此type的的指針變量,,因此在這裏type*是類型(是一種指針意義上的數據類型),someobj是這種類型的一個變量
根本上引用只是一個指針的別名,因此你可以由一個指針得到一個或多個引用(即關於該指針指向的對象的引用)
指針本質上是一個32位longint(因此在Generic progamming領域有“用整型代替類型”的說法),定義一個指針時可定義void*型指針(因爲指針並不一定出生時就要指向一個對象或一塊內存,而引用要求有一個初始值,因爲引用本質是指針的別名),因此它可以被指定爲0,即空指針,也可以被重新賦值(此時它就不指向它原來指向的對象或內存了),,換言之,,多個指針可以同時指向一個對象,你可以爲一個對象定義多個指向它的指針(但是它並不擁有這一內存,也就是說,當它指向的對象發生了變化,指針僅僅作爲指向這個對象在內存中的位置的意義就會失效或過時,它只擁有對象的adress值而非value值),而你可以通過這些指針或引用來操作該對象
一般來說,引用常常跟const在一起(加上const只是爲了強制跟保險),那是因爲定義出來的引用常常不會改變
type*
這個形式就表示一種類型,,,,如果說type本身也是一種類型的話,那麼在type後加一個星號也表示一種類型,,這種類型叫“指向這種type的指針類型(重要的是最後的指針類型這四個字)”,,你可以聯想dephi中的ptype類型
所以你就可以這樣定義東西
type* someobj;
(*可向type靠近或someobj靠近,C程序員偏向於向someobj而C++程序員偏向於向type靠近,這樣的話就有二種等價意義,type *someobj表示*someobj是一種type變量,type*someobj表示someobj是一個指向type的指針)
someobj就是一個指針變量,它代表指向此type的的指針變量,,因此在這裏type*是類型(是一種指針意義上的數據類型),someobj是這種類型的一個變量
根本上引用只是一個指針的別名,因此你可以由一個指針得到一個或多個引用(即關於該指針指向的對象的引用)
指針本質上是一個32位longint,定義一個指針時可定義void*型指針(因爲指針並不一定出生時就要指向一個對象或一塊內存,而引用要求有一個初始值,因爲引用本質是指針的別名),因此它可以被指定爲0,即空指針,也可以被重新賦值(此時它就不指向它原來指向的對象或內存了),,換言之,,多個指針可以同時指向一個對象,你可以爲一個對象定義多個指向它的指針(但是它並不擁有這一內存,也就是說,當它指向的對象發生了變化,指針僅僅作爲指向這個對象在內存中的位置的意義就會失效或過時,它只擁有對象的adress值而非value值),而你可以通過這些指針或引用來操作該對象
一般來說,引用常常跟const在一起(加上const只是爲了強制跟保險),那是因爲定義出來的引用常常不會改變
4.14 真正的typedef
typedef是類型替代名(typedef=type dfine嘛),在理解時可以按以下的方法進行

1.typedef是定義一種類型的子集,,,比如typedef int INT;(這意思就是說,INT是int的一個子集)

2.類extern的這樣一種聲明(也即僅僅是對類型的向外的一種再聲明),如extern int myint;(這意思就是說,作爲變量的myint這裏作爲一種“新的類型”,而且是一種“int類型”,,,這是一種當已有類型的再聲明)

也即,extern就是相對於變量,typedef就相對於類型(注意它們二者並不實際等價,也即不會有myint這個實際變量被聲明出來)

注意,一般myint要大寫,但是這裏爲了說明還是採用它的小寫形式

由第二種理解方式可以導出很多,如typedef int (*myint)(),,,可理解爲extern int (*myint)(),,這裏的myint也是一種類型,這種類型表示“指向一個返回int類型的函數的指針類型”

4.15 真正的指針類型
type*
這個形式就表示一種類型,,,,如果說type本身也是一種類型的話,那麼在type後加一個星號也表示一種類型,,這種類型叫“指向這種type的指針類型(重要的是最後的指針類型這四個字)”,,你可以聯想dephi中的ptype類型
所以你就可以這樣定義東西
type* someobj;
(*可向type靠近或someobj靠近,C程序員偏向於向someobj而C++程序員偏向於向type靠近,這樣的話就有二種等價意義,type *someobj表示*someobj是一種type變量,type*someobj表示someobj是一個指向type的指針)
someobj就是一個指針變量,它代表指向此type的的指針變量,,因此在這裏type*是類型(是一種指針意義上的數據類型),someobj是這種類型的一個變量
根本上引用只是一個指針的別名,因此你可以由一個指針得到一個或多個引用(即關於該指針指向的對象的引用)
指針本質上是一個32位longint(因此在Generic progamming領域有“用整型代替類型”的說法),定義一個指針時可定義void*型指針(因爲指針並不一定出生時就要指向一個對象或一塊內存,而引用要求有一個初始值,因爲引用本質是指針的別名),因此它可以被指定爲0,即空指針,也可以被重新賦值(此時它就不指向它原來指向的對象或內存了),,換言之,,多個指針可以同時指向一個對象,你可以爲一個對象定義多個指向它的指針(但是它並不擁有這一內存,也就是說,當它指向的對象發生了變化,指針僅僅作爲指向這個對象在內存中的位置的意義就會失效或過時,它只擁有對象的adress值而非value值),而你可以通過這些指針或引用來操作該對象
一般來說,引用常常跟const在一起(加上const只是爲了強制跟保險),那是因爲定義出來的引用常常不會改變
type*
這個形式就表示一種類型,,,,如果說type本身也是一種類型的話,那麼在type後加一個星號也表示一種類型,,這種類型叫“指向這種type的指針類型(重要的是最後的指針類型這四個字)”,,你可以聯想dephi中的ptype類型
所以你就可以這樣定義東西
type* someobj;
(*可向type靠近或someobj靠近,C程序員偏向於向someobj而C++程序員偏向於向type靠近,這樣的話就有二種等價意義,type *someobj表示*someobj是一種type變量,type*someobj表示someobj是一個指向type的指針)
someobj就是一個指針變量,它代表指向此type的的指針變量,,因此在這裏type*是類型(是一種指針意義上的數據類型),someobj是這種類型的一個變量
根本上引用只是一個指針的別名,因此你可以由一個指針得到一個或多個引用(即關於該指針指向的對象的引用)
指針本質上是一個32位longint,定義一個指針時可定義void*型指針(因爲指針並不一定出生時就要指向一個對象或一塊內存,而引用要求有一個初始值,因爲引用本質是指針的別名),因此它可以被指定爲0,即空指針,也可以被重新賦值(此時它就不指向它原來指向的對象或內存了),,換言之,,多個指針可以同時指向一個對象,你可以爲一個對象定義多個指向它的指針(但是它並不擁有這一內存,也就是說,當它指向的對象發生了變化,指針僅僅作爲指向這個對象在內存中的位置的意義就會失效或過時,它只擁有對象的adress值而非value值),而你可以通過這些指針或引用來操作該對象
一般來說,引用常常跟const在一起(加上const只是爲了強制跟保險),那是因爲定義出來的引用常常不會改變
4.16 真正的函數指針

C和C++都不允許一個真正的函數作爲參數,,將函數作爲參數傳送的辦法是將它用一個指針去指向這個函數,,,,

而且,通過函數指針和函數模板,可以很有效地實現業務邏輯跟界面邏輯分開
學習函數指針完全是一種挑戰,你能分別出以下的嗎?
int *(*(*foo)(int))[5];
這個就表示:foo是一個函數指針(這就是括號帶星號的結果-即把*foo括起來的那個括號),它指向一個函數(以指向它的指針foo命名的函數),它帶一個int參數(即函數指針foo緊靠右的那個括號和括號內的int),,並返回一個指向數組的指針
4.17 真正的句柄
句柄一般有三種意思
1,Window的資源句柄

一個語言跟操作系統的關係是什麼??

一個操作系統可以用一種語言寫成,,因此如果語言出現在前(歷史原因),那麼操系統(或其一個小節)會用這種語言來描述,,,比如Windows的對象,,就是說如果Windows是用C++來實現的,那麼這個對象就是CUI(**—對象

根本歷史,,一方用另一方的東東來描述的關係

比如文件,,操作系統普通把文件作爲硬盤上的東西,,可是文件在語言的觀點裏根本就不是這麼一回事,,,文件只是data flow,,或者一個file map(也即在內存中作引象),,,,

2,包裝被指對象爲其值的智能指針
3,形如type**的雙層指針
4,其實通用意義上的Handle就是句柄的意思,句柄還有一種用處出現在編譯原理中
5,如下

如果B是A的一個子類,,,有如下代碼

(A)new B


那麼以上可以理解爲,,,上面產生了一個對象,,這個對象指向B(指針),,,但是其句柄爲A

即a handle of A pointing
to a B,

也即第四種指針是用父類來操作其子類對象的一種機制

4.18 真正的循環
for的條件表達式可能短路,,它的update部分可以是任何算式,甚至不要updata部分
一般把最常發生case的情況放在最後面,如果你學過彙編就知道,編譯器是從後到前搜索的

注意彙編是沒有邏輯運算符的,只有移位運算符(而往往C++中把它們同用)

有三種,,邏輯(與,或,異或,取反),移位(無符號左移,,右移,,高位補0等等),

在彙編中

contine和break的區別,contiune放在循環語句的某一具體層中,當滿足條件時,繼續執行下一個當前循環(即重新判斷條件),而break可以放在一個循環的任何地方,是跳出當前循環(注意因爲循環可能是嵌套的,所以這個當前循環是指break所在的那一個嵌套層次),,然後繼續執行這個循環外的第一條語句(即結束循環)。
4.19 真正的static
在C++中,一個結構也有它的構造函數,這個構造函數(接口類不應包含構造函數)往往作爲初始化該結構的初值使用,因此一般將它們作爲static來定義,這就不得不說說static到底給語言增加了什麼?
static的成員也不是靜態的,它也可以進行自加自減這樣的操作,
static是在編譯期發生的,因此不能在運行期改變它
在一個函數體內定義的static數據,,相對整個程序來說都是全局的(直到被銷燬)
因此static提供了一種把局部自動變量轉化爲暫時全局性的東東,,比如對於一個在函數體內的常量來說

static也被作爲類級的私有成員數據或行爲(類屬函數),因爲類只是定義對象的規範,因此它應該沒有一塊內存實際存在用於調用它的成員(一般方法是具體化出它的一個對象然後調用對象在類中定義的成員),但是static提供了一種“全局靜態”的概念(類級的全局),比如Java中systme名字空間的API就是全局的API(包級的全局),可以直接拿來用

函數退出這個常量就失效了,解決的方法是用static或const
static的作用就在於“保值”時空限度爲定義它的整個模塊,,這是相對靜態局部變量來說的,,還有靜態全局變量
要注意,一個函數的原型和它的具體實現是分開的,你可以overroad(複寫)一個基類的private的函數聲明,但卻不能調用它的實現
也即,只能複寫聲明,而不能調用定義(實現)

在C和C++中,聲明和定義是分開的,如int i=9;就是一種定義(因爲連初值都賦過了這說明分配了內存),而extern int i;只是一種聲明而已(向外界宣告它的存在),而Java中就沒有聲明跟定義的差別了。上面是對於變量來說的(特別是對動態對象—也即對象變量這種表現更加明顯),,對於函數的聲明和定義的區別就更加明顯了。

4.20 真正的數組索引
指針經常跟數組在一起,因此它也經常跟索引相關
因爲字符串也是一個數組(更準確來說是可以用數組來實現的線性表,只不過它的最後一個字符是/0而已),所以形如char*的字符串也一定跟數組有關
如果一個指針指向一個new type[num]形式開闢的數組,那麼經常有下面的形式出現,
數組名=指針=數組的索引1的地址=用雙引號括起來的一串字符串
char* myvar = “iloveu”是用字符指針指向字符串第一個字符在內存中的位置,char[] myvar2=”iloveu2”也是成立的(字符串往往以字符指針或這種字符數組來表達)。
指針加1等價於索引也加1,但是這其中發生的本質是不一樣的,,指針值加了sizeof(type)的值,,而索引只是向後一個索引遞進而已
指針只能在堆上存在,,,而不能在一個函數的棧幀上存在,alloca可在一個棧上聲明
“同一”與“等價”的區別就在這裏出現了
實際上數組的地址的確等於它的第一個元素的地址,,然而數組的地址並不是它的第一個元素的地址,而是這個完整數組的地址開頭
mfc的消息機制就是一種表驅動

4.21 類型和屏看原理
BCPL作爲C語言的始祖(就連32位彙編語言都引進了變量和類型,類型是一門語言的基礎,用來表示爲程序所用的內存的抽象,即多少內存,可以在這個類型上執行什麼操作),它是沒有數據類型的,實際上一切都是抽象,C語言中的基本類型正是抽象了“以何種方式使用內存(更確切地說是使用內存的位,因爲這是彙編語言和機器邏輯最終要考慮的問題,在高級語言中得抽象一下不能直接用位組)”,使用內存的方式就成了基本類型,這是一種抽象。。將基本類型發展爲Struct聚合類型又是一種抽象,,將數據類型封裝爲ADT(同時封裝了可施加其上的操作)就更是一種抽象了。。基本類型中爲什麼會有數值,字符就知道了,這完全是一種趨向現實的抽象,這是每一種語言的每一個程序,每一個現實問題最能廣泛體現到的抽象,當然,基本類型完全也可以是函數,如Lisp語言,,只要是一門語言直接支持的類型,那麼它必定在內存中有一個位存儲模式,彙編器規定了什麼樣的操作可以施加在這個位組合上。,

要深克地理解C語言中的“屏看”原理,比如下面二個例子
void (int *p1,int *p2)
#define mysub(x) x^3/x

在第一句中,參數是p1,而不是int,即不是*p1,我們優先看到p1,p2這樣的變量,即參數是變量

在第二句中,define的是mysub(x)這個整體,而不是x,我們優先看到mysub(x),即define語句的整個前半部分

一些名理:

命令行就是觀念裏自由的世界(編程時從觀念裏出來,去除表現邏輯和其它邏輯,就事論事,不必輸出到gui上,只直接面向輸入輸出到標準,這些語言級而非應用邏輯級的東西),GUI就是強加了面具做人的不自由

能圖靈處理的問題,就是計算機能解決得了的問題,可通過程序手段反映出來。

我們稱最小接口爲原子操作,因爲由它們可以演化得到更高級的接口。

一個源程序模塊或二進制模式能運行,一定同時包含了指導CPU的指令和待處理在內存中的數據。。

解釋器就是直接運行中間碼,不需要完成到目標碼的轉換。。在編譯前端,它還是比較跟編譯器一樣的。。
4.22 位操作與多維數組指針與元素
首先C語言提供位一級的操作是爲了迎合彙編語言,,回憶一下CPU的指令集,,有專門的位操作指令,C只是稍稍地將它們進行了下抽象(這樣的話,C有了位操作再加上指針,它就成爲系統和控制語言了,抽象不但隔斷不必要的細節,而且在另一層維度上,提供了對於人來說更爲強大的功能,這就是抽象的二大特徵),對比一下我們就知道它們的差別並不大,,CPU還有OF,和CF這二個寄存器來表示移位和運算時發生的進位和溢出現象。。

我們知道,在存儲位的時候(更確切地說是一個字節級的東西),Intel的CPU是按高位在前(書寫或打印時顯示在左),低位在後存儲的(在右)。因此位往左邊移的時候(因爲左邊是高位,位到了左邊就佔了一個高分位,整個二進制會按基數越來越大),就是乘以2的冪,體現在它的十位製表示會越來越大,C語言規定,一個類型的值向左移位的時候(右邊會溢出不用),在它的右邊低位加0,一直補滿這個類型應有的位數。。而這個值向右移位的時候,位到了低分位,整個二進制按基數2越來越小,,移了幾次位就相當於除以2的幾次冪。。移位的時候,左邊補0,如果原數左邊是0,是個正數,那麼還是補0,如果原數左邊本來是1,是個負數,那麼根據不同的編譯器會採取不同的動作,有的編譯器把它看成“簡單算術右移”,最左邊那個位還是補1,有的編譯器會把它看成“邏輯右移”,,會把最左邊空出來的位加0..

有人會問了,移位會不會把周圍內存的位給擠了,不會的,因爲所有的移位操作,只侷限於這個類型的這個值,跟內存中其它值(只要它們都不參與此次位移運算)都沒有關係。。如果二個不同類型的值用一個二目移位符連接時,編譯器會進行對齊操作以使它們有相同的位數。。

位定義了幾種運算符,有與(實際上不是邏輯乘,但我們按乘的原理可以得出它的計算方式),或(邏輯加),異或,取反,各種運算用在不同的目的下,與可以用一個屏蔽字屏蔽給定值特定位,就是把它們置0,,,,或可以用一個屏蔽字屏蔽給定值特定位,,就是用1去保留它們。。異或可s以反轉特定位。。

這樣所有的工作就成了找一個需要的屏蔽字(根據原值,,面向要作什麼樣的屏蔽要求,這二個因素)。。

在單維數組中,,指針跟"&數組名[索引]"或"數組名+索引"的方式有點相似,,實際上這是C語言語義的二義性,,這是爲了讓C語言變得靈活而保留的。。而在多維數組中,&數組名[索引]"或"數組名+索引"往往並不是一回事。。這一切都是因爲C語言中的多維數組其本質還是單維數組,,因此用單維數組的索引或指針方式用在多維中,就會出現很多語義。。

我們來分析一下,可以得出以下結論:

首先,我們知道數組名並非指針,它只是一個意義上的等同,實際上存在數組名作爲指針,,但是不存在"&數組名[索引]"這樣的元素,,它表示一個地址(從...開始的地址)而非表示一個元素。。尤其是在多維數組中要分明白。。
4.23 變量與VOID
C語言中有一種void類型,,無類型指針。。我們知道變量是有三個要素的,1變量的名字,2,變量的地址,3,變量的類型,其中3是最重要的,它影響了2,,這也就是說,對於一種變量來說,一談到這種變量,它的大小就清淅了,,變量的最重要的特性是它類型影響下的大小。。。這是變量的本質

所以,對於變量的指針這種類型來說,,在使用它之前,一定要知道這指針是指向什麼變量,,這樣編譯器才知道從這個地址,以多少大小的範圍去操作它,否則void類型的指針是不能被解引用的,只能供調用參數用。。
使用void來作爲函數參數時,主要出於這種目的,比如我們想讓一個函數處理多型數據,就可傳遞這種數據的指針,因爲是多型,我們不直接定義int *,float *,這樣多的類型,而代於void作參,這樣處理時帶來void解引用後的結果(當然,得cast成int*或float*這樣實際的指針變量才能使用得到它指向的數據)。


而cast是一種什麼樣的過程呢,C語言的隱式轉換是編譯器的動作,手動轉換是程序員的事,但是,無論是這二轉換的那一種,都不改變原指針值,它只是在內部作了一份拷貝(這個道理就像傳指針也是傳值,不過傳的是指針值,因此另外一種意義上來說就相當於傳地址,,它跟直接傳值一樣,也是作一份拷貝),你可以將這個拷貝值看成爲C++新增的指針語法:變量別名。

新手編程導論(六)

第5章 抽象


某某牛人說過,增加抽象是一切解決問題的方法!!
時代改變了!!以前是手工軟件作坊時代,,現在是軟件工程時代!!!

5.1 人與軟件
應用越來越接近人了,比如web2.0,3.0的出現,這是指軟件產品的應用,實際上在軟件被作爲產品被產生出來時也作了靠近人的調整,編程領域的三個東西,問題域,方案域,人都在相互影響,這種影響產生了一些技術,導致了一些編程的變革,並最終與人們的觀念結合,比如OO,比如設計模式,這也將導致架構變成軟件的功能性"實現"要考慮的,在某個維度上加深了複雜度,然而卻在另外一些維度上提供了簡單的形式和更有效的應用
某些東西越來越統一和規範了,這加大了學習的門檻,比如xml出現,就統一了文檔交互的格式,並導致了很多邏輯知識,產生了一些新的邏輯,需要被學習,但這是合理的,因爲形式更加簡單了統一了,並改變了一些應用的形式,比如軟件分發delopy的統一形式等,
另外一趨勢,應用越來越分佈了和趨向web,這實際上是很多年前某些大公司的戰略,總有那麼一羣人(有些人研究應用形成架構,有些人研究編程低層形成架構和思想),先知先覺地認識到一些東西,比如.net的出現,網上的資源服務器越來越變成一般應用服務器,富客戶端的flex,silverlight等等,只是它們是慢慢被民間所識所學習.
一切技術都是面向被應用,因此人無論如何都是主導.將反過來最終影響技術的被利用形式而隱藏了低層實現,一些離最終應用跨度太大的低層實現不必知道其原理,靠近人的一端要提供儘量簡單的形式,比如xml,比如oo,面向機器的一端永遠有它的實現
5.2 軟件活動的特點
別空想設計,設計也是源於迫切要解決的問題(設計跟需求分析有關),你說的設計需要可能是一個空想的禁固。。

軟件需要你考慮每一個細節,因爲這不比蓋房子,,做軟件需要你從大量小邏輯促成應用(每一個部分都可能需要你造輪子)。
方案域用於表達應用的抽象元素只有類是最大的,庫是最大的,這種細小性決定要去的地方,要達到的應用還很遠。軟件活動是一個真正體現人類心智的地方。是一個真正工程級的活動(程序員作爲實現者可以不懂大設計和系統級的設計,但設計師要管)。
而且現實生活中軟件活動通常都是變化的:代碼在變化,設計在變化,連需求都在變化。
C++爲什麼不是單一OO泛型而是多泛型的呢,因爲設計就是多選題,你無法找到其單一性,這就跟policy based design一樣的道理。
實際上,軟件的設計可以不具有任何需求的成份,,只是在人類的具體活動中,往往需求會影響設計而已。。

模式分爲三種,問題模式,設計模式,代碼模式,MVC是一種表達窗口的現實問題模式,此時用mvc複用設計模式就最好表達了。而mvc被policy based design實現了,所以它首先也是一種代碼模式了。

設計期聚集於程序員,運行期聚集於用戶,,比如當設計深入應用時,語言的設計和運行功能都要很強,,設計時不可能預測用戶點了什麼菜單。


5.2 抽象與接口
什麼是架構,架構是設計的上上層部分..比如網絡七層模型,甚至PC的馮氏模型..這些設計的上上層部分深刻影響了計算機後來的東西.不可輕易更改.

不可思議的不是代碼,不是編碼,而正是人類的大腦,而正是設計,,

在設計一種平臺時,語言和OS是一個架構的基礎部分(在OS內部更多地存在設計),應用再在這個上面慢慢搭建.

如果你看過google的手機平臺設計,就會發現語言這個圖靈裝備往往是架構的最基礎部分。如果說機器語言或彙編語言是嚴格基於平臺邏輯的,那麼高級語言基於嚴格的語言機制和語言語法,具有嚴格的圖靈裝備,,負責產生後來的邏輯和用戶級的應用邏輯,本來OS是不需要語言的

語言儘可能小,直接面向真實平臺不要虛擬機,因爲加了虛擬機執行方式會很慢,,而且,語言應作爲OS的內核一部分.就像WINDOWS把GUI接口加入core一樣,這樣的語言執行效益很快.這樣的語言也就稱爲系統開發語言..與之對應的是應用開發語言,或稱腳本語言.

在底層,彙編是一種幾乎對應機器語言的東西,因爲彙編語言無語法.只是名稱指稱,助記符..C作爲中間語言,彙編只需要提供接口給C,C還應在編譯期提供一個編譯期多態

爲什麼腳本語言多稱爲語言粘合劑呢..因爲腳本語言往往提供對多種系統編程語言.數據類型的內存模型的封裝.提供了對這些語言的多種接口,,,但更主要的,腳本語言被稱爲腳本語言,,,更在於它面向用戶的其它特域相關和特定工作相關的語言機制(比如Lua的多協程常被用來遊戲,,而這顯然不是通用語言應在語法級應提供的)也許腳本語言只需要提供與其它語言的粘合接口就可以了

在上層,我們應該提供一種"無語法"語言的DSL腳本(比如SQL,好像腳本語言跟解釋型語言幾乎同意),,這種語言最好還是無類型的,,這樣做的目的是方便程序員,但不好用來開發平臺邏輯(因爲平臺語言,要求嚴格的類型機制)

這種語言是自然語言描述性的,,重複性的句子並不會產生雙份的運行負擔

設計應該被配置,而不是被編碼,設計語言應是一門非圖靈完備的獨立語言(要考慮到它的通用性而不是DSL就更好了)。

我們看到的大多數語言都是圖靈完備的,而我覺得C++應改編成一個解釋執行、無語法的語言,因爲應用語言往往需要做成即時修改的腳本語言

應該如何設計接口呢,OO=抽象+接口,,那麼其體現又是怎麼樣的??只有深克地分析了應用和計算機本身,你才能分析出通用和最精簡的接口機制,而OO正是這樣一種機制..因爲類=數據加函數,,數據加函數既對應計算機世界,又是解釋現實事物的一種機制...當然.OO不單是這種理念,還要學習它的運行期多態,繼承等(語言語義)..

學高級語言應不僅學它語法,而且要學它語義..因爲被翻譯後的代碼纔是代碼最初被產生的地方,是最應該被研究的..

接口應儘量窄,提供儘可能少的東西,,但在概念上一定要廣,,以備後來的擴展需要.這樣的接口,對於複用來說,也是可以花最少的精力來學習的.什麼是抽象呢,,,就是抽取對於人來說象的部分....把多種特徵的東西在概念上整合爲一個認識,,,抽象出接口就是這個意思...其實,抽象正是爲了簡單,,而不是複雜化(它只是極力把複雜化的低層細節隱藏,透露給人們一個通用的,可認識的抽象概念,即接口)

deepest concept,lowest interface = min learning

對接口編程即,將需要維持不變的東西,即設計,維護在接口層,即變化的東西,實現,維持在接口層以外的地方。

如何從以上二種機制理解OO呢,,,OO爲什麼這麼流行,,因爲OO“很好很強大”..

5.3 過度抽象
C和C++只是抽象多少的問題(對硬件架構和軟件系統,構成的系統編程環境進行抽象,以靠近人腦開發),linux之父還跟別人爭得起煙,,在Windows的GDI中,用C實現了一個對象系統,連C都抽象了桌面對象,還有什麼不能抽象的,,,只不過C++在語言級實現了一個OO而已(而WIN的桌面環境對象系統是在邏輯庫級),
一個問題出來了,,抽象顯然是爲了簡單化,但是這是對系統編程環境的簡單化,,在高級層次會造成更大的複雜,但至少脫離了大量需要原來人腦去維護的細節, 人們只需要掌握大概就可以去複用了(接口),,,這是一個很大的進步,,,而且有些被實現了被開發過的東西和輪子可以直接被拿來複用,C代碼卻要基於平臺的考慮修改大量細節,這種修改的代價超過了太大,就沒有可複用性了,,,所以無論如何,C++比起C,,使人們掌握細節和系統的機會減少了,但需要人們掌握設計和複用上的知識多了(人們更專注應用領域).而且在這個時代,由於JAVA語言這樣的語言的出現,語言標準得於統一,因此可複用性首先不用考慮語言本身,更而且,JDK統一,SUN的J2EE規範統一,,可複用性就成了人們之間的契約文化了,可複用性的地址會越來越下降.C的難點就在於系統本身,,系統細節和語言細節都過多,,造成人們掌握它的成本過大,但C的代碼沒有繞OO的彎子,跟系統邏輯幾乎一致,因此效率很好。

什麼是過度抽象呢?如果抽象不是提出一個簡單模型,而是在封裝的基礎上提供了一個很複雜的模式,甚至超過了人們學習C和系統的那些知識,那麼這種OO模型(或稱呈現的供可複用架構)就是不成功的,

模塊化也是一個重要方面,設計中的抽象過程固然要求模塊化,但這決不是抽象的終點,,因爲有時雖然模塊化了,但是模塊之間卻偶合了。所以,科學的對於問題的抽象應該是模塊化加鬆偶合的過程。

5.3 OO爲什麼不是銀彈 - 過度抽象與抽象偏差
C的程序員在用C++表達數據結構與算法時,出現了二種情況: 要麼寫出來的東西雖然被套一個class名字,實際上還是過程編程 要麼雖然能類化相關概念做到真到的面向對象,但是感覺不如C的三種流程來得直觀,感到OO很彆扭。(參見stl的寫法和對stl的使用,還有你能找到的OO實現的數據結構庫)

實際上人們已經習慣於用三種流程來表達數據結構,跟類比起來,三種流程加簡單數據類型的語言機制已經足夠。如果再強制他們使用OO來表現。那麼他們實際上做了二件事: 對數據結構和算法能解決的問題做了解決(數據結構) 對代碼進行了抽象(代碼結構)

我們知道OO是C++帶給程序員的抽象機制,對於瞭解這層抽象的人來說,他可以很靈活地寫出質量好,邏輯清楚的類(比如STL的作者,雖然那是template class類,泛類,但我們這裏還是把它作爲OO的類來看待),但是如果回顧學習過程,大多數人會覺得諸如迭代器,type榨汁器這樣的東西是晦澀的東西。 原來C程序員做的事,換到C++程序員(當然,我們這裏指的是真正會用OO來寫程序的人)來做,他必須完成上面的二件事,對於不理解它的人來說,他覺得簡單的語言機制都能解決的問題,爲什麼硬要套上一個class呢?這就是過度抽象給程序員帶來的麻煩。對於瞭解它會使用它的人來說是利器,對於不會使用它的人來說實際上是障礙。抽象的好處是某一個層面上的相對容易性(比如OO將複用維持在public member,protected member等接口上),但是在另外一些層面會對不理解它的人造成更多層面上的迷茫。

面向對象程序員之間很容易寫出質量參差不齊的程序,有的人寫的程序用了大量迭代器之類的抽象概念,有的程序員寫的程序全是class pig,class cat之類的實體概念。對於前面程序員寫的程序,如果不能理解,基本上很難複用(即使有class public member在那裏)。後面程序員寫的程序,有時又會顯得太繁瑣(比如他會把一些不該抽象的東西抽象成爲貓爪子,而實際上並不需要這層抽象)。

抽象給人帶來的好處和壞處並存,對於多人合作的軟工項目來說,一些語言的機制是禁忌,比如模板,他越抽象,就會造成越來越大的混亂,OO雖然統一了代碼形式爲類,但並沒有統一設計和抽象的方法(在同一個應用中,有人會寫迭代器,有人會寫阿豬阿貓)。所以OO並不是銀彈,銀彈是那些能統一人類思想,形成契約文化,經驗的東西(比如我們說小說的那些套路),而不是簡單的class這種面向複用的小技倆。 而在軟工界,除了OO,實際上還存在很多很多其它的過度抽象。

謹以此文自娛。
5.4 真正的設計與編碼
大師的禪語並非無所指,一切都只是因爲我們並沒有拿到那個水晶球而已!!

比如:

不要拒絕接受所有的觀點(當你是某個領域的專家時,你往往是尋求別人的思想跟你已成體系的思想的相合之外,,有取捨地學,,因爲你有自己的理念),,也不要不接受任何觀點(當你對一個領域還是一無所知的時段,,你的最主要任何是無條件理解和接受別人的思想)
學習的四段決
想,,想,,,還是想(一開始是對細節的學習),,,不要一直想(當你有自己的理念時),,,,

思想本非也不該是高高在上的東西,只是我們沒有勇氣跟它平起平坐而已,而且求知的你只是缺少一個求知切合點而已(這就是每個人心中的“實踐認知”,,某個特別的維度認識)。

因爲這三個思想實在太重要了而它們又獨立於所有知識,更並且,它們不被任何其它書籍提到並組織,所以在這專門作爲一章來講解:)



軟件開發過程分設計和編碼,組織這二者(以及對其它細節的工程可控性要求)的工程化方法就是XP啊,UPS啊什麼的,設計階段的工作是可以涵蓋整個軟件開發過程的,而且可以跟編碼同步(一旦已經在寫代碼,那麼你就開始在設計了)或異步(最好還是先想好設計的每個細節)進行,

與設計模式相比,算法體現的是一種更泛化的問題解決方法的說法(或者說它更測重於說明如何實現能不能實現,而設計體現是如何設計,要設計出什麼架構來表達什麼邏輯達成過程),在《領域數學》節中提到的算法是數學界對於問題的Solution(VC7以上的工作空間被稱爲Solutiom).

設計的嚴格意義是廣泛的,,
我們這裏說的設計是指定義某種思想上的架構(軟件架構往往是一種思想構架,然而必須最終在代碼上體現出來,,設計模式也可稱爲架構模式(實際上設計模式可用在大架構或小邏輯上都可)或邏輯模式,),然後在這種架構下編碼構建應用(世俗的眼光裏好像編碼的地位一直要次於設計^^),這不是一種泛思想的活動(雖然嚴格意義上的設計的確是泛義的),而是面向產生類文件的設計,但我們正對事物或問題進行設計,所以在源端是非常不確知的高構,在目標端是一定要產生類文件,

因此編程界(注意這三個字)的設計有三種
1. 基於通用設計傳統的設計,比如採用一切語言級和非語言級的多範型設計
2. 一開始並不直接面向編碼的原語設計,後來才轉爲面向編碼的範型設計
2. 技術上用策略來實現的設計
3. 混合了你自己創建出來的一些架構思想的設計

用原語設計去主導你的設計,再用修飾過的原語設計去主導你的多範型設計(有效的設計應分分這二步),最後才產生類文件。。

現在很多領域都向原語發展(人們正在更好地認識這個世界),,比如下面的一張圖,左邊是設計(偏重思想),右邊是實現(編碼),左邊在不斷地靠近右邊
1.類是對對象的設計,,,模板是對類的設計,

很多現象表明,編程正慢慢向設計偏移,細節正慢慢向它所屬的大領域靠攏



5.5 真正的構件庫
構件是運用了組合的思想之一,然而組合是一種更大的思想,有些時候,組合的個體之間並不需要接口,,只是當它們運作起來,它們便突然構成了這個璀璨的世界。就像這個世界,水,火,大海,高山並不是某個神預謀爲了某種美感而創立的,神只是分別創立它們(沒有留專門的接口吧?),然而它們各自存在而已,然而突然有那麼一天,神創建的人類突然發現這簡單的組合也是這麼美和合理。(請原諒我用了這麼一個不嚴格的神話來描述這種思想,然而作這種泛化思維有好處的)

在編程領域,庫跟構件嚴格上是二不同概念,然而大體上等價,這裏不區分二者。

按使用形式分構件一般有五種
1. C的庫,可能是運行時庫,語言函數庫,用戶發佈的應用庫,OS級的API庫,等等,這些庫由於往往只有函數級的接口,因此只要明白參數調用或入棧規則就可以了
2. C++的庫,這種庫也可以是上述四種庫,這些庫由於是用C++寫成,可能採用了OO,因此其內可能含有某種架構,我們必須懂得其內置的架構才能更好地使用它們。
3. 接口庫,比如COM,這種構件就是嚴格意義上的構件,因爲它直接內置接口定義,需要懂其內置的架構才能更好使用它們,更高級的還有DCOM等
4. 構件套,比如ActiveX(主要用於網絡分發)
5. 類JavaBean直接爲編程環境服務的組件。

這幾種庫一般都用DLL來實現,DLL是一種裝載機制(它可以在運行期動態裝卸,比起靜態庫的另外一個優點來說就是可以避免雙份的庫引入),而構件是構件,這種關係要明白

一個很重要的思想,千萬不能讓庫的架構主導你的原先的架構,,你永遠有你自己的大智慧(在你的應用中你有自己的架構邏輯),也不要做嚴格的學院派高手去研究某個庫的接口實現(設計一個好的庫的架構也是一項大工程),除非你是爲了學習,否則不要去探究它的實現,而且庫的架構不必複用到你的架構中,你永遠只是庫挑剔的顧客與使用者。

按組合關係來分庫存在有平等關係,上下關係,多對一關係,比如有三個庫



如圖,庫A與B,C是上下引用,也是一種一對多的引用(單向箭頭),庫B與庫C是獨立平等的關係(可能BC並不引用各自的邏輯,說它們平等只是相對要引用它們的A來說的)

這種構架就是我們呆會在第四部分提到的總架構,庫A是GameGeneric,庫B是ShowGeneric,庫C是LogicGeneric,當然在每個庫下面還有很多庫引用關係

什麼是上下關係呢?就是說誰更靠近應用誰就是上,平等關係就是它們作爲中間邏輯靠近應用的程度是一樣的(這主要是因爲它們爲共同一個庫提供邏輯,而那個庫更接近應用,比如這裏的B,C對A的關係)

按性質來分有架構庫和工具庫(庫都是中間邏輯的封裝者,然而這中間邏輯也有“是架構”還是“非架構”之分),非架構邏輯就是或工具函數(實現邏輯),架構邏輯就是對一種思想模型的描述,,對其它庫的引用邏輯,使用邏輯(封裝邏輯),通過這種方法,將利用外來庫的行爲封裝爲自己應用的架構

我們在這裏反覆提到架構與實現,那麼到底什麼是架構呢,廣義的架構就是中間邏輯互飾以構成最終應用的過程中,出現的一切中間邏輯間的關係(無論這些中間邏輯有沒有被封裝成爲庫)這就是架構,,,狹義的架構就是指反映設計中某種物體模型的邏輯層次(比如我提到的GameGeneric由表現和世界邏輯組成云云)

比如上圖中,中間邏輯的有窮互飾就會形成最終應用邏輯,但是中間邏輯這個說法永遠都是相對(於最終應用邏輯)說法,越靠近最終應用的中間邏輯越是高級邏輯

要知道,,抽象達成到最終應用中,,需要設計的接口(這裏主要指函數級的)精度和量度都是巨大的(可以用單邏輯的類文件,也可用庫來集成一些邏輯作爲二進制的中間邏輯,但是如果是使用別人的庫,,那麼往往需要作對它的引入邏輯,也即對這個庫的使用邏輯,,也即你自己的接口才是你的應用主體,別人的只是拿來用的(並形成你的應用的接口),,比如Yake對其它的庫的引入邏輯),爲了未來的更好複用性考慮,越遠離應用邏輯的接口,,對它的設計應儘量考慮接口上的放大化

GameGeneric向你透露面向遊戲和腳本級開發者的那些接口(包括它自己的一些邏輯,和一些對庫B,C的封裝邏輯也即入接口邏輯,庫A向用戶封裝了B,C的細節而提供一個大接口給用戶使用,當然庫A可能也有它自己的邏輯和架構),這些接口是直接面向高階應用和開發的需要而創立的,既然GameGeneric建立在B,C之上,那麼在引用A的時候(利用A進行編碼實現或在其上面架構更高層邏輯的時候),B,C是不是變得無可訪問呢?不是,我們其實還是可以在這個步驟中訪問到庫B,與C的內節的(庫的組合邏輯絕非一方屏蔽一方而是一方對一方的歸納簡化和擴展導出)。

這是爲什麼呢?因爲大架構是供你使用架構下的細節的(這後半句話纔是重點),,大架構只是邏輯歸約以更好面向程序員,它是邏輯簡化而非邏輯替換,,而程序員最終要引用的邏輯是架構下的細節邏輯。。

是的,,明白這些有什麼用呢??但是反過來想一想,,如果連這都不懂,,,那麼那纔是你的損失之處呢^^(因爲這是實踐思想,,很多書都不會講到)

5.6 大邏輯與小邏輯
SOA,COM,框架,這樣的術語着眼於解決大邏輯間的開發,而不像OO語言本身只能將邏輯提供到類這一層次(雖然寵大的類也可稱爲大邏輯,但我們這裏僅指有限屬性和行爲的類邏輯)
C跟JAVA是二種完全不同理念的語言,因爲它們的運行環境不相同,產生的目標碼根本不同,前者是爲機器加OS這個本地產生目標碼,而JAVA爲JVM產生目標碼,由於這二個平臺的不同導致的理念是根本的差別,JAVA不能稱爲系統編程語言,因爲這個系統指代OS,C是編譯成本地目標碼的,而JAVA碼只能稱爲JVM的系統編程語言.VB5之後有編譯爲本地碼的特徵,因此是半系統編程語言.
C跟JAVA是二重天,因此對它們的開發理念完全不同,一個是面向本地,一個是面向JVM,可以不管OS平臺的任何差別(這就是說不用學習程序運行環境給語言帶來的機制問題和複雜細節,不用學習關於JVM的任何知識來進行JAVA編程,人們只需關注那些他們要完成的邏輯,而平臺邏輯可以忽視不顧,因爲JVM被移殖到任何地方時,人們將面對毫無差別的JVM和JAVA代碼,代碼不會因爲平臺不同而產生新的要解決的問題,,這是指移殖,,而且,要寫JAVA代碼,也不必瞭解任何JVM的知識,,因爲根本沒有必要爲JVM編程,,JVM雖然能給你進程,,給你SOCKET資源,但是在庫中已經被妥善地封裝了,人們對它固定了一個認識,,不必從JVM中再對它們經過原始理解,再引申爲JAVA所用,因爲關於這些資源的接口足夠簡單了,減少了人們對系統的理解,一談到虛擬機的語言,就要明白這跟C,跟C++這樣的編譯語言是二重天).人們說JAVA並非好的教學語言,因爲JVM畢竟不是我們真實的系統,,它是一種架空的虛擬的完美的人工運行環境,而OS加硬件架構這樣的本地纔是我們要認識的本地,
5.7 什麼是範式
在最開始,可將範式想象成一種特別聰明、能夠自我適應的手法,它可以解決特定類型的問題。也就是說,它類似一些需要全面認識某個問題的人。在瞭解了問題的方方面面以後,最後提出一套最通用、最靈活的解決方案。具體問題或許是以前見到並解決過的。然而,從前的方案也許並不是最完善的,大家會看到它如何在一個範式裏具體表達出來。儘管我們稱之爲“設計範式”,但它們實際上並不侷限於設計領域。思考“範式”時,應脫離傳統意義上分析、設計以及實施的思考方式。相反,“範式”是在一個程序裏具體表達一套完整的思想,所以它有時可能出現在分析階段或者高級設計階段。這一點是非常有趣的,因爲範式具有以代碼形式直接實現的形式,所以可能不希望它在低級設計或者具體實施以前顯露出來(而且事實上,除非真正進入那些階段,否則一般意識不到自己需要一個範式來解決問題)。範式的基本概念亦可看成是程序設計的基本概念:添加一層新的抽象!只要我們抽象了某些東西,就相當於隔離了特定的細節。而且這後面最引人注目的動機就是“將保持不變的東西身上發生的變化孤立出來”。這樣做的另一個原因是一旦發現程序的某部分由於這樣或那樣的原因可能發生變化,我們一般都想防止那些改變在代碼內部繁衍出其他變化。這樣做不僅可以降低代碼的維護代價,也更便於我們理解(結果同樣是降低開銷)。爲設計出功能強大且易於維護的應用項目,通常最困難的部分就是找出我稱之爲“領頭變化”的東西。這意味着需要找出造成系統改變的最重要的東西,或者換一個角度,找出付出代價最高、開銷最大的那一部分。一旦發現了“領頭變化”,就可以爲自己定下一個焦點,圍繞它展開自己的設計。所以設計範式的最終目標就是將代碼中變化的內容隔離開。如果從這個角度觀察,就會發現本書實際已採用了一些設計範式。舉個例子來說,繼承可以想象成一種設計範式(類似一個由編譯器實現的)。在都擁有同樣接口(即保持不變的東西)的對象內部,它允許我們表達行爲上的差異(即發生變化的東西)。合成亦可想象成一種範式,因爲它允許我們修改——動態或靜態——用於實現類的對象,所以也能修改類的運作方式。在《Design Patterns》一書中,大家還能看到另一種範式:“繼承器”(即Iterator,Java 1.0和1.1不負責任地把它叫作Enumeration,即“枚舉”;Java1.2的集合則改回了“繼承器”的稱呼)。當我們在集合裏遍歷,逐個選擇不同的元素時,繼承器可將集合的實施細節有效地隱藏起來。利用繼承器,可以編寫出通用的代碼,以便對一個序列裏的所有元素採取某種操作,同時不必關心這個序列是如何構建的。這樣一來,我們的通用代碼即可伴隨任何能產生繼承器的集合使用。

 

 

新手編程導論(七)


第6章 抽象之數據結構


6.1 所謂數據結構
編譯時是編譯期的事情,運行時是運行期的事情,這裏的時可以理解爲期間(運行期間),也可理解爲空間(運行邏輯關於使用reg還是stack的邏輯),
一門語言同時提供編譯器,和這門語言實現的運行時解釋器,一個編譯邏輯,一個面向某平臺的運行邏輯(針對本地機器的一般稱爲運行時,針對類JVM機器的一般稱爲解釋器,所以jvm跟解釋器是分開的,一個是平臺,一個是平臺下針對Java代碼的runtime實現),

在學習C++的運行期動態OO對象時,我們也要學習一個稱爲rtti的東西,,一切高級的語言機制,都可在運行期探求它的平臺實現細節,如何用stack內存和reg展開,。。這樣纔是深克理解了高級語言該機制(因爲了解了某一平臺下具體實現的細節,而且是最細節的彙編邏輯,因此該語言抽象也被在高層次被體現並理解了)。。

比如C++類的成員函數,它實際上是一種變態的函數。。從他的彙編邏輯中可以看出來,,在壓參時還壓入一個this指針。從這個眼光來看。跟普通的cdel,fast,pascal函數都不一樣
所以說,函數作爲一種機制,有不同的實作品。

runtime的意思在這裏進一步明顯,,實際上不只執行函數要用到stack runtime和stack frame,在執行諸如堆棧隊列數組鏈表樹這些高級數據表示與組織的內存邏輯時(即運行時邏輯,這也屬於運行時邏輯),,不一定直接用到stack runtime機制,,雖然執行函數時的stack機制的是代表機器就是一種堆棧機運行時的典型。。

數組是編譯期就指定的(數組大小固定,因此每個成員都被硬編碼爲內存地址,動態數組並非運行期數組,只是指定數組大小的是一個變量,實際上還是一種靜態數組),鏈表是動態運行時構造的,數組和鏈表都是一種存儲機制,而堆棧隊列數據結構是一種數據抽象機制(抽象瞭如何訪問刪除數據成員的通用接口),數組可以摸擬這二者,當然鏈表也可以。。(這個道理表明,數據結構分存儲數據結構和邏輯數據結構,中心是數據成員本身處理,不可能有離開數據存儲地談數據結構的道理(所以每一種抽象數據結構,必有存儲機制


數組的特點就是可以下標索引,,抽象了對內存地址的需要作的干涉(只需操作索引就可操作數據),從數組中增加刪除一個數據是可能的,但數組本身業已變化,數組增刪一個元素都要經過大量的重新對齊和移動操作(因爲索引得視增加刪除的位置重排),,要形成一個新數組,從這個意思上看,,數組是根本運行期不可變的。它的變化只能發生在它的一個複製品上。 因爲在靜態期被固定了
數組的另一特點是它只存儲同型數據

而鏈表則不一樣,因爲它內置了指針,沒有抽象對內存地址需要做的干涉工作(對於程序員來說,這都是他們的工作,這樣可以動態分配,開闢,增加刪除成員),,對數據的讀寫訪問(我們知道,這是數據結構的中心存在意義所在)沒有抽象上的索引可用,,只能操作指針和底層。。。

對鏈表的插入和刪除是很方便的。因爲只需往往改動一個指針,,所有的改變都是在這鏈表上發生並自更新,

樹是一種可用鏈表和數組摸擬的抽象數據結構,,因爲它內含了大小邏輯,,因此可以索引(左右索引,而不是一般數組的前後索引),,也可指針索引
6.2 算法+數據結構的本質
高級語言編譯器所涉及到的那些東西,就像OO一樣,對於計算機來說,完全是一種迂迴。

因爲高級語言編譯器的出現是爲了迎合人類能讀懂的文本源程序(面向行的編譯器),所以它先提出一套語法,爲了這套語法就造出了正規式,自動機,最終到語言本身這中間的諸多邏輯

而OO和框架(FrameWork)也是一樣的道理,,計算機直接執行的是二進制,,但人類若要組織這些邏輯,就得用OO思想....因爲這是人類工程的需要(對於計算機來說就是迂迴了)

編程設計的本質在於將語言機機制邏輯轉化爲應用邏輯,完成它們之間的變換,

任何語言都有算法+數據結構之說的設計,但C更爲突出,因爲C只有這種設計範式,相對其它語言衆多的語言機制來說(除了基礎流程,類型系統那些東西不說),這更像是C的設計全部,爲C而生的,在用語言機機制表達應用方面,C++有“類”,有設計模式,有模板,有編譯期多態,有範型,當然也有“算法加數據結構”,但C彷彿只有這種““算法加數據結構””,,,是設計的非常原始階段和手段。。
6.4 算法不是設計
(算法更多的不是代碼邏輯的設計,用最小內核的流程控制比如C都可以實現算法跟數據結構),

一種算是通用的解決問題的方法,不限於編程開發
一種算法是算法設計方法,如遞歸
一種算法是計算機的執行方法,圖靈算法,證明算法可行性
一種算法是跟某具體語言結合的數學問題的解答,如雞免問題等
一種算法是設計算法,架構邏輯
一種算法是綜合設計算法,
6.5 函數增長與算法複雜性分析
從這一節開始,我們就慢慢進入算法了,,爲什麼把算法放在這裏而不是放在數據結構那一章呢,,因爲算法是屬於數學和計算機的,,跟它們的結合要更前於數據結構(雖然也嚴重與數據結構有關,但是數據結構是進入到計算機以後的抽象,算法是它以前的概念)

,,先講講什麼是算法,,它的分類與有關證明
遞歸算法,,,分支定界,,二分法,,貪婪法,等都是用來描述算法的(它們本身不是算法,,,只是用來設計算法的方法)
程序正確性

算法的有效性包含二部分,算法的正確性和算法的複雜性,,這裏討論算法的正確性,,複雜性在前面2.2節函數增長部分討論過了

對算法的正確性的研究用到邏輯規則,證明技術(如數學歸納法),算法

不考慮語法等其它因素,,,除非測試了所有可能的輸入,,程序都給出正確的答案,這就是程序正確性的概念

如何把以上概念分解爲形式定義使它具有可操作性呢?

在有輸入的情況下,只要第一部分證明:若程序終止,則獲得正確的答案,,第二部分證明:程序總是終止,就可以斷言這個程序是正確的(二者之一成立是部分正確)

第三章討論算法的正確性,這裏討論算法的複雜性
在假定輸入值一樣的條件下,,求算法的空間或時間複雜性
由於空間複雜性跟數據結構有關而本書不涉及太多的數據結構
因此在這裏主要討論算法的時間複雜性,,即步數
運算次數的單位是整數加法,減法,,等基本運算
6.6 數據結構初步引象(1)
一維數組(向量)
二維數組(包含行向量與列向量的矩陣)
表table也是二維數組,它的行是記錄,列是字段
List有三種意義,VList,廣義表,狹義指linked list,,普通意義下的list表是有序的,typed 的ordered的即線性表(alistis
an orderedcollectionofentities/items),但不一定是sorteded的(Asequenceis 循序而非順序?another
name, emphasizing the ordering and suggesting that it may not be a linkedlist.)
查找表是同型數據的無序(sorted or unsorted)有限序列。二叉查找樹也叫二叉順序樹。
二叉樹遍歷的本質在於動態地將非線性樹轉化爲線性表。,
線性數據結構就是邏輯上有頭有尾的一對一的數據結點組成的結構,,當它用於表時,就是linear list,線性表用順序存儲的方法來存儲就是順序表sequences
list了,注意線性表只指出結點有序(表的意義所在)一一對應(線性的意義所在),至於如何有序它沒有作規定,而數組是一種用下標index來體現’有序’的方式,indexied
的線性表即index list(與linked list對應)=vector,因此是一種順序表的方式(當然index
list就字眼上說並沒有完全地指出它應有的限制,準確的說法是index linear
list)。是順序表最簡單最基本的形式(注意數組是一種抽象數據結構)。與鏈式存儲的單鏈表一樣,是最基本的,因此常有sequence stack,link
stack的對比(而其實數組有多維數組,鏈表有循環鏈表等)。當然這二種表都是屬於邏輯上的linear list.
6.7 數據結構初步引象(2)
數據結構的討論是在抽象了數據類型之後纔出現的(因此數據結構的準確含義是“計算機開發領域中的數據抽象學”),彙編不需要變量是因爲程序員包攬了內存分配,而高級語言提供變量,變量的內存分配由編譯器或運行時完成,因此可在這個基礎上發展基於靠近人的抽象數據結構,而OO既是對數據的一種抽象(當然,它跟數據結構對數據的抽象是站在不同角度的),也是一種對代碼的抽象

數據結構中也有文件的概念,實際上當這個文件(數據結構一般處理數據在內存中的模型)放在外存中(被持久化)就成了數據庫,數據結構中也大量用到字段,記錄,鍵等數據庫概念,,在計算機領域,數據庫和數據結構本來就是同根共源的,數據結構的抽象模式就是內模式,,關係數據庫與其它數據庫的區別在於關係數據庫會返回一個集合,而不是一些特定記錄,在數據結構的討論中,頻頻用到關係代數的概念,比如排序線性表sorted
list.它按照某種順序來組織數據,比如<,這實際上是一種偏序關係。

List有三種意義1 linked list 2 廣義表 3 (ordered) list,其中3就是普遍講到的“有序線性表”,而sequence
list=sequence order list(以順序結構存儲的線性表,區別於linked ordered
list),,,線性表除了有序線性表這種形式之外,還有sorted ordered list.即排序(線性)表。。

如 binary search tree,= binary sorttree

查找表(lookup table?search
table?)是一種無序的集合,這使得只能以順序比較方式操作,因此我們必須對它們強加一些關係,形成sorted的,動態查找表必須動態變化以維持它的表序(即它是一種動態查找表,表是自變化的以維持某種利於查找的順序)。。

鍵可以是一個記錄中的某個字段,或字段的組合,單字段的以這個字段值作爲鍵。。

(一般)樹,森林,二叉樹之間可以相互變換,,,樹可以通過對節點的限制或修飾發展出多種抽象,比如平衡樹,AVL樹,B+(B_的一種變體),紅黑樹。。。當然還有很多,因此數據結構實際上是一門可以無限深入的科學

如果說樹是一種層次關係(樹是嚴格分父子的),那麼圖就是一種鬆散關係,在任何節點間都會產生關係,因此最符合現實模型。

對圖的學習涉及到關係代數,線性代數中很多知識

其實要注意各種數據結構的存儲結構,圖的多重鏈表,樹的最小帶權路徑,,圖的多路查找
等等知識,這些都是高級話題。。

6.8 數據結構初步引象(3)
交換排序是置換順序,插入排序更理應被稱爲交換排序。

雙端隊列是棧和隊列的一種泛化(因爲它的二頭都可以出或入)。棧和隊列跟其它數據結構遍歷不同的是,對於棧和隊列的內部是不可訪問的,只有棧頂和隊列頭
這幾個元素可以遍歷到。

查找表是一種集合,是同類型數據的集合,,因爲集合是一種無結構的無序鬆散排列(表是一對一更,樹是一對多,圖是多對多,那麼集合對這些都沒有規定,實
際上有表,圖,集合三種大數據結構,樹是表的推廣,圖是樹的推廣,但表和樹都有作爲自身存在的意義和作爲樹和圖的雙重意義存在,在所有數據結構中,反而
只有圖纔是用得最爲廣泛的。。
)。。

用位向量可以表示集合(這有點像用鄰接矩陣表示圖),其它方法也可,比如鏈表等,,但位向量表達法在提供了位操的機器上產生的效益是很高的。

圖也有根,還有無序鏈表unsorted 還是unordered??連通和強連通,,,迴路和路徑,,環弧邊,無向圖是無向還是雙向圖,這些都是微妙
差別的概念。。
.
6.9 數據結構初步引象(4)
線性表是最常見的數據結構和邏輯結構,然而只有圖纔是最常用的數據結構和邏輯結構。。

其實樹狀結構有天然的轉化成線性結構的優勢,因此也有作爲線性表的樹的存在
(因此說樹是樹性表的一種推廣,教學的時候完全可以從線性表引伸到樹,,
樹有作爲線性表存在的樹,樹也有作爲樹本身意義存在的樹,和作爲樹圖意義存在的樹,這三種情況都需要被討論。)
此時從樹的眼光來看這種原來是樹的數據結構它還是存在父子關係,從線性表的眼光來看是變換了的平等關係.
6.10 ordered與sorted
Lists廣義列表,list線性表
有些教科書混用ordered與sorted,比如二叉排序樹它並非二叉有序樹,當然,
爲什麼又有無序樹,有序樹呢。

Ordered表示一個接一個,順序不可改變,如果被改變(比如一個元素後接的元素不再是以前那個了),就不再是以前那個ordered list了,
所以如果一個unosorted表被sorted過了,它肯定不再是以前那個ordered list了(但它肯定是ordered的,因爲它要維護它
線性表的特性即ordered不過它當中有些元素的ordered位置換動了而已),而是一個新的ordered且sorted的表。
.
6.11 數據結構與抽象
(1)
程序如何分類呢,從算法和數據結構的角度看我們可以發現,數據結構加算法等於程序。
因爲數據結構源於從一套相似的算法中找出操作對象的共性這個現實

而從複用來看呢,,又可以產生設計和接口就等於程序這種說法

因此這完全是不同事物的不同唯度而已。。根本沒有可比性。(至少二者都可以產生程序這個概念,於是,程序=機器加電也是正確的)

OO化並不強求以靠近現實的模型來設計應用,而更多地是利用OO的訪問控制以同一種方式看待代碼與數據(設計代碼,定義數據)

在命令式編程語言中,控制結構等等被證明爲可以產生一切邏輯。。因此具備三種控制結構的語言都可以成爲產生一切邏輯的語言。。
(2)
抽象完成了之後,只要不是過度抽象,那麼所有後來的事情都是另外一回事了,比如抽象了數據類型,那麼關於數據的邏輯都成了數據結構學了

算法並非代碼邏輯,而只是附屬於語言和數據結構學交界的那些東西(算法是從屬數據結構的),只有設計模式纔是代碼邏輯和代碼抽象學。。

從開發(者)的觀點,我們可以把OS看成是提供API的軟件抽象機制(這本書主要是從開發的角度抽象地講解一系列專業概念),同樣從計算機開發領域的角度看,我們可以把算法看成是數據結構的附屬,,因爲數據結構是源於算法的,而數據結構是開發中的數據抽象,,,,因此作爲從開發眼光來看的算法是數據結構的附屬(因此人們說算法和數據結構是數學,計算機軟件,計算機硬件三門學科之間的交叉學科,)。


在程序設計中,抽象無所不在,OO就是一種對數據和代碼進行統一抽象的方式。。
在架構中,抽象也是很常見的,比如七層模型,只有解決了前面的問題,才能着手下一層更高級的問題,這是目的也是原因,,是起點也是下一個終點。

我們知道抽象的本質在於對人的簡單性,比如OO的三重機制製造的抽象就在於統一數據和代碼,於是產生了複用效益。抽象的本質在於遠離問題,從靠近人的一
個高層角度去解決更高級的問題。

這就是抽象.

6.12 真正的邏輯數據結構只有二種
字符串這麼平常,然而卻需要涉及到不淺的數據結構和IO,,這使語言表達字串方面成爲學習這種語言的一個重要方面。

當然,數據結構絕非僅僅數值數據結構。數據結構不僅用來研究數值,節點數據可以是任何類型。或adt

其實理解很多數據結構(data set 和data relation set)前要理解的東西就是什麼是數據和數據節點,,這纔是最重要的,但很多人忽略了它,那麼數據節點體現了什麼重要概念呢,1是order還是sorted,2是keyvalue的分別,計算機能處理的數據節點是邏輯上存在order的,所以產生無order,即集合,有order,線性list,層次tree,,,另外,計算機能處理的數據節點是key value的,這就是計算機能處理的“數據‘和它能形成數據結構學這樣的科學所加的二個traits,,數據結構絕對不是廣義數據的結構,而是有一定條件的概念(我們人類目前的科學都是在一定的限制下討論某具體問題的)。
明白了以上這樣我們就可以理解一種特別的數據結構了,即hash table,首先它是key value對,如果不是,就是hash set hash map之類的東西,,這滿足第二點條件traits。。。第二,它的hash,是針對數據結構來說的,hash一定要是hash個數據結構出來,而前一些數據結構是uniform的而hash table,map,set之類的都是uniform的而已。這滿足第一個traits.

真正的數據結構其實只有二種,表和樹,因爲按order來看,前者是線性,後者是層次(我認爲只有這二者纔是劃分和形成概念的標準),如果說硬有第三種,那是unordered的,即集合,集合纔是取代圖的概念,而不是圖,圖只是邏輯結構居先
6.12 樹與圖初步引象
樹是圖的一種特例(沒有迴路的連通圖,樹是sparse圖),圖的樹的區別在於,樹是嚴格分層次的(它的前驅是父,後繼只能是子,因此產生出父子,節點的高,寬度,樹的高,寬度等概念),而圖是任何頂點間都有可能發生關係(即存在邊,存在邊就叫鄰接),除了樹,圖之外,還有森林(相比樹對圖的定義來說。它少了一個條件,森林是沒有環,可能不連通的圖,一棵樹也可以是林林,但森林不一定是一棵樹),,二叉樹與一般樹的區別在於1,一般樹不可以無節點,而二叉樹可以沒有節點,2,二叉樹的某個節點至多有二子節點,即二叉樹不但分層次,而且子樹也分順序,因此對於一般樹的邊歷過程有三種,,先(根)邊歷,中(根)邊歷,後(根)邊歷,,但是正是因爲二叉樹有左右子樹之分,因此中序邊歷是二叉樹特有的。

樹和圖都可以作爲數據結構(主要作用在於存數據,附帶一定邏輯的數據)或邏輯結構(主要作用在於表邏輯),節點可存任何東西,比如一個條件,一個值或一個結構都可以。邊也可以加權表邏輯。。比如最小生成樹是最小加權生成樹的簡稱,,注意,樹有最小子樹,圖也有最小生成樹。

如何存儲呢,因爲用圖可以用來解釋樹,因爲決定圖的特徵在於它的節點,因此它也可以用跟圖一樣的存儲結構來表示自己。但是因爲樹是圖的稀疏表示,一般不用鄰接數組存儲節點的每個所有特徵,,引線二叉樹才這樣做(因爲它賴此達到一種維護它訪問的能力)。
.
6.13 樹初步引象
(1)
如果將樹某個節點的從左到右的子樹看成有序的(有左右的order之分),那麼就是有序樹,即ordered樹,但卻不是sorted的,因爲只有二叉排序樹binary
sorted tree纔是排序樹(不過它不是左右子樹之間的sorted,而是左右子樹與它們的根之間的sortness)。

就特徵的逐步放寬而言,存在,二叉樹(order),二叉查找樹(sorted),N叉樹(un
ordered),多叉樹(之所以不把N,多叉樹這二者等同,是因爲我們特別強調N叉樹子樹間不order,而多叉樹根本不考慮這一點,根本不考慮左右子樹之間的關係)

樹爲什麼稱爲遞歸的呢,就是因爲它的子樹也是一棵樹,,而這棵樹的子子樹也是一棵樹,因此被稱爲遞歸定義的。

樹的高度就是深度,因爲根的深度就是高度。(因爲遞歸定義中非葉節點就是子樹,所以非葉節點的高度就是該子樹的高度寬度等特徵,即用根節點特徵來代替子節點高度等特徵)

線性表的線性“序order”和樹的層次“序order”這二者有什麼意義呢,這種意義決定了對於它們的遍歷邏輯。搜索邏輯主要跟sortness有關,而不是orderness有關,線性表決定了它的元素順序是有序ordered的(雖然並不一定經過排序sorted)因此遍歷可按前驅的方向或後繼的方向但不一定搜索一定要嚴格按這個順序因爲那是由sortness決定的,因此樹的搜索方向可先(根)序,後(根)序。而樹的遍歷其實任何訪問都是從根開始,,所謂中序,其實也是從根開始,只不過它處理數據的順序是不跟它的訪問順序一樣的而已。而且它是隻有二叉排序樹纔有的。

兄弟是同一個節點爲父的子節點,而堂兄弟是父節點在同一層的子節點。

通路跟迴路,七橋問題解決的是歐拉通路而非迴路,

最小生成樹是最小加權生成樹的簡稱,但是存在另外一種最小非負加權生成樹的東西,,就是最小代價cost生成樹了。

在樹中,有height balanced tree也有weight balanced tree,這個weight就是一個層上節點的最大值。。

作爲圖的樹的深度優先有時要求回溯,深度優先遍歷時,它從一個節點開始往下遍歷時,在到達葉節點時需要重新向上“溯樹”。
這就需要額外維護一個棧來記錄已經訪問過的節點。當訪問到一開頭那個節點時就“標記已訪問並存入棧”,下次從這個節點的兄弟作下步向下遍歷完成並作回溯時需要將其彈出堆棧,,,以此類推完成遍歷(因此它是一個由樹的遞歸特性決定的遞歸過程)。。

圖的廣度遍歷用到了隊列邏輯。
.
6.14 B減樹
B-tree就是B減樹(因爲存在一個B+樹所以會導致這樣的誤解,實際上並沒有B減樹這個概念存在,-號只是一個連字符而已也可寫成B_),也不要跟
二叉樹binary tree混淆了。

那麼什麼是B減樹呢,一般說它是Bayer’s tree的縮寫(bayer是這種樹的創建者之一的名字,另外一種最常見的命名方法是
b=broad,bushy,因爲這種樹所有“外部葉節點”在同一level上聚集)。它適用於樹的內部節點被頻繁訪問,但又一般不常訪問這些節點以下
節點的情況,比如計算機的二級存儲器硬盤等設備,常以結點爲目錄,葉節點爲文件,,一般我們頻繁訪問目錄但又不常深入最終每個文件。

實際上b減樹也是要求一定的balance的(有人也說B減樹的B代表balance,不過這樣的話就與普通的平衡樹相重意義了所以並不常採用這
種),,它是一種動態查找樹,因爲需要在每次update後都動態調整它的節點情況。只不過它並不需要作特別頻繁的rebalancing動作,因爲B
減樹將外部節點維護在同一層次上這個特點使它只需要只很少的調整便可保持balanced。

那麼B減樹到底具體是如何一種數據邏輯呢?它到底如何使保持平衡只需很少的調整呢?更重要的是,B減樹到底有什麼用呢?

首先,這種樹的某個(或每個)“內部非葉節點”的下層子節點(直接子樹)是數量可變的,而且在一個區間裏變動(我們呆會再來討論這個所謂的區間),這個非葉節點維護
一堆elements和pointer,其中elments用來區別各個子樹的取值範圍,比如這個非葉節點有3個子節點爲根的子樹,那麼它需要維護2個
(子節點數-1個)elments,假設爲key1,key2,那麼第一個子樹的所有值都小於key1,中間子樹的所有值都在key1和key2之間,
最右邊子樹的所有值都小於key2(當然,這是N叉樹,3叉樹,這裏不是說二叉樹的左右),很顯然地,key1,key2這些elments是
sorted有序線性表,那麼points部分呢,它指向每個子樹..有幾個子樹(子節點)就有幾個pointer.

所以,每個內部節點維護(該節點所擁有子樹數-1)個的elements和(子樹數)個的pointer.
葉節點在同一層上,沒有element也沒有pointer.,不帶任何信息。

現在來討論那個所謂的區間,以m爲order的B減樹(稱爲B-tree of order m),再設n是一個內部節點允許的最少節點數,那麼那麼區
間就是[n上界,m下界],以這種區間爲表徵的B減樹一般呈現出以下特性
(1) 每個內部子節點(只要不是作爲該子樹的根或葉子)都至少有m/2個子節點;
注意,最多m,最少m/2;此時n相當於m/2
(2) 每個內部子節點(如果它就是作爲子樹的根並且不是葉子)那麼最少可以有2個子節點;
這說明,該內部節點最大子elments數可以爲m或m-1,最少elements數可以爲m/2或m/2-1(n=m/2或n=m/2-1);
(3)每個內部子節點(如果它是葉子),,,那麼不帶任何elemnets或pointers

所以,“子樹數-1個elements”,“子樹數個pointer”,“葉節點不帶任何信息”,“n-m的區間所體現的上面三點”,,這些都說明了什
麼呢,這些特徵都賦於了這種樹什麼樣的特性和能力呢?

我們可以看到,如果內部節點不是作爲最終葉節點,那麼它(要討論的這個內部節點)的子節點(它的下一級子節點)個數至少是半滿half full的,這
意味着,在[m,n]區間內(或稱[m/2,m])二個半滿的內部節點可以進行合併形成一個合法的新內部節點。一個全滿的內部節點可以分離成二個合法的
新內部節點(只要父節點中可以容納這些新子節點就可以),從這層意義上,的確不需要太多的高度上的平衡。

更多的關於這種樹的刪除,插入算法可以從這層意義上引申而來.
6.15 圖初步引象
在數據結構中,我們一般是從最普通的情況談到施加了各件條件和限制的情況),比如有向圖是無向圖的一種特殊情況(施加了有向這個條件,而無向圖是一種更
一般的圖,因此圖論中一般是談無向圖及遍歷等操作,再談有向圖及其特性。(特別DAG)。

線性表是數據項平等的集合(抽象數據的最高境界最一般情況是集合,因此在對各種ADT進行定義的時候都涉及有集合),無論對其進行什麼操作,只有維護一
個線性關係而不管它是無序還是有序的,就是線性表,而樹的各數據項是有層次level的,無論對其進行什麼變換,只有作爲樹的眼光從根向下來看,它總存
在邏輯意義上的父子,這層邏輯意義是作爲樹的意義存在的,在對樹的各種操作中都考慮進去了作爲處理時的因素的),

因此從邏輯意義上來說,樹是線性表的推廣,,,圖卻不是樹的推廣,,,因爲雖然它也處理數據項集,但它處理的是數據項間連通關係而不是地位關係,在其各
種存儲表示和後來高級邏輯中都會加入鄰接考慮。

如果說其它數據結構只有數據項集,那麼圖不但有數據項集,而且有頂點連通關係集。參見對其ad t的定義就知道了,,而顯然連通關係並非前驅後繼關
系,,在關係代數中,,存在有全序偏序對稱自反等關係。。

也即在圖中,結點的地位關係我們不考慮,不存在線性平等也不存在樹型父子,而是不考慮這樣的地位關係,這一點上它像集合,(集合,是不考慮結點之間地位
關係也不考慮連通的),圖只考慮一種稱爲連通關係的不倫不類的關係,這一點上它區別所有的數據結構,它的二個頂點間可以連接,這一點跟樹相同,但是頂點
並不經常用作存儲數據用,這一點又跟樹不同,樹的二頂點存在的是地位關係,而圖的二頂點要處理的是連通不連通關係。。

因此我們規定,邏輯數據結構只有二種線性和非線性,只要不是線性,那就是非線性,而不管它是節點地位關係導致的非線性,還是其它關係,比如連通不連通導
致的非線性(其實這二者無關,因爲圖也可用矩陣來存,,而矩陣,雖然是一種非線性結構,但以某種眼光看,它又是線性的)。

數據結構也可是邏輯結構,樹可作爲數據處理結構也可作爲邏輯表達結構比如流程,因此圖不但是一種數據結構而且還是一種組合數學中的離散結構(比如拓樸學
就是關於有向圖的)。。。.
6.16 樹的平衡與旋轉
一般教科書把廣義表跟(多維)數組放在一起講解,是因爲有時泛意義上的數組就是廣義表的一種。

如果你知道函數對於結構化程序設計的重要性的話,那麼你也會明白堆棧對於函數調用邏輯的重要性,所以理解結構化程序設計範式的重要手段是理解堆棧這種ADT。

相比線性表O(n)的複雜度來說(輸入的元素個數是最主要的影響因子),對二叉樹的各種操作(插入,查找刪除)效益直接受制於高度,即h = O(logN),即樹提供了O(logN)的複雜度,其中N是節點數,(二叉樹中)我們並不直接把N作爲影響操作效益的因素,h纔是,因此需要對height進行balancing才能控制操作效益。

BST的構造是嚴格取決於待輸入系列的,當待輸入系列本身就是一個sorted的系列時,那麼當這些序列被用來構造BST時,它就是會退化成對應的“鏈表”。在操作上會失去樹的優勢。

一般存在“節點的深度”,“節點的高度”,“(子)樹的高度”這三種說法,節點的深度是從根開發,到這節點爲止的那條唯一路徑的長,節點的高度就是從節點出發,到這個節點能到達的某個葉節點的最長路徑的長度,因此如果這個節點是內部節點,那麼其深度就是其所在層次(葉節點時爲0,不存在時爲-1),其高度就是從這個內部節點到與它相連通的最長路徑的那個葉節點的長度,當爲根節點時高度最高,當爲葉節點時,高度爲0,而樹(或子樹)的高度就是以此爲根的那個樹或子樹的節點的高度。

實際上avl樹只是自平衡二叉樹的一種,高度差爲一是這種樹的最古老定義(self balanceing並不僅僅是heightbalancing 動作。而AVL僅僅是balanacingthe height),還存在其它不以height的調整爲其平衡方式的樹,比如紅黑樹,但是隻有AVL樹是嚴格用平衡因子去定義的(因此是“平衡”的最正宗意義所在),而紅黑樹不是,因此紅黑樹不是平衡二叉樹,實際上它故意允許一定程序上的不平衡。

紅黑樹不是平衡二叉樹但它“保證”2*O(logN)的最壞情況下的平衡度(人們往往根據這個原因把它歸爲跟AVL一類),AVL樹是1.44*O(logN),這二種樹只“保證”(注意只是保證)最差情況下的下界爲1.44*O(logN)或2*O(logN),稱它們爲平衡樹不是因爲其內部是不是極力在保持平衡還是不是,重點在這裏(最壞情況下保證了多少的平衡度),,綜合考慮插入,查找,刪除等操作來說,紅黑樹的效益要好於AVL,因爲AVL是嚴格平衡的因此只對查找intensive。查找時只需向上查找1.44*O(logN)個節點就行(經過AVL平衡的樹只需對height bounded at 1.44*O(logN)個節點進行處理)。。

一個節點的平衡因子是左右子樹根節點深度之差(注意並非高度之差),因此不要跟這個節點所處的深度本身搞混了,一個只有左子樹無右子樹的樹,它的樹根的深度是0(樹根的高度越到根越大,樹根的深度(相對整樹來說)永遠是0空樹爲-1,這個深度並不影響樹根的平衡因子),平衡因子是1(只跟左右子樹的深度有關),因爲右子樹不存在,深度爲-1,(而左子樹深度爲0),因此它們差的絕對值爲1,,即平衡因子爲1.

在建立AVL樹(向一棵普通意義下的binary sort tree進入插入一系列key碼)時,邊插入邊判斷(插入點的平衡因子是否導致了不平衡),在發現因爲插入動作導致的不平衡現象時進行rotation.,以維護其平衡性,直到所有的數據插完,這樹依然維持binary sort性質(因此稱sort tree爲查找樹,,即查找特性intensive的,作爲數據結構的樹,其實它的本名最開始是sort tree然後纔是search tree,searchtree一般有二種,第一種是每個節點都含key 和value的,所以它的每個節點都包含數據,而有些search tree只有葉節點含數據,其包括根在內的內部節點都用來search,只包含有key)和heightbalanced 性質。
那麼怎麼樣進行判斷並在需要的時克rot(根據情況不同有時是二次rot)呢? 在這之前,我們需要規範一些概念。1.rot所涉及到的主體(插入點和支點privot)2,最小不平衡子樹。3, 扁擔原理。


最好的方法是舉個例子。
比如我們將考慮把{20,35,40,15,30,25}製造爲一棵binary sort並heightbalanced tree,,當插入20,35時,20爲根(第一個插入的當然作爲“整棵樹”的根),35爲其右子樹(因爲binary sort要求它作爲20的右子),當考慮插入40時,40>35也應該被插到作爲35的右子樹,此時雖然binary sorted了,但是卻不height balanced,因爲隨着40的插入它將這(整棵)樹變成了不平衡的樹(明眼人一看就知道這屬於明顯的四種不平衡情況中的RR型不平衡,當然爲了學習的目的我們還是打算仔細地說明一下,而且存在比這樣的僅僅由三個節點構成的RR型不平衡的情況更復雜的情況,需要我們考慮一套更通用的處理方法,在這裏第一個R是指整樹的R,即35,第二個R是指整樹的R35的R,即40,顯然地,40節點處的深度爲2,35節點處的深度爲1,20節點處的深度爲0,但在這樣的樹中35沒有兄弟,整樹沒有左子樹,而40也沒有兄弟,即35沒有左子樹,這導致三個節點的平衡因子不一樣,我們從上到下再一層一層考慮一遍,,首先拿20來說,它的右子樹即35深爲1,而“整樹”左子樹不存在爲-1(這就是第一個R,因爲只要左子樹爲-1就可能產生深度上不平衡的劇變開始),因此20平衡因子爲2,請時克提醒自己牢記這是棵二叉樹因此只有L,R的情況需要被考慮,,再看35的平衡因子,40作爲35的右節點也作爲整樹的葉節點,其深度爲0,而35的左子樹不存在爲-1(這就是第二個R,這裏討論的是35的R而非跟前面那個作爲整樹R的R,,這第二個R的左子樹也不存在,因此提出這個R,,因爲它也有可能產生深度上不平衡的劇變),因此差絕對值還是在1之內,既然出現了不平衡,而且我們也知道爲什麼不平衡(就是整樹根處平衡因子超出了1違規了,,由於前面那二個RR,終於在這裏產生了真正的劇變)。那麼接下來就是處理掉這個不平衡了。我們不妨細化這個不平衡到一個稱爲“最小不平衡子樹”的地方。然後再考慮處理之。

即從插入點40的眼光來看,我們可以找出一個 “最小不平衡子樹”, 不平衡的地方一定發生在這裏(這個“最小不平衡子樹與插入點相關,因爲它就是距離插入點最近,而平衡因子卻產生違規的第一個節點爲根的子樹”),我們需要對其進行平衡處理主要在這裏進行。(在上面的例子中,是“整樹“作爲“不平衡子樹”。因爲20處出現了違規而且那是整樹的根, 這裏提出了20這樣我們就可以開始着手解決開篇提到的處理主體的問題,即一步一步導出“最小不平衡子樹”這個概念相關的插入點和支點,下面我們談到扁擔原理);

對這個以20爲根的“最小不平衡子樹”的處理涉及到“扁擔原理”,在用扁擔挑東西的實踐中,如果前面重我們就把支點向後移動,如果後面重我們就把支點向前移動,(雖然都是以“支點”移動,但挑扁擔中是移動,支點是我們的肩膀,在這裏我們是作二叉樹的平衡處理也稱爲“旋轉”,那麼樹旋轉中的“支點”呢,是不是也可以找到能類比的概念呢?是不是就是最小不平衡樹的樹根呢?),首先我們要明確提出“最小不平衡子樹是爲了確定作平衡處理的範圍”,而找出“支點”卻是另外一件事,(與找最小不平衡子樹的目的不同,找支點是爲確定從何處爲軸進行旋轉,我們找出了最小不平衡子樹並不意味着這裏的“支點”就是最小不平衡子樹的根,-------其實上面情況中的支點即RR的第一個R35,,我們能找出它,是因爲在RR模型中,這樣的模型所在的最小不平衡子樹中必定存在三個節點,即由“最小不平衡子樹的根節點”,“根節點的右節點也即第一個R”,“再就是第一個R的再下面一級R即第二個R”這三者組成的模型中,“支點”就是第一個R(而不是最小不平衡樹的根),上面問題中,顯然35纔是那個“支點”,至此我們找出了支點,接下來判斷哪裏重的問題),“重”即“支點需要往支點的哪個方向移動”這個邏輯。

那麼究竟往哪裏移動呢?根據挑扁擔原理,當然是往重了的方向移動,但這裏有二個“重”的邏輯,首先第一個,我們知道20不是支點,20的右子樹深1,而左子樹深-1,這裏存在一個以20爲中心但左重右輕的情況,那麼它是不是就是“重”的意義所在呢?顯然在這裏是右邊過重了(平衡的情況是要麼左子樹大右子樹一,要麼右子樹太左子樹一,這裏卻大了二下,重了二下),如果這裏是“重”的意義所在,支點就需要往輕的地方即左子樹方向移。

再來看第二個重的意義,在RR模型中,如果說第一個R是支點,那麼顯然地比起上面提到的第一個“重了”的意義來說,這裏的重是指代第一個R的父節點平衡因子過大爲2,而R的R即第二個R爲0,這也產生了左重右輕,(RR模型中,第一個R的父節點是產生違規的所在,正是它帶出了RR,這三者由於旋轉的需要又產生了“過重”,又需要找出一個“支點”進行平衡處理,這個道理也很自然,而且找出的支點是嚴格以平衡因子來決定其左輕右重或右重左輕的),但是顯得有點蹩腳,因爲這裏的支點並不嚴格相當於現實生活中挑扁擔的支點,,這裏的支點完全是從一種模型中比如RR模型中選中的第一個R),這雖然成立,然而,以這樣的意義導出的支點再層出的“重”不是上面第一個意義的直觀的含義,是不太符合現實中扁擔水平移動那麼簡單自然的道理,這裏的重是考慮了“R的父節點”+“第一個R”+“第二個R”的,因此是一種有層次的樹的模型(而不是對應於扁擔加扁擔二邊的重物那樣選肩膀就可以作爲支點的模型,因爲我們上面談到的第一層重的意義不是支持支點移動的意義所在,,這裏的是只需要從RR模型中選擇前一個R作爲支點,而且其旋轉是一種左傾主義的旋轉)產生的“非水平過重(從樹的旋轉圖中可以明顯看到)”。。。


但反而這裏的“重了”纔是“重”的真正意義所在, 在這第二層意義下,我們再看詳細的例子就會理解它們了。

這樣我們就解決了開篇提到的“扁擔原理”,綜上所述,我們討論了RR的情況(如何判斷違規以及如何作重平衡處理),,下面談到的是LR的情況了。請自行理解。.
6.17 完全與滿
Vector之所以稱爲index list,,我們知道index是索引式存儲,list是邏輯結構,於是四種存儲結構和四種邏輯結構可以組合到16
種組合方式。而index list正好是這其中的一種。

滿二叉樹的概念容易弄懂,而完全二叉樹(二叉樹都是從左到右有序的)這個概念實際上並不突出“編號”,它表現的是這樣一種樹:如果在某一層上,左邊的節
點爲空,那麼(在這個節點)右邊就不能有節點,無論是這個節點右邊的節點是個兄弟節點還是堂兄弟節點。所以滿二叉樹一定是個完全二叉樹,而完全二叉樹不
一定是個滿二叉樹。。

樹的遍歷分先序,中序和後序,又分遞歸遍歷法和非遞歸遍歷法,故有6種遍歷方法(也許只有前序才能遞歸?因爲它是根,只有根纔有遞歸意義?)。 當把對樹的遞歸遍歷轉化爲非遞歸遍歷時需要一個輔助棧外加幾個循環
(我們知道加棧是遞歸算法轉化爲非遞歸的通用手段之一)。

完全二叉樹中的完全跟完全(無向)圖中的完全以及完全有向圖的完全意思是不一樣的,前者並不是一種規則形狀(比起滿二叉樹來)而後兩者是規則情況。

因爲連通圖對無向圖有意義但對有向圖卻沒有意義,因此對有向圖引入強連通分量的概念。
.
6.18 多路234樹與紅黑樹的導出
數據結構間都是有聯繫的,比如234樹其實可以導出紅黑樹也可導出B樹。

紅黑樹也是一種二叉排序樹,因此一方面保證了查找效率(中序遍歷時可以快速到達根部),另一方面,它也保證“最深的深度不大於最淺的深度的2倍”,這使得紅黑樹有一定程度的balance 特性,因此對於查找之外的另外操作,它也有很不錯的效率。

紅黑樹主要是通過爲每一個節點着色來達到以上特徵的
1. 首先一個節點要麼是紅要麼是黑只有二色可以被用來着色。
2. 根節點默認爲黑色
3. 對於葉節點來說,不管它的父節點是黑色還是紅色,一律着色爲黑色。(每一個葉節點我們都可以假設它是一個內部節點因此有有二個不存在的子節點設爲NULL)
4. 對於任何一個節點來說如果它是紅色,那麼它的二個子節點都爲黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點,因爲從葉子到根的路徑是唯一的,也即它就是從根到這個葉子的路徑,因此它是這個葉子的深度)
5. 高度方向上(注意這裏不談到具體的高度,而是指高度方向上),從任一節點到其每個子孫葉子的所有路徑都包含相同數目的黑色節點。

以上五條特性導致了

深度上,“最深的深度不大於最淺的深度的2倍”, 從根到葉子的最長的可能路徑不多於最短的可能路徑的兩倍長。


我們來推導一下上述性質,並規範一下“從節點到根,從根到節點,從節點到葉節點”這些說法的準確含義。(注意有時候高度就是深度,當一條邊的層次決定它有多少個點附着上面的時候,這也就決定了它的高度和深度具有同一性)
.
6.19 快速排序思想
快排是一種交換排序,這種交換髮生在待排序列本身之上,首先,將序列按中間位置分爲二部分,隨便從整個序列中取一個臨時成員,這個成員就是“待排”的對象,這個待排對象完成它的一次排序後,那麼整個快排也就完成了,因爲它最終到了它應該被排在的位置。

取出這個待排元素後,以中間位置爲基準,分別以從左趨向中間,從右趨向中間的順序找二個對象與這個待排對象比較,,,這所謂的二個對象是滿足條件的(從左趨向中間找的元素要大於待排對象,從右向中間找的元素要小於待排對象,首先是從右向中間找,然後纔是。。),而且可能有多個(顯然在二邊可能會有多個大於或小於待排對象的值),,所以這個過程要持續幾次,,這個待排對象才能最終到他應該去的位置。

那麼這是一種什麼樣的“交換”排序呢,我們知道待排元素所在位置空出後,,從右邊找的值填充(右端第一個大於待排對象的值)這個位置後,那麼這個值所在的位置我們置空它,到這裏爲止,待快排對象原來所處的位置被填充,,只是它的值尚未被放入一個確定位置,而且,這裏又空出來一個位置,,就是在右端比較的那個值填充待排對象原來空位後空出來的位置。

現在在左端進行與待排對象比較的過程,同樣在找到第一個小於待排對象的對象後,將這個左端這個空出來的位置空出來,將這個值填入上面右端比較時空出來的位,那麼到現在爲止,第一套左右值被找出來了,而且到現在爲止,待排對象沒有被置入一個位置(因爲只是比較了第一套左右值),而且左端又空出來一個位置(但是此時還不是結束快排的機會,因爲還存在其它的左右值對可供與待排對象比較併產生新的空位—左邊或右邊的)。。、

那麼在進行第二套,最後一套左右值比較時,一定最終會多出一個左空位或右空位,此時將待排對象置入,,就算完成了快排。
.
6.20 數據結構之數組
數組是一種順序數據結構,它本身獨立作爲數據結構來用時,也是一種線性結構,然而它用來模擬樹時,或用來實現其它數據結構時,那麼這些形成的數據結構就談不上是線性結構了(當然作爲底層的數組它還是順序的)。。

線性是針對邏輯數據結構來說的,數據結構可按邏輯結構和存儲結構分類,當數據結構按邏輯來分時,有一種是線性的,因爲數據結構總有結點,線性數據結構中的結點存在線性關係,即每一個結點都有且只有一個前驅和後繼,這種現象可用一種關係數學的表達符來表達(關係數學是關係數據庫的數學基礎,而計算機的數據結構學跟數據庫學又是相通的)。。當然,頭結點和尾結點除外,與線性對應的當然就是非線性了,比如樹,除了根節點作爲沒有前驅的之外,包括根節點的任何節點卻都在邏輯上(作爲樹的邏輯)有二個後繼,還比如圖,任何一個節點都在邏輯上可以包括多個前驅和後繼,,,邏輯上都有前後之分只是線性的是一個對一個,而非線性的是一對多,或多對一。。

(下面我們用結點來表示邏輯數據結構中的獨立項,用節點來表示存儲結構中的獨立項,用記錄表達索引邏輯的獨立項,這是三個完全不矛盾的詞,),

當數結按存儲來分時,有一種是順序的,即它申請了一塊連續的內存來構見自己(至於它是用這內存實現了線性的還是非線性的邏輯上的數結我們不知道),,數組用存儲結構的相對位置來表達它的邏輯結構(這就是索引),,數組可以獨立成爲一種抽象數據結構(這就是普通的以數字爲下標索引的數組),,也可以用來實現更高級的抽象數據結構(比如樹)。。

按存儲來分時,還存在鏈接型數組結構,索引數據結構,離散(散列)數據結構,其中順序的當節點沒有使用完全部空間,它就顯得有點浪費,而其它的還行。。下面一一介紹。。

鏈式的很普通,索引式的就是系統(你的應用邏輯)維護一張索引表,裏面有各個記錄(我們稱要索引纔會有記錄)的記錄,比如一個記錄的長度,存儲位置,等,通過查找表,就能找到記錄。

而散列的,系統維護一個函數而非一張表,函數體的邏輯計算出“記錄的存儲位置”和"key(KEY就是索引邏輯面向用戶的一層)"之間的對應關係。。

所以無論是順序,鏈式,散列,索引表,我們都是在進行一種最終到記錄的存儲位置的索引工作。。數組名[下標]這樣的一個形式,用計算機的眼光來看是內存地址,用人的抽象來看是索引工作。。而無論是順序數組,還是鏈式,索引,散列,都是通過某種抽象形式(下標,,全局表,函數)來最終尋找到內存地址。。。
這個道理就像,我們通常不用&變量的形式來獲得指針的意義,而是用*,因爲&是面向內存的,而*是面向用戶的抽象.
6.21 數據結構的抽象名字
在C中提供了很多支持數據結構和算法的元素,比如數組,鏈表,指針,struct,typedef,相比其它語言,c還提供了比其它語言的更好的支持數據結構的語言機制。。特別是,C中的一些語言機制,如位移(其實大都機器提供的位指令並不直接操作位,而是對整個字節進行位操作,進而間接控制所需要的位,我們常常通過對原字節進行位移的方法得出bit mask,再將這個bit mask跟原字節進行與,或,異或,最後得出需要的效果,比如提取1位,消去0位,顛倒特定位),跟搜索算法中特定一些算法直接相關,,相比之下,其它高級語言的數據結構都是基於高級語法結構的.甚至於像lisp這樣的語言就將adt內置爲其一級類型。

算法源於用計算機去解決實際問題,對由研究算法過程中出現的數據結構問題的研究也是一個很重要的工作,特定數據結構和作用於其上的算法們,就稱爲一個adt..

有時,上層的抽象adt可以有自己的一套算法獨成一種adt,與它們的子adt一樣,,,,有時,adt之間進行某種意義上的組合形成新的變種adt,,,又有自己的一種算法,如果組合了2種adt,那麼就是一種帶有2種意義的adt,有時,一種adt以另一種adt作爲實現,自己作爲一種抽象,,這樣的方式演化成一種新的adt.我們必須明白這所有抽象叫法之間的關係,明確他們其實所指的東西有很大的不一樣。。而且要明確這些叫法之間的特指與泛指的層次關係。。誰由誰通過誰抽象得來。


我們一般稱線性表爲list,或者sequences,但其實後者比前者意思更清楚一點,因爲一般list就是指linkedlist,這字面上的相似給我們理解造成了麻煩,而sequences正好指出了線性表的二個方面,(1),sequences這個詞意思是序列,list不但是一種序列,它更是一種有序序列,即ordered list,這就是說,前面一個元素是接着後面一個元素的,可由相鄰的節點按一定邏輯到達下一個元素,雖然list不一定是sortedlist(各個節點按數組順序或字母順序排列),但它至少是ordered list,(2),sequences很好地與linked list在名字上區別開來,它跟linked list是泛指和特指的關係,除它之外,數組,集合,隊列,堆棧這樣的adt都是list.



關聯數組包括普通數組,關聯數組是一種可以被稱爲廣義數組的數組,我們知道關聯數組是key value索引對,只是它的key可以是任何類型,而普通數組只是interger,,,關聯數組有很多抽象,甚至只是別名,比如在smalltalk,objectivec,.net,python,realbasic中被稱爲dictionaries,在perl和Ruby中被稱爲hashes,在C++和Java中被稱爲maps,在commonlisp和Windows powershell中被稱爲hashetables,在php中存在關聯數組,只是索引被限制成整型和字符串,這就變成普通數組和字典了,在lua中唯一隻有關聯數組這種數據結構,被稱爲table,這也是關聯數組比較正統的稱法之一,

我們知道集合也是一種關聯數組,不過它把key value對中的value給忽略了,把key作爲value,從有keyvalue對這一點來說它像關聯數組,另外,它的索引就是1到n的某個子集,從這一點來說又像普通數組,聯繫一下bit vecotr.

數組跟向量這個說法的關係是什麼呢,其實向量vector一般被實現爲動態數組dymic array,我們知道靜態數組的空間是在編譯期被分配的,那麼動態數組就是用malloc函數在運行期構成起來的數組邏輯,我們知道數組能線性時間隨機訪問,但是不好重建和插入,因爲需要移動插入點後的所有元素,比起linked list來它的索引字段並非指針,而是直接對應內存位置的硬編碼索引,因此比不上linked list在發生插入時可以僅僅通過轉變指向的方式就可以實現數據結構內部的重建。。而動態數組就是這樣一種在運行期有優化了的重建能力的數組邏輯。。

vector的學名叫向量,也即數組index list的一種,linked list也即鏈表而非索引表,跟數組有關的不只向量,還有隊列,堆棧關於數組的表示,甚至還有樹,圖,矩陣(一種多維數組,C語言中把foo[m][n]看成是foo[m*n]的一維數組),關聯表等,除了數組是實現之外,,其它的叫法,它們有些不是指同一個東西(比如向量跟矩陣是不一樣的東西,數學上的向量是矩陣中某維爲1的情況下的子矩陣,是構成矩陣的量,向量是向量空間的概念,而矩陣是線性空間的概念,不過他們同構,於是在運算上涉及到了一起,數據結構上,一般vector就是一維數組,而matrix是多維數組),,有些是一層一層的抽象關係(比如vector是index list的一種,indexlist又是順序列表的一種,圖有一種形式是樹,而樹又是bst的基礎)。都是建交在C數組這個語言要素上的抽象(前者是實現,是存儲抽象,後者是高層抽象叫法).
6.22 真正的ADT
在講解算法與數據結構的教科書,有一種語言抽象機制屢屢被談到,這就是ADT。

類是真正的數據,,比類的成員數據更能代表數據的意義,我們的程序就是一個一個的數據,類是用用戶的觀點來表達現實生活中出現的各種各樣的數據的最好方式,因此會有面向對象數據庫的出現(數據庫技術中,關係模式的下表達的數據給人的引象似乎是它是爲專門的數值數據而建立的)

ADT就是指封裝了事物屬性跟作用的指代物本身,它的屬性和作用是ADT之所以爲ADT的意義所在。即數據類型不再是基本類型,而是結合了數據跟代碼的抽象了的模型(是一種數據類型)

人腦往往不適於長輻記憶,因此也需要對象來表達一些數據(類型)或事物.(抽象抽取對象的可用部分),再在這裏抽象上構建更爲高層的抽象。也即,從問題到解決不是一步而就的,而是一層一層通過OO來建立抽象達成的,這就是多範型的“方案領域”和“應用領域”的概念。

多範型開發讓你在高於面向對象的範疇裏看待軟件工程,所以真正的編程學習過程是要明白設計在先,這是基礎的思想,然後去學習編碼,,(以上二點都是方案領域)然後再去研究編程領域對現實問題的解法(這就是應用領域)

而且如果你知道可複用和可擴展對於軟件工業來說是多麼重要的一件事情,,,你就會知道面向對象是多麼好的一種機制了(面向對象和麪向構件,面向構件可以不用對象的方法,,但是很明顯面向對象和麪向構件都有一個共同點那就是它們都提供了可擴展,而這就促進了可複用)

數據接口是另一個很重要的概念

接口實際上就是將要用的部分作爲某個接口提取出來(就是定製要使用的實現的某個子集而已),而所有實現就是接口下的所有代碼,即數據本身,故其實數據與數據接口是分離的

設計模式不致於使我們聲明對象的過程變爲硬編碼,,,這樣就使得整個軟件的對象生成是動態的,這個過程就如同動態分配內存,尋求一種好的方式來組織類的過程稱爲設計模式或策略

接口這個概念其實無比自然,無論是爲了隱藏實現的安全考慮,還是爲了更好地讓這些實現爲用戶所用這個方面來說,接口都是必需的,

接口幾乎改變了現今我們開發軟件的方式
舉個例子,在現實生活中,經理人可以賣票,,,個人可以賣票,國家也可以賣票,,在面向對象的範疇裏(注意這裏並非指某個具體的面向對象設計),我們一般是把國家,個人,經理人都各聲明爲一個對象,再爲它們各自添加一個“賣票”服務,,但是事實上,我們需要的僅僅是賣票這個服務,(如果這個服務被包含在一個發佈的第三方代碼庫中爲我們所用),我們只需提取這個可用部分,,而不是要知道提供這個服務的各個提供者的細節(還好上面只是列舉了三個對象提供了這個服務,,然而現實生活是複雜的,還存在成千上萬個對象也可以提供這個服務),,因此,對於複用者我們來說,我們需要的僅僅是賣票這個服務,而不需要知道賣票服務這個背後的情況(而且,對於發佈這個第三方代碼庫的第三方來說,這可以更有效地隱藏它的實現細節)

接口就是把我們所需要的對象部分封裝起來提供給我們,而把我們壓根不需要知道的關於對象的細節隱藏,,,也就是說,,接口是關於一個對象如何能被使用的形式的封裝者(一個對象可以有多種形式被使用,因此可以一個對象提供多個接口),是真正的對象(實現)和外界複用的包裝器和橋樑,也稱適配器(即接口就是直接面向使用的中間件,或封裝了給二個不同架構提供適配作用使它們能協作運轉的中間邏輯)
.
6.23 Vector的觀點
vector就是數組的數組,我們知道邏輯上的多維數組必須轉成一維數組的方式被存儲,C中的一維數組是地地道道的數組,,這個意義上的數組是實實在在的,,因爲它們是按內存線性地址存儲的,,所以明顯地,,C語言中的其它泛數組的adt都是抽象的,,從C用一維數組實現多維數組這個意思上,可以看出C語言的確是面向底層的,,它企圖用底層解釋一切。由於它的這種作爲,導致了表示方式上的一些多義性,比如多維數組的指針跟元素的表示混淆不錯。

C中泛數組有普通多維數組,動態數組和vector,其中又以vector最爲典型,我們知道,多維數組要轉爲邏輯等價的一維數組,,有三種方式,,1,row列優先,這就是C的方式,,比如foo[3][4],它有三行4列row,它先每一行排3個,再4列,,2,行優先,這也就是pascal的方式,但是這二種方式都有侷限,因爲它們只能實現爲方形規則的數組,要突然這種侷限就是vector的事情了,

3,,就是vector方式,,C標準庫中倒是沒有一個vector,我們講c++的stl中的vector,一門語言要模擬多維數組,必須要達到一種“多維數組就是數組的數組”這樣的抽象,而vector就是這種思想的本質,,因爲3維數組是2維的,這就是說,3維數組是2維數組的數組,而由3到2,就是把3維中的其中一維標準化了,,縮爲1了,,這正是vector的思想,不要把線性代數中的矢量跟這裏的混淆了,但在線性代數中這樣的比較有巧合性,比如矩陣(對應多維數組)是由矢量(某維縮爲1的矩陣)所組成的,,矢量就是數組中的數組了。。比如foo[3][4][5],可以是:

(1)一個下標爲3的一維數組,,每個元素都是[4][5]的二維數組;
(2)..
(3)..
6.24 真正的數據結構
數據結構是什麼?它是組織內存中對象或基本類型數值(primtive types)的形式,爲了更好地組織和使用這些對象而慢慢發展起來的固有形式,慣用法(idioms),

數據分類與ADT

基本類型數值是沒有構造函數的,而Java中對於數值類型的封裝形式就有構造函數,這是它們二者本質的不同,像C++中用new動態聲明一個對象,它一定用到了這個對象所屬類的某個構造函數,可以說C與C++的最大區別就是C++用到了對象ADT,而C沒用到(因此C壓根不需要構造函數來着),STL可以說是一種總數據結構.

很多地方都用到到向量,,在學彙編時,,中斷向量表就是一種向量,爲什麼STL沒有arrary而只有vector呢?因爲數組一般都用來實現vector,而且數組是語言的內含類型,一定程序上不提供太多的接口(根本因爲是作爲數值形式的數值沒有被wrapper成一個first class因此不能作爲函數的參數—因爲沒有複製構造函數,只能作爲指針形式間接來操作它,也因此不能成爲一個函數的返回值),而Vector可以提供跟數組類似的結構(也有多維數組)和比數組更高級的使用接口(一般數組類型只能是靜態的不可伸縮的,而Vector可作爲動態數組動態改變大小)

實際上多維數組並不是在內存中是一個標準的矩陣(學過線代就知道,任何一種矩陣都可以化爲它的等價三角矩陣),比如C或C++就用一種行或列優先的方式來索引其元素。

數組是一個靜態配置的空間,在命名方面JAVA做得比C++好(個人看法),比如INT MyVar[10];作爲數組名的MyVar帶了序列,而JAVA中INT[]MyVar;這就有點相當於type *是指向某種type的指針類型,而type[]是某種大小的數組類型一樣好記

下面來闡述幾個易混淆的概念

順序線性表,,有序線性表(orderd list),hasp map(可用關聯容器來表達)都是列表,比如字典順序,也即字母順序的一種關聯機制

線性表即通俗意義上的“線性數據結構”,線性的意思是什麼呢,它一定要滿足幾個規則(1,有唯一的第一個元素和最後一個元素,2除了第一個元素都有一個前驅,,3,除了最後一個元素,都有一個後綴)

上述的元素就是數據結點的意思,數據和數據結點之間的關係可表達爲B=(K,R)的二元組,滿足關係的二個元素一個稱爲前驅一個爲後繼

計算機存儲數據的方式只有二種,順序存儲與離散存儲(又有鏈式,索引式,Hash式),這是面向計算機端的存儲結構,本向用戶端的邏輯結構有集合(set),,哈希(hashmap是一種map),,表(table),數組(array),,向量(vector),,圖,樹,map映射,線性表,節點鏈式表linked list,多維數據結構(matrix)等等,這種關係就有點像數據庫的內外模式之分。邏輯結構間也有高級的演變方式,比如用vector來實現矩陣和table

map是映射,,,set是集合,,都是關聯型的數據結構,因此可用關聯容器來表達


集合這種數據結構是用位集來描述數據集(可能以一個數組的形式存在),通過移位和位運算

線性表就是不能隨機存儲的表,像數組就是線性的,堆棧和隊列也是線性的,有雙向鏈表的存在(list),,dequece(double end quece),
表table就對應關係數據中的模式,,也即一個記錄是一個表,它的各個字段就是表的豎維
聚合數據類型就是像圖(map)啊,樹啊之類的,樹是一種特別的圖(每二個節點都有通路)而且是簡單圖

圖的存儲結構主要由它的鄰接矩陣來表示,或鄰接表,而樹的存儲表示主要由以下三種表示:結點表示法,兄弟子女表示法,等

這就是關聯,,關聯一定有key和一個value,,它們一同被存入數據結構,然後運用某種機制(可能是hash)通過key來索引value
圖是一種離散結構而非一種數據結構
圖的邊就是頂點之間的關係,這種關係是單向的或者雙向的
像MFC的消息系統用的就是表驅動方式(它的消息映射表就是一個靜態表)
我們知道,棧是一端開口的,往往從高端壓和出棧(稱爲棧頂)後進先出的(但是後出先進這種說法是不存的,因爲當一個棧沒有數據時,它就不能出任何東西,因此只能說後進先出,先假設它有至少一個數據存在)因此它有當前指針和棧頂指針這二個元素來表示(當前指針指),棧往往用數組來模擬(++p,p--這樣的形式),棧其實是一種跟它的實現形式無關的思想而已(是一種邏輯結構),因此可以說是數組(指針數組)來摸擬(順序存儲的存儲結構),可以用數組的一端,當,而隊列是一種先進先出的,它二端開口,有三個描述元素(尾,頭和當前指針),它往往被實現作爲一個管道(因爲隊列從意義上來講它也其實就是一端進而從另一端出的數據結構啊)作爲緩衝,或forward給其它處理
list是列表的意思,有序列表orderdlist,鏈表(linked list是一種orderdlist),堆棧,隊列都是一種orderd list
就像鏈表和數組都可以仿真堆棧一樣,,樹啊,圖啊都是思想模型,鏈表和數組纔是實際存儲的機制
對數據結構的討論中經常用到遞歸,特別是樹中,因爲樹本質就是一個遞歸結構,在回溯節點時就是回溯同一種節點(因此這個節點可用遞歸描述 )

遞歸跟回溯,棧

遞推與迭代還是有區別的,遞歸就是用自己來定義自己,,一般不需要一個循環,,而迭代需要從1開始,將這個循環變量一直自加到最大值(循環不變量?),,需要一個循環,一般來說,迭代比遞歸更有效率(在某些專門對遞歸進行了優化的環境中除外)

對一種數據結構的討論常常不但要明白它們的工作原理,還要明白它們的操作,如查找,排序等,數據結構存儲結構和這些操作就構成了ADT

《數據結構C語言版》

前言:這是我在2005.7月 - 2005.9月署假看《數據結構C語言版 - 清華大學出版社 黃國瑜葉艿菁著》時寫的讀書筆記,現在把它發佈出來,希望對大家有用,也算是作個備忘錄吧,不科學之處,還望高手斧正.
6.25 堆棧與隊列
堆棧與隊列都是數據結構(更復雜的還有樹,圖)在關係上是平等的數據結構,實際上堆棧與隊列都是"內含在空間裏的數據塊",堆棧,隊列的本質就是"數據塊",不過它們都是包含在特定內存空間裏的"有序數據塊",如下圖(4.bmp,用數組模擬"特定內存空間")
"特定內存空間"可以用數組表示,也可以用鏈表表示,數組是內存中的線形空間,也就是說,數組可以在內存中開闢一段空間,該空間是線性連續的,對該空間裏任一元素的存取要根據"索引值"來進行,這些索引取從0~MS-1(如定義一個intqueue[ms]或int stack[ms])是線性遞增的,與數組空間從低地到高地的排列一一對應,數組的每一個空間都可有數據,也可無數據,每個空間的大小爲一個int,整個數組大小就是ms個int,但是無論如何,對其中任何一個元素的存取(存是往數組任意位置裏存入一個大小爲int的空間,或在某個無數據內容的數組空單元空間裏賦值,顯然,這個數組空間是本來就存在於數組內的,而不同於鏈表要動態開闢一個新空間,而如果是前一種情況,數組就不再是靜態的空間了,因爲它的大小由ms變成了ms+1,而這是不可行的,同理,取是釋放一個空間單元,這就使數組總空間大小由ms變爲ms-1,或在某個空間裏賦值0,術語稱置0,而事先不管這個空間裏有無值,如果有值,有的是什麼值),其實,對數組索引的描述不但可用0~MS-1,當然也可用1~MS,但是爲了方便考慮,把前一種看作爲常用的,專業的方式,當然還可用2~ms+1(3種方式在定義了一個大小爲ms的數組的情況下都可行可用),因爲數組空間是一定的,對其的表示方式當然可以自由地使用不同的方法,一切的一切,只要保證能正確存取到所需的數據爲程序所用爲準,因爲這是數組這種數據結構要最終達到的作用和總則,另外還要保持易用(像0~ms-1就顯得專業並且簡單易用,1~ms就人性化,而2~ms+1就什麼都不是),上圖中的數組示意圖都開了"口",是表示只能從數組的開口的那一端存取數據(數組每個元素都是空間裏包含的內容值,即數據,請搞清"元素","數組每個空間單元","內容值"等的說法),是一種形象的表示方法,而標準的數組圖示方法可如下表示5.bmp)哪爲什麼要開口呢?一個數組爲什麼能開口呢?這是因爲前面提到,這個"數組開闢的空間"將作"堆棧空間"使用,也就是"用數組模擬堆棧",因此標準數組示意圖就要經過一些變形以適應能正確表示堆棧(空間)的要求,首先第一點變化就是"開了口",第二個變化就是還加入了一個top指針,這二個變化適應"能正確地表示堆棧(空間)"而生,其實要說top指針,它還不是標準意義上的指針,指針是一個32位的整型,而這裏的top本質還是索引值,而非內存或外存地址單元名稱代號,只是因爲它發揮了類似指針"尋址"的作用,因此將其看作爲"索引型"的指針,數組"堆棧"相比鏈表"堆棧",數組"堆棧"中的top是索引,而鏈表"堆棧"中的top纔是真正的指針,因此數組的索引本質上是一種線性循序查找,而鏈表"堆棧"的top纔是指針,分散在內存空間非線性不密集,只能由指針指定查找,這裏就比較了數組與鏈表的本質(數組是內存中一段緊密的空間塊,各個空間單元的連接是線性緊密的,這是對數組的低層討論,反應到數組中就是其中的各個元素,將上一個元素的索引加1就可定向到下一個元素的索引位置,將上一個元素的內存空間地址加一個空間單元長度可定向到下一個元素在內存空間的位置,用0~ms-1或1~ms這樣的遞增性的索引就可表示數組的各個空間並引用它們,存取其中的空間或元素),有高地址和低地址之分,索引由小到大遞增的方向就是內存地址低地到高地的線性遞增方向,而堆棧是內存中各個分散的節點數據,各個節點數據就是元素或者更確切地講,各個節點數據中的內容值,或稱數據值而非指針值,就是鏈表的各個元素,相比起數組的空間單元的"連接",各個鏈表單元空間,也即節點空間的鏈表使用一種指定式的數值指定法而非數組採用的循序式的查找定位法,各個無素之間分散的鏈接由內含在各個元素(節點數據)中的指針字段而非內容字段,數據字段來完成,因此對其每個元素的存取前首先確定元素位置時是不能像數組中循序進行的,而是已被指定的,當前元素的內存位置就在上一個節點數據(元素)的指針字段裏,而鏈表結構裏,顯然就沒有高地與低地這種數組裏纔有的特徵.
另外,要注意堆棧和隊列中所說,它們都是有序列表,上面講到,4.bmp中各個空間裏的"有序數據塊"纔是確切意義上的"有序列表",即堆棧,隊列這二個詞語作爲概念所指的實體所在,那麼,它們的有序性是靠什麼來體現的呢?堆棧靠的是遊離於0~ms-1之間的索引值top,那麼堆棧就是指0到top的數據塊,top有一個特殊情況,top也可等於-1,顯然,此時它指向序列爲0的空間的更低地址的一個空間,表示堆棧爲空(即堆棧中沒有元素再供出棧了,出棧<=>從棧中輸出一個元素<=>從堆棧中釋放一個元素<=>刪除一個元素),前面談到,出口和top的引入都爲用一個標準數組變爲"堆棧"數組提供了可能,堆棧數據塊是出入有序的,因此稱爲有序列表,那麼,堆棧是如何依靠top來實現其空間裏數據塊元素出入的有序性的呢?在堆棧裏,出口是唯一的數據輸入輸出(壓入即輸入push)通道,而隊列有2個數據出入口,準確的說法是一個出口,一個入口,而堆棧的出口和入口集中在數組空間的一端而已,示意圖就是隻能從數組空間的高地址方向端輸入輸出數據(而堆棧有2端前端後端或稱頭端尾端,即front,end與head,rear),從rear端入棧,從f端出棧,堆棧數據實體就是從f到r的數據塊,在數組表示的圖示中f在下,r在上,在鏈表表堆棧中,f在前,r在後(f此時也可稱爲頭,r也可稱作後端),在數組表示中,各個數據實體是各個數組空間裏的內容值,而在鏈表表堆棧中,各個數據實體(f到r),堆棧就是各個節點的內容字段鏈接而成的,這些內容字段鏈接起來構成的一串數據值就是堆棧數據塊實體,從f到r,可見f到r不是從低地到高地,也不是從高地到低地,因爲鏈表整個空間是分散於內存中的"節點空間"鏈接起來的,各個節點空間是分散佈列在內存中的,因此,各個節點空間的value字段也是分散於內存裏的,鏈表的各個空間間實際上沒有一條一條的鏈,這只是示意圖中的形象表示,鏈表的各個空間的鏈接橋樑就是內含在各個節點空間中的指針字段,鏈表與數組的一個重要區別與優點就是鏈表使用指針而非索引來尋址,這樣就可以通過改變指針的值來重新形成鏈表空間或增刪元素(實際上也是重新形成鏈表空間),這樣鏈表就是一種動態配置的空間,而數組就是一種靜態配置的空間,數組中的每個空間在數組被定義後就存在了,其中的任一個空間都不能被增刪,只能向其賦值內容值0,表示置空此空間單元,這就是靜態配置的空間.
圖片在我的Q空間的像冊裏.

數據結構淺探•其它
評論(0)發表時間:2006年6月2日 0時53分
.
6.26 真正的遞歸
遞歸是一種思想,只要問題本身滿足遞歸你才能,遞歸涉及到三個概念,回溯
遞歸定義
即用遞歸來定義一些離散結構(集合,函數),3.4遞歸算法,用遞歸思想來解決問題的一般化步驟(算法即解決特定問題的一般化步驟,”停機問題”證明不存在解決所有問題的算法)
這種關係就如同二叉樹的定義和查找,因爲二叉權的定義就是指明它的左右節點存在次序關係,所以可以用這種定義作爲思想來對二叉樹進行查找,這並不難理解
用對象本身來定義對象就是遞歸,,這是遞歸的描述性定義,,是不確定的,,,非形式的,
集合的定義和算法的定義只有在形式語言裏面纔有形式定義,,(特別是圖靈機證明不存在所有問題的一般化算法時用到的集合和算法的概念)


遞歸是用自身來定義自身這種說法成立嗎,先來看一個問題


你能描述以上一副畫嗎??(如果它的中心是無限循環的)

你可以這樣描述,,,一副畫的中心區域的內容是它自身(也是一副畫,而且具有跟前面描述的一樣的性質),這樣一副畫就是如上的畫的定義

顯然遞歸的說法在這個例子是成立的

序列函數的定義比較容易理解,,集合的遞歸定義常常用來產生一些合式公式

迭代與遞歸在不同的情況下各有其優勢

兔子和斐波那契數

例4 免子和斐波那契數 考慮如下問題,一對剛出生的免子(一公一母)被放到島上,每對兔子出生後兩個月後纔開始繁殖後代,如下表所示,在出生兩個月後,每對兔子在每個月都將繁殖一對新的兔子,假定兔子不會死去,找出n個月後關於島上兔子對數的遞推關係。



如何理解該月新生兔子即爲距該月二個月前的兔子總對數?如6月新生的兔子爲4月總兔數,4月新生的兔子爲2月總兔數,3月新生兔數即爲1月總兔數?

對某個月的討論要追到與它的前二個月的情況(題目意思如此:每對兔子出生後二月纔開始生育),這裏是某個月的新生兔子與它二月前兔子總數“相等”的情況(注意這是一種遞推說法,在任意差爲二的月份間都存在這種聯繫,比如3-1,4-2,5-3,6-4,而6-2則不能考慮,因爲它超出了遞推爲2階的階數2),從第三個月開始,放上島的第一對兔子A+A-生下了一對兔子B+B-(假設每生下來的一對兔子都是一對公母可生育),在B+B-被生育下來的這個三月份,B+B-並不生育,這對A+A-在第四個月繼續生育C+C-,而B+B-在第四個月也不生育,第五個月A+A-繼續生育,B+B-終於開始生育出一對兔子D+D-,,

上面描述的可以作爲典型,因爲從第三個月開始,它就滿足“每對兔子出生後二個月纔開始繁殖後代”“任意出生在a月份的兔子在二個月後的b月後才生育,b-a=2”的題目要求,作爲典型的3-1,5-3的情況被提出,後來處理任意相差二個月的兔子情況都可參照以上的描述了.

這樣問題便被縮小到相鄰二個月之間的情況,,

(爲什麼這是對的呢,作爲典型的3-1,5-3爲什麼可代表一切相鄰二月的情況,上面說了,這是題目意思告訴我們的,而不是遞推,n個月後的兔子跟n-1總數與n-2總數纔是遞推,,這個遞推也是在假設不知道存在這個遞推的情況下列出的一個式子,列出之後才發現它是一個線性組合的遞歸,列出這個式子之間設了an,這並不表明事先就知道an一定是個滿足某種遞推的通項,只是在列出式子之後才明白是一個遞推,而列出式子的過程僅僅依賴於題目意思)

既然已經得知,5月兔子與3月兔子間存在聯繫,那麼這種聯繫是什麼呢?重要的是知道這個聯繫本身,這個聯繫可用於任意相鄰二月之間(題目意思)

從以上3-1,5-3的描述中容易看出,


漢諾塔問題:



圖1 初始狀態


圖2 柱1n-1個盤移到柱3後的情形

我們的目標是計數移動步數,,,並不是如題目所說得出移動的具體方法和每個書面步驟
因爲等我們得出步數這個結論後,就會發現如果具體去得出移動步驟會多傻
而且,尊照規則(一次只能移動一塊盤子,最後全部原樣移動到柱2而不是柱3,並且最重要的大在下小在上)把n個盤子成功轉移,移動的方法也可以千千萬萬,,所以我們要求的移動步數是最少的移動步數,那麼對這個問題(求出最少移動步數)該如何建模呢?
首先,我們設想這樣一個步驟:第一步,把柱1的n-1個盤子移到柱3,保持小在上大在下的順序(如圖2所示),保留最下面一個底盤,第二步,把底盤移到柱2,第三步,再把從柱1移過來的n-1個盤按步驟1的方法和順序移動柱2,至此完成(注意方法和順序的說法,方法:步驟1是怎麼樣移盤的這次也怎麼移,順序:保持小在上大在下)

容易看出,使用更小的步數是不可能求解這個難題的

下面具體建模
按照上面設想的所產生最少移動步數的移法,首先,設Sn是把n個盤子從一根柱子移動到另一根柱子的需要的總步數,(問題是什麼我們就設什麼,,雖然我們並不知道這其實是一個爲了滿足遞推關係的設法,雖然我們也不知道有了以上設法,再用遞推關係就可以很好地解此題,因爲從形式來看Sn像一個序列的通項,它指明Sn就是把n個盤子從柱1到柱3所需要的Step,S2就是2個,S10就是10所需要的Step,這個設法假定Sn與n之間存在序列關係),按照步驟1,移動次數可用Sn-1來表示,步驟2可用1來表示,步驟3亦爲Sn-1,故有
Sn=2Sn-1+1 (Sn爲把n盤從柱1移到柱3所需步數或直接就是)
注意Sn爲什麼不直接說是“設Sn是把n盤從柱1移到柱2所需步數”呢,這樣說當然沒有錯,不過爲了嚴緊性和通用性考慮還是這樣設(本題當然是從柱1移到柱3,如果沒有規定其實移到柱2作爲中轉也可,最後求移動到柱3上的步數同樣滿足Sn=2Sn-1+1,,況且還有很多同類的題目,移奶酪到盤子裏什麼的,所以爲了通用性考慮還是如上設)

最後的問題:我們用迭代方法來求解這個遞推關係


5.2 求解遞推關係

有一類重要的遞推關係可以用一種系統的方法明確地求解,,在這種遞推關係中(好像我們見過的都是這樣的),序列的項由它前面的線性組合來表示

請注意,遞歸,遞推,迭代在措詞上的區別

遞歸是用自身定義自身的,或過程調用自身,用在序列通項表示上,,形如,an= an-1,用在過程體內,這僅僅是一種思想,而不能說是一種算法,,顯然這種思想是成立的,,,遞推關係和迭代是用了遞歸思想的2個概念而已,是用到了遞歸思想的所有東西的集合中的二個元素,
要對它們三者定性的話,遞歸是一種思想,遞推只是一種說法,迭代是一種算法(常用來求解滿足遞推關係問題)

遞推是一個序列的某個通項可由它的前幾項組合推導而來的關係,,強調的是通項和推出它的前幾項之間的關係滿足這個“遞歸推導而出”說法,因此它一定用到了遞歸的思想,滿足遞歸

序列中,遞推是發生在某個項和它的相領項之間的關係,,但是指定了初始項和這個遞推關係,可以得出所有的項,即通項,,實際上“某個項”的定義一旦被提出,它就表示了通項的意義,,因此序列所有項=由通項關係得出每個項後,這些項的組合=初始項+滿足遞推的相領的某幾項

(我們設這裏是二項,而不是前幾項)所以,所謂遞歸只是二個項之間的關係,,但是由於這二個項可以是任意項,,因此,遞歸是所有項中任意二相領項的關係,可用來求解整個序列每一項,求此也求解所有項

定義X:一切序列可以用到遞推要求它滿足遞歸,這永遠是前提(實際上並不是所有的序列都滿足遞推和遞歸)

迭代是用迭次代換,(常用來求解一個“線性組合”型遞推關係的方法,也可求解不滿足線性組合的一些遞推關係)把一個項逐次展開,,用到一個迭代器i,比如求解an,必須使i從第n項到第k項逐次遞減,(k通常情況下是1,即第一項),,迭代也只在幾個相鄰項之間進行,因爲它也借用了遞歸和遞推思想而已,這一切都是因爲用到迭代的序列(或其它問題)也要由定義x而來,,不再贅述
.
6.27 樹與單鏈表,圖
除了單鏈表之外還有高級鏈表,包括雙鏈表和循環鏈表,但是它們的基礎依然是單鏈表,對單鏈表的討論可運用於高級鏈表,也即無論單,高級鏈表,本質都是一樣的數據結構,都是鏈表,即鏈表的本質是節點的單向或雙向串連,注意"串連",這是所有鏈表區別於樹這種數據結構的所在,因爲樹是一種節點的"分支"鏈接,(樹是較線性表,圖更爲複雜的數據結構,它的任何二個結點間都可以發生聯繫)它們的共同點就是:這些節點都是分散於內存中的節點,由上一個節點(鏈表)或父的一方節點(樹)的指段字段指向,樹和鏈表的示意圖中,圓形就代表節點,包含數據字段和指向下一個節點的指針字段,這是對鏈表來說的,而對於樹來說(這裏說的是二叉樹)圓形節點就包含數據字段和指向其左右節點的指針字段,或稱左右子樹,因爲這些左右子樹是以這個節點爲根的子樹,注意左右是有順序性的,不能顛倒,用Left,Right區別.無論是鏈表還是樹示意圖中的直線都是指針,有方向的,不是互逆的,這處互逆性決定了對單鏈表的"首->尾"遍歷和對二叉樹的"二叉查找"遍歷方式(中序,左右序)的單向性,可見樹與鏈表的實體數據都存在節點中,這是節點的主體存儲空間,鏈表,樹作爲數據結構用於組織程序中要用到的數據,它們的節點單元的Data字段發揮的是主體存儲作用,而它們節點的指針字段是輔助性且必要的,用於樹,鏈表的查找,遍歷,插入,刪除等操作,嚴格來說,圖並非一種數據結構,書中對圖的討論只能稱爲對"與線長,面積大小"無關的點線面的拓樸討論,而由點邊組成的圖形中居然沒有一個點或邊發揮數據存儲作用,而這是一種數據結構首要完成的任務.

3.數組,鏈表爲什麼能仿真堆棧?

其實這個問題被提出來一點都不可笑,對這個問題的討論很有意義,它能讓我們明白一些細微而重要的東西.
數組,鏈表,堆棧,隊列,是四大數據結構,在關係是並列的,爲什麼又有"用數組和鏈表仿真堆 棧"之說呢,其實數組,鏈表是較高級的複雜的數據結構,還記得本書序言中的一句話嗎?數據結構是人類在長期的編程過程中總結歸納出來的一套科學有序的方法,數組是,鏈表是,堆棧和隊列也是,但是在人類總結歸納這些數據結構並形成術語進而形成數據結構這門計算機科學時,永遠是從簡單低級的數組和鏈表開始的,對它的討論和總結歸納先於堆棧,隊列之前進行,當人們總結出數組和鏈表數據結構時,並得出對數組和鏈表的刪除,複製,插入等操作後,形成了數組鏈表等數據結構概念及其操作的一整套理論後,人們進一步探索數據結構,發現了堆棧的存在,而堆棧又是基於數組,鏈表的高一級的數據結構,對堆棧,隊列的研究與討論經歷了與對數組鏈表相同的過程,對數組和鏈表的研究和技術總結業已成型的情況下,就應該採用現在的知識來描述新出現的知識,因此有以上的說法.
6.28 樹
樹是一種數據結構,稱爲樹狀結構,簡稱樹,樹的本質是一個或多個節點的有限集合,樹只有一個節點的情況是例外的特殊的情況但也是允許的樹的形式,而一般情況下,樹都有不止一個的節點,有限集合這四個字指明:一棵樹,無論它由多少個節點構成,這些節點的數量都是有限的,可以計數的,也即,從來沒有一棵樹,它的節點個數爲無限不確定的.
在一棵樹的所有節點中,它們的地位是不一樣的,每棵樹必定有一個特定的節點,稱爲根節點(root),根節點與這棵樹的其它節點構成了這棵樹(當然這些節點並不是單獨地存在無聯繫的,不然就無法從這些節點中搞出一個爲根的地位節點,這些就構不成一棵樹),可見,"根"是相對於"樹"來說的,根這個概念是樹一級的概念,只要有樹,便會有根,根的本質是一棵樹的"特定節點"(第一個節點,其它節點由它分支而來),而樹又是一種特定的數據結構,請記住,根是依附在樹概念上的概念,脫離了樹便無所謂根概念,總之,根是與樹直接掛鉤的一對概念,爲什麼我要在這裏這麼花心思地說明根是相對於樹的概念呢?因爲這是理解7.1節中關於樹的其它概念的一個基本點,掌握了它,你便不會在理解這些概念中迷失.
說完了根,再來說其它節點,其它節點(實際上這裏說的其它節點準確的意義不是說除了根節點外的一棵樹的其它所有節點,而是說根節點的下一級節點,可以有0個,可以有n個,0個就是沒有下一級節點,而n的大小便決定了這棵樹的分類性質,如果n=2,便是二叉樹,後面會談到)是根的子節點.(8.bmp)
從樹結構的圖示來看,樹與現實中的樹雖然類似,但是,數據結構中的樹是一棵倒樹,根是相對於樹的概念與樹直接掛鉤,而子節點的概念直接與根節點的概念掛鉤,而與樹不掛鉤,根節點與子節點的概念僅僅存在於一個節點與該節點下一級節點之間,與樹不掛鉤,這就像主程序與子程序的概念,主程序與子程序永遠是相對的概念,如果前面說的子程序它又有它的下一級程序,那麼主程序與子程序有主子關係之外,還可以說前面談到的子程序與該子程序調用的子程序也有主子關係,此時這裏的子程序是主程序而子程序調用的子程序就是子程序,這2種說法都是可行的,因爲主子關係是相對的可變的而非絕對的,主子這種說法存在於只要滿足一方是調用者而另一方是被調用者之間,而非絕對不變的,再接着上圖講,那麼B,C,D,M就是A的子節點,而不能說B,C,D,M是樹T的子節點,沒有這種說法,樹只有根和子樹,葉節點與其掛鉤,而只能說(因爲B作爲根節點其下還有P,Q2個子節點)B,C是樹T的子樹(D,M不是),這裏,B,C,D,M作爲A的子節點,同時B,C又是樹T的子樹,子樹B(以它的根節點爲名稱故根節點就是B)的根節點B又有自己的子節點,T的樹C的根節點C又有自己的子節點R,若一棵樹中的任意一級(根)節點最多有n個子節點,則稱這樣的樹爲n元樹,二叉樹的得名也來源於此.

12.樹的本質是什麼?

樹是一種數據結構,所以樹的本質是一種組織內存空間的方法,就像數組是靜態配置內存空間的方法,且數組配置出來的空間不但是靜態的,而且是緊密相連排列的線性的各個"小空間",是一塊內存中的塊狀數據靜態的存儲區,由它的一個小空間的地址可以推導出下一個小空間在內存中的地址,而鏈表是一種動態的配置內存的方法,程序中,數組空間在數組被定義出來時就被開闢,在它的生命期內從此不能更改大小,而鏈表被定義出來時還要用內存開闢函數malloc()來實際分配內存,所以它開闢出來的空間是動態的,而且這些空間單元不是線性緊密排列的而是分散的,分散於內存中的各個"節點"空間,是不可通過索引下標循序查找定位的,只能通過內含於各個節點數據內的指針字段來"指定查找",形象示意如下1.bmp)那麼樹呢?樹的本質是"節點的非空有限集合",跟鏈表有一定的相似性,首先,鏈表與樹(基礎樹,書中所講爲二叉樹,這屬於簡單樹而非廣度意義上的樹,但是對二叉樹的討論可以延用擴展爲整個樹的範疇)都是動態內存配置的方式,鏈表空間間連接依賴於各個節點數據的指針字段,而樹的節點空間連接的方法爲"格式上的約定",樹的節點空間和鏈表的節點空間都是這二種數據結構實際存儲實體數據的存儲場所,是怎麼樣一種"格式上的約定"呢?以二叉樹爲例(其實樹各個空間之間沒有什麼鏈接,鏈表各空間發生關係的紐帶與橋樑是指針字段,而樹的各個節點空間間發生關係的鈕帶與橋樑爲"格式上的約定","約定俗成的方法來格定"),二叉樹的每一個節點空間實際各各分散佈列於內存中,它們發生聯繫的手段就在於各個節點的性質,前面談到,樹是節點的非空有限集,如根就是樹的第一個節點空間,如果是二叉樹,這個節點自然會跟其下一階的二個子節點發生關係,這就是父子節點關係,當然不止根與其下一階節點,在其它節點間也存在父子關係,除此之外還有兄弟關係,樹->子樹關係這種"關係機制"是遠遠不同於鏈表依賴指針表示各節點空間聯繫的手段的,所以這是一種"節點關係"上的"約定",是一種全新的組織節點空間的手段和方法,而沒有用到指針,當然可以用指針來模擬和仿真,這就涉及到"用鏈表來表示二叉樹"的知識點,這是後來的內容.

13.圖的本質是什麼?

要說圖是一種數據結構實在很難理解,因爲我第一次碰到圖的概念,根本就沒有發現圖形結構中的哪部分用於存儲數據,378頁對圖形的定義"在圖形G中包含了2個集合,一個是由頂點所構成的有限非空集,另一個是由邊所構成的有限非空集,可用G(V,E)表示",按照這個定義,圖形就是頂點和邊的有限集合(至少要有一頂點一邊),暈死,那麼實體數據存在哪?頂點中?不是,邊的描述值權嗎?好像也不是,署假我只看了這本書17天,所以對圖我沒太多深入研究.

6.29 真正的散列表
一般抽象到了某個程度,爲了獲得計算機作爲底層的馮氏能力,,就不應該再抽象下去了。
開發模型不需要再變了,數據抽象到數據結構級就是頂級了再抽象就不是開發問題了()

Hash表就是hashed list,,,它是邏輯上的list(所以也有hashmap,hash set等),但按hash方式存儲地址作爲存儲,

Hash是一種利於搜索的啓發過程,一般的搜索是對搜索空間uniform的,而hash function是對搜索空間的目標問題建立的一種啓發機制的函數。

Hash的最重要的意思不是提供一個映射函數,那反而是hash解決的第二個問題,即地址問題,,它最重要的意義,即第一個解決的問題是基於統計的本質。進行的對搜索空間的一種抽象(即hashtable而不是hashfunction問題,比如元素會出現一次,還是二次,這樣抽象對操作元素有好處,但顯然此時並不出現hasing function的意思),

一般設計散列時,說的都是設計散列表,然後處理衝突處理,,,如何散列所用的函數是附帶問題。

也即其實hash table的第一層意思是table,,即形成數據結構纔是他的第一層意義,,,而不是如何hash,,,並提供一套hash 機制,,,
它着手於"在解空間和目標數據空間建立一套具有inform關係"的數據結構,,,這纔是它的第一層意思,即名字中的table一詞
至於如何hash並解決衝突,,那反而是它解決的第二個問題..即名字中的hash一詞
只有理解了這點之後,你纔會明白hashtable是如何來的,以及爲什麼存在,,與其它數據結構作比較時所呈現的那些不一樣的特點(比如爲什麼要進行hash,,爲什麼要處理衝突,而其它的數據結構則不需要這樣的分析過程).
6.30 算法設計方法
攤還分析不僅是解釋數據結構性能的工具,而且也是設計時要考慮的因素。就跟NP完全一樣(遜於圖靈不可計算機的停機問題)。如果複雜度超過了10的確良8次方這個計算機的數量級就沒有意義了。

存在很多類型的問題,比如最優化問題,,找點問題,理解諸如貪婪算法的前提是理解這些問題在先,比如最優化問題可以很好地解釋什麼是貪婪設計。。

迭代是方程求根的一種方法,並且它也體現了一種算法設計方法。

遞歸體現了分治的算法設計思想,但它提出的子問題一定要跟原問題接口一致。。 遞歸的終結條件是不需要遞歸也可直接求解的條件,,因此是終止條件,,當以從下到上的眼光來看時,它是超始條件(直到問題規模n)
其實並不是只有所有明顯遞歸性質的問題纔可以用遞歸來解答,而是隻有能把原問題分解爲子問題並能製造一個接口的情況下就可以利用遞歸來求解它。

遞歸是從上而下,所以有時要求用輔助棧來保存中間結果,而遞推只有一個函數,並不發生欠套的函數調用,並沒有出入棧的時空開銷。複雜的遞歸結構轉化成遞推時,需要回SU處理,二個概念僅一字之差,一個歸,是向下,一個推是向上,

關鍵字相當於字典中的單詞,,而數據項相當於這個詞的詞義,造句,等全部的詞條信息,這樣說你一定不會明白,說實話我第一次看到這樣的說話也沒能明白。。

實際上就是說,我們要查的是關於某個詞的詞條義,音等,,但我們是通過某個單詞找到該單詞的詞條義,音的。。在字典中單詞和其音義是一同被存入字典的,都是(某記錄的)數據項,但單詞是作爲鍵的數據項。.

新手編程導論(八)


第7章 抽象之高級語法機制


7.1 真正的OO解
什麼是系統?系統是關於一個事物的邏輯上的有機組成部分,因此系統分析師的工作就是用面向對象去表達系統(劃分對象),這就是OO用來進行設計的意思。

對象之間是分配了責任的,如何把這些責任更好地分配呢?(即對象內邏輯和對象間的交互邏輯,UML中的對象消息)

一個分配得好的對象組合(兼顧了當前使用和未來擴展能力)往往不是現實生活一個具體的模型,而且有些時候你一點現實生活中的對應物影子都找不到,而往往是一個思想模型

設計模式的學習是無比重要的,只有掌握了設計模式,才能不會讓思想侷限於現有的代碼,纔會在一個更廣泛的領域裏去拆解對象並進行設計(原語領域去看待事物,因此程序設計實際上是一個反映你心智和哲學水平的活動),往往有些時候,,,真正的再編程能力不再是語言細節和問題細節,,而是在掌握了語言細節和系統細節之上的高層構架的學習(設計上的學習)..

往往有時候,,,開發一個程序是一個真正工程規模級的活動(不是指那種利用諸如.Net,JAVA之類的真正OO語言聲明幾個類,創建幾個對象來進行敏捷,快速的開發過程,,而是指那種預計到未來擴展的需要而預留了很多發展餘地的大型開發過程)

敏捷方法極限編程XP和RUP(ROSE公司提供的大型軟件開發“方法學”)是二種軟件開發的方法學。

而設計上的學習往往要求你掌握關於此問題的所有細節(在目前的科學技術水平下對該領域的現解),和與此問題有關的很多周邊問題,所以要從原語領域去看待此問題和進行基於對象拆解此事物並構造這些對象之間的邏輯的系統活動,

比如爲什麼會有OO的出現呢?因爲OO是對現實世界“Object”的抽象(不可否認我們周圍的世界的確是一個一個的對象,注意這是用OO眼光看待問題域),我們可以在抽象的基礎上構建抽象,進而發展出大型的系統(由土和石到房子,由房子到,不可否認我們一直在做這些的事和思想上的活動),而表現在OO編程工具上,我們用Class來表示一個Objects(注意這是應用領域,雖然這種CLASS對於表達現實的確顯得有點單位過小-----運行時不可控的抽象,但我們可以通過不斷地包裝抽象和組合抽象,或者通過策略---設計期可控的抽象來達到更大的抽象),,CLASS的出現是歷史必然的,以前編碼時是分開代碼和數據的,一個CLASS就是對於代碼和數據的整合抽象(計算機就是用代碼去處理數據嘛,這是計算機最低層,面向機器的抽象,在這層抽象上我們可以構建更大的抽象,以達到接近人的那一端的抽象,一切解決問題的方法都是抽象的演變,一切皆抽象!!)

比爾願意招一個物理學家而不是計算機學者,是因爲他考慮到,物理學者往往思考的領域比較廣,而且他們擁有很好的數學基礎,這些人往往比專業計算機科班出身的人(指機械程序員,只侷限於一個類庫比如MFC進行開發達七年,雖然在計算機領域掌握一種技術可以吃香好幾年,然而一個有發展前途的程序員必定是一個學習新技術的高手)更能迅速地掌握一些思想級的東西。
7.2真正的對象
在當初DOS下用匯編開發程序的時候,一般都是先考慮這個程序會用到什麼數據,要用什麼樣的數據結構在程序內部去表示它,然後才考慮開始寫代碼,,這個時候數據與代碼是緊密相連的,再後來出現了模塊化但是面向過程編程,這個時候,利用臨時自動變量可以降低子程序間的藉合度,而當出現面向對象後,我們完全可以先定義一個預對象(什麼是預呢,就是說這個類被寫出來的那一瞬那,它並不像面向過程子程序一樣存在某個一個rotuine執行路徑中,而是作爲預定義的一個程序組件,除非你定義並引用了一個該類的實例,這個以前定義的類中的代碼纔會進入某個執行路徑),即類,一個類是對象是預定對象變量與對象實例(注意這二個概念,變量可以是一個指針,是聲明性的並不分配內存,而對象實例是一塊業已經過new分配的內存塊,實例也可以是一個句柄,是指向對象實例的指針,因此它是指針的指針,形如VOID**或SOMEOBJ**,指針與句柄的區別即來源於此)的對象模板,而類模板是預定義的類模板,類這個東東來源於結構體,其實一個結構體也有它們的構造函數,在這種意義下,類與結構體很接近。


面向對象的方法成員廣泛上的意義就是“對數據的操作”,也即計算機指令,,狹義上的意思就是“表示對象的主動或被動性作用的方法或函數”,,,面向對象的數據成員就是“數據或變量”,也即計算機要操作的數據,,這對應於面向過程中的函數跟變量,,,而面向對象將它們推入一個更泛化的境界(一切計算機數據都可成爲數據,特別是OO對象)。

面向對象並不是一種憑空冒出來沒有根據的概念,相反,,沒有它的存在倒是很令人不自然的,面向對象和基於面向對象的設計模式使軟件設計進入了一種高速發展的狀況,甚至個人獨立開發體系複雜的軟件都成可能,這就是面向對象的作用之一所用所在,,,因爲本質上,面向對象是符合現實生活的,,,那就是:面向對象特別類似於建築學的過程,需要不同的原料並組合它們才能構成最終的軟件或建築,我們只要發明了磚和水泥這樣的原料,就有可能發明樓房,(爲什麼說僅公是有可能呢?即使你發明了需要造一間樓房的所有原料你也不一定能造出樓房,這是爲什麼呢?磚和水泥應該如何組合,塗料跟牆體怎麼樣配合才能最終成功着色?這就要求對象間必定要有一定的邏輯,如果說造一間樓房是需求的話體現在建模工具中就是case view,那麼你造出的所有原料就是compent view中的所有對象,,這些原料如何組合的邏輯就是logic view要告訴你的內容,這些邏輯實際上一部分被包含在當初分別創造這些原料的各個過程中,你必須爲原料或對象預留“接口”,要考慮這些對象什麼時候會被用到,以那種被動被調用的方式或那種主動調用別的對象的方式會用到,即一個類的方法並不一定就是類對象主動性的作用,一切關於此對象可能的操作都可以用一個成員函數來表達。屬性也是一樣,另外一部分被包含在當用全局的眼光考慮業已創建好的這樣對象的時候,如何運用設計模式來組合它們得出一種適合的邏輯,而這些都是lgoic view的內容)
l 這樣僅僅是一個軟件內部的考慮,如果不在源程序的級別,你要提供你的軟件爲別人所用,或者是一個類或者是函數,你不可能向別人提供源程序,你就要在二進制級別上向別人提供面向對象的特性(即可複用特性),,這通常是建模工具deployment view中的內容,面向對象的三重機制,繼承,重載。
7.3真正的繼承
面向對象的缺點不在OO本身,而在於人們對OO的濫用,這主要體現在人們對繼承的概念的偏解上,設計模式指出,對於複用,我們應該儘量用聚合而不是多層繼承(單層繼承和對等繼承還是允許和鼓勵的),JAVA不支持多重繼承,一般也提倡不超過五層以上的繼承(然而在策略的提出中,多重繼承卻起了不可替換的作用)

不可複用問題的出現絕對不是面向對象纔有的問題,,面向過程更甚,實際上對象機制的確在一定程序上也促進了模塊間的藕合,然而設計良好的對象構架可以很好地爲以後的更改預留空間,並且似乎也並沒有更好的比面向對象更好的機制出現,比如完全面向複用語言,,極端化地求完全可複用性又是不對的,因爲複用這個概念本身完全是一個相對的概念,,,考慮一下MFC,它內部的各個組件都不能拆開來用,只有在作爲MFC這個整體的前提下各個組件纔有效,在這個意義上它是不可複用的,,,然而,就其內部來說,當你在MFC下使用MFC編程,你真的可以做到重用其中的一個組件可以避開不使到另外一個組件(這就是說,可不可複用從來都有一個最小可複用單元的概念存在),stdafx.h就允許條件編譯和預編譯(stdafx.h是VC給你的標準預編譯頭,它可以通過project wizard爲你清理出一套專用於win32 app,win32 mfc app,win32 console..etc的文件頭,當然你也可通過project.settings,C++.precomplierheader來定義自己的預編譯頭),條件編譯常被用在一些大型庫和軟件系統的編譯上,比如一個由很多可plugable的Dependencie組件組成的庫,常常提供有具體的宏條件編譯來讓你自定義哪些組件要被編譯進來,哪些組件不被編譯進來,而每一套編譯出來的庫都是有效的,,還比如一些用到include了dx頭文件的庫,有時爲了讓沒有安裝dx sdk的用戶能正常編譯,就提供有宏讓自用戶選擇不inc進這些東西。
7.4真正的OO
抽象使高層置爲頂端,面向對象就是一種抽象,這種抽象使我們看不到我們不想用到的事物的一些方面,而把那些我們能用到的事物的方面用來作爲描述此對象的全部,,(即抽象了的對象根本不能完整地反映對象本身而且壓根就不能),接口是關於如何應用對象提供的服務的全部抽象,如果說面向對象和接口是代碼級複用的機制,那麼構件是二進制級真正的複用機制,接口把一個系統的可用部分按不同的形式透露給複用者,

抽象使高層置爲頂端,而它的可定製部分都集中在頂端作爲應用,這有點像網絡的七層模型,只有定義了一個抽象而且是合理的抽象,才能爲未來的應用預留擴展空間

一個OO程序本質是一些類數據(jar文件包裏全是class文件),類是真正意義上的數據(類型),然而在JAVA中,JAVA提供了數值類型對象的對象封裝型和非對象封裝型,(因爲對所有的東西進行對象封裝有時不符合現實而且對象機制一定程序上比面向過程慢--這就是C向C++轉型中有人擔心C++其實由於它的封裝技術而導致的速度問題,你可以把整型看作一個對象但是傳統觀念裏已經把它作爲數值看待,一般程序設計語言都將數據類型的數據作爲first class來看待,就是那種Java庫src包內的那些對象,這些對象是高級對象,因爲程序設計語言一般爲他們裝備了足夠多的功能和運算符,可以由它們派生很多其它的數據類型)

面向對象數據庫db4o的出現,使我們開發的重心不再集中在如何存儲和聲明數據本身,轉而將注意力集中在這些業已創建的對象的邏輯上,如果說UML建模工具可以用來規劃這個過程,那麼DB4o就是實際產生和持久存儲了這些對象,

現實生活永遠是多元的,可定製的東西可拔插可擴展的永遠是最好的,巨型的應用只能集中在某一種平臺上,因此很多軟件比如sql都發布了它們的embed版

當今潮流下,很多東西都越來越變得合理和可擴展,侷限於某一個平臺之上的應用正漸漸消退,一個應用程序的exe主體不再是一個包含了大量對象和對象邏輯的二進制體,而往往是一個執行框架,它的主體在配置文件和麪向對象數據庫內,配置文件指明此可執行體的平臺等屬性,數據庫內存有可執體內用到的全部數據

現在再也不是早是不是要用OO還是不要用OO的問題了(這個問題已經早過時了),因爲JAVA和DOTNET出現了,它們的出現表明,,不但要用OO,而且還要用虛擬平臺上的一套OO,(也即所有的應用應該從平地再加一層邏輯,加到虛擬平臺上去,以前我們是從彙編到C,這是對CPU編程轉到對OS編程的改變,從DOS到WINDOWS的編程平臺轉變,,對網絡編程和對本地編程平臺的轉變等等。因爲我們OO編程時總是用一套OO庫來進行我們的開發嘛,以前我們是直接用本地平臺上的OO庫,現在好了,我們被迫用虛擬的OO庫,因爲它建立在平臺之上,同平**立,因爲這層抽象最大的作用就是1.使我們利用JAVA或DOTNET開發出的程序是真正的OO程序- 因爲JAVA和DOTNET提供的虛擬編程OO庫都是嚴格組織和科學劃分的,專門爲OO而設計,而且更重要:2.它們獨立平臺,這樣編程的時候我們的眼光就會不再侷限平臺而專注跟平臺無關的通用應用領域,問題域,如J2EE)

應用往往是分佈的(現在的網絡服務器主要是應用服務器而非資源共享服務器),面向對象領域的很多東西都越來越趨於WEB這個分類粒度,Web使我們的應用分佈於網絡,使WEB往往成爲一個分佈式的併發OS,(網絡最初是純粹的通信網,後來才更名爲計算機網絡,是因爲出現了NetWork OS,今天,面向對象和RMI,RPC,Corba,DCOM,使對象可以在網絡上不同的計算機間交互,)
越來越趨於WEB這個粒度(粒度是我們作原語設計時用到的一些輔助用詞)這個事實表明,分佈式計算和麪向構造軟件設計的企業部署需要被學習.
Dcom是微軟用來抗爭Corba的東東,,微軟是一個很有自己理念的公司,Java等的崛起,使虛擬平臺呈現多元發展

Java和.NET是真正的OO語言,獨立平臺,然而它不是本地平臺,一方面,不由本地OS直接分配內存,另一方面,它們是動態成型的語言,而不是編譯期靜態語言,因此速度上會比Native普通程序慢好多(雖然也有JIT技術的支持),但是據稱,,JAVA速度越來越接近於C++(不知道是本地C++還是C#,這裏說的JAVA是指JAVA庫和JVM的綜合)


7.5真正的OOP
在一個常見的C++編譯器中,總是會帶一個精心安排的簡化程序設計的公用庫,比如MFC,VCL等等,MFC顯得有點過時了而且它的實現比較繁複,C++標準把STL加進來了編譯標準,一般C++編譯器都支持STL,

庫跟語言的關係是什麼呢,,語言可以通過庫來擴展,,《C++沉思錄》中說“庫就是語言”,“語言就是庫”,,比如數組是語言內含類型,但是基於面向對象的需要,,就提供了Vector類來wrapper數組,,這就是庫對語言的擴展作用。。(相比來說,如果語言是一種使用規範,那麼一個庫是爲了某個特定領域—比如OO,而精心設計的,一般要求精細的設計)

OOP不僅僅再是指單純的繼承啊,多態啊(這樣面向對象的機制本身),還指一些公用的oo程序設計手段idioms,,比如容器(用來盛放對象),迭代器(用來遍歷對象),適配器(統一對象的接口)也即OOP包括面向對象和Generic programe技術

模板的初級應用是作爲容器(異型或同型)來使用的,實際上它還有作爲Generic programe的很多高級應用,如編譯期策略

STL被稱爲總數據結構(因爲它的元素是數值和對象都可以-異型或同型容器,而且它包含了對於一種數據結構的存儲,,訪問的方方面面的抽象)
這就是對數據結構的整合(stl)還有策略(可以稱得上是對設計模式的整合)
stl甚至都抽象了多種數據結構的使用方式,stl被作爲數據類型的模板(但是它顯然還有更更高級的應用,,作爲container的模板只是模板的初級應用而已)
這些原語程序設計手段也作爲一個庫(即stl也跟mfc一樣是OOP的一個重要方面,,,當然還有構件庫比如ATL等等)被提供在一門語言的編譯器中,但是.net就完全整合了這二種庫,,而且它是跟本地脫鉤的
7.6 真正的構造函數
構造函數不會常常被聲明爲virtual,,,因爲子類不一定非要實現它自己的構造函數(而往往在它的基類中就有過實現)
一般來說,我們爲某塊內存中的對象設立它的指針,只爲能使用它,,,因此,指針就是關於此對象的使用抽象,中間層,,我們不必知道這個對象的具體情況就可以操作它(即使不知道它是一個什麼變量的情況下),,這個指針便提供了操作它的全部接口,對這個對象的訪問與修改操作都是通過這個指針而來的,,,明白這個道理有什麼呢
比如有如下一個程序
int var1;
int * var2 = new int;
var2=&var1;

爲了使用變量var2,,我們當然可以直接通過var1變量(也即var1名稱本身)來訪問到它,但是有什麼我們還需要定義使用它的中間層,這就是指向var1的指針(換言之,我們並不想通過var1變量名稱本身來訪問變量)
一個對象的指針是關於這個對象到它的使用級的第一層間接,指針的指針就是二層間接,,
也即,爲一個變量定義一個指針等價於計劃通過這個指針去訪問對象,(這個指針也可實現訪問控制)
如上所示,這第一層間接和第二層間接都是通過type*的形式出來的,這樣就保證了與原對象的充分脫鉤
也即 int * var完全可以和原對象脫節(var2這是第一層間接的其中一個指針,因爲可以爲同一對象聲明多指針)
這樣到底有什麼好處呢?
有時我們想訪問var1的同時不想改變它,這固然可以變int var1爲const int var1,然而這樣改變了var1的屬性(也即我們需要在不改變var1的屬性下實現“訪問var1卻能保證這個訪問過程並不會改變var1”)
於是,我們可以用const修飾修飾指針指向的對象,
int var1;
const int * var2 = new int; (這樣*var2就是const int了,因此var2指向的是一個不能改變的int值,雖然var2的值依然是var1的地址,,但我們的確實現在使用層就保證使用過程也不會改變原var1的值)
var2=&var1;
對於var2來說(雖然我們不知道還有多少指針會跟var2一樣指向對象),,它作爲一個指針出現以訪問和操作它指向的對象,,,在var2的層面,我們只能通過這個指針去修改對象,,,可是,var2被定義爲constint*,,,因此並不能使用它來修改指向的對象
即,除了類型之外,指針的聲明可以與它指向並要操作的對象無關(比如關於操作的const,,,它就可以僅僅並施行於var2上,而並不要求它的指向對象爲const,,因爲var2只是關於原對象的使用邏輯而已)
總結:指針是關於一個對象的間接訪問層及訪問控制邏輯集合點

7.7 真正的私有,保護和公有
權限修飾出現在二個地方,1,在一個類內部對成員的修飾,,2,用基類派生子類時,對基類作何修飾,,這是修飾派生方式的修飾,,這二個因素共同決定了外界以如何權限組合,,讀寫一個類的成員(這纔是我們最終要訪問的目標,但是有二重門要過),當這個相對基類是。

7.8 真正的重載與複寫
Overroad與Override,重載是根據已有事物定義新事物(這個新事物是與已有事物貌合神離的,通俗來說就是在一個具體類中,可以定義多個函數名相同但參數不同的成員函數,前面可以用Virtual或沒有這個關鍵字),覆寫是將舊事物升級爲新事物,因此重載時的舊事物與新事物都有效的而覆寫時的舊事物作費(通俗來說,就是在一對具有基類與派生類的二個類中,派生類中可定義一個與基類成員函數名和參數均相同的成員函數,此時基類函數前必有Virtual關鍵字),誠然覆寫可讓基類同名函數作費,然而在C++中還存在其它函數作費規則,這裏“作費”是指派生類的函數屏蔽了與其同名的基類函數,規則如下:
(1)如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。
(2)如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。
7.9 C++的元編程
編譯期解釋是被發現的,而非被髮明的,在一次國際會議上,有人發現編譯輸出的錯誤中居然也有運行期相似的結果,,這是模板實例化的結果,,於是C++的這種語言機制就被發現了
編譯期解釋本質是什麼
我們知道C++主要是用模板技術來實現元編程的,傳統上C++主要在動態運行期發揮作用,然而如果我們對編譯期編程(靜態編譯期),這將發展出一種元編程的技術.(利用模板實例化機制於編譯期執行一些計算。這種通過模板實例化而執行的編譯期計算技術即被稱爲模板元編程。)
模板實例化是生成採用特定模板參數組合的具體類或函數(實例)。例如,編譯器生成一個採用 Array 的類,另外生成一個採用 Array 的類。通過用模板參數替換模板類定義中的模板參數,可以定義這些新的類。當然它跟模板特例化不一樣,具體的模板技術細節請去參考其它書
先來說一下編譯語言
我們知道這個過程,詞法分析和語法分析構成了編譯器的前端(得出一個抽象語法樹),然後是語義分析,然後是中間碼或目標碼生成(如果有一個虛擬機,比如JVM和JAVA語言,那麼這個目標碼就是JVM中解釋器要執行的目標,如果是裸機器本身,那麼就是一種接近二進制但不是最終二進制的表示形式,當然一般在生成最終目標碼之前要先生成中間碼),然後是對生成的目標碼進行優化,優化之後進行彙編形成真正的二進制
再說一下C++中的模板技術,一般用typedef來實現模板中的假變量定義-因爲模板中不能直接定義全局變量(以下提供的例子是一個數據結構模板)
template

struct Typelist

{

typedef T Head;

typedef U Tail;

};

顯然,Typelist沒有任何狀態,也未定義任何操作,其作用只在於攜帶類型信息,它並未打算被實例化,因此,對於Typelist的任何處理都必然發生於編譯期而非運行期
7.10 模板的意義在於編譯前端的設計
看過stroupre的C++簡史的人都知道,模板最初是用來取代宏(預編譯)的,這說明它的地位是靠近於編譯期寫代碼時的設計過程的,(雖然它也一直參加編譯以及後來的代碼生成,這就是它跟宏不一樣的地方)。而且模板也是類型嚴格的所以也是語法的一部分,既然是語法,就是語言的一部分(而宏函數雖然被冠於函數之名,但其實他不是語句也不是函數,根本不參於編譯)。。

模板,是在編譯期靠近編譯前端那一部分對類型進行控制和抽象的語言機制(即部分地在語法層次上直接寫程序,針對編譯時的實例化寫程序不需等到運行期才檢驗結果,而且更不是專門針對運行期語意和運行期語言機制設施寫程序。比如C++運行期OO),是C++建立在它的類型機制(特別是class通用類型)上的語法機制(所以不光有函數模板,結構模板,還有類模板),在這個語法機制上。C++可以做很多強大的事情(特別是設計上的,集中體現爲泛型編程與元編程),,運用這種編譯期的能力我們可以將C++視爲另外一種語言。即編譯期以模板爲腳本機制的語言(而模板是主要針對處理類型的)),.


但其實編譯非運行,我們知道運行期才能申請到系統資源,才能進行計算機的所謂運算,


其實對編譯期和運行期的誤解一切的罪原是解釋語言跟編譯語言的區別(如果沒有編譯後端,那麼運行期的目標就是中s間代碼),我們知道,在編譯前端完全之後,代碼就被生成了,對於解釋器來說,編譯後端是不必需的,此時它就是邏輯上可運行的一個整體。。即設計邏輯=編譯結果=最終運行邏輯。。只有等編譯後端是爲具體平臺生成代碼時,,這個時間纔出現第二個運行期,,要打亂設計邏輯,將語法級的編譯時的設計邏輯轉化爲變相的平臺邏輯,運行時就可以申請到系統資源了。。(因爲類似C++編譯器它的編譯部分和運行部分是他大爺的分開的),,這纔是運行時的標準定義。。

而模板絕非爲後一個運行期而存在,它在中間代碼層次上就可以工作了,因此模板中不需要分配變量這些運行資源(只能typedef,提供編譯符號給它就可以),它只是關於類型的純語法邏輯。因此類型也可以僅僅是一個語法佔位符(很難想象類型佔位符也能參與運算吧)。就跟宏一樣。。在寫模板時並不產生代碼,只有模板邏輯被實際調用時才被生成代碼。。

這對用語言來進行設計尤爲有好處。因爲他可以站在範類型的角度上進行脫離了運行考慮的編程(泛型,比如可以寫出type function這樣的語言結構,就跟純虛函數一樣,只是一個接口抽象。),

這說明,設計越來越靠近編譯期(甚至是設計期,比如對類型進行template化的策略行爲),而非運行期,等到運行期纔去驗證設計,,,這種用設計(而且是符合語法的設計)來制導編譯和代碼生成的過程就像語言中內置了一個UML一樣。。


而且模板是編譯期的,可以控制編譯器對邏輯的生成動作(元編程,比如它可以刻意不讓某些類型的模板進入編譯,你寫有關模板的代碼時,並不算待編譯的一部分,只有那些實際發生作用的類型的模板代碼才被編譯才被生成代碼,因此是一個很好的設計時機..

那麼編譯期運算(就是實例化,所以它會發生代碼膨脹問題)是怎麼回事呢?traits和policy是什麼意思呢?

首先我們要弄懂實例化
7.11 C++的元編程和泛型編程
相比C#和JAVA,RUBY這樣的語言來說,實際上即使是C++也沒有直接在語言級上提供太多的抽象機制,而其它的語言,比如JAVA在語言級就有代理,RUBY在語言級就有混入。相比之下C++中只有OO和模板,(故稱C++是第三代語言,而前者是第四代語言),而且C++的OO是二進制導向的,一直爲後人稍稍詬病,但C++的模板技術卻大受吹捧,它的抽象能力不小,首先它正用能得到generic programming,偏用能得到meta programming,而且絲毫不比第四代語言的那些抽象弱。這迎來了C++的再一次發展高峯。

模板中不需要變量這些運行期的設施,只需要type和常量,,它又是語法導向的(所以其複用的手段往往是提供源程序,而且其缺點是不能提供強大的運行期能力,比如Croba),而不是二進制導向的,, 因爲運行期纔有變量的概念,我們知道C++是個強類型的語言,因此無法做到運行期的duck typing,但模板使它具有編譯期的duck typing功能,OO的那種產生類型的機制是不純粹的,可侵入的(我們稱這種語言爲面向對象),而duck typing纔是好的產生類型的機制(我們稱Ruby,python動態語言實現的OO爲基於對象),而C++對於類型控制的能力在於它的編譯期(因爲它是強類型的),,而不是運行期的RTTI,實際上C++的RTTI是很弱的,它用來支持OO是需要花費運行期資源的,而作爲一門靜態類型語言的C++在編譯期就有了關於類型的一切資訊(比如它實現多態就把這稱爲靜態綁定),而如果將這種決策推遲到運行期(來實現運行期的OO多態),那麼就必須藉助於語言的RTTI機制。

meta programming是對模板的一種偏用,即trick而不是C++的一種技術,它是被發現而不是被髮明的只有範型,人們發現編譯期也能實現一些運算,比如模板中也可使用if else和遞歸這樣的語言機制,但實際上模板不是語言,無法直接識別if else和遞歸,實際上它只是傻傻地實例化(即使是這樣也能圖靈完備),但是從另一種眼光來看,因爲模板機制能間接支持 if else和遞歸,,所以它終究是一種圖靈完備的系統,即模板實際上可以看成是C++的一種子語言,另一種意義上它也是C++的embeded language)。(比如它用這些tricks艱難地實現了一套編譯期的stl,詳見c++meta programming那本書,而我們在用的源於HP的stl是運行期的)

最新的C++0x標準或許可以改變這種局面。C++0x標準爲C++提出了一系列改革,的目的就是爲能使元編程成爲模板的正用。。
7.12 元編程和函數式編程
lisp語言常常被作爲教學語言,因爲它源於嚴格的圖靈理論,lamaba高階函數理論,它的變體shemhe語言採用list,即廣義表作爲數據結構,這種數據結構可演化成任何一種數據結構,採用函數作爲語言內first class來作爲主要的開發範式,,,因爲這種開發方式是圖靈完備的,實際上它的計算能力是等價於任何一門語言的。理論上可產生一切邏輯。
C++中的函數並非first class,但它的元編程實現了一種metafunction,,這使得function(實際上是變形了的類模板)可以使C++在編譯期實現“函數式編程”,(在這種意義上C++的模板就成了圖靈完備的子語言了,實際上C++所謂的元編程主要是在模擬函數式編程)當然這種動作是深受限制的故而需要作抽象迂迴的,(比如它的metafunction不像stl的仿函數是利用運行期支持的設施實現的,BOOST MPL庫中的每個function獨成一個文件,都需要include才能用,這主要是因爲模板實現的函數並非類型,編譯期並不認識函數調用及函數原型不能以函數(參數1,參數2)的形式進行調用,事實上它只認識模板以及相關機制,實際上你還可以看到編譯期C++的好多奇特的語法都是源於編譯期不完善的設施在看相關書籍的時候一定要處處提醒自己把握這個要點(除非改良C++標準以使編譯器廠商支持編譯期待性比如最新的C++09的提出),
我們下面再舉一些編譯期的語言設施及導致的迂迴方式(某某說過,抽象是解決問題的一切方法)。
1.編譯期只識別遞歸(比如在遞歸中一個特化可以指定遞歸結束條件,要知道遞歸首先是一種思想,,它成立的本質有二,1,自身用自身來定義2,結束條件,,,如果說template<類型>是遞歸的第一層意義,那麼template<0>這樣的特化體就是遞歸的第二層意義使得遞歸得於成立),
2,enum(變量就不能用,只能用類型和常數這樣的編譯期就確定下來的值比如typedef),struct(用struct而不用class是因爲struct默認它的成員是public的)這樣的語言設施,而且只有int,bool作爲模板參數可用。
2,模板不能以模板爲參數,,除非“類模板模板參數”,這樣就不能直接產生foo(class foo1,class foo2),這樣的結構,,,要繞一些彎子,比如tagdispatch function.
3,循環展開通常採用了複雜的迂迴技術,
4,讓模板的成員帶value,模擬函數式編程的返回值。
5,因爲元編程實際上是對產生關於類型邏輯的一種編譯期策略(要知道多態化是設計的一個重要方面,編譯期模板使C++變成動態語言或無類型語言,而這使C++成爲跟Ruby,python一樣的高抽象設計語言),它將要做的事情要完成的邏輯提早到編譯期完成而不用到運行期的資源(因爲C++模板賦於它這個能力可以這樣做),所以通常可以用它來進行設計,模擬設計模式等等。因爲模板是語法導向的,所以它的源程序是需要按源程序文件的發行的,進行語法級的複用(模板程序只需要編譯前端就邏輯成立),而面向運行期的程序是二進制導向和複用的(事實上除了C++,沒有那門語言有這樣的能力吧),,這就是所謂的二進制複用,因爲變量,class的內存映射,,全部是運行期的編譯後端的事情。。
6,用list數據結構實現typelist.
寫在後面的話:其實C++的元編程屬於十分高級的抽象能力,我們不應該把精力放在這樣的語言機制上面,基礎薄弱的讀者和程序員只需要把精力放在學好C++的運行期OO和數據結構就行了。因爲大部分公司開發中甚至都不會用到這些技術。

7.13 C++的模板編譯技術本質
編譯器的工作在於將高級碼轉化爲平臺(這裏的平臺之意主要指CPU)目標碼,但是現代的編譯器和IDE還加了一些高級單元,如彙編器,裝載器,重定位器,鏈接器,調試器,這些的功能來直接生成可運行文件.
一個編譯器,如WINDOWS上的GNU,VC,BC等等,,對應於一個特定平臺,編譯器實現,鏈接器實現都是平臺相關的(這裏的平臺主要指OS)往往有一個RUNTIME 庫,,這個庫是什麼呢,,一個OS一般由C編成,這些C的庫一般向程序(向編譯器編譯出來的,往往是用戶空間的程序)提供CPU進程,,線程,SOCET等資源,運行庫就是提供這些訪問功能的地方,這就是編譯器跟平臺發生聯繫的地方,一般一個編譯器都有它對應於某個平臺的運行時庫..
那麼什麼是編譯期呢,什麼是運行期呢?什麼是編譯期靜態,什麼是運行期動態?什麼是靜態語法錯誤,什麼是動態邏輯錯誤呢?RTTI與RUNTIME有什麼聯繫,爲什麼有new這樣的動態分配內存.
編譯期靜態和運行期動態主要是指類型來說的,
那麼泛型編程跟模板又有什麼關係,一門語言的語言機制必定可在它的編譯器實現找到答案.那麼模板技術在編譯器中是如何實現以支持泛型編程的呢
我們知道,一等公民在一門程序語言中是重要的,C++不支持數組的直接拷貝和賦值,不支持對象的直接賦值,不支持範型類型,這就是因爲對象和數組是C++用其它方法抽象得到的語言機制,比如按位拷貝這種機器很擅長做的事,其實範型編程是可以用很多方法達成的,C++唯獨用了編譯器的"參數化類型"作法來實現模板再由模板實現泛型編程,而RUBY等語言有它自己的範型編程做法,,基於對象編程跟面向對象不同,不要以爲你會寫CLASS就是面向對象,如果沒有用到OO的繼承和多態,你同樣是在寫基於對象的東西.。
7.14 OO的缺點
比起函數式語言來說,因爲在一門OO語言內它的一等公民不是函數體而是OO對象,而且OO直接跟我們的設計思想中出現的元素掛鉤,因此OO成爲代碼複用和軟工時代最好的語言,(軟工這個字眼就指出它是計算機離散傾向人的結合.OO的提出就是爲了解決編程離散形式與人思想中出現的因素的對接,解決複用和開發中人人的交流等問題,所以離開OO是不可想象的),但是構建在OO之上的設計模式卻導致了越來越深的複雜性,這是因爲人們沒有想到OO以後的思想世界和現實世界應用,其實比OO這種形式要複雜得多,比計算機內部的離散形式還要複雜得多,換言之,OO並非一切,相反,在一定程序上提供方便性的同時,在另外一些維度和應用上導致了另外的複雜度,因爲思想永遠是複雜的嘛(殺雞用了殺牛的刀)
而函數語言或動態語言卻可以繞過設計模式實現同等的功能,因爲函數作爲first class是一種最接近計算機處理的形式(而OO同時接近計算機和人的思想,或者更確切地說是接近人的思想,看OO以後的GP,DP就知道了),,反而可以用這種唯一的形式繞過很多DP要花很多勁而函數式語言輕易就能搞定的一些東西.
但是不要認爲動態語言就能提代OO靜態語言(這種說法真可笑,複雜性有它的好處,複雜性同時也是相對的而已,RUBY在一些領域導致的複雜性比起JAVA來說會更多,只是不同情境的問題而已),哲學指出,任何問題都要實事求是,我們需要在問題需要什麼類型的語言纔去考慮選擇什麼語言,,考慮一門語言優劣時,並非純粹技術問題,有時是多維的問題,,比如語言的優劣還得用它適用不適用軟工這個指標來考慮呢
還比如JAVA的動態機制(反射加代理),這種歷史複雜性是爲了實現動態語言的"由行爲決定類型",,,這就是AOP,,JAVA居然站在它本身是個靜態語言的基礎上去做動態語言做的事,造成了很多歷史複雜性,討厭
而且,RUBY這些寬形式,自由的語言,正是因爲太自由了,就像C語言一樣,太自由,反而不能成爲軟工的首先,因爲軟工要解決複用和簡單意味着統一的形式的需要,即編程語言的離散形式和人的關係(所幸RUBY本身也提供了OO)..
所以,JAVA在WEB開發中,OO以後的那些WEB框架,是現實所導致的,並非JAVA本身導致的,,,改變J2EE框架就行了,並不要把JAVA改造或加個JRUBY就行的,,,相反,我們應更多地解決現實世界的思想模型,而非增大或擴展JAVA或JVM平臺自身,感覺在JVM上集成JRUBY實在是個蠢的作法,憑空增大了抽象和運行時開銷,就像把車拆了放到車庫裏明天又拼裝起來再開一樣..
其實以我來看,所有語言都是一種形式,要解決的問題纔要弄明白,如果這種形式不能表達更深的問題,那麼問題就出現在形式上,,,,OO最大的特點不是繼承,不是多態,而是"OO接上了計算機離散和人的思想中的因素體,促成了軟工時代的出現"
所以,不要去爲了技術而探索技術,,,現實世界纔是我們要最終解決的問題,而不是那些表達現實的形式語言..語言終究是形式.技術問題的複雜有時是歷史原因導致的,,,,細節和理論,形式和應用,,是二個可以並行發展的維度...其中現實應用更重要...RUBY聲稱它能提供編程對於的簡潔性,這就是一種很好的作法。
7.15 模板的繼承

7.16 模板的偏特化

7.17 真正的策略
策略爲什麼跟設計模式有關?

設計跟編碼之間無法精確定界,因爲設計可以僅僅是提出一個用於實際編碼所用的架構,也可以是考慮了所有編碼細節的詳盡設計,設計的最終目標可以是一張UML圖,也可以僅僅是一張草圖或一堆小卡片(Wildcard說法即來源於此,當然,並不一定要求設計要成檔,但是將它具現化表達出來還是很好的行爲),然而歸跟結底,我們設計最終是想產生一堆有機的類文件(UML圖也是,卡片也是),也即我們在進行設計時,我們的目標(Dest)是產生“編程領域對於現實事物或問題抽象的OO解(OO解只是解空間的其中一種而已,單純使用OO的三重機制這只是初級和簡單的範型,但已經是非常強大的範型,它產生的類文件已經可以實現出一個很大的架構,然而,結合了模板和策略的範型就更加是功能強大的範型了,可以更爲靈活地產生出一個架構)”,這往往就是一系列的Class文件,而設計的源(Src)則是“經過了編程抽象的現實事物或問題”,設計模式就是用來描述這個過程的,然而它又跟算法不同,算法體現的是大領域內對於某個事物的解決方法(這就是爲什麼算法也可以用計算機來表達的原因所在)

我們知道設計是高於編碼的,這就是說在我們進行設計時,進行的是一種純思想的活動(非常重要的是這裏的設計二字遠非設計模式中的設計那麼狹隘),並不預先想到要面向編碼而設計,然而我們得出的某種設計方案(比如一種架構邏輯,,設計模式)並不是不可以在技術上用一種方式來實現,因爲狹隘的設計模式之說的設計不過是根據設計方案產生一堆類文件而已,設計模式高於成碼跟設計模式可以用一種技術來實現並不矛盾,關鍵是找不找得到一種技術實現手段的問題,你可以聯繫ISO網絡參考模型來考慮,一種思想或協議的確可以用來實現。。就憑這點,我就十分佩服策略的提出者

策略是屬於設計的而不是編碼的(雖然策略也是寫代碼),,,這是本質,明白這點非常重要

策略就是這樣一種技術,模板技術天然就用於參數化類(這是模板的本質抽象)併產生類文件,這在設計期是一個天然的技術實現,比如一個類文件可以產生一個職責或對應設計中的一個對象,而模板可以跟據對象本身的特徵(比如對象的類型)去決定產生的表示這個對象的類邏輯,,而這正是策略要解決的問題

策略是多範型設計的較高境界,雖然OO也可很好地表達設計和思想,其它範型也可以,然而策略更接近設計,比如DP的Sington,Boost 的EnableIf,,從這些字眼上看就直接跟思想和設計已經十分接近了。(不要忘記DP中一種是模板方法設計模式,,這更一步反映了模板與設計的深層淵源)

策略與OO是什麼關係呢,(OO和OOP出現在前,,然後是DP,GP,Policy這些範型),因爲模板就是一系列可運行的參數化類的邏輯,那麼對這個過程也可以用OO(OO與策略並不是包含或被包含之類的關係)來實現,,因爲模板也支持OO,比如STL的源碼中,對容器,通用算法這些的策略大都沒用到OO而是一堆面向過程的過程模板(函數模板就是子過程模板,類模板就是用到了一點OO的模板),,(如果策略本身也是用OO範型(通常說的OO就是類文件級的OO)來實現的,那麼這可以稱之爲二階OO,第一階是1,模板產生類的過程中用到了OO,2類本身也是一種OO,)

因爲策略就是模板技術描述的設計思想,設計思想可以是Gof,Boost,Loki,Core J2EE Spced DPS這樣的大思路,也可以是一切反映設計的小思想,,因此策略不僅僅是我們現在已經見過的上述一些實現,,,它可以是千千萬萬的不同思想的模板實現形式(因爲思想有千千萬萬嘛)


什麼是編程,什麼是抽象能力,編程能力,什麼是學編程(即使這樣看似最最簡單的問題也其實不會簡單,網上的確有太多這樣的爭論,那麼這裏將會給你一個有形式描述的概念,關於我在一個維度的認識,因爲我們在上面提出了諸如設計,抽象,原語之類的用詞,因此接下來的描性會變得容易界定,這就跟設計模式內部的那些功能分工的說法一樣,,只有給出了能形式化的中間邏輯用詞,那麼才能不會在爭辯時連最細小的中間共識都不能達成,那麼也就不可能得出一個最後的結論),比如我在第五部分會寫到一個世界邏輯庫,因爲要在這個邏輯世界裏產生很多Actor,,而Actor本身又有Type,,Ablility之分,那麼我可以用模板表示,,(ectc),如果你能像我這樣想,那麼恭喜,你已經在非常有成效地學編程了,,,而你在看我寫的代碼,,那麼再一次恭喜你,你已經在實踐了

所以什麼是編程,,編程就是用各種範型來實現設計,,,

學編程就是在大範型之下學大量細節的小范型(所以說JAVA並不是一種很好的拿來作爲教學語言的語言。因爲它在OO方面和OO後面的事做的很好,然而在OO前面的那些範型全部被忽略了)

抽象能力就是對外來架構的理解能力,和掌握大幅中間邏輯,,以構造新邏輯的能力(計算機與人腦的區別就在這裏,人腦可以主觀能動地發明抽象,,而計算機只是我們用來反映抽象的工具,它本身並沒有主動意識和主動構造抽象的能力)。
7.18 爲設計產生代碼
 什麼是模板會產生代碼呢,這個說法其實無比簡單,只是當我們擺出諸如“爲設計而設計代碼”,“編譯期而非運行期多型””C++的元編程技術”這樣的別稱時,它就變得遠離我們的理解變得面目生疏了,
  
  我們知道,在編譯型語言中,比如c和C++中,寫代碼就是爲運行做設計,程序設計期designtime的就法就因此而來,寫代碼的本質就是在C的數據封裝抽象基礎上,C++的基於對象抽象上,C++的OO抽象上這些語言機制邏輯上+操作系統封裝了的CPU邏輯上,寫最終面向CPU的二進制代碼,這就是我們說的,系統編程語言的本質在於基於系統邏輯(並且語言本身也是基於系統邏輯的,我們把計算機系統離散基礎,語言實現離散基礎,CPU彙編語言所涉及到的那些機器離散基礎都稱爲系統邏輯),寫面向系統邏輯的代碼。(與它對應的腳本語言更多地是爲了直接爲應用服務因爲它極力讓開發者繞過系統邏輯)。程序運行的本質是什麼呢,,就是向操作系統和CPU請求資源,CPU執行二進制,(任何我們寫出的源程序,在經編譯優化後形成目標平臺二進制碼,我們向PC編程以構造應用的過程只是系統邏輯到應用邏輯的的一個維度變換),這是指比如C++這的以本地平臺爲運行環境的系統編程語言,而非指自帶VM的,向VM編程的腳本語言而言的。
  
  設計期就是寫代碼期,是用戶動作,發生在編譯期前,編譯期不是用戶動作,運行期又不是用戶動作。。
  
  模板是一種參數化的泛型,泛型這二個字決定了模板沒有一個類型(在寫模板函數,或模板類時的設計期,能難想象沒有類型的東西能夠堂裏皇堂的參加運算吧^-^,只有編譯器技術到家就可以),,在寫出這個函數模板或類模板後經編譯時它並不產生代碼(機器動作時),只有在接下來實例化時(注意,這個實例化是指在設計期用戶動作期,用戶爲這個泛型佔位符指定一個具體類型並寫出關於它的代碼,這個實例化跟我們在OO程序設計中,由一個class定義一個對象類似,接下來,在編譯期機器動作時纔會在編譯期實例化產生實際代碼到中間目標文件中比如obj中(非飛行模式),
  
  什麼是實例化呢?注意這個字眼
  
  在OO程序設計中,由一個class定義一個對象並不實際動態分配內存,因爲這還是指爲運行期作規劃的設計期並沒有實際進入到運行期並分配了一個內存,如果用了new關鍵字,還是沒有分配內存,編譯期永遠不分配內存,只是爲程序將來在運行期如何獲得內存作分配上的控制而已,,
  
只不過一個實例化用了不同的情景而已,實例化並不一定指爲運行期實際實例化(也就是說實例化並不一定就是一種“爲運行而作設計”的設計,,它還可以是一種“爲設計期而作設計”的設計期動作,)它其實就是一種編譯期的宏,只不過它有類型的判斷,因此遠遠不是簡單的文字替換性質的宏,,而是一種編譯技術,模板由於有這個特性,第一,因爲它是泛型,所以爲C++帶來泛型編程機制,第二,模板給C++帶來元編譯技術。
7.19 真正的metaprogramming
不是指.net那種共用一個庫的CLS
面向對象在複用工作方面做得很好(比如它提供了繼承,多態,還提供了二進制複用比如COM,還提倡用類聚合代替繼承而不是tear off,還出現了諸如設計模式這樣的複用經驗),但是這是相對現實生活的那一端做的工作,,然而它對於編程工具端(編譯器)本身來說是不友好的(程序源碼必須要進入runtime才能讓我們看到這所有的OO裏面發生的事,在編譯階段(一般也稱爲design- time)我們不能控制這些OO對於問題域的實現),我們應該在沒有讓程序進入某種具體編譯器之前,,就讓它得以被控制,而不僅僅是預測這些編譯的文件進入runtime以後會形成怎麼樣的邏輯
也即,類的職責單位是類文件,這種機制有一些的缺陷性,問題域是巨大的,如果我們動手一項工程,我們不希望被無窮的細節所困擾(現實問題總要分解爲一些類,最終要形成類文件,一般每個職責形成一個類),我們希望有一種介於編譯器和現實問題之間的更大的整合層來考慮事物(而不是一個一個的類文件),,也即,我們不需要考慮現實問題到類的實現路徑,我們希望在設計期就考慮現實問題到一個“比類還大的”,“更接近現實問題”的邏輯層上去,再由這個邏輯層到最終的類實現路徑(比如單例模式,就是指代設計中只能出現一個實例的邏輯實體,這已經十分接近設計了)
如果這個層面被提出來,它甚至不佔用到運行的時間,,即增加這項抽象,並不耗費運期間任何成本(因爲它只發生在編譯期)
因此它是語法導向的,而不是虛擬函數集導向的
這個整合層就是策略,,,模板技術允許我們在編譯期就用“策略組合”加“模板技術”來生成源程序,這實際上也是編寫庫爲用戶所用時所要考慮到的問題
用戶希望能從庫中提取功能的子集,這勢必產生這裏提到一個trait的概念,簡單描述一下先

這就是元語言,
問題域永遠是多元的,因此需要元語言,數據格式是多樣的,因此會有表示數據的通用方法 XML,一般用XML格式的metadata來封裝其它格式(即:不可能把所有格式的數據包括二進制啊,PE啊,,TXT啊都做成XML,但我們可以把這些數據格式放進一個XML的字段內,XML就成了一種通用的數據打包形式並用它用於進行統一數據交換過程,然後在使用時從這些這段中取出各個格式的數據再解碼成所需的格式,W3C組織正在策起一個關於XML二進制格式的方案)。
7.20 元編程技術
承接我上面一篇文章《爲設計產生代碼》
  如果實例化不是爲運行進行設計,,而是爲了爲設計期產生代碼這樣的目的而進行的設計期的設計(很顯然,它本身也是一種設計,並不面向運行實例化,這是一種靜態實例化,編譯期的實例化,即它向前推進了一個意義層次),而且當模板機制的編譯技術實現涉及到詞法處理,語法分析這樣的編譯原理知識時,,,那麼模板就是一種地道的編譯器的編譯器了,是一種元編程技術。
  很多時候,理解以上的東西,不但要求你有實踐經驗,而且還需要你有精深的抽象組織能

新手編程導論(九)



第8章 抽象之設計和領域邏輯


8.1 大設計
範意上的設計是廣泛的,不僅限於計算機的,也不限於軟工抽象,軟件的設計哲學是可以用來解釋一切的,因爲它是真正的哲學,而真正的哲學並不僅適用軟件開發(軟工和計算機是二個完全不同的抽象,雖然沒有人提出過計算機抽象到底是什麼,軟工抽象到底裏面有哪些抽象存在,我們僅能站在某個或某些維度上給出一個描述性的概念而不是有限集,這也就夠了,如果能站在一個大全的維度上說明到軟工的全部抽象,雖然這是不可能的,但我們還是給得出的這個結果取個名字,叫範式,範式在意義上是大全而的抽象,然而人類的範式總表現爲某些維度上的產物(只是我們在一本無論多麼長的書裏都寫不完而已,如果有那麼一種生命力和那麼一本書存在,我們也堅信任何一個道理都不可能寫完)。

設計並不僅僅面向於創新,有時是形式的重組,而不是內容的創新,應用形式的改觀,設計出的產品,要源端是面向人的,因此要提供足夠簡單的使用和訪問形式,在目標端是要達到足夠豐富的應用邏輯(比如XML統一文檔交換,但可由node,root這些形式導致足夠豐富的深層功能,深層這裏是指上層),因此越複雜越大而全越好(但是如果沒有足夠人力,我們應考慮設計出別人想不到的商機),應用形式和應用邏輯作爲設計中應主要考慮到的問題,有很多人想到用一種形式來統一既存的東西,比如在jvm搭建Windows和其它一切軟件,然而這是多麼無聊的事啊?
明白了原理跟實踐,細節與思想之間的關係,我們就會有選擇性地去學習細節,,一定要研究細節,因爲細節是重要的,你得用這種技術細節實現某個東西,不一定要研究細節,還是因爲你要不要用到它,然而思想是重要的,明白了這個思想,你就明白有些知識是技術細節,有些思想卻是根本,當然,沒到考慮思想的水平,技術細節永遠也還是重要的,不要企圖去鑽研一切細節,因爲很無聊,除非你是在滿足自己,一生學習細節,這又是多少無聊的事
明白了抽象,我們明白一個東西,應做個明確這個東西的抽象所屬,即它所在的人類範式.
在設計軟件時,我們主要用UML工具,但是這東西是靜態語言用的

8.1 什麼是設計
我們必須明白,設計這個詞是一個表示人類活動的詞,比如有建築設計,藝術設計,,而程序設計只是一種。它可以解讀爲用程序設計語言提供的基本語法和高級語法,以及獨立於程序設計語言但跟語言有關的那些手段(比如Corba,設計模式),來進行對方案域到目標應用域的一個邏輯映射(一種能產生最終產品但並不以產生最終產品爲終止條件的人類活動過程,它包括選擇語言的考慮,應用設計,模型抽象,維護各個部分的整個綜合過程,也即設計等於軟工)。故涉及到不止程序設計語言一個方面。下面我們將就這些方面一一進行解說。

l 因爲程序設計是人的活動,所以它首先是一種對人類活動本身的設計。設計模型的目的正是在於規範程序設計的各個過程。使開發過程可控化,這就是程序設計模型要解決的問題(XP編程啊,瀑布模型啊)。

l 再次,設計需要對代碼邏輯的控制,因爲是由人來寫代碼而且也是由人來維護代碼的,所以選擇一種能切合人類層次又靠近語言支持的方式是必要的,過程式使人類能以發號施令的方式寫計算機邏輯。面向對象單方面站在人類抽象的角度將計算機邏輯封裝爲一個一個的對象。並不需要考慮對計算機的實現過程(當然在每個class內部也可以有過程式),相對面向過程來說,面向對象是更抽象的。當然,這些都是大的代碼邏輯控制手段,和高級語法機制,小的可以小到“流程控制,循環”這樣的課題。

l 在語言選擇方面,如果是通用編譯型語言,因爲類型是語言表達客觀事物或客觀邏輯事物的機制(使之成爲語言的數據使語言成爲一個DSL),所以代碼邏輯的抽象首先是一種類型抽象的過程,C++中有OC(面向類化),模板範型這樣的對類型進行抽象的機制,如果是通用腳本解釋型語言,它將類型動態化,這樣就能更好地表達領域詞彙,DSL抽象。相比通用編譯型語言來說,這二者都存在設計的抽象和實現(因爲都存在類型),但顯然腳本語言更靠近實現,,這二者的唯一的區別在於,編譯型語言用了嚴格類型的抽象來表達DSL邏輯,而腳本型語言採用了活動類型的方式來抽象DSL邏輯。

l 程序設計還是一種對應用的抽象過程,因爲任何軟件的編制在於解決目標問題,面向一個目標領域,無論是架構師還是程序員方面,都需要面對程序設計要解決的問題和領域進行抽象,,提供一個科學組織的抽象框架以更好利用複用或更下一步的抽象,軟件產品的用戶有三,最終用戶,最終腳本用戶,程序員用戶。其中,2,3是能有機會進入到程序邏輯內部的用戶。如果是寫代碼的那個人來維護程序,實際上就是下一步的抽象,下一步的抽象和複用不過一個詞說法的二個方面。

然而,雖然抽象過程並不需要預先考慮軟件複用能力,但是科學組織了的,鬆偶合,緊實現的抽象過程顯然可以爲整個設計中的所有階段帶來好處。

綜上所述,無論如何,設計的最終目的是使軟件產品具有更強大的生命力,對其內要做到科學抽象和組織,對其外要做到利用複用,易於重構。

什麼是重構呢,重構並非重寫,重構是面向爲已有系統增加新功能或完善已有功能的需求下,在原有系統已經存在的情況下,變更其設計方式(注意設計這個詞),修補式地加進一些新的設計元素或去掉一些舊的設計元素(故往往用到設計模式,因爲設計模式可以很好解決此類問題,所以在介紹設計模式的一本書裏,總是會有這樣的問題和需求發生在先,然而才導出一個個的設計模式),重構往往是解決代碼維護問題的一個重要方面,當然,正如上面提到的,軟件產品在最初被設計和產生時也要考慮到一個科學設計的過程,這樣才有利於後來產生新需求和新問題的重構過程。

這裏首先有一個實現與抽象的區別所在(人們往往把設計和實現對立而模糊了對實現的理解,其實,抽象和實現纔是一對對立體,找準了這個才能正確地理解實現,就像對腳本語言的理解,如果你一開始知道它是系統編程語言的對立體,那麼你就不會產生腳本語言是不是一定是解釋語言這樣的疑問,因爲它一開始是非系統編程語言,其使用解釋或編譯是它第二個要解決的問題所在),但設計和實現無法分界,這首先是因爲語言沒能爲它們提供一個有效區分的機制(Delphi 的單元文件pas中有接口和實現這樣的關鍵字,C++默認將h文件和cpp文件分開,然而這都不是嚴格的抽象與實現分開的機制和行之有效的方法,這根本是因爲這二者根本就是具體問題具體分析的事,無法在形式上進行區別,但提供h,cpp這樣的機制也並非錯誤無效的方法---請參照選讀中的“形式主義”),OC可用於設計中的抽象,也可以用於在先前設計抽象基礎上進行的下一層抽象(實現)。

那麼是不是可以將抽象分層呢,一部分是DSL類,一部分是字串這樣的計算機實現類,將前部分看成設計,後一部分看成實現,,但實際上這樣也是不行的。因爲無法精確給每一種這樣的抽象分層,而且即使分層,也是有很大侷限性的。

比如在庫的設計和利用庫進行實現這二個過程中,都可以寫函數(以過程範式編碼),顯然地,庫的設計是高度面向複用的,而實現是一種函數調用,然而爲什麼庫的設計中的函數纔是設計,而實現中包括了函數調用的函數體就不是設計呢?他們同樣是有函數原型的接口的啊。這就說明了語言本身並不能嚴格化這個區別。能嚴格化的就是具體問題中的具體區別方法。

這就說明,存在接口的地方就存在設計(因爲可供再被可複用,而設計的一個意思就是爲二層用戶提供複用邏輯考慮的過程,比如接口,我們在比較大的層次舉個例子吧,比如用C實現的工具庫和抽象庫)。我們應該嚴格在源程序中標誌出哪些接口是作爲最終工具使用的接口,哪些是能再供抽象使用的接口。

因爲抽象最終要形成源程序,要在源程序中反應出來,而源程序是由自己或別人查看的,所以說抽象對設計是至關重要的,因爲抽象是擇其事物一方面或某維度進行抽象,所以設計不必大而全,那些“理想”靠近現實事物模型的抽象反而不是最好的抽象(比如嚴格用OO設計一個遊戲的圖形世界,把每一個樹葉都封裝爲對象,還把導演,這樣的概念抽象出來),因爲這樣的抽象往往太大了,或不必要帶了太多錯誤的臆想。抽象只能是現實事物的一個或某些方面的變形。甚至帶有邏輯模型抽象,而不全是實體抽象。
8.2 編程能力,代碼控制能力,複用與接口,輪子發明與使用
你應該知道,真正的編程能力不是使用輪子的能力(即不是複用能力),而是將現實問題用編程語言來解決的能力(這就需要你具備設計能力,即很強的代碼控制能力,能把現實問題抽象爲代碼而不僅僅滿足於使用別人現成的庫)雖然複用能力與設計能力都貌似語言能力,從語言的眼光來看,複用能力是屬於設計能力的,但實際上他們還有很大不同的要求有不同的語言能力,有些人具備在不懂STL原理的基礎上使用STL的能力,但它們就是不能用STL來實現一個STL。。這就是上述二種能力的根本區別的證明。

(往往我們把複用能力當成編碼實踐能力,把設計能力當成廣義的編程能力)。


有時難以理解的絕對不是代碼本身(語言的語法和語句有限),難理解的代碼是其中體現出來的設計元素和應用元素..也就是所謂的算法和設計,,有時是設計模式,有時是現實問題模型的相關抽象(其實算法和數據結構也是計算機開發中歷史形成的抽象,,即某個“現實問題”,但一般將它們獨立出來)。

要知道,程序的編制者跟程序的使用者有時是不一樣的,這集中體現在C++的模板的複用上,C++的模板是用來書寫高抽象庫的好工具,然而類型的泛化意味着通用,因爲像STL一樣的東西意味着它能並適合於提供某種抽象上的規範(接口),,我們使用STL就不必處處屈就通用了。我們只需要“使用”,(我們實際上也不必提供太多的輪子方面的東西因爲我們正是使用輪子,因此只需開發出使用這些規範的實踐,實例),就跟遊戲引擎和遊戲的區別一樣(面向通用的庫或面向實現特化的庫都有)。。因此這二者涉及到的模板編程技術是有高有低的。會複用並不一定會寫。當然,理解stl庫的實現過程中使用到的模板理念顯然可以幫助我們複用這些規範(接口)進行實例化(使用接口)的過程。

實際上,數據結構,設計模式,現實問題這三個東西跟任何一門程序設計語言都沒有關係,因爲任何一門馮氏模型決定下的開發語言都滿足這些東西,,而且計算機界的一切軟硬基礎都是算法加數據結構的集中體現(再往人類的軟工一點,就有設計模式了),我們編程往往涉及三個能力
1, 語言語法的,比如流程控制,數組,IO,字串,
2, 數據結構和算法的。
3, 設計模式的
4, 現實問題的。
用某種語言來編程,體現的代碼控制能力只有1是必要的,然而具備了1並不意味着你就能定出某種數據結構,或設計模式,,或抽象出某個現實問題,,因爲那根本是另一些領域需要你掌握的東西(而不僅僅是語言能力)。所以你彙編碼能力並不意味你有編程解決現實問題的能力。你有編碼能力所以一定有複用能力,但你不一定有實現這些輪子的能力(以及閱讀這些代碼時認識到這些輪子設計的能力,這樣你閱讀代碼的能力和眼光也會受到限制,只限於簡單的複用能力,因爲複用能力只要求你擁有接口複用能力,,真正的邏輯不只是接口間的簡單複用,而是接口間複合形成什麼樣的抽象以及如何邏輯上形成這樣抽象,這就不僅僅是接口複合作用了)。

所以真正的編程能力在於2,3,4,加1編碼能力,,所組成的綜合編程能力;
8.3 OO,模板,設計模式與設計
設計即邏輯的邏輯,而且是面向人的控制邏輯的抽象即設計的第四種意思是讓抽象以人.需要的方式進行組織,即人影響抽象的能力,

實際上設計無所不在,從你寫第一行控制流程代碼開始,你就在設計了,只不過是隱式的,機器並不會流程控制,是語言賦於你能以人類看得明白的流程設計邏輯向機器發令的。如果說流程控制設計是細小的,那麼OO是一種顯式化的設計,當你用OO來寫代碼時你就在設計,雖然你寫的是實現,設計與實現間無明顯分界,,,你照樣是在設計,因爲你用到了OO的三重思想,繼承,泛化,封裝,,這就是控制邏輯的邏輯,,是設計因素。。一句話,設計無所在不在,即使在C那樣的緊實現的語言中也存在設計。。

所以存在三種設計方式在C++中,OO,模板和LOKI。

模板的能力在於抽象類型,所以設計的一種意思是類型控制和抽象能力(動態類型更側重實現談化設計所以它首先去掉強類型,而提供弱類型或無類型或動態類型),模板只能只做類型控制,然而LOKI可以做到控制邏輯級。這就是單純的模板用於設計和LOKI用於設計時的區別。。


在抽象類型作爲設計手段的方法上,OO和模板是差不多的,然而不同的是模板是“泛化”類型OO是純粹只用“類型化”來實現設計(對即邏輯的抽象邏輯,我們知道,實現中也離不開設計,設計庫的設計就更離不開設計了),,所以OO的設計手段不足,而設計模式是一種新的設計理念,語言並不直接支持,,C++用STL作爲語言的數據抽象,,用LOKI作爲語言的設計能力(OO,模板相比來說是小規模的設計手段)。

注意,OO的類型化絕對不能說成對象化,OO定義成面對對象是極其錯誤的譯法,,OO是用於設計的,,所以O這個字眼只能是“類型化”的“類型”,而不能譯成對象,,因爲對象是實現裏面,不是OO設計的原義所在。


設計的唯一特徵是站在高階的泛,這就成爲區別實現與設計的根本,而不是大小問題的邏輯包含互飾關係(因爲這樣的話,設計與實現只有模糊分界),而泛成爲區別這二者清楚的界限。

爲什麼template僅有type as type一項就足於跟OO匹敵呢,因爲類型就是語言的一切,提供關於類型的機制就是一門語言的設計能力的一切。。

動態類型語言也有類型,,不過它的類型不需要一個設計期的type來確定指定而已,,其本質也是某類型的某個value.

而動態類型沒有type,所以沒有對數據的泛化,也即無法抽象數據,也就無法抽象代碼和控制(設計裏的)邏輯,所以無法設計(設計的一種意思是類型控制和抽象能力,,動態類型語言不存在對類型的抽象,所以至少這種意思的設計在動態類型語言是不存在,因爲動態類型是DSL語言不是通用語言無需考慮類型及類型抽象上的設計)。它只能在線控制邏輯的運作。

所以動態類型語言是實現語言。。應該稱動態類型語言爲在線類型語言。模板是離線類型語言。

我們知道設計是複用導向的,最終用戶(領域內用戶,腳本用戶)和程序員用戶(本語言內的複用,語言非通用的)是二級不同的面向級,前者用動態實現語言即腳本語言,而後者用靜態語言爲宜。

解決可複用銀彈問題的根本可以改造開發模型,,也可以將設計邏輯儘量多地沿伸到實現,而且是腳本用戶級。因爲此時沒有編程工作,只有配置工作,銀彈問題根本不存在
8.4 設計能力和程序員能力模型
設計能力第一個是結合了數據抽象和代碼抽象以及語言映射能力到現實問題的解法的能力,第二個是構架架構的能力(提出一種規範一種抽象模型,就像Java規範和七層模型一樣,相比第一種設計能力來說,這是高級的能力)。

設計還有一個方面是面向複用的工程設計。就是開發庫的設計意義所在,

理解設計對理解C++新思維那樣書裏的觀點有效。

馮氏模型的侷限和優點同時體現在數據結構和設計模式上,它使人無論如何都要最小在這二種子抽象上工作,,優點是,統一了開發抽象,,這也使軟件邏輯變得統一。這對軟工是尤爲重要的。

程序員的能力模型,語言30%,數據結構50%,對現實事實的抽象理解能力10%,,設計模式能力10%。。 ==100%。

真正的編程能力是設計能力!!對思維模型的學習!!而非對細節,對平臺編程!!事物的OO解只是事物解空間中的一種而已!!

8.4 自上而下設計和自下而上設計
基本概念

抽象在軟件開發中的重要性是不言而喻的。如果一個系統有了正確的抽象,那麼這個系統就更容易理解,更容易維護,開發起來也更爲高效,最爲重要的是也更容易把事情作對。Grady Booch甚至認爲抽象是應對軟件複雜性最爲有效的手段。在面臨一個複雜的系統時,往往只要再提升一層抽象層次(當然要是正確的抽象),那麼該系統就會立即變得清晰、簡單,理解、開發、維護起來也更爲容易一些。不過,值得注意的是,雖然有了一個正確的抽象後,可以大大降低理解、開發和維護的難度,但是要想得到一個正確、合適的抽象卻是非常困難的。



提起代碼自動生成,可能大多數人立即會想到這樣一種情形:只要獲取了系統的需求,那麼就會自動地從這些需求生成系統的代碼。這種想法固然很好,但是在目前的科學發展水平(這種技術不單是軟件技術的問題,它還和人思維的生物基礎有密切關係)下卻還無法實現。需求和能夠自動轉化爲代碼的精確的形式化規範之間有一個巨大的鴻溝,目前這項轉換工作還只能由人來獨立地完成。因此,這種技術在目前來說只能是一個神話。我們在本文中所指的代碼自動生成是針對比較侷限的領域而言的,即需求已經被正確的理解並由人轉化爲解決方案領域中的抽象模型。代碼自動生成並不是爲了替代人去完成系統的軟件開發的,它只是一種支援抽象的工具而已。



領域特定語言(DSL)

其實,大家在每天的軟件開發中都在不經意的使用着這項工具。當我們在使用面嚮對象語言進行軟件開發時,其實我們是在一種抽象的層面上進行工作。有了這層抽象,我們所編寫的軟件就會更有表現力,更爲簡潔。比如:當我們寫下下面的代碼行時:class Cat extends Animal。我們想表達的是Cat is a Animal,面嚮對象語言爲我們提供了強有力的支持。而這個抽象的實現細節則由編譯器來完成,我們無需關心。這樣我們就能夠在一個更高、更直接、更易於理解抽象的層面進行開發和交流,同時也大大降低了出現錯誤的機會。當我們使用支持泛型或者AOP的語言進行軟件開發時,其實我們是在另外一種抽象層面上工作。比如:當我們寫下如下代碼時:

template

T Add(T, T)

我們想表達的是求兩個類型爲T的變量之和,而不管T到底是什麼具體類型。想想看,如果語言不支持這種表達規範,我們要寫多少個雷同的Add方法。有了這種表達規範,我們就可以直接、簡潔地表達出我們的意圖,而具體的轉換工作就有編譯器代勞了。還有,如果我們在支持AOP的環境中進行軟件開發,那麼我們只要使用該環境提供的AOP語言規範定義出我們希望的橫切關係(其實就是一種抽象),剩餘代碼的編寫和插入工作就由該環境幫我們自動完成了。雖然編譯器或者開發環境在底層生成的實際上也是大量重複的代碼,但是這些代碼的抽象規範卻只有一份,而人們開發、維護、溝通所基於的正是這唯一的一份抽象規範,底層的重複實現細節對開發者來說是不可見的,並且是自動和抽象規範保持一致的。可以說,在開發者的頭腦中,是沒有重複的。從而有力的支持了“once and only once”和DRY原則。試想,如果語言種沒有提供這種描述規範,那麼我們要編寫多少晦澀、難懂、重複的代碼才能描繪我們想要表達的概念。



上面提到的抽象是一些比較通用的機制,因此一般都是語言內置支持的。也正是其通用性使其有效性範圍受到了限制。一般來說,上面的一些抽象機制是僅僅針對開發者羣體而言的,並且使用這些抽象機制進行的表達也是由編譯器來自動生成底層執行代碼的。但是,還有一種抽象表達更爲重要,它的作用是在開發者和客戶之間進行溝通、交流。說它重要是因爲它和所要開發的系統是否能夠真正滿足客戶需要密切相關。這種抽象表達更貼近具體的問題領域,因此也稱爲領域相關語言(Domain-Specific Language(DSL))。比如,如果我們開發的是一個金融系統,那麼如果我們能夠使用一套金融術語及其關係來刻畫金融領域中的一些業務邏輯,那麼不但表達起來會簡潔、直接得多,更重要的是客戶也更容易理解,和客戶溝通起來也更爲容易。再如,如果我們要開發一個科學計算系統,那麼如果我們擁有了一套描述科學計算領域的詞彙,那麼在表達該系統時不但會容易、自然很多,而且也更加高效。有了這套DSL之後,剩下的工作就是要自己實現一個編譯/解釋器,來把DSL自動生成爲目標語言。由於這個DSL一般都侷限於某個特定領域,因此其編譯/解釋器實現起來也不會有多大困難。



敏銳的讀者一定會發現,我們在前面列舉的支持面向對象(OO)、泛型(GP)或者面向方面(AOP)的語言,其實也是DSL的一種。只不過它們所針對的是更爲通用的,和軟件要面臨的實際問題領域無關的領域。它們的作用是爲了提高一些通用問題描述的抽象層次,並且也爲構建更貼近問題領域的抽象提供了基礎。



自上而下 還是 自下而上?

寫到這裏我突然想起一個非常有趣的問題,那就是大家常常爭論的自上而下開發和自下而上開發。一般認爲,自上而下的開發方法更具目的性一些,也更爲自然一些。其實自上而下的方法是有很大風險的,一是往往很多預先的設想很可能本身就是錯的,一是這些預先設想落實到底層時要麼無法實現,要麼無法很好地適配。其結果就是生成一個具有大量冗餘、醜陋粘合層代碼的系統。而採用DSL思想的自下而上方法則具有很多你可能沒有想到的好處。你可以先實現一些DSL中的單個單詞,然後再實現一些更大的單元,並試着把這些元素組合爲更大的領域邏輯,按照這種方法實現起來的系統往往更加簡潔、清楚、乾淨,甚至更加易於重用。一般來說,如果你想採用DSL的開發方式,動態語言大有用武之地(Python、Ruby都是不錯的選擇,在www.martinfowler.com/bliki中,Martin Fowler對動態語言和DSL的關係進行了深入、有趣的描述)。



自下而上的做法實際上是在改變、擴充開發語言,使其適合於所面臨的問題領域。當你進行軟件系統的開發時,你不僅僅只是把你所構思的程序直接映射到實現語言,同時你還不斷對語言進行增強,使其更加貼近你所構思的程序,並且表達起來能夠更加簡單、更加直接。比如:你會想語言中如果有了這個操作符,表達起來就更加清楚、簡潔了,那麼你就去構建它。這樣,語言和程序就會一同演化,直到二者能夠形成一種完美的匹配關係。最後,你的程序看起來就會像是用專門爲它設計的語言開發的。而當語言和程序能夠很好地相互適合時,所編寫的代碼也就會更清晰、更少、更有效。



值得注意的是,自下而上設計相對於自上而下設計來說,並不意味着用不同的順序來編寫同樣的程序。當採用自下而上設計時,常常會得到一個和自上而下設計不同的程序。所得到的不會是一個單一的單片機(monolithic)程序,而是一個具有更多抽象操作符的更“大”的語言和一個用該語言編寫的更“小”的程序。此外,這個語言的抽象操作符也很容易在其他的類似的程序中得以重用,所編寫程序也更加易讀、更加易於理解。



還有一點值得提出,那就是自下而上的開發方法可以儘快地得到來自代碼的反饋,可以及時進行重構。我們大家可能都已經知道,設計模式一般是重構的目標,這裏我想特別指出的是:DSL往往也是很好的重構目標。



抽象、庫和DSL

C++之父Bjarne Stroustrup經常強調的“用庫來擴充語言,用庫來進行思考”( http://www.artima.com/intv/elegance.html有近期對Bjarne Stroustrup的採訪,他再次強調了這個問題),其實就是在強調DSL以及自下向上開發的重要性。庫就是DSL的一種形式,Bjarne Stroustrup所列舉的科學計算庫的例子就是科學計算領域的DSL。構建庫的過程其實就是在朝着更加貼近問題領域抽象的方向邁進。明白了這一點,我們就不難理解Bjarne Stroustrup一直強調的泛型在構建一個優雅、高效庫的方面的重要性了,因爲泛型爲構建這種抽象提供了一個堅實的基礎。



不僅C++如此,其他一些語言(比如:Java)也擁有衆多的庫。這些庫在我們進行軟件開發時,爲我們提供了強大的支持。不過,這些庫往往都只實現了它所針對領域的一些通用基礎性的問題。因此,在某些特定問題上,可能無法直接地使用這些庫來進行表達。此時,你就應該考慮建立一個特定於自己問題的特定庫了。



結論

作爲開發者的我們,該如何做呢?正如本文開始所說的,抽象固然很好,但是要找出適合於自己手邊問題的抽象是一件很困難的事。它應該是一個不斷從代碼編寫中得到反饋,然後再修正,接着再編寫代碼的循環反饋的過程,這也是我爲何認爲自下而上開發更爲有效的原因。我們不應該侷限於僅僅會使用某一種工具,某一種開發環境,而應該多想想這些工具、開發環境背後蘊涵的思想。我們應該注重於培養自己發掘抽象的能力,這方面能力的培養既需要很好的和客戶溝通的能力,同時還需要有堅實、高超的軟件能力。



此外,近期熱的不能再熱的技術MDA,其核心思想其實就是DSL,不過我對於其試圖使用一種語言來刻畫所有領域模型的做法並不看好。與其使用它,倒不如逐步演化出一個特定於自己問題領域的Mini DSL和一個DSL編譯/解釋器,並基於這個DSL來進行開發,這樣或許更爲直接一些,更爲輕便一些,更爲清晰一些、更爲有效一些,更加具有針對性一些,也更爲經濟一些
8.5 大中型軟件和複用與邏輯達成
我們知道一個庫的發佈總是帶一個samples目錄,然而那是小例子,,大中型程序是怎麼開發出來的呢,,你該如何着手寫一個大型的程序呢?一般來說是找輪子和寫實現。

如果開發中存在造輪子的工作,那麼很多邏輯都在這裏面,,就拿開發遊戲引擎來說吧,引擎部分總是要考慮進很多邏輯,有時是複用別人的庫,有時是很多預考慮的邏輯,有時是設計模式方面的邏輯,考慮OGRE就知道了。

就複用邏輯(比如一個使用了OGRE的遊戲的實際代碼)來說,,它往往是OGRE中的samples演示小程序,如果是一個用yake寫成的大型遊戲,那麼起碼可能還存在造其它輪子的過程(比如你還用到數據結構,而不想用STL,那麼你需要自己設計—這是在造輪子了,或直接複用GLIBC),,邏輯間的大量複合,和與其它維度上邏輯的結合(比如不僅是圖形方面的,還有網絡方面的代碼等),就會構成一個大型軟件了。

然而幸好有一個YAKE的程序考慮進了幾乎你能用到的很多庫,,當然如果你想用其它的庫替代,或者加進其它的庫邏輯,或者自己在複用中會想到自己開發自己的庫邏輯,也是完全可行的。但是我們一般不改造YAKE(因爲如果它不提供SRC我們就無法改造,其它很多庫都是這樣的)。而是拔插使用其內組件庫。。

所以大中型軟件的形成,,有的是庫邏輯和實現邏輯,即設計能力,,有的是複用邏輯。。即編碼能力。。因此那些說程序員是簡單的複用員的人是可笑的,,因爲一個程序員在編程中總會涉及到設計能力(即那麼比如設計一種數據結構庫的能力)。。。

所以什麼是設計能力就出來了,它泛指一種真正的編程能力,即將現實問題映射爲語言邏輯的能力,(找輪子是作設計的一個步驟,),那麼設計能力要求你有什麼樣的能力呢,,當然,現實問題的模型你是要清楚的,而且語言邏輯也是你要清楚的,複用的庫你要從語言的觀點去搞清每一個接口的真正意思(無論用什麼語言,,數據結構,設計模式和語言本身這三者的邏輯是不變的,,其它的就是具體問題映射到語言的能力了),在整個設計過程中(映射中),,你必須清楚每一個步驟,,因爲軟件設計真的不像拼零件(強烈鄙視這種用在小打小鬧模式上的接口拼起來的軟件上),,大型的軟件接口太多,,你必須熟悉每一個功能模塊,,和預見每一個功能接口。。這種細節的無限性決定了軟件的複雜性和設計實現時的清楚性,,否則就是編譯不過,不成軟件。。那意味着項目失敗

架構師的能力就是設計能力,,,程序員的能力就是編碼能力(然而在每個小問題上每個小模塊上也存在設計,所以程序員也是一個設計師,不過他不直接面對複雜的大系統而已)

8.6 通用設計與專門設計
像C++,C這樣的語言都是被設計爲通用的,要通用,因此往往基於某種靠近計算機底層的離散形式,而DSL實現特定領域事情的語言,(相對C++,C來說)不強大,不深入底層,不能控制計算機幹任何通用事情,因此往往基於高層模型,,
因此,C++,C這樣的語言必須要涉及到彙編原理裏面的東東,而DSL可以以任何高層的形式被體現,比如不需要編譯的UML圖都是,POWERPOINT代碼都是DSL,根本不需要編譯器這樣的圖靈完備裝備
這就是腳本語言比不上編譯語言這樣的語言對計算機編程方面的功能強大.因爲腳本語言的虛擬機往往是高級機器,根本不像我們的硬件機器那麼底級,圖靈模型對應我們的硬件機器和架構,而虛擬機往往跟硬件架構差別過大,因此腳本語言和系統語言是爲二個不同的機器設計他們乾的事,,,,而一般虛擬機作了高級邏輯,比如GC等,而X86不可能在硬件架構級就用了GC,如果CPU芯片可以用硬件加速的方法直接支持語言的垃圾回收機制就好了,這要求語言跟CPU一一對應,,而且OS也提供了大局方面的GC(並不僅僅針對某個語言,比如WINDOWS的操作系統級的資源釋放)
C跟C++到底有什麼區別呢,我覺得第一個加號是一種理念上的疊加,第二個加纔是語言要素上的改變,C跟計算機離散和底層接近,解決的問題是如何實現,專注於計算機對問題的實現,因爲C語言就是機器的觀點
C++跟人接近,解決的是如何更好地複用,關注於問題本身,脫離了實現邏輯如平臺邏輯,人們如何更好地寫代碼服務工業化,OO就是人類的觀點,C是實現域擴展,C++則完成了一個從實現域到問題域的一個維度變換而已,
C和C++解決問題時,,站在二個根本不同的維度而已.
8.7 具象與抽象
抽象源於一個簡單的事實,把事物從邏輯上分開,這樣就會解偶它們之間的聯繫。

抽象要適中,不能抽象得太像了,爲程序員複用的相對低階接口變成了爲腳本程序員的高階接口。

複用由二次開發的程序員決定(這個事實決定了你將向它們提供什麼大層次上的功能比如ogre的複合模式,大的邏輯或小的接口),所以複用是程序員面向的,純dp的設計方案要求二次開發的程序員也要了解DP。
對api的調用在此設計的最下層,由二次複用者提供。(即數據由它們提供,代碼邏輯由我設計)

其實你可以將複用擡高到場景這樣的層次(這就是領域邏輯,當然設計也可不深入到這麼高的境界,這就是代碼模式,設計模式和現實模式之間的整合設計所在),這是由複用面向決定的即需求決定的。而不僅僅是單方面的設計。

首先來對遊戲進行劃分。
8.7 架構與應用
因爲功能是邏輯疊成的產物,越到底層,它對高層的邏輯依賴應儘量少,因爲高層往往是應用邏輯,而底層往往是功能邏輯和業務邏輯(高層和低層通過一個隔離層意義上的邏輯來達到功能上的完成但又不致於增加這二個層次上的複雜性,使得在這二個層上的工作可以分別完成),因此底層核心架構應儘量簡小精短(比如linux的core,但其中的應用可以無邊無際),但是簡小的同時卻要提供最大程序上,或全部程序上的可擴展性,這樣在完成了整個core後,後面的功能慢慢趨向應用時,就可以不致於改變到core,也即這個core可適應一切小,中,型的平臺,不必做重複工作.或者即使需要被改變時,涉及到這個core的修改量也儘量小.
對底層的邏輯應儘量提供迂迴用的接口,迂迴即抽象,增加了迂迴即增加了另種一種維度上的抽象可能性,而維度永遠無窮無盡,因此抽象以任何一種姿態被創建都可以是一種新的抽象,我們用代碼的形式表達我們所需要的功能,即用計算機能處理代碼的本質維度摸擬人的思想功用的維度,這就是"原語設計"的概念所在
邏輯的抽象粒度永遠有它關於具體的複雜性,我們不能定義一個具體的邏輯塊,指明它爲core或者還是應屬於core,但是正如這句話說過的,問題有它自身的複雜性,因此我們應具體問題具體分析
這是哲學所指明的,哲學不是人的東西(雖然它是人發現的),?
底層完成了(底層往往是一些首先要解決的邏輯或者在設計意義上具有優先產生其它事物的概念體),其上的應用可以無邊無際,但應用應儘量追求形式簡單,一個合理的架構和其上發展的應用對於人來說應越來越簡單,,,這纔是合理的,我們不能掌握的複雜度應儘量隔離.因此我選用沒有歷史複雜性的open jdk,gnu c,擁護開源的linux
.
8.8 應用與設計
應用越來越接近人了,比如web2.0,3.0的出現,這是指軟件產品的應用,實際上在軟件被作爲產品被產生出來時也作了靠近人的調整,編程領域的三個東西,問題域,方案域,人都在相互影響,這種影響產生了一些技術,導致了一些編程的變革,並最終與人們的觀念結合,比如OO,比如設計模式,這也將導致架構變成軟件的功能性"實現"要考慮的,在某個維度上加深了複雜度,然而卻在另外一些維度上提供了簡單的形式和更有效的應用
互聯網的最初靈感來自對學術自由和開放的嚮往,而現在它已成爲由企業和運營商控制的商業平臺。企業應用與網絡的發展密不可分,這二者相互發展,成爲影響軟件工程界的二大主力
多維這個字眼本身就提倡從多個方面(可見多維就是多方面,當我站在某個維度爲我自己說話時,我將同時失去另外其它的維度)
某些東西越來越統一和規範了,這加大了學習的門檻,比如xml出現,就統一了文檔交互的格式,並導致了很多邏輯知識,產生了一些新的邏輯,需要被學習,但這是合理的,因爲形式更加簡單了統一了,並改變了一些應用的形式,比如軟件分發delopy的統一形式等,
另外一趨勢,應用越來越分佈了和趨向web,這實際上是很多年前某些大公司的戰略,總有那麼一羣人(有些人研究應用形成架構,有些人研究編程低層形成架構和思想),先知先覺地認識到一些東西,比如.net的出現,網上的資源服務器越來越變成一般應用服務器,富客戶端的flex,silverlight等等,只是它們是慢慢被民間所識所學習.
一切技術都是面向被應用,因此人無論如何都是主導.將反過來最終影響技術的被利用形式而隱藏了低層實現,一些離最終應用跨度太大的低層實現不必知道其原理,靠近人的一端要提供儘量簡單的形式,比如xml,比如oo,面向機器的一端永遠有它的實現.
8.9 與軟件有關的哲學 聯繫
範意上的設計是廣泛的,不僅限於計算機的,也不限於軟工抽象(軟工和計算機是二個完全不同的抽象,雖然沒有人提出過計算機抽象到底是什麼,軟工抽象到底裏面有哪些抽象存在,我們僅能站在某個或某些維度上給出一個描述性的概念而不是有限集,如果能站在一個大全的維度上說明到軟工的全部抽象,雖然這是不可能的,但我們還是給得出的這個結果取個名字,叫範式,範式總是某些維度上的產物而不是大全的維度產生,學習計算機的很多哲學思維可以解決其它域的哲學),設計並不僅僅面向於創新,有時是形式的重組,而不是內容的創新,應用形式的改觀,設計出的產品,要源端是面向人的,因此要提供足夠簡單的使用和訪問形式,在目標端是要達到足夠豐富的應用邏輯(比如XML統一文檔交換,但可由node,root這些形式導致足夠豐富的深層功能,深層這裏是指上層),因此越複雜越大而全越好(但是如果沒有足夠人力,我們應考慮設計出別人想不到的商機),應用形式和應用邏輯作爲設計中應主要考慮到的問題,
在設計軟件時,我們主要用UML工具,但是這東西是靜態語言用的
.
8.10 與軟工有關的哲學 唯物主義
沒有絕對科學的東西科學,維度給了我們一切答案,永遠有更偏更僻的維度存在,因此拿唯物主義來說,如果它僅僅是表達那麼一種"理"(它僅僅只需要說明這點),而沒有宣揚它是正確的(實際上唯物論本身站在另外一些唯度上看就是片面的,而片面就是一定程序上的不正確),那麼唯物論在這個維度上就算是做到科學了
唯物主義表明事物是不以人的意志爲轉移的,但是這裏面的最基礎的一個說法都不科學,什麼是事物?哲學都沒有從一種形式上去說明這個問題(就像字典內對"強間"應如何定義呢,法律範式內應如何對這個詞進行界定呢)
當然我們討論問題時並不需要把所有涉及到的邏輯都拿來解釋一通,但是作爲哲學維度的"事物"是應該被深入和專門界定的,而且"事物"這個字眼本身就是不嚴格的,而對象對象中的OO中的O,在計算機內存中,它是一個運行單元,在表達出的思想所指時,它又是不可形式化的.(在內存中總是那種形式,然而卻可以產生不同的邏輯,這就是形式與邏輯的關係)
如果我們曾把生命中一段時間用於考慮這些哲學知識,那麼我們就會與常人"分裂",正如寫小說的人如果想說出與衆不同的小說故事到達到無人深入之境,就必定要付出和得到一些靈魂,,產生排拆他人的想法,但是要知道,世俗的維度是我們生活所在的維度集,我們如果能認識到這種客觀性,並尊重它,那麼我們就不會分裂,
人只能是簡單的,人只能是一個行者,在有生之年接受有限的知識,進行自認爲或世俗認爲正確的觀點和作法並行動.而不可能永遠是一個哲學者,沒有人有足夠的生命力來最終解釋自己,得到的解釋也只能在一個維度上成立而在另外一個維度顯得可笑,,選擇需要學習的人類知識去學,不要做一個大而全的學習者,在利用所得知識進行設計自己的應用時,應根據經濟學所指,做別人沒有的東西,纔會顯得有優勢,你不必在任何一個方面都出色,但一定要在一個方面最出色(實踐要達到"別無它,唯手熟而這樣的境界"),這就是你的資源優勢,可以轉化爲經濟優勢
邏輯

很多邏輯的意思都是不可言傳的或者難以言傳的,所以要看別人的代碼時,除非別人在類文件形式的邏輯,組件形式邏輯的命名上直接讓你明白很多信息,?否則你就不能有效地明白作者寫這些程序時的想法,所以讀人家的程序是難的,因此雖然你是面向源程序形式,但你其實是在求索別人的思想,更要命的是,你明白了這個類文件是什麼意思,你還不能很有效地告訴別人這個類寫了什麼東西,,體現了什麼邏輯,這是感覺是窒息的
軟件的本質就是一種邏輯的疊成物,,某某牛人說過,一切問題和功能的達成都是邏輯!!軟件的眼光裏,一切皆邏輯,,在這個軟工年代,最終的產品是多人合作代碼的產物,因此相互之間能明白對方的源程序裏到底能提供什麼功能是重要的,,在以前的程序="數據結構+算法"的年代,程序員之間很容易在這方面獲得溝通,而在現在這個軟工年代,程序=數據結構+算法+架構"的年代,你還得明白人家程序邏輯是如何組織的,哪些是功能級的實現,哪些是架構級思想級的東西(這些東西爲了以後的可擴展而存在,雖然一定維度上也可以稱爲功能級的實現),,

所以邏輯該如何命名,,我們只能滿足於用特定的,固定的形式去描述性地表達它們,比如用類文件的名字,組件的名字,,設計模式的中間用詞,等等.
8.11 真正的設計模式
我討厭在講授一些思想的時候提供大量的代碼,因爲我覺得接受思想的過程應該是一種快樂的如同閱讀一本小說的過程,而不是花大量腦力研究某個細節的過程

而且這個世界,往往知識都只是相互轉載,國內沒有多少人會像那些歐美大師特立獨立發明一些新的思想和論述,而我願意寫出我的一些思想與你們共享

每一個設計模式中都出現了一些角色,然而使用某個設計模式的個體(Client)不屬於設計模式的某個角色,而只是使用這個設計模式的客戶,,設計模式的目的就是爲客戶提供一個好的對現有對象的訪問方法,設計模式是一種高於任何成碼的思想和經驗模式,因此不能直接用某個工具建模下來,在使用設計模式的過程中,,,總會產生一些新的抽象(而且有時不只一層抽象),,這些抽象隔離和解偶了客戶(Client)與現有代碼之間的關係,,,在它們中間作爲中間抽象出現,而所謂抽象,,往往都以一個類的方式存在,(因爲JAVA中一個類默認只承擔一項責任或實現一個對象數據描述,因此一個抽象往往就是一個類,當然,抽象有時以方法的形式存在,某個設計模式也會以方法的形式存在,比如工廠“方法”模式,一般來說,設計模式都會形成某幾個抽象類,對應該設計模式中的幾個角色Actor),,

設計模式終歸是一種迂迴的方法(因爲增加了抽象所以代碼變得有點難於理解而且類層次增加這變得運行時變慢了一點),,然而這種方法成全了一種好處,,那就是:它部分或完全都解偶了使用者與現有代碼之間(實際上設計模式可用在開發的各個階段)的關係,,,這使得以後的對軟件的維護工作和修改需求變得易管理和易實現,使軟件不致於由於當初設計上的欠缺而變得難於修改而瀕於死去.
8.12 設計模式與數據結構
對於設計模式,一般有下面幾種理解: 重構時的設計模式。修補式的設計模式(client角色濃重) 大設計時的設計模式。全新式的設計模式(可以沒有client角色在某一套模式中)

其實。不妨把設計模式稱爲抽象模式更好(我們知道抽象問題領域是設計中的一個重要步驟),,因爲它更多地跟着眼於解決具體事物有關(就跟數據結構一樣,不是跟具體語言有關,不是屬於某種代碼結構。)正如數據結構是擇“數據”這個維度來抽象對現實事物映射到計算機解法的做法一樣,設計模式是擇“模式中的各個角色和關係”來映射對現實事物的模型,從而求得一個現實問題到計算機的解法一樣(就目前所提出的一些設計模式來看,他們都是抽象現實事物模型的初步組件,一般有傾向於用面嚮對象語言來實現的趨勢比如四人幫那書)。數據結構和設計模式都不會跟某種語言和語言機制有關,跟“面向對象”這樣的代碼抽象有本質上的差別,是實現模式,實現結構,而不是代碼結構。着眼於如何解決和抽象問題,而不是如何抽象代碼以進行更好能被複用這樣的軟工目的(當然,這二者是不分家的)。

我在《OO爲什麼不是銀彈-過度抽象的利與弊》中談到,OO並不是銀彈,銀彈是那些能統一人類思想,形成契約文化,經驗的東西(比如我們寫小說的那些套路),而不是簡單的class這種面向複用的小技倆。 設計模式正是上述所謂“契約文化,經驗”之類的初步體現(不可否認,我們所看到的設計模式跟具體現實事物還是有很大距離的),等到有一天,所有的問題都用設計模式來抽象的時候,成千上萬的設計模式會被提出來。人們會傾向於用大大小小的設計模式來解決問題。那麼設計模式就會到達它的顛峯。

然而對於程序員來說不利的是,數據結構已經被很好地映射到C語言中,而設計模式幾乎在C語言中找不到它的影子。這正是它不成熟的地方。也許有一天會有一套“設計模式”專用語言出現。
8.12 設計模式之基礎
研究數據結構我們目的從來不是那些底層的東西,,而是抽象的比如優先隊列,多重集等。

四人幫的那本書是基於面向對象來談設計模式的,因此它先提出一些面向對象的知識,比如一個類的class和type(interface)的區別,提倡對接口編程而不是對class定義即實現編程,提倡對象的組合而不是繼承。而且它稍後提到的諸多設計模式中,都有對象,職責,請求,之說,這些都是OO裏面的知識。。

不要小看了這裏的對象組合它實際上是對接口編程的小化說法

8.12 真正的開閉原則
我們應該對擴展開放,的同時(注意這三個字),,,保證對修改的關閉(一個工程,應該在設計時就要考慮到將來修改的需要,而且要保證未來修改時能儘量降低工作量,對於一個真正的工程級的規模,人力管理工程應該儘量簡化),
幾乎沒有接確過設計模式的人(除了完全外行和真正的編程高手外)看到這句話都會感到疑惑,而且會產生一個很普遍的疑問:不修改何來擴展?
高手與低手的差別就在於這裏,高手往往看重的思想(即設計能力,,,識別架構和建立構架的能力,但是因爲計算機能理解的設計只能是多範型設計,因此高手着重的這種思想往往也是受計算機實現的限制的設計),而低手考慮問題的第一切入點就是源碼本身,因此產生“不修改源碼何來功能擴展”的疑問也就很自然了
而其實,在高手的眼裏,只要定義一些抽象,產生一些迂迴就可以解決問題了,這些迂迴(實際上就是產生一些高層邏輯,這些邏輯就是具體某個設計模式中的某些角色Acotr)可以讓我們clients通過這些高層迂迴避免直接接確到低層的實現(雖然我們client無論如何最終是要進入到具體實現的,但我們可以不直接而間接迂迴地進入啊!!這些低層的實現就是現有代碼了,是實現部分(可能是某個你要使用到的第三方庫代碼),我們經常要對實現部分修改,或者說對現有代碼的修改,而要求要有最少的工作量,而一個沒有定義抽象或者沒有定義好合理抽象的工程要涉及到很多修改工作),而這,,真真實實就是解偶的意義所在。。

我們再來說這些邏輯,其實這些邏輯都可以稱爲中間邏輯,,然而這些邏輯的地位又是不同的,,有與具體實現接近的那一端的邏輯,,這些邏輯也是高層邏輯,,但是把與接近client使用者的邏輯看作爲相對更高層的邏輯。。

說個故事吧!
《西遊記》大鬧天空時,要求當天庭大官,太白金星向玉皇獻記說讓孫悟空當弼馬溫,太白金星的智慧就體現了開閉原則,一方面,在孫悟空方面,太白向孫悟空說明玉皇已同意他上天(對擴展開放),,另一方面實際上只是給了他一個放馬的差,實際上按天庭規則(系統原有結構)孫悟空是不能上天的(),然而迫於孫悟空的力量(修改的需要),太白只是稍微迂迴了彎子(增加了一層抽象),就暫時平息了玉皇(玉皇本人不知道如何擴展這個需求,因爲這是與天規相背的)和孫悟空二邊.
8.13 真正的通米特原則
之所以不稱通米特原則爲通米特法則是因爲在設計模式領域內實在不存在一個法則之說,
通米特法則也稱爲最小知識原則,一個事物對另外一個事物知道得越少,那麼它本身就越安全(這可以聯繫武俠小說裏小人物碰巧目睹了對殺手殺人的整個過程,那麼這個小人物就會有殺身之禍,),

這裏的安全是指對修改關閉

實際上無論對象組合還是繼承都會造成類與類之間的引用,都會造成不可複用的問題,然而,相比繼承來說,組合可以極大地減少這種複用的偶合程序,而繼承壓根就是不可分離的,因爲本質上組合是一種Has-A的關係(組合對象與被組合對象),而繼承關係是一種Is-A的關係(基類與繼承類,或稱父類與子類,注意這二個概念還是有點區別的,一般說到父與子關係時就是指父對象與子對象,而說到基類與繼承類時往往描述類與類之間關係的用詞~~)

還有一種關係是Link-A的關係,這種情況下的不可複用性按情況下來定,,Is-A的準確意思是什麼呢(這裏的意思指語義)?如B is a A,,那麼“B是一個A”,,可能是一個A,但是不一定必定是一個A.而且如果B是一個A,,那麼反過來就不能成立(子類化,雖然站在類型轉換的場合下可以但是現實生活中這樣理解不通)

一個代碼的修改量應只取決於它最低層的實現,如果某個低層引用了過多高層邏輯接口的實現,那麼這隻能說明,對這個實現的解偶還沒有規劃到家,理想的情況是,應該只讓這個實現的修改不觸動到任何間接使用它的高層邏輯!!(因爲自頂向下的引用對於頂來說,如果底部被修改頂部是不用作任何改變的,而如果是自底向頂引用,那麼當底發生改變時,一定要涉及到頂部也要改變,,而這就是不恰當的高層抽象,違背了好萊塢原則和通米特原則)
.
8.14 真正的好萊鎢原則
好萊塢原則(不要給我打電話,我會打電話給你們)強調高層對低層的主動作用,即低層應該只管好自己的工作(具體實現),而高層自有它自己的工作(這就是管理低層的邏輯們,或者說從client到具體實現的一系列中間邏輯),在不需要到某個低層的時候,高層並不會調用到這個具體低層,低層永遠不需要向高層作出表示,,說它需要被調用,,(即在所有的處於使用者與現有代碼的中間的,用於隔離和解偶二者的,那些中間邏輯中,低層邏輯永遠不要涉入高層的實現,而只要高層通過某個邏輯去涉入低層的實現,也即低層應不要調用高層,只有高層纔會去調用低層,這纔是合理的,我們應儘量避免向上調用和相互調用).
8.15 真正的策略模式
Open和close一點也不矛盾,當它用在同一個架構上,open指出這個架構的可擴展性,而close指出這個構架的內斂性,open是相對高層來說的,,而colse是相對內部實現來說的,,,一個構架應對高層open,而對內部實現close,,,

策略模式將可變的行爲集封裝起來,這符合OO封裝“可變部分”的原則,可變部分就是實現,我們修改一個軟件直接修改的就是實現,而非抽象(實際上也不應該也沒有必要對抽象進行修改,如果你的工程存在對抽象的修改,那就只能說明,當初在定義抽象的時候壓根定義的抽象就是不合理的抽象,真正合理的抽象將使用者客戶和現有代碼極大地解偶,這使得以後的修改工作只需在低端實現進行而無須觸動高端).
8.16 真正的觀察者模式
好萊塢原則指出,類之間應儘量避免低層(實現)向高層(抽象,邏輯)的引用,
觀察者模式中,觀察者,被觀察者,一個被觀察者管理諸多對象(觀察者),這些觀察者通過
.
8.17 真正的裝飾模式
裝飾模式就像是一個用類來修飾類的機制(這就添加了新的職責到被修飾的類,,這裏說的修飾本質是什麼呢?就是類的組合,讓一個類被修飾者成爲修飾者的一個實例變量),,,這要求修飾類(可能是多個)和被修飾的類有一致的接口(也即它們同共都曾實現implent了某個接口,或者繼承了某個有接口作用的抽象類extend,,這樣一來,就可以在動態運行時用一方代替另一方,然而客戶並不會知曉其發生過內部的替換)

裝飾模式可以讓很多具有對等地位而且擁有共同接口的類進行有窮互飾,這樣可疊合多個類進行某個共同的接口作用,並獲得最終的修飾過的這個成員作用
.
8.18 真正的單例模式
某些只能夠擁有一個實例的類對象必須通過某些方法來保證它在程序運行期只有一個單例,而且,更重要的,,必須提供一個全局域訪問入口,這個入口必須是類層的,,

通過這個全局域訪問點,你可以直接調用類的某個機

因爲它的產生實例的構造函數是私有的,只能從類的內部去產生和獲取這個實例,換言之,你不可以通過繼承或組合的方法去獲得一個實例,而且這個方法往往被定義爲fina,,也就是C++語言中的CONST,即子類不能覆蓋它,

因此,可以用類方法(也即靜態方法),這種方法下,從繼承

.
8.19 真正的迭代器模式
如果你知道什麼叫遞歸和遞推,那麼迭代器本身這個概念你是很容易理解的,迭代器跟集合(集合就是通俗意義上的對象集合,雖然存在很多不同質的集合,比如用數據結構表達的對象集,或者用函數索引的hash集,但是只要是集合,它的內在總有一些對象及對象邏輯,對象邏輯就是操作這些對象的根據,比如遍歷算法,而至於本象本身,可以是無意義的對象,或者同性質的內存節點,或者離散的東東,然而上面說了,這些集合內部必有一種方法作爲邏輯可用來遍歷他們各自內部的對象,)的關係就是:無論是什麼集合,它都可以把一種抽象抽象歸納出來,就是遍歷它們各自內部對象的算法
所以,對抽象的提取,往往是找相同的部分,把這些相同的部分提到高層,而用這處抽象來封裝可變的部分(這裏指各個集合內置的不同的遍歷算法),這樣就形成了一個所有集合能共享的遍歷接口(當然這個接口並不爲集合所用,集合自有它們自己的遍歷算法,而是爲client所用,不同的client都能面向和共享一個共同的,使者這些集合來進行遍歷集合的算法,而不必管這些集合自身具體是如何遍歷它們自身的元素的)

.
8.20 真正的工廠模式
工廠模式用來實例化對象,,,可被形象理解爲一個封裝了專門用來產生對象的某種邏輯(這種邏輯可以是一個方法的形式存在-這就是工廠方法模式,也可以是一個類的形式存在-這就是簡單工廠模式),因爲大凡產生對象的過程都是低層的(調用New方法實際創建實例對象,屬於實現),它壓根就不應該跟高層(這裏的高層指的是需要引用那些實例或間接引用到那些實例的抽象或更高層抽象,由於一個類只能負責一種責任,一個抽象只能被作爲一個類,因此當有多個抽象存在時,有必要將它們按職責分成不同的抽象層次,形成不同的層次類放在一起.

這個道理就像:我們生產出一系列的東西(我們當然可以把這個產生過程直接放置到某個未來應用中-這個未來應用要使用到產生過程中產生的對象,這樣一來所謂的“某個具體未來應用”就會跟產生對象過程直接掛鉤,因此我們把產生對象的過程獨立出來,歸納它爲專門的產生對象實例的過程,而應用這些對象的一些應用--雖然不知道未來會有多少應用會存在,而這個“不知道”的說法,本身就反應了它符合未來的擴展性--放置到另外一層去),然而會有其它一系列
.
8.21 真正的門面模式
門面模式也稱爲外觀模式,它提供一個易使用的接口作爲它的外觀,只是爲了使現有代碼client和要使用到的對象集(往往是多個具有不同行爲不同接口的對象)通過這個接口(製造出的目標接口)能被更簡單地使用而已,也即打包某些對象行爲(並透露出一個基於高層應用邏輯上的接口),常跟適配器模式放在一起被討論,因爲它們都是爲了提供接口而存在的,,適配器模式是轉換接口爲了“能夠被使用”,而門面模式是簡化接口爲了“更好地被使用”(讓被適配對象被client被使用,通過一個目標接口-注意這後半部分的說法才最最重要的)

.
8.22 真正的命令模式
將命令本身封裝起來作爲一個對象,讓它的調用者(注意這個調用者不是客戶Client,Client是模式之外的使用者,而是命令模式中的一員Actor,是這個命令模式抽象層中的一層)和命令對象通過對象組合的方法

8.23 真正的模板方法模式
模板方法用一套模式作爲定義方法和行爲的大致框架(注意是大致,而不是全部,這個機制就允許掛鉤,和一些需要它的繼承子類實現的抽象方法),這跟策略有一點相似之處,因爲他們都封裝了作爲可變部分的行爲,然而它們之間還是有差別的,

然而,模板方法使用繼承模式,而不對象組合模式,模板方法因爲是一個抽象方法,因此如果有子類繼承它,那麼這個子類必須要實現這個抽象方法

8.24 真正的適配器模式
Adapter,不是接口的意思,它更準確的意義應該是適配,真正的“接口”在不同的應用場景下有不同的意義,現例舉如下:
1,Java的一種機制,這種interface語法是一套抽象機制,如果實現
2,接口類,這些接口類往往是抽象類,
3,二進制複用的接口,比如COM,也就是構件接口
4,接口方法,某個class非private的方法(無論是抽象的還是帶有實現的都可以稱得上是一個接口,一般是指抽象的成員方法)API都可以是一種接口
5,邏輯模型,通俗意義上的“抽取歸納”某個接口,或者說是高層入口,通過這個高層入口,所有的

以上只是爲了不跟OO中的接口相混淆,所以強行把適配器模式說成是適配,,其實適配就是適配二個擁有不同接口對象的接口對象(也即這個產生的目標對象“接口對象”也是一個接口,)

8.25 業務與邏輯分開
業務就是你做軟工的設計階段時,所要明確的"邏輯本身",界面就是"表現此邏輯的應用形式"(面向用戶的一端),也即邏輯是"我們要搞清的問題"(面向低層的一端),要解決和麪向的問題領域,這個所謂的"問題"是嚴格的,它決定了我們在編碼要體現什麼樣什麼維度上的功能.因此在設計中,"搞清你要實現的問題"永遠是重要的
另外一個概念是數據,數據處理邏輯要做成獨立於界面和邏輯的,此時要提出一個架構,進行新舊系統的分合與整離,讓應用統一於某種低層邏輯或界面形式,或由這種架構創建新的應用.
現在的網頁,即使它用到xml做數據源,也是不完全的"業務與界面分開",我們應保證"如果一個頁面被刷新,那麼那些不與數據相關的界面元素根本無須變動",這就是徹底的分開
這種理念可以讓網頁反映速度提高很多倍,,應用的多元化絕對是可以被統一的,只要你能提出一種合理的架構,架構的提出不僅是一種IT觀念改革(對某個抽象有了新的認識),而且是一種極大創新的活動(人們可以由此發展出很多改變了形式的應用).
多少人明白domain這個詞的意義呢,如果泛化起來,會是什麼意義,任何問題求本溯源就是一個世界,一個領
域)其實domain這個詞是在原語領域描述事物分類的,每個事物都有一個name,受某個domain類name來管理,因此,以什麼粒度以什麼元meta來分類事物並命名以產生一個命名機制呢,就是domainname這個詞的由來,(元是老子提出來的,古人希望把世界的本質用元這個形式形式化下來)

Java的源程序文件夾也是這樣,你難得找到一種命名爲你的所有大大小小的邏輯命名並人爲區別,所以sun找了一個domainname形式,,分類學與命名學是對軟工尤爲有意義的,,只有sun意識到了它
8.26 架構不是功能的要求,但卻是工程的要求
所有的術語都可以被重新定義,,遊戲是什麼,,其實火星人可能也在玩一種叫"遊戲"的東西,,我們的BBS論壇也可以和魔獸世界一樣被歸爲網絡遊戲,,,這就是重新看待一個領域的抽象,,給一個術語重新格定它的含義的範圍,如果性質相同或相似,,就整合它們,再發展形成一個架構,將它們發展成此架構下的分支實現,,比如facebook,它以技術的形式統一了很多web2.0的應用,,,這就是說,應用這個東西,,是可以以技術的形式被統一的,,,從架構web2.0的眼光來看,,blog,視頻點插,,,諸多web2.0應用,,都可以被看作爲web2.0,,,
這種分離與整合現象其實在IT界每天都在發生,,以上facebook是個例子,還有adobe的Javadeveloper ide? ecillpe??,Java?平臺開發庫將內存流,網絡,本地文件都看成"流",,這就是對一個術語重新進行定義,格定它的範圍,,而這是合理的,,因爲我們對一個術語的定義本來就是歷史現象,當歷史發展了,,一個術語要麼被增加新的內容(量變),要麼被完全演變,,成爲一個新的術語(獨立發展成一個新東西,雖然原來的那個術語也有效)
?我們所看到的概念,,如果重新被設計,,會產生很多新的抽象(在另外的維度上甚至會產生更多抽象,只不過我們是人,有限的生命不能允許我們同時或異時站在多個維度去想東西),,和由此而生很多應用,,這就是sun所玩的遊戲,,比如它提出一個"xml",,實際上xml的最高境界就是"文檔互換的標準",,由於xml的成功流行,這由於它是符合應用的,它就成了標準,實際上如果隨着歷史發展,xml就會過時(Jnos出現了),,xml只不過是人類知識的臨時品,,,總會有它的代替品出現,xml相對"文檔交互的標準"這個說法是個實現,而"文檔交互標準"這個說法是個思想,一種思想反映在IT界,可以用代碼實現(細節級的),,也可以用構架來形成一個觀念上的應用規範,,比如XML規範,,這種思想一定要理解,
火星人也把他們玩的一種東西稱爲"遊戲",,任何到現在爲止我們能耳聽目見的東西,,其實都不像我們想象的一樣簡單,,當你學習西紅柿的單詞時,如果你不能瞭解到它其實是一種外來詞,,這種現象,,這種對一個術語的"歷史抽象",,那麼你就不能有效地學習它,只能說片面瞭解了它,,而這對學習是不利的?

8.27 你需不需要一個庫
IT開發中,,只有屬於底層開發的,,,一般才稱爲開發,,發明輪子,,,而複用成風的今天,JAVA這樣的語言體現的是一種高級邏輯配置式的開發,當然也算開發,,所謂庫,是一種面向通用和複用的中間邏輯,,接口邏輯,而非終極的應用邏輯本身,,,庫面向應用複用提供接口,而應用邏輯面向應用本身.語言的功能和可複用性,,一個很重要的方面是除了語言自帶庫之外,還有沒有第三方爲它開發大量的開源庫
  
  所以,如果你不是專門爲了通用的目的考慮,就根本不需要開發一個庫
  
8.28 可複用與可移殖的區別
在相關書籍中,存在很多相似但其實有很大區別的概念,比如可移殖與可複用,接口與實現,接口與抽象,下面試區別之。

在一個大型軟件系統中,抽象是分層次的,,粗略地來說,有的抽象是系統平臺相關的抽象,有的是對於目標問題領域的抽象。注意這個區別只是粗略的絕不是精確的(所以也可說是無層次的)。

設計中經常將實現和抽象分開並各自集中,如果抽象中過多地混入了細節考慮(即有硬編碼和實現出入的地方),那麼它必將在以後的擴展過程中產生麻煩,因爲對於一個龐大的軟件,其內部邏輯複雜,牽一髮而動全身,語言給於實現和抽象形式上的劃分方法只有頭文件和CPP文件這樣的初級方法。實現和抽象的分離從來都高度掌握在源程序的作者手中,我們知道,抽象不全是爲目標領域作抽象,有一部分抽象是爲接口作抽象,也就是爲可複用的有效形式作抽象,即接口是抽象的一個部分,是抽象的簡單形態,,其目的是爲了給使用它的客戶提供一個複用(或實現)的原型和規範,比如庫,函數API,這裏的客戶是程序員用戶用戶。(但是像虛函數那樣的語言內接口,又不完全是爲了面向人的複用,而是爲了面向程序內邏輯客戶的實現。這個客戶跟據這個接口產生出一個關於這個接口的model),如果不是庫,則不需要提供接口設計,而實現是未端抽象(這就要求設計者具有良好的對系統的可複用考慮的設計能力)。更多的關於接口與實現的區別在文尾有述。


在一個複雜的軟件系統中,可移殖邏輯主要集中在那些與系統編程相關的邏輯中,而不是對於問題的領域設計邏輯(雖然如果對問題的領域設計,,抽象得不得體的話,這樣同樣會導致不可複用問題,但決不會產生不可移殖問題)。比如對某語言密切有關的字串邏輯的依賴,對某平臺密切有關的某個socket..鑑於對不可移殖問題的考慮,,我們往往將它與領域邏輯分開。所以可移殖問題只是可複用問題的一部分,二者絕不是同一意思。

所以,應該怎麼樣做呢?這樣才能同時達到可複用,又最大程度地可移殖。(當然,只能是最大程度地這樣。)

編程涉及平臺支持和目標領域問題。一個用編程語言寫就的,用OS運行的,“軟件系統”中,必將大量存在這樣的“平臺編程邏輯實現”,相比之下領域邏輯少得多,(我們將由領域邏輯主導實現邏輯。)。
一種方法就是廣爲談到的“抽象與實現分開”,我們需要將各個“實現”按文件物理地分開放置(此文件將會是引用的未端,不被“設計”直接作爲頭文件引用,而是作爲最終可用可棄的實現未端,由它引用目標領域邏輯)。當然這個過程中,我們應注意模塊化(所有的編程範式都是模塊化的),然而正如上述所說,模塊化不能複合,不能高下相互引用,比如“設計”引用“實現”
。(因爲設計中有自上而下和自下而下,故也不存在各個模塊之間平等不相互引用的情況)但是如果不考慮最終軟件系統的實現的話,光就設計來說,確實也存在各個設計模塊之間平等絕不相互引用的情況存在。


除了上述不可複用問題來源於不可移殖之外,還存在以下幾點不可複用問題產生的源頭:

l 解決不可複用問題的方法是增加迂迴(一層抽象接口),將實現逼回底層,這個動作出現在二個過程中1,對於目標領域的抽象過程中,2,對於重構時的過程中。

然而,所謂的迂迴,其實也是系統中的抽象,,也會對可複用產生障礙,一定意義來說,系統中抽象層次過多,,或數量過大,都會直接對可複用性產生麻煩,這爲了解決不可複用問題而設計的另一層抽象正加大了某種程度上的不可複用性。所以是一種以毒攻毒的方法。解決問題的方法正是產生問題方法的來源。

l 不幸的是,語言機制也會造成不可複用,比如模塊就沒有函數來得可複用性強,然而複用從來都是相對的,存在一個比較時所採用的最小考慮單元,僅在C++語言內部而言,模塊是可複用性很高的,在所有語言面前,函數接口和過程式開發無疑是複用性最大的。。這就是linux之父跟別人爭吵的源頭所在。這也就是說,C++的抽象性能反而帶來了不好的地方,越抽象的東西越會阻礙可複用性。
l 當然,最後,抽象的方法不同,產生的抽象結果不一,由此產生的不可複用問題是最嚴重的。因爲複用者一需要理解你的設計抽象,,在理解了之後,才能進行復用。如果你的抽象過於複雜,複用者不會有太多興趣。

編程能力就是學會如何面向可複用考慮去進行抽象。當我們設計自己的系統時,爲了提高它的最大可複用性,我們將它設計爲與語言無關,與OS無關,與複用的庫無關。這種工作是相當難的。設計中的目標問題抽象永遠是自己的。那麼如何將這些如上的“實現”逼到未端呢?

首先,要想做到與語言無關,就要用那些最初步的語法機制和開發範式,比如函數過程式。三種控制結構(事實證明它們可以產生一切邏輯)。或者自己開發一套自己的語言,在自己的語言領域之內作“固步自封的複用”。但我們知道,這(當其它語言不存在)實際剛好阻礙了其它語言對其的複用。

其次,要做到與OS無關,當OS不存在吧,不要引用OS的任何東西,,,照樣是在自己的語言的基礎上發展自己的GUI庫,等(這實際上也是很多語言提出可移殖理念最初的出發點),一系統的系統平臺相關的庫。

要做到與可複用庫無關,只有自己開發功能相當的庫了。即一切輪子自造,包括語言。然而你可以改造語言,卻不能改造英語編程語言,那是一個未知領域,你更不可能在馮氏模型之外發展這樣的語言,你不能改造PC模型,也不要指望改造OS,更不要指望改造電腦的能源爲光腦。

所以,一切好自爲之吧。既然輪子的創造是一個無底洞,,何不直接就複用別人的庫呢,用大家都用的語言呢?更重要的,作大家都在做的領域設計方案。這樣別人才能理解你的設計。

===================================================================
接口是實現的原型,,一般談到實現,就是一個複雜系統中的終極未端邏輯,意指其不必爲複用留有餘地(因爲複用就是利用接口作進一步的抽象,複用跟接口密切相關,所以實現也指不必爲接口設計作餘地),一般談到接口,意指爲下一步的抽象提供統一的原型和形式,即爲如何複用提供設計,

8.28 再談可複用
可複用問題的由來一是實現的不能可複用(還記得在C語言中寫上大量的預處理代理來實現跨平臺邏輯嗎?),二是架構邏輯的不可複用,,,,實現指的就是跟計算機離散相關的平臺邏輯,,架構就是人爲爲程序的可擴展性加上的設計邏輯,,大多數是OO之後的東西,,我覺得提出二門語言,一門C,一門類RUBY的腳本語言來進行程序的編寫,,,,在底層用C,在高層用OO腳本,這樣的辦法很好...因爲你在低層不需要架構,,而只要在高層考慮架構問題.,,這樣一來,可複用性就是二個階段的事,前一階段只管實現,不考慮設計,,這種過程是承接性的,只要先實現了,才能被設計得架構上更科學.

8.29 真正的可複用
可複用到底追求一種什麼樣的效果,又能最終達到什麼樣的效果?運行期的效率或重構期的不可複用和窄擴問題(這是二個並非絕對統一的東西),一切都可歸究到設計期的問題。

編程界的可複用主要是面向對象和構件複用和設計模式和設計複用,庫也是語言內部的可複用(就跟你擁有庫的源文件一樣.因爲有頭文件也是一樣的,因爲你還至少清楚庫的構架,這也就跟理解並應用一個庫只需瞭解其API就行了但不需要了解其SRC級的實現一個道理.ode的頭文件集卻是一個例外),COM的複用就是純粹的二進制的複用,因爲有真正的接口的隔離作用(此時你根本不知道庫的構架),在庫定義的接口中,你必須透過接口才能深入接口更下面的邏輯(可能是另一個庫的實現),因此接口一方面提供了方便性,另一方面也增加了屏蔽性,這是一對矛盾,接口的定義是爲了引入某種架構或橋接二種架構使其配合工作,而這種機制在提供了方便性的同時也增加了理解和使用該接口的複雜性和運行時空的代價。

用OO來表達世界的觀點,,,物體可以組成世界(所有其它的東西,比如物體之間的關係也是另一種意義的物件),,,因此編程抽象了OO,那麼編程就可以用來用計算機模擬世界,這種思想是成立的。

庫的組合=功能的組合(類庫設計是一種跟語言同級的設計),當然這種邏輯在使用同一種語言下是成立的(不同語言時也可以用Swig等技術來改造或Bind),然而庫作爲中間邏輯的封裝者(庫讓你跳過庫的實現即中間邏輯這些細節而直接面向大邏輯大架構編程,只要引用它們就可以在自己的程序中實現它們),可以一直細化接近最終實現,誠然單邏輯的一個類也可以被封裝爲一個庫但是往往不樣做,一個庫封裝了一套互飾的中間邏輯的有機組合,,這裏的中間二字是相對最後的應用邏輯來說的,往往把最終的應用邏輯稱爲實現,這就是一種實現邏輯了而不再是中間邏輯了(這就是說庫可以是一種內含高抽象的架構邏輯或具體的工具函數的實現邏輯,或基於其它庫之上的架構邏輯或實現邏輯),庫可以直接深入到實現細節,但是我們要控制這種過程,一方面是中間邏輯與最終應用邏輯不可精確定界,另一方面是因爲設計與封裝是個無底洞,不必做這種深入,第三方面是有其它的庫可以plug進來然後在這些“輪子”上實現(庫應只提供BaseGeneric這個庫構架(此時庫本身是一組架構邏輯而非實現集)和對一些其它外來支持庫的引入接口(一般接口需實現,邏輯需繼承,此時其它庫可按需進行plug in 或者out),這就是庫引用庫,這種情況下有一些未端的實現是不應該加入中間封裝的,比較好的作法是用一個庫定義架構和基本工具函數集,以及對其它未端工具庫的接口邏輯(此時先前定義的那個庫就是主庫,其它的庫是可選的輔庫,比如Yake的實現就是這樣),實現就是最後一節提到的幾個Demo(作爲基礎的邏輯已經被庫封裝起來,其它的就是實現了)

像Yake,它提供了一個Base core和很多構架上的接口邏輯,每個接口邏輯都實現了一個對外來庫的引用,Base core是工具函數集(也有一些接口邏輯),這是Yake的主體部分,而接口邏輯(Yake也在這裏實現了一些工具函數庫比如)和對其它庫的引用邏輯(也是一些Adapter)纔是Yake的重要部分(Yake包括它的base和對其它庫的引入邏輯這二大部分,當然還有它的一些工具實現,這樣算起來Yake有三大部分).

接口是可複用中一個很重要的概念。

8.30 你能理解XP編程嗎
大設計是一種動用一切資源,從整個思想領域去設計計算機應用的過短,,這完全是一種預設計,編碼過程變成了純粹的被設計預見的集成的一個過程,,這種設計往往首先從思想出發,,完全不考慮計算機實現,語言機制對應用的限制或表達能力,,,提出一種標準和理想模型,把思維過程出現的任何一個過程都作爲設計的一部分,設計過程中任何動作都不跟計算機和程序語言相關,,最後僅留一點餘地作爲編碼,在編碼時考慮其跟計算機實現和語言機制實現的結合,編碼的地位很低很終端。。

XP編程出來的時候,,人們大呼設計已死,,因爲這種邊設計邊編碼(在編碼中形成設計)的方法大大忽略了設計超越“編碼”的“預”,,設計變成了跟編碼並行的過程。。

實際上,該如何處理設計呢??比如設計遊戲。。

我們知道設計是無底的,這種無底性決定了我們應有限地把思維中出現的理想設計和想法體現到計算機邏輯和語言機制能表達的邏輯中,,,而且應儘早地這樣做,,任何應用領的邏輯都要最終被轉化成計算機邏輯和語言邏輯。。也即,我們不必做超級設計和完美設計。。

遊戲是什麼呢?如何設計一個遊戲呢,,遊戲這個字眼可以無限被放大(應用領域可以無限深化),WEB論壇可以是文字遊戲,3D遊戲也是遊戲,,網遊也是遊戲,,是不是要在你的設計中體現這所有的元素呢(一個具體的設計總是針對某個應用域尋求它在計算機和語言域的對應,如果你知道算法和數據結構你就深刻理解這個說法了,我們總是向語言和OS中尋找某種可能適應我們問題的數據結構,即使再通用的邏輯,比如庫的設計,我們也不應),並用一種“設計模式”中的模式來組織這所有的元素呢,,不能,,而且不應該。。你不可能在有生之年把它們(設計中出現的需要組織的邏輯們)的地位作一個組織或你自認爲合理的排列。。


你可能會說我不直接提供這些無素的實現,,不直接在設計中體現這些,我只需預見它們,,並在設計中爲他們預留接口,,但這樣也是不行的

那麼最後出來了,,什麼是XP編程

預設計,大設計是一種“一次性設計”,企圖把應用設計中的大大小小所有過程整合到一個大設計中,,,這樣的代價當實際編程開始時如果遇到不能前進的錯誤會損失很大,而且設計本身花費精力也不少

而XP編程先是提出一個不夠完美的框架(針對某個應用,有應用本身和它產生大大小小的其它應用問題,這不夠完美的框架是針對整個應用本身來說的),或者不提出思想模型,,,它並不試圖分析整個應用,,以及對它們的設計(因爲它相信設計不可能是一種大而全的,只能具體問題具體分析設計,人們不應把所有可預見或在後來出現的問題整合到同一個設計中),,並不着手預見可能出現的問題和對它們的大大小小的設計過程,當具體應用問題中的大大小小問題出現時,就着手一個即時設計(比如設計遊戲時,這是個具體的大的應用問題,針對遊戲本身可提出一個不夠完美的框架,,當在他下面遇到有很多小問題,,比如網遊時間同步,,我就看語言中提供了什麼線程和語言機制,或者如上面說的數據結構或算法,來進行一個小設計)

這就是XP編程的全部意義所在。。

8.31 構件與接口,軟工
UML,IDL,MDA,COM.CORBA,WEBSERVEICE,XML軟工新潮流
關於接口,也發展出一種語言叫IDL,,語言跟應用的關係是什麼呢??因爲語言可以接上計算機處理跟人們的應用需要之間的接口,,所以語言和編寫語言下的程序成爲擴展計算機也擴展自己的手段,,甚至還有DSL爲了解決特定問題而產生的一種語言,語言的實現即編譯器,語言規範等,,
而MDA是不同的概念,MDA是給定一個領域的描述,,然後寫出一個依賴於接口的xml格式的web services.
這直接促成了構件的產生,,,在SOA中,因爲要集成新老系統,當構件作爲一種比對象還要大的軟工邏輯粒度時,,它們共同需要都需要接口,,但是構件
不要小看了這個構件??它幾乎可以是一場軟工變革,跟OO有相平之處


編程與範型(設計)

初始OO,我不過認爲那是一種編程語言支持的工具,,真正懂得它時,我發現我走入另一個迷惑,一個更深的迷惑,,如果OO是一種思想,,,所以我要怎麼用語言去聯繫它?

8.32 設計方法論
“在原語空間內進行設計”前應該是現實世界問題域,在“產生最終類文件”後應該是編程域的OO解(或其它範型解),即設計的FrontEnd面向的是用戶,設計的BackEnd面向的最終是計算機,這中間的“原語空間設計”,“多範型表達設計”,“ROSE工具實作範型”都是“設計演化”,即設計是一種從用戶到機器的抽象過程(它包括前面提到的三個主要過程,原語設計是從上到下,泛型設計是從下到上,明白這個道理有什麼用呢?這至少可以解釋爲什麼好的架構可以擴展出足夠豐富的未端實現,因爲從架構設計到功能實現是互通的,這二者不是矛盾的相反是統一的,明白這個道理還有什麼用呢,這也可以解釋爲什麼Yake的BaseGeneric不是包含架構邏輯的Generic,而是一組與平臺native本地有關的DLL引入邏輯,數學函數,Logging機制什麼的,這是因爲需要先提供這樣一些實現,才能獨立平臺,而這是Yake首先要解決的問題,因此只能把這層邏輯放到最低層再慢慢發展其它抽象,另外,YAKE使用的庫中,比如OGRE和ODE就用到了數學函數(並非所有的問題都能靠提供架構和中間抽象來達成並解決,因爲有些問題不是要不要封裝和不封裝的問題,而是能不能實現的問題(能不能用OO來表達跟能不能在算法等級實現是二個不同領域的問題,一個是軟件的設計,一個是算法,前面提到了這二者之間的區別),比如一種算法,什麼是架構什麼是實現,這裏是一個很好的區分例子),底層必須先解決並提供這些抽象,以我來看,Yake真正的主體不是BaseGeneric,它是基礎而不是主體,它的主體是對其它庫的引入邏輯,這纔是Yake的架構邏輯,因此說,架構邏輯有時僅僅是被體現,而沒有並封裝成DLL,也即,架構是否被體現與它是不是要被封裝成爲一個庫是沒有必然關係的,哪些沒有表現爲庫的中間邏輯也可以是架構邏輯,明白這個還有什麼用呢?原語設計是不受限的面向用戶的設計,然而當進入多範型設計時慢慢轉入用計算機的觀點來看待設計,因此像Yake這種與平臺息息相關的表現邏輯必須在底層就解決平臺抽象和數學抽象,而LogicGeneric就根本不用考慮這些,因此多範型設計相比原語設計來說,它是從下到上的),而實現是一種從機器到用戶的過程(實際上我在這過多強調設計與實現的差別是不對的,因爲這二者無法精確定界,然而如果所有中間邏輯都被封裝爲庫,這二者差別就很明顯, 庫作爲中間邏輯可以參與進來以縮小這二個過程差距,把應用架構稱爲設計,把中間邏輯封裝爲庫(架構也可以表現爲庫),實際上在這裏,中間邏輯與最終實現纔是對立的說法的二方,設計與編碼纔是另外二個對立物(設計就是原語設計而編碼就是多範型設計),

設計演化(從問題到類),,實現演化(從類到問題),,前者是從人到機器,後者是從機器到人


邏輯互飾構成的邏輯的巨集組合,就是一個越來越接近應用總邏輯的大邏輯,(上下互飾就是誰更接近應用邏輯的道理,至於最終應用邏輯前面的邏輯,都可稱爲相對的中間邏輯)

然而設計與中間邏輯不是沒有關係,把庫外的未端邏輯稱爲實現,在這種說法下,基於庫之上的實現跟設計共享同一些中間邏輯,庫使這二者有機結合不產生縫隙,當然作爲泛義的庫是縮小任何二個架構之間差距的機制),其實在“ROSE工具實作範型”之後還存在一個“設計載體與設計方法”,即UML圖,或卡片啊(設計載體),設計方法主要是“找事物的共同點與不同點(就是多範型設計那本書的作者提到的)”,還有就是UML教學中出現的“給出一個句子,找出主語謂語等”(其實這些方法歸納開來就是做列舉題和判斷題,列舉出一些細節,再判斷它應屬於那個接口中,這在第四部分“確定GameGeneric應提供什麼樣的高階接口”那一節有清析的講解)

實際上我的思想和說法比他們還要超前和規範一點,原語設計三種思想(抽象,原語,組合)就包括上述的說法(找事物的不變點就是指抽象出事物的本質,這是設計過程中一個很重要的能力)

在觀察者模式中,

8.33 真正的interface
正如對象的索引纔是真正的對象一樣(C++中提倡使用引用優於指針),對接口編程纔是真正的編程工作,一個程序員大部分情況下只是一個接口粘合者(因爲我們不需要重新發明輪子,發明輪子-中間邏輯的工作纔是真正的對實現進行編程),發佈時我們也是發佈接口庫和其說明文檔,大多數情況下,我們都是利用第三方代碼庫編程(這是語言之外的接口,程序員也在語言內部編制函數等接口)

接口設計是一個編程工作中常常要考慮到的問題,要考慮提供這個接口的實現會在什麼地方會用到(因此它跟需求分析密切相關),以此來設計接口的參數信息,一個接口不單單是一個函數,雖然函數的聲明部分在大部分意義下作爲接口的意義,,,,

所以delphi的單元中有實現和接口這二個字,,,接口的集大成者是COM,所以borland以它的IDE很好地支持接口而著名
這是語言之外的,
設計一個面向需要程序設計語言永遠是不可行的,需要永遠是可變的,程序設計語言只能遵守一個固有模式而提出,有固有模式去表達和創立新東西(面向對象就很不錯),編程是人的動作,人力對工程(一個大軟件就是一個工程)的控制應該儘量簡化,

特別是要掌握對象(或稱工件與產品)與接口-組件(對象接口與組件接口是不一樣的)概念所在,前者是語言內部的,後者是語言外部的
在內部
純數據對象稱爲死對象,(可用但無用)純實現對象稱爲工具對象,不可用
在外部
純接口對象稱爲抽象對象,(可用但無用)純實現對象稱爲工具對象,不可用
面向接口,(一個一個的接口,函數稱爲函數接口)接口歸接口,實現歸實現,實現不是函數
軟件就是包裝器,就是一大堆接口的有機組合,不提供繼承機制

一個類型的數據可以獨立構成一個數據結構
對數據結構的描述包括它的類型,它的結構,它的存取規則

軟件設計是一種什麼樣的過程?
軟件就是對象組合,這些對象通過他們的接口按一定邏輯組合成可工作實體
在語言內部的繼承是對象,在語言外部的繼承是組件

對象的概念,包括函數對象,變量對象,數據也是對象(作爲一個元出現在一個特定的數據結構裏),因此數據結構也是對象,操作實現也是對象,實現稱爲處理器,數據或實現的組合也可稱爲對象,注意,對象的組合只能是被稱爲對象組合,只有那些能在一起工作的對象組合(compent view)才能稱爲軟件,這裏引入工作邏輯的概念(就是ROSE中的logicview)

在內部
純數據對象稱爲死對象,(可用但無用)純實現對象稱爲工具對象,不可用
在外部
純接口對象稱爲抽象對象,(可用但無用)純實現對象稱爲工具對象,不能由它派生出實例

面向接口,(一個一個的接口,函數稱爲函數接口)接口歸接口,實現歸實現,實現不是函數
軟件就是包裝器,就是一大堆接口的有機組合,不提供繼承機制

一個類型的數據可以獨立構成一個數據結構
對數據結構的描述包括它的類型,它的結構,它的存取規則

數據是對象,邏輯也是對象,操作也是對象,一切皆對象的概念,在機器內部一切皆比特,在用戶眼中,一切皆對象,因此數據庫的數據二字是有通用意義的,,,因此會有面向對象的數據庫

至此只是軟件內部,那麼在軟件外部有develpoment view

數據組合接口形成一個數據結構

對象和(包括接口和實現)構成一個組件DLL或LIB,一組對象和一組接口就是一個DLL(稱爲一個產品),需要一個調用協義接口
.
8.34 真正的對接口進行編程
不存在一個“對接口編程”的真實過程(雖然這是一種過程定義),我們只是說,要爲”實現”定義一系列使用它的接口(這樣實現才能更好地被使用和被修改),,這種行爲纔是對接口編程行爲,而接口本身是什麼還是沒有說哈哈

一個爲未來擴展而寫出的工程中,大部分代碼只是框架(抽象的思想模型,也就是爲了擴展需要--也是爲了使用需要,而定義的一層又一層的抽象),真正的實現部分(調用API啊,用某個算法啊,某個完全具體工作的實體對象,或某個完成某個業務過程的會話對象)很分散,而且分散得很有規律性(因爲被抽象接口經過了再組織,所以變得有規律地分散),,這樣的分散機制就像把真正的實現逼到最尾端,而最高層往往是使用這些尾端要如何被使用的應用邏輯--被抽象成了一個或某些使用的統一接口形式,而且是高級邏輯,(即接口實際上是關於如何使用這些實現的隔離層,,中間層)

這樣抽象也稱爲爲客戶調用(或使用)的協議定義

很多時候,在一個工程中,所有的實現都可以由一個Demo直接寫出來(寫成CONSOLE形式,也可以是一些對象集上面說了),然而,真正形成產品時,我們需要再組織這些實現,讓它們最終形成的產品出現(因爲一個真正的產品,必須要考慮到未來修改的需要啊)這往往是一個比寫實現還要難的過程,因爲我們在寫“如何使用這些實現,如何把這些實現分散封裝到未端”的接口邏輯,而這個邏輯,往往有時比寫實現本身還要難!!
8.35 實踐方法之極限編程
極限方法只是敏捷開發中的一種,,,軟工指明軟件開發不只是軟件本身,,而是軟件跟人的關係,,因此這出現了設計與編碼,,,設計與編碼是軟工的二大主體,,,,好了,,實踐方法的出現就是爲了解決這二者之間的矛盾
?開放的標準使我們的規範保持中立,所有人都可以接受,而不受某一個開發商的控制,而開源可以使我們得到大家都接受的一個實現,而不受某個開發商的控制,這兩者的結合非常有力。這個實現你可以不使用它
開發中的分佈要求,產生了二個複雜度,1網絡開發,2軟件要獨立於平臺和硬件架構,,這就是WEB的特徵
.
8.36 設計模式複用與框架複用
設計模式是可複用策略,,是思想級的,,但不是不可以用代碼來表現(編程即換維重現,即將思想級的東西轉變爲語言級的東西),,軟件設計的終極目標就是符合軟工,擴展軟件的可擴展能力和生命力,,設計模式就是服務這個的,,設計模式因此有一些原則,比如LOC,控制反轉原則,不重複自身原則,,這就是設計模式對於軟工,所要達到的目的,常見的設計模式有哪些呢?,比如MVC,工廠方法,工廠,單件等,,MVC可以說是一種框架,,也可以說是一種設計模式,,因爲MVC是設計模式的組合,,它被作爲一種框架時,比如 strcut,spring,也是成立的,,可以說現在的一些WEB開發框架比如STRUCT,SPRING,ROR都是設計模式的實作品,而設計模式是一種思想,,設計模式這種思想,這種設計目標和設計手段,,被體現在代碼上,,就是用了設計模式的軟件,,或用了設計模式的可複用框架,,比如設計模式表現爲一種補丁時,什麼時候表現爲補丁?就是老總說,某某公司要求我們開發一個軟件,但是這個公司提供了一個庫,要讓我們現有的代碼,用一種方法能使它跟這個庫協同工作,,因此要利用到設計模式,,就是現有代碼,,跟可複用的別人的庫,,,,在這二者之間用設計模式進行連接,,,,發展出一種可運行的邏輯,,而非補丁式的設計模式的應用,則是在產品沒有出來之前,,不需要適配既存可複用庫和要寫出的代碼這二者,,採用的一種預先的,,大而全的設計方法,,非補丁式的設計模式,,是一種真正的設計,,,此時模式二字反而可刪掉,,是一種預先想到可能想到的所有擴展能力,,決定採用什麼設計模式來編碼,,在這編碼之前 ,,決定採用什麼設計模式,或採用什麼別的方法,,,這就是真正的設計,,預先的,,如果可能,儘量大而全地考慮,,,當然設計不僅是面向可複用,,還面向應用設計,,如何設計應用,,如何設計用戶界面,如何分析業務邏輯以便於擴展出關於這個業務邏輯的新邏輯,,不僅是在設計如何編碼了,還在於調動計算機資源的能力、思維的建模能力、分解和搭架能力,,很多人以爲設計模式是補丁其實是很狹隘的東西,,其實設計模式本來就不是編碼,,,只是當人們站在編碼角度來理解設計模式時,,他立馬就錯了,,設計模式是一種流於建築和軟件界,通用的可複用策略,,是思想級的,雖然它不是不可以在代碼上被表而而已
引用給了我們什麼.

新手編程導論(十)


第三部分 進階:C,C++代碼閱讀與控制
如果說在本書第二部分裏我力求向你們提出一個思想體系,那麼在這本書的這部分我將要講述的就是代碼控制和實踐能力了,這是程序設計的基礎二部曲。這本書完成之後,你應該具備基礎的編碼實踐能力(或閱讀別人代碼的能力)。

這部分主體是,C,C++語法,,及各自標準庫的分析與使用,stdc,stl,glibc,boost,loki,(設計模式,數據結構,業務邏輯),而且更側重使用(因爲分析出來的是庫邏輯,是設計抽象密集的)。比如自己開發例子出來(這纔是使用密集的)。
目錄可以這樣定:輪子分析,再造輪子
1.C,C++基礎(語法級的,,數組啊,字串啊,控制結構啊,初級標準庫級的,比如IO,,OO啊,模板啊)。
2,數據結構,STL(分析庫不如自寫來得權威,,但是參照別人的src也是最好的方法)
3,設計模式上的,LOKI,我在這裏不把BOOST拿來討論,是因爲我覺得成爲語言標準的只需要數據結構作爲數據抽明和設計模式作爲代碼抽象就可以了,,而BOOST有太多的對具體事物的抽象,,實際上隨着問題的增多,BOOST會越來越大,,這(對各個事物的抽象解決集)不應該成爲一門語言的標準。。而LOKI剛好對應只用模板解決的DP問題。。顯然合理。

4,這是放在第二部分最後一節(抽象之領域邏輯)分析的某個庫比如OO的OGRE,我在1月2號決定取消這一部分,,那麼我作了什麼決定呢,我決定寫一個實現比如實化版的yake或者是一個yake類的引擎加一個遊戲實現(直接把其當成寫第六部分的內容,而不再分析並使用別人的遊戲引擎,),可能用到其它的庫(比如net,mysql,boost),直接產生一個遊戲,,

具體如何進行呢,儘量少用輪子,只用OPENGL這樣的必要的輪子。考慮進STL和LOKI,做成一個簡單的引擎,然後寫遊戲實現。儘量在寫的過程中與ogre,yake對比,突出爲什麼全的設計是它們,而現在自己在寫的是一個剋意不用其它輪子的輪子(爲了教學的目的)。。

在第四部分,纔是一個系統的解決方案(一箇中型例子,而不再寫庫或分析庫了,,,應該找什麼現成的例子呢還是自寫,,在這裏要分析出設計的細節,大中型軟件如何形成的),提出一箇中型程序,,第四部分 一個例子,結合OO,模板,語言,領域邏輯寫出的大型邏輯的軟件

如果說STL是templaate與C的結合,那麼LOKI有些更多靠近OO與template的結合。

第9章 語法與初級標準庫


參考書《C++ Primer 第三版》Neckman
9.1 C++的基於過程設計
9.2 C++的基於對象設計: 模板與設計
不幸的是,理解STL深層的原理是需要懂與模板相關的設計的,比如仿函數的本質,迭代器,配接器的本質,模板導致的泛型開發與它提出的這些設計相關的東西可以另外寫成一本書。學習STL首先是學習這些設計手法,再學習其數據結構和算法的實現。

泛型有二層意思,第一,基礎泛化,它把泛型參數化,用於動態產生關於不同型別組合的相同邏輯(可以聯繫函數聲明和函數定義來理解),這也就是一般泛化了,第二,它把一切設計中可能出現的因素都類型化(template class化),即在templateclass這個字眼中不主要強調template泛化而是class類型化,(只不過它也會用到泛型的第一層基礎泛化作用而已)比如,迭代器,仿函數實際上都是(模板)類,這其實更像是C++的概念而不是泛型的概念。(因爲class是c++的而stl及它導致的template手法是另外一個人發明的)

爲什麼需要把指針,函數封裝爲class呢,這是因爲在C++中,class幾乎就是一種邏輯粘劑(即將數據成員和函數成員,,當然在模板中也可以是模板成員數據和函數,封裝爲ADT),在這裏並不強調這些Class運行於runtime的那些特徵,比如多態,等,而是強調class封裝邏輯成adt並提供private,public,protect修飾機制的能力(相比之下C++的struct太簡陋因爲它只能提供全public,而且不能成爲adt,因此沒有adt的諸多好處,比如C++的只對class有效的運算符重載,,而class+oper overloading+template class你呆會會看到,,這在泛型設計中是多麼有用處的東西。),,所以在C++中,相比面向對象來說,這些基於對象的開發範式也需要被重視。。

一句話,class化可以獲得語義級的value, 只要給該class一個copy ctor就可以複製並傳統它,給它一個重載的括號就可以成爲跟函數動作一樣的東西出現在C++語法相容的東西(雖然語義實際跟標準的對應物不一樣),,,

template class是泛型設計中的重頭武器,因爲:
函數+oper () overloading+templateclass化 = function objects,這就是stl中的仿函數。
pointer+oper * overloading(和->)+templateclass化 = smartpointer,這就相當stl中的interate.
Multiple inhert + template class作爲另一個template的參數=policy,即loki中的策略。
Struct+template=nested type,,內欠型別。。
元編程裏的metafunction


重載是多態的簡單形式,,模板特化與實例化是不一樣的,,,其實任何一個模板,都存在二套參數,一套是泛用的,在tempalte關鍵字後面,另一套是特化或偏特化用的,在具體的模板後。特化與實例化的區別在於實例化不需要人去幹預。。
9.3 面向對象設計
9.4 泛型開發與初級StdC庫

第10章 數據邏輯與STL庫


參考書:《STL源碼分析》完整版 侯捷
10.1 仿函數
仿函數是C++基於對象編程的典型,,,它把對象class化{也可能是templateclass化},使之具有copyctor可被複制,再給它提供一個重載的小括號這樣在語法上就可以跟普通函數一樣寫了,整個過程並不需要面向運行期(面向對象),,,所謂模板加基於對象的基於對象之說只適合發生於編譯期。

STL中爲什麼需要仿函數呢?它爲了成爲算法的某種策略,,loki中的仿函數用來實現一種command的設計模式,因爲仿函數可以用來封裝一系列請求;
10.2 iterater
Stl中,與template相關的一個設計手法就是迭代器,迭代器成爲表達各種數據結構以及跟它們有關的各種算法操作的支持概念。當然也是一種泛型手法。Idioms 其實也是一種設計模式,即迭代器模式(traits,adapter泛型設計手法可用於廣泛目的,相比之下iterate好像只用於數據結構的設計手法設計模式),

既然是泛型編程,迭代器是在什麼樣的泛化需求下以什麼樣的泛化方式被提出來的呢?

我們知道數據結構都是某種區間,把數據結構視爲區間這本身就體現了某種泛化(能泛即能提供通用性,可複用性,所以是一種對代碼趨近於人的設計抽象),某種抽象,實際上無論是以何種結構形成的關聯式(key+value=pair對)還是非關聯式數據結構(),,迭代器都將提供一種遊動於元素(一般來說元素只是value的說法)或節點(一般來說node=key,value)之間並能對算法提供迭代支持。只不過迭代器作爲泛型的型它也可以有多種iterate associated traits而已,有的是input,有的是單向,有的是雙向,有的是const,有的是mutable而已。
10.3 adapter
配接器的說法很形象, 你可以聯繫現實生活中把ps/2鼠標加一個ps/2 2 usb接口,,把它轉爲USB接口的這樣一種動作,,這種動作就是改變接口的機制使之由舊接口變成新接口,一種設計策略(改變原有代碼,使之適應某種複用考慮,,所以是人控制代碼的過程,,是設計抽象,,這個動作也稱爲重構,即不改變原有系統設計的情況下,利用設計手段修補式地改造原系統,因此跟DP相關),,往往把它歸爲專門的一門設計模式。

因爲客戶(你的電腦ps/2口壞了不能插ps/2鼠標了)只能使用某種接口的東西,所以需要對原有接口(原有代碼)進行接口重新封裝,使之向現呈現客戶能用的接口。這是典型的設計模式應用於給代碼打補丁的情形即複用的情形。(當然設計模式也可一開始用於純設計的地方)

那麼stl中的這些配接器都是些什麼呢,又怎麼樣在stl的整個設計中發揮了作用呢??

第11章 高級代碼邏輯與LOKI庫


參考書:《C++新思維》中文完整版
11.1 typelist
對類型的設計纔是設計。

爲什麼寫代碼需要設計呢,因爲代碼是人寫給人看的,所以對代碼邏輯的控制是需要的,而這就是設計,,設計更多指一種人類活動,比如藝術設計,所以它包括測試,重構等諸多過程組成的與編碼相對的過程。設計首先是一種對問題的積極抽象過程,booch甚至說抽象是解決設計問題最有效的方法之一,當然,維護,重構也是,所以說抽象問題只是設計的一部分然而是最重要部分。

在C++中,設計首先是對類型進行設計進行抽象(泛型這個字眼本身就表明了對各種類型其功能通用,所以是一種設計抽象),有OO。有template。OO是類型化即面向對象,template是泛型化即主要用C++的基於對象機制來工作。

泛型編程中對型進行的抽象,有make types to be a list,有mapping type to sth,有get traits from types,儘量在編譯期間將型別抽象到應用,形成設計,因爲靜態語言的編譯期間正好提供強大的類型功能,,,而這裏談到的typelist就是一種。。

對類型作了這麼多抽象之後,再提出iterate,等設計手法用於stl,提出policy用於policy based design,,學習範型編程,始終要提醒自己把握這個精神(即一般泛型設計會分成三個層次,第一層是型別抽象,第二層在第一層的基礎上提出鄰域相關的設計手法,第三問題本身,STL和LOKI中都是這樣)。。

11.2 traits
Traits是剝離器,是一種設計抽象(往往人們也把它稱爲concept,,滿足concept的實現就是它的一個model,即concept是編譯期關於類型的interface),廣泛應用於stl,loki,等設計理念中(剝離器一般只用於泛型設計,因爲需要從泛化了的型剝離並獲得它的traits,實際上這個詞更多地強調的是結果),是成就stl,loki等的支撐邏輯。因爲類型有“一般type”,作爲template class的迭代器,等等,所以也有相應的type traits,iterate traits.即模板的參數可以是什麼,那麼泛型也可泛化什麼,形成相應的泛化意義(比如traits),因此泛型可以將型別泛化,可以將操作泛化,甚至可以將一大類的操作和型別泛化。更甚至,可以將“template template參數”泛化,注意我並沒有多打字,現列舉可能作爲template所有參數的情況,並一一加以解釋:
11.2 policy
爲什麼需要policy呢,,因爲我們知道在應用開發中設計往往是做多選題,對應於應用域在解域和語言域中有大量可供選擇的方案,所以可複用組件最好是給用戶小的設計組件,用戶才能藉以組合它們形成更強大的設計方案(具體到每個領域,它的設計都應該如此,比如loki,的智能指針,仿函數等具體領域都是策略based的)。因爲設計元素只能分解而後才能複合,而不應該是一開始就複合了。。如果一開始就提出一個do it all全功能型的接口,那麼往往復用性從一開始就固化了(OO就是如此,單根繼承往往涉及大量不必需的東西進入設計,組合纔是科學的機制)。這往往很不好。因爲它只能向前發展不能向後發展。那麼組合大量小policy形成的對某個瓴域的某套組合polices,給了我們後退的空間,我們可以組合需要的去除不需要的,這纔是我們需要的,即設計中可以在此做選擇題的能力和場所。。

第四部分 一個例子:遊戲引擎和實現
War3這個遊戲引擎我認爲是我見過的頗爲先進和完善的引擎,不知你有沒看過War3的“銘謝”CS,它提到這個引擎用到了比如ZLIB,TRUETYPE等庫,下面我們來實現一個屬於我們自己的War3吧,這個War3就叫NotWar3了!

是使用本書前面三部分這些知識的時候了,在對這個框架的設計中,我將向你呈現大部分出現在本書中的思想或細節,比如對外來庫的使用,三種思想的運用,建立自己的中間邏輯即庫,基於策略的設計(MetaDesignPolicy)等,並給出作者寫這個框架過程中的一些合理或不合理嘗試,模擬一次真正的軟件工程!

第12章 設計(需求分析)


12.1 第一天:接到一個案子

客戶要求我做一個類WebGame的遊戲,那麼這是一個什麼樣的類WebGame呢?他們用列舉的方法來提出了以下幾點要求
1. XML統一格式的方法來打包客戶端文件,,用xdbm客戶端統一打包(他們事先指定用yake和ogre的xml資源,,,model ,,sketeonl,,,scene),
2. 綠色客戶端,如果第一條要求是html,,那麼這就相當於C/S中的瀏覽器,,客戶端升級接口
3. 實現類war3的對戰和遊戲內容,類War3的CS過場界面和錄像機制
4. 實現腳本機的擴展,,跟War3一樣提供一個“護展編程接口”, 玩家發佈任務,WorldEditor,
5. 界面設計:遊戲界面有三大塊,,用一張圖來表示,,,input設計要好
6. 利用Jxta(C的Jxta)來構建網絡環境,遊戲客戶端由於集成Game Logic,因此可設置一個UseLocalLogic和UseFarLogic來選對與P2P環境內的玩家對戰或連接上一個C/S遊戲服務器玩RPG遊戲(此時Use Far Game Logic)。
7. 登入時自動下載上次存儲在服務端的遊戲輔助設置(Input設置,自定義頭像和模型)
8. 爲了能讓這個遊戲更好,,任何你能想到的請自由發揮(可斟情增加報酬)

這些需要大部分只是細節要求(很少是架構需要),因爲我們是在模擬外行的用戶不分鉅細向我們提出要求(對於遊戲邏輯,都閃閃其詞),我們需要整理這些需求以用於後來的設計(說實話以上的需求一般現實見不到)

設計跟需求分析之間的關係很重要,這要求這些需求在設計時就應該被全面地提出來(爲了擴展的需要,設計應該在允許的範圍內做到全面化和合理化,,但是注意,我在對世界邏輯的設計過程中提到的設計涵蓋面是巨大的,這依其說是僅僅面向需求進行分析,,不如說是大而全的通用設計,而一般人則是分析需求,然後依據經驗直接簡約設計,而這裏我是爲了純粹教學,所以請千萬明白這裏的問題),否則在進入設計期時,就只能通過重構(重構是軟工中一個很重要的部分)的手段來添加新功能!

12.2 第二天:需求分析

爲什麼要提出這麼一個過程呢?這(對需求的分析)可作爲設計的參考,這即爲設計的參考技術總結(設計最終要體現這些,從現在開始就要考慮設計與計算機實現的結合點,因此可以用來指導多範型設計)

在一個需求中,什麼是遊戲的邏輯部分,什麼是遊戲的客戶端部分,什麼是遊戲設計中後來才需考慮的細節部分,什麼是在進行遊戲設計時先考慮到的架構部分,在哪個架構中提供什麼接口,一個接口應被置於哪個架構中,這不是一個有人作過專門研究的課題,然而如果給出一個具體的例子,我們應能很快地判斷並決定它應放置於客戶端邏輯還是遊戲邏輯(這就是設計方法論學應解決的問題,遊戲邏輯就是業務邏輯,而ClientSideGeneric或ServerSideGeneric就是表現邏輯了,我們也應該能確定需求分析中的哪些是架構哪些是細節),,


9. 利用Lua Bind C++的本地庫來進行擴展接口的設計(客戶端和服務端面向Developer的編程擴展接口),可以動態測試,不用重啓服務器(提供Lua導入功能的界面接口),不過War3的遊戲邏輯全部是用腳本寫的,而這裏用C++寫LogicGeneric和DevelopGeneric,然後導出爲Lua所用,再在此基礎上進行擴展出一個具體遊戲(事實上僅僅到這裏爲止,才進入“遊戲”的設計,前面的GameGeneric實際上稱爲VRGeneric更爲合適)
10. 針對界面部分,,好像中國的遊戲都是策劃主導編程的,,,策劃提出來的想法大部分都是表現(數值設定,遊戲故事,,等等都是外在表現),,很少是引擎內部架構(我們說原語設計先於多範型設計,那就是說原語設計是對應用內部架構進行設計而不是一開始就設計應用的外部表現),,實際上編程主導策劃纔是對的(當然如果僅僅從技術上來說是這樣的),因爲如果在中間邏輯層次提供了足夠好的接口,那麼外在表現(即實現)可以無限和快速地擴展(然而遊戲開發功利的目的使我們往往顛倒了這種關係,這樣做出來的引擎只能是。。。)客戶端設計DllFromInterLogics,Main.DLL,,,
11. 客戶端是瘦客戶端,,這往往它的EXE只是一個渲染框架和界面資料(媒體資料),客戶端資料比如地圖這些大件可以從官方網站或某個下載點直接下到(分文件或一次性全下載),或者(這裏纔是重點難點)在遊戲中進入一個地圖時如果檢查本地沒有此地圖即顯示First Time Enter This Area,並下載地圖,如果取消下載玩家進入一個黑暗區域但是依然會有障礙,故稱這是一個類Web的Game(真正的WebGame就是用網頁呈現的遊戲,通過80端口進行通信,這樣的遊戲有一點迂腐和小兒科)
12. 對於xml統一打包(比如地圖和對戰時的P2P文件共享),封裝各種二進制的格式(比如客戶端的各種格式的文件和Lua代碼,Developer的護展發佈---一個地圖文件封裝Lua代碼),XML也可作爲Jxta的消息通用文檔格式,我注意到有些遊戲引擎的實現把模型資源封裝爲XML,這在我個人看來並不是一種好的方法,XML作爲Wrapper是好的,然而用它去描述內部某個XML節點數據又是不必要的(個人看法,SQL的擁護者認爲XML DB已經超出了數據存儲的原語空間,他們認爲XMLDB-這裏着重指NavtiveXMLDB是一種過度設計)這裏用xdbm,,把每一個客戶端文件都作爲xdbm的二進制格式,,,這樣就保證了二進制載入和打包的雙重需求
13. 對於綠色客戶端,,將GameGeneric置爲一個單DLL文件,把Yake的所有中間邏輯封裝者即DLLs和LogicGeneric全部編譯進一個DLL文件,稱爲GameEngine.DLL(如果直接在YakeBase中和LogicGeneric中刪除DLL加載邏輯這就需要改動源程序尤其是YakeBase中的Logging機制,而且如果刪除,以後DLL就不能動態裝卸了,而且不利於對GameEngine.dll級的擴展,當然如果不刪除,那麼DLL就以一系列中間邏輯的樣子躺在客戶端這樣不免難看)這就需要定義另外一種加載插件的邏輯,同樣可以做到動態屏蔽和啓用(而非裝卸)和以後對GameEngine.dll級的的擴展(繼承這個屏幕和開啓接口就是了),


不要懷疑,我們正是在做軟件工程(的需求分析階段,,雖然只是初步的分析)和麪向接口編程(因爲接下來我們也要做庫和架構)

以上都是需求分析,描述一下接下來我們的開發路線,當然是先設計後編碼了(在進入真正的範型設計前,我們先進行一個原語設計,,,整理自己對遊戲的認識,,這中間用到外來庫因此還得說說它)
設計前工作
14. 遊戲原語設計,,表現設計與細節設計(設計界面等細節,實際上這個步驟可以放到最後,因爲這是表現邏輯應次於以下的業務邏輯進行,不要被這個主導設計邏輯)
15. 瞭解要用到的庫的架構和接口(Yake表現邏輯)
設計部分
16. 範型設計並實作LogicGeneric(單OO泛型實作設計)
17. 爲LogicGeneric寫ExtendGeneric
編碼部分
Demo編碼(這就是實現,其它的實現通過lua去擴展或爲三大架構自寫Plugable 的DLL)

第13章 設計(領域分析與抽象)


13.1 原語設計

你理想中的遊戲是什麼樣子呢?什麼又是遊戲(你對遊戲的概念直接影響了你對遊戲的設計),每個人的看法都會不同吧(反問一下,什麼又不是遊戲,連BBS也可以是一種文字遊戲,但是我們不做這種超級泛化,我們只對3D網遊作泛化)?以下就是我對遊戲尤其是網遊的看法,更準確地說它是框架設計(在完成了這個框架和所有中間邏輯之後,最終的實現編碼的過程很簡單),,這裏把遊戲泛化爲“可擴展的虛擬現實對戰”,並設計出一套思想級的協議(就如同下面的一張圖表示的),並用一套具體代碼庫實現它,但是這顯然是一個巨大的工程(就好像根據P2P和一些其它的規範比如XML來實作出Jxta),。。

以下是一個一定程序上反映我的理想的一個框架設計,哈哈


我想象中的一個遊戲總原語

由上述原語設計(尚屬非常初級的設想)演化到多範型設計或UML實現,中間還需要很多設計上的細節,但我們會一步一步來進行,下面我們繼續進行對虛擬現實這塊進行細化



我想象中的一個虛擬現實原語


具體介紹一下

世界邏輯管理天氣,地理變遷等遊戲世界自然變遷邏輯和遊戲世界社會關係邏輯(可產生一系列遊戲系統GameSystem,用Plugable的DLL來實現),數據邏輯(虛擬世界與現實的接口) ,這所有的邏輯由一個叫WorldLogic的組件來封裝(上面的描述),這個組件就是獨立於表現邏輯(Show Generic)的遊戲邏輯(又稱Logic Generic),

遊戲世界就是一個社會,因此WorldGeneric就作爲一個單例代表整個世界,這個世界分爲SocietyGeneric和自然Generic,


圖1-3 我想象中的一個虛擬世界邏輯原語

至於表現邏輯就用Yake,注意Yake僅僅是VR的表現引擎,而WorldLogic是遊戲邏輯引擎,而且可從這二個組件定義出一個具體遊戲(這中間還要通過一個下面要談到的Game抽象層次和建立在Game之上的擴展接口ExtendGeneric),

這裏主要任務是深化對世界邏輯的認識,爲以後的設計演化服務
實際上世界邏輯設計可以無限地大(實際上我最後實作出的設計也會很大,而且現在一般的大中型遊戲中也的確涉及到了大量的世界邏輯),,,所以現在的遊戲引擎一般都是表現引擎,,真正引擎之後的遊戲源碼部分纔會出現一點零散的世界邏輯(而且沒有被形成一個架構,因爲人們普遍覺得像世界邏輯這樣的東西根本不應被用來架構並單獨封裝,因爲這的確是一個無盡的任務,世界邏輯可以無限啊),,,然而,就現在的大中型遊戲中出現的世界邏輯引擎來說的確可以總而歸爲一個架構(我們正是要針對這些作架構,,正如你將會在我的設計中的大量細節),所以在設計中找準問題和思路方向是重要的,用什麼範型只是後來的事情

如果一個計設中存在大量細節,,難以分清什麼是細節上的,那些是架構上的,,那麼一個方法是多範型設計中提到的“變點與不變點”,,怎麼說呢,,比如一個遊戲邏輯世界,,總有角色和地圖吧(在經過分析得出大量設計細節後而難以得到一個滿意的分析結果時,這個想法相對來說是比較容易想出來的,歷爲角色是實際存在的,,這個分析結果就是能提出一個架構,,並串聯上所有的設計細節),,,,,這二個不變點就可以引導所有的你不能確定的“變點”(比如各種遊戲系統)

實作GameGeneric就是用UML來設計接口和大架構,先來設計一個多範型設計圖

這個庫我準備將其開源發行,其代號爲gvwl=Generic World Logic Libary
GameGeneric爲Generic Virtual Reality Lib gvrl
這一個完全與平臺無關的庫(除了它的DLL形式邏輯,它的內部完全是平臺無關的邏輯),

好了,找準了問題之後,現在來歸納問題(世界邏輯到底是什麼,有什麼,,)明白這些事實對以後的設計演化非常重要,而在原語設計部分提出的那個邏輯只是非常高層的我們需要在這裏深入並得出一個確切的設計結果(這樣才能依據它得出確切的範型設計)

1. Actor type應有PC(),NPC,道具(又有很多分法),電腦之分
2. Stage就是任務,有世界任務,國家任務,職業任務,組織任務,居民任務,,玩家任務之分,,
3. AI有吸怪AI(AgrossAI),,,電腦羣攻AI
4. 時間就是紀元,當一個設定的世界任務stage被完全(世界任務就是隱創的全世界玩家的關卡),,,玩家進入一個新的紀元,,天器和器候機制,,對資源分佈和技能發揮,,物品掉落,任務細節,等有影響
5. 這個世界邏輯引擎就是一個Actor_Stage_Driven_Dymic_World(完全可以脫離玩家運作,,需要提供一個初始狀態集),,,,有二種干預方式,GM管理方式和gm Player(比如一個國主玩家,,國政系統)二種干預方式(admin adapter)
6. 勢力範圍演變(個人勢力,國家勢力,,組織勢力),,世界地圖,,,資源分佈變遷,合成機制
7. War3like 多元故事機制,,多職業機制,,,採用War3的多職業,,技能組合=一種職業,,只有種族之分無明顯的職業之分,,但是種族對技能學習有影響,,玩家遺蹟系統(聲望和稱號系統,,匆濫,,聲望和稱號極其難得,,特殊道具難得)
8. 裝備機制,,玩家裝備位,,,
9. 少怪多人,,,boss爲電腦,,怪物
10. 元素互克系統,,傷害數值(保證適當量度的耐打,少團滅,秒殺),能力數值機制(攻擊方式,攻擊傷害,,攻頻,,視野能力,,白天黑夜之分,記憶系統),,,技能分職業技能和自學技能機制,,技能連攜機制,
11. 拍賣行和叫賣場,,,叫賣類寵物(dual box like),,,野外僱傭兵機制,,
12. 打鬥機制,,城建系統,國戰場,,道具升級系統,,陣法系統,,,Macro和自定義UI的物品/技能欄
13. Actor都有一個id和一個名字集,,
14. 交通機制,,傳送機制,,時空廣闊,有星外,天上之分,裏世界,玩家可轉服
15. 此遊戲

以上這些細節如果寫開來,,就是一份很詳盡很佔篇幅的策劃書,,,,因此我們採取了簡單列舉的方法,,,,具體的這些設計的細節和對這些設計的範型實現細節在下面一節會講到。。

那麼當設計完成進入多範型之後,我們是用一種自上而下(先提出一種架構然後慢慢發展支節邏輯)還是自下而上呢(先獨立考慮各個支節,再在它們上面架構一個大架構,),這裏的多範型設計我們使用自上而下(因爲我個人認爲這樣設計得比較全面)

我對Game的看法是,


圖1-2我想象中的一個Game原語


那麼擴展呢?這裏只說腳本機的擴展,Lua擴展並非真正的功能擴展,而是功能的導出,(當然寫Lua之後的代碼的確可進行功能擴展,但這裏是相於Lua之前的中間邏輯來說的),腳本機的作用在於動態調試和發展用戶邏輯(就是類似War3WorldEditor的開關編輯器了)

1,作爲GameGeneric一部分時


2,獨立於GameGeneric之上時



什麼是擴展出來的某具體遊戲呢?它可以是單機(比如做類War3的戰役任務以熟悉遊戲,這就要求遊戲邏輯在本機),或對戰(類War3的對戰地圖遊戲),或C/S式的RPG(遊戲邏輯也可以在服務端),這樣的遊戲並不侷限於FPS或RPG或其它類型,這是一種泛RPG遊戲,形象來說就是服務端的爲客戶端和服務端共享這個最終的遊戲就是一個Lua寫成的GameGeneric的派生,可動態測試,Lua解釋器就是一個啓動Shell,因爲啓動遊戲等表現邏輯都在ShowGeneric內),這樣的遊戲,通過它的擴展接口,你可以在一張地圖上設置賽道,這樣就是一個賽車遊戲,可以通過擴展接口,改變遊戲視角,提供一些特別的地圖,就可以做成一個FPS遊戲,但它大體上是一個RPG(遊戲在道理上本來就是一個完整的世界),因此說它是一種泛RPG遊戲(這再一次體現了,通過某些很好的設計,可以整合某些細節或繞過它們,因此說設計是優於編碼的)
這面即爲NotWar3的原語圖


我想象中一個具體遊戲原語
13.2 瞭解Yake
以上談完了原語設計部分,這一節講要用到的庫,就是YAKE了,這一節可能要佔很長篇幅,因爲Yake本身是一個很大的架構


我發現Yake是一個很好的遊戲引擎(它自稱是VR和遊戲引擎,,但是它明顯沒有提供虛擬世界的內部邏輯,而只是提供了VR和Game對於計算機的表現邏輯而已,爲什麼選擇Yake呢?就因爲它功能全面至於運行效益也要考慮),它集成了我們以上提到的多數組件(而且DLL形式的組件可以選擇不編譯而且各個DLL之間沒有相互引用關係對於Yake主體來說完全是Plugable的)(但是我們將按需去掉一些組件,將它集成到GameGeneric上去,比如將LuaBind邏輯和Lua提到GameGeneric上去,把YakeBase中的DLL封裝邏輯和Logging機制去除,因爲我們將把Yake編譯成一個靜態庫再爲GameGeneric所用,把oSceneLoaderLib去除,因爲根本不需要這個功能-不需要把場景圖中每個節點都利用XML展現出來,我們只想用XDMB的二進制Wrapper場景文件(場景文件有它自己的非XML形式的加密格式),而用XDMB去封裝場景調入邏輯),我們應瞭解它的架構先,並理解其存在哪些API,但是並不深入探索它們是如何實現的,因爲我們僅僅是想使用它 (這就表明,我們僅僅要做庫的顧客而不是讓它去主導我們自己的架構)

對庫的理解要深入到函數API級(只是瞭解一下並不探索其實現因爲我們只是顧客只滿足於調用它並在自己的邏輯中封裝它,但是要當高級顧問時卻要了解它與其它API的聯繫,即該API的實現,因爲此時我們調用到了該API的低層,這勢必要清楚它與其它API的聯繫),而不僅僅滿足瞭解其庫的架構(瞭解Yake總體大架構還是基本,如果能完成理解它的主體即引入其它庫的那些邏輯就更好拿來調用了),對Yake的研究可以讓你學到不少東西

我爲什麼要強調“我們只是顧客呢”,人們並不真正理解那些從一出生就耳聞目見的字眼的真正意義,我想信如果不是我加括號,很多人也很難理解出括號內這層意義出來(而顯然這是在我們前面反覆講到的)

能寫出像Yake之類高抽象的代碼(除了要懂模板和OO這二種範型的各種技術細節之外,還有策略,可以稱爲是OO和模板之上的抽象泛型),更重要的是具備我上面提到的那些設計思想以及對遊戲的認識,流行於程序員之間的用卡來表達設計的方法,最初就是思想的活動,提出一些思想字眼(設計用詞),然後再形成具體類文件的名字寫到卡片上,最終形成一個計算機觀點能接受的範型設計。

正如Yake所言,它是一個VR引擎,又是一個Game引擎,它的Base部分提供了DLL裝卸機制和Log機制,這就一定意義上對應我們接下來要談到的“可複用”策略(ORDL),它除了RAF,Client,Server之外的表現部分是VR的表現部分,而Raf,Client,Server這些都是Game Show部分(顯然Yake沒提供VRLogic和Game Logic部分),它的LuaBind就是腳本級擴展接口,而Base的DLL和Log機制就是程序員級的擴展接口,下面我們來深刻了解一下這個Base Generic並探索它可以被如何完善的地方,首先來看一個概念

面向複用的設計庫(Ori Reuse Design Lib),這個庫是一個策略庫(我們把用模板範型和OO範型來表達設計的技術實現統稱爲策略,這樣策略就不只表示DP了),Generic Design Poliy Lib(通用策略設計庫),那麼這是一個什麼樣的庫呢,這個庫基於以下思想


這個庫的另一個別名是:GenericInterLogicPolicyLibrary,ComplilerTime 實現與Design Devidded Policy Lib,或Complier Time face可複用 Plugable Pocidy Lib(請原諒我用這麼長的描述,但這不是標題,而且如果缺少任何一個字眼都不完整),如果把Yake的Base Generic進行完善,它就會這麼一個庫

這個Policy用了一些語言內的範型,比如OO和模板,還用了語言外的範型,比如Plugable的DLL,這樣的庫爲設計服務(注意這句話),它主要達到一種什麼樣的目的呢, 通過在設計期就分開實現和設計,這樣就達到了在設計期就達到充分的可複用(而可複用顯然無論是設計還是實現都最先考慮的問題,OO和麪向構件都是爲了可複用而設計,因此這個庫實際上就整合了這二個方面,語言之內是OO,語言之外是構件),(你可以看到這個庫爲VREngine和NotWar3的各個中間抽象層次所用)

與設計期有關的東東常稱爲元,比如MetaProgramming,強調設計期的作用,還比如MetaData(強調對數據的描述而不是存儲,關係數據庫的擁護者認爲存儲纔是數據庫真正要考慮的東西,而存儲是具體數據庫的事,這個道理就像我在本書最後一部分提到的Game與VR的關係,到底具體數據庫纔是數據庫,還是對數據的描述纔是數據存儲?(什麼是遊戲,具體的遊戲是遊戲,還是遊戲這種泛型說法纔是遊戲)這是雞生雞蛋生蛋的問題,以我看,雞蛋問題並非不可解,答案就是先是雞後是蛋,因爲在問題中,雞字蛋字都是具體的)因此上述庫還可稱爲MetaDesignPolicy


運行期=計算機實現=泛型實現(RTTI),因此可複用的最大限度是你的泛型實現

圖的左邊是“與平臺有關的表現”,稱爲表現Generic(需用與計算機平臺有關的多範型表達),右邊是“不與平臺有關的純思想”,這樣就做到了在設計期就充分考慮了實現與邏輯分開(設計與編碼是統一的,設計期就充分考慮了可複用,那麼在編碼時自然會繼承這個優勢,一切編碼和實現級的的不可複用問題和難於複用問題都可在設計期找到答案,設計與實現本來就是統一多於其矛盾的,他們的矛盾之處在於泛型設計與原語設計的矛盾,比如在設計期把遊戲中的一片葉子當成一個對象有時是一種好設計,然而當被用於多泛型實現時,在運行時就會造成效率的低下,這就是不成功的多範型設計,當然,實現與邏輯分開這個說法絕非這麼簡單,因此我們在SHOW和LOGIC內部也需要考慮將其OO化或策略化以增強其內部的實現與邏輯),誠然,OO可以一定程序上做到“實現與邏輯分開”,但是還有策略,然而這是語言內部的,在語言外部是庫(Plugable的DLL形式發揮了重要的作用),這個技術也可提供設計時的可複用(當然可複用永遠是相對的,在應用上述圖作出的應用架構,在各個Genric外至少是實現與邏輯分開的,當然各個Generic的SHOW或LOGIC部分也要做到可複用),這樣就是

1. 要保證在實現與Logic內部也是實現與邏輯分開的和充分的Plug化
2. 要保證Logic部分絕對是思想而沒有與平臺相關的細節
3. 在每一個Genric層次,可以無Logic,但不可沒有Show

以的圖可對應到NotWar3的原語設計部分,而它們共同的基礎就是這個面向複用的策略庫

1. Extend Generic=Game Generic+Lua Bind Generic(或者GameGeneric自成一個Generic,此時就要求Lua Bind不是僅對GameGenric進行Bind,而是對下面各個Genric進行Bind)
2. VR Generic=Yake+World Logic Generic
3. Game Generic
4. 它們的下面是Yake Base

即見下面這個總原語圖


請注意到,上述圖中很多用詞顯然僅屬於思想過程的中間描述(即僅僅是描述原語的用詞,比如GameGeneric中的Generic這個詞就表示它可能是一種架構邏輯也可能是實現邏輯,這裏就用Generic來整個描述這個層次抽象, 即Generic=”一切中間邏輯”代詞,它並不一定被封裝成一個庫也並不一定要在後來的多範型設計中要求被體現,可能會在源程序中用名字空間來表達,C++中的名字空間表達了一種什麼樣的本質呢?一個名字空間就是一個應用架構中某個子架構,或稱爲抽象層次的總稱,而我們這裏提出了四個即1.Design Policy Generic2,VR Generic3,Game Generic4,Extend Generic,其中3,4是特別的,如果3是爲4服務,那麼3,4實際上可以合併,如果Game有它自己的架構,那麼3,4是分開的,Generic,Generic就是一種組織所有某個抽層的架構和實現的形式,名字空間也使我們的源程序能夠很好地硬盤上被組織,Java的源程序包中的文件夾形式就是一種表現),因爲我們此時在進行原語設計,不必考慮面向代碼將其反映出來,這樣我們考慮問題的範圍會更廣一點(實際進行多範型設計時要反映出來的就是那些能實際作用代碼的用詞,像上面大部分後帶Generic飾詞的中間邏輯集都要被體現,我們在實作部分會給出一個經過修飾的原語圖,從原語設計到多範型設計到UML設計我們稱爲設計的演化),但無論如何要明白,一些對設計過程中用到的思想輔助用詞是不必考慮進設計的代碼實現的。原語設計跟多範型設計中間存在比較大的距離(真正的原語設計不是超級原語,雖然它名爲原語,但它是在可控範圍內權衡做到的原語,真正的多範型也並不是一團沒有根據的計算機實現,它要也要面向前面的原語設計得出的結論,它們之間的聯繫要優於它們之間的區別被我們考慮)

不必拘泥以上的思想模型(任何人構架的邏輯模式和邏輯用詞都可以不一樣這不是嚴格的,但最終的應用反映在代碼上要能工作這卻是嚴格的),你畫出的原語設計圖同樣正確,再簡單也沒關係,也不一定要成檔,因爲這只是嚴重參雜了個人看法的東東(只有當後來的多範型設計時才慢慢走入編程的束縛,因爲這是參照編程界現成方案的設計,因此大家做出的設計都差不多一樣)

這裏再談一下可複用與“邏輯實現”分開之間的聯繫(主要出現在設計期和重構期)

我們一直提倡邏輯與實現分開(這是設計的基本準則),這樣設計有沒有做到呢?,這裏要說一下實現與邏輯分開,然而即便是這個說法本身也有點含糊,說實話這絕對是一個具體問題具體分析的活,(就以上面的設計來說,我至少可以找出幾個邏輯與實現的說法,比如,但是大體上可以分爲二類,Generic外部的和內部的,反正我自認爲上圖已經體現了足夠好的邏輯與實現分開,如果你不這麼認爲,那麼你一定有比我更好的方案,不妨說出來聽聽或指出我的缺點,對於實現與邏輯分開,我曾認真思考過爲GameLogicGeneric中的每個個體增加一種聯繫到GameShowGeneric,但是這個設計我後來放棄了,這樣做看起來合理實際上只會增加實現與邏輯的偶合性而且在實現上也是很複雜的,而且壓根就不需要這樣做因爲GenericInterLogicPolicyLib已經是一種很好的方案,已經是一種根本的實現與邏輯分開策略,,需要作一層複雜的中間封裝邏輯,這層抽象可能被集成到GameGeneric中)一個遊戲GameGeneric就是LogicGeneric加上ClientSideGeneric的總和(邏輯上下或平等互飾構成邏輯總和,應用於是被建立起來)並由此定義出一個遊戲比如NotWar3(這纔是真正的編碼與實現部分,就是幾個Demo中要談到的),或者我們可以把實作出的LogicGeneric置於ClientGeneric的底層(,這正是我們要採取的方式,作爲中間邏輯的庫之間應如何架構,它們之間的邏輯應如何,這也是我們要考慮的)。

我的一些不成熟的嘗試和不成熟的思想導致的設計缺陷

1. 我曾設想Lua部分寫Game.dll,我把總架構做到了擴展邏輯之後作爲Lua代碼,因爲我認爲那是具體遊戲的事而不是GameEngine的事了,因爲我還覺得“具體遊戲纔是遊戲”,而這裏的GameEngine似乎稱爲“用計算機模擬遊戲世界的引擎”(ComputerSimutWorldEngine)更爲合適,因此GameEngine.DLL最好的說法是VREngine,但是另一方面,也有另外一種做法那就是把擴展邏輯和VR邏輯中間放置一個Game總架構邏輯,這樣G總架構邏輯就在Lua之前了,會是C++的代碼,這樣也是一種非常好的選擇)遊戲是一個對等或C/S的模型,因此分PeerSideGeneric和ServerSideGeneric,(我一開始設計時GameGeneric曾把這二個設計原語加了進去,說實話這就不是一個好的設計,因爲只要在具體遊戲的邏輯中才會出現客戶端和服務端之說,而GameGeneric管理一些更爲低層的邏輯,這樣做就犯了設計的大忌,過早把實現混合成架構做進了設計中)

2. lua擴展是不是限制了lua級擴展的瓶頸(要深克明白腳本機擴展導出與動態修改的作用區別),因爲導出的邏輯有限?當然這個擴展接口並不能導出全部的中間邏輯,實際上也沒有必要(LUA級的擴展不需要導出從一開始到GameGeneric的全部中間邏輯)這裏要深刻理解這個擴展接口(因爲多範型設計與細節有關,只要明白各個中間抽象層次應有的細節,才能依據它來進行多範型設計),擴展接口架構在GameEngine之上(但並不需要導出這個構架),但是擴展邏輯不只是封裝GameEngine這個中間邏輯層的某些接口(即它不是一個純粹架構於GameGeneric之上的中間邏輯,雖然在原語設計圖中它是,然而在範型實現中並不是這樣),而是分別對其它中間邏輯層次有引用(即它必須對OIS有導出邏輯才能在擴展接口集中提供一個屏蔽鼠標的LUA接口,必須對RAF作導出邏輯才能在擴展集中提供啓動一個遊戲的功能,必須對CAMERA作導出才能在LUA級提供一個變換視角的遊戲—而這個功能正是LUA級擴展必要的,,因爲玩家寫地圖時總要調用到變換視角的功能,,比如通過LUA擴展出的一個泛RPG的FPS遊戲,LUA護展級應提供什麼功能和接口,可以參照WAR3WORLDEDITOR中的全部開關功能)LUA級的擴展全部是函數級的接口,而不是OO級的架構,,一方面是因爲用LUA編程的玩家你不能要求他們有OO範型的思維,另一方面是因爲實際的通過LUA擴展的過程只要用面向過程就夠了並不需要形成一個架構,無非就是轉動視角,控制遊戲開啓這些簡單對外的邏輯,對內的擴展只能通過爲Game.DLL寫DLL來擴展----(而且LUA本身支持OO也不是很好)當然也存在另外一種做法,這就是把GameGeneric這整個抽象層次作爲爲Extend Generic服務的基礎,也即它並不指代一個“Game”,並不打算把它設計成一個架構,而只是簡單的對要導出的庫(比如VREngine的方方面面)擴展封裝,這樣GameGeneric實際上就是1,GameGeneric加2,Extend的綜合了


13.3 GVWL1.0開發
荒世:一個簡單遊戲引擎的實現

這一部分的目的就是爲了產生一個遊戲引擎可複用庫,向讀者展示的是設計能力。。

這裏的設計在不同的情境下有二種意思,1,面向複用的工程設計,2,用語言映射現實問題的解法。。

當然,這不是遊戲引擎,,僅僅是個可稱爲圖形引擎的東西(當然,限於教育目的,我並不打算把它寫得跟ogre一樣面面具到不提供很多接口過多選擇,比如GL,DX)。然而,通過增加網絡邏輯,等其它方面的邏輯,它就可以形成爲一個遊戲引擎(作業:重構裏面有相應的作業)

這個遊戲引擎充分利用了stl和loki邏輯。而且還複用了OPENGL,我始終相信stl和loki纔是標準庫,爲了最大清楚程序地呈現利用C++和這二個語言標準庫來設計應用,,,我沒有引入太多的庫,,我剋意不引進太多的第三方庫,(所以很多小邏輯我都直接發明輪子以避免使用到第三方庫),,因爲這是基於前述清楚教育的目的而且我個人能力也有限,,(比如下面我不會的碰撞檢測邏輯所以我剛好省掉它);,所以這個遊戲引擎也是省略了很多功能的(比如碰撞等瓴域邏輯,本來一般圖形引擎和遊戲引擎是需要ODE的但我省了),。但這個庫作爲遊戲引擎,最最基礎的功能它還是都包括的,你完全可以二次開發,加入更多複用邏輯或自己的邏輯,,將其擴展成大全的遊戲引擎。

這個產生的庫要求很強的擴展能力(很強的被複用能力,所以要求基於複用的設計元素),所以我用了LOKI,OO方面的東西用得比較少(因爲我相信template 加loki的設計能力和理念作爲設計標準庫,就該善用它,而OO更應該用在非設計的實現中,因爲用OO設計複用時需要大量的學習成本,不應用OO 的眼光對設計分層級)。

當發展具體遊戲也即實現部分時應該遠離設計, 畢竟複用是設計複用,而不是實現複用,,但這個實現也有這個實現意義上的設計(因爲二者根子上都是軟件抽象的本質),設計與實現這二者是在某個範圍相對的,而且只有模糊分界,,面對整個系統時架構師有劃分,而且在程序員某模塊中也有小設計,所以C++的各項語言能力,都可用來設計或編碼,它是應用語言也是設計語言,,只是OO不太適合出現在面向複用的設計中):不要提供很大的跟OO一樣的logics,,,只有通過設計模式串聯起來的纔是大邏輯(這纔是銀彈,OO維度太低了),,其它的都是小邏輯,就跟boost裏面的庫一樣。。


設計中主要解決的大大小小的瓴域邏輯有如下(因爲這些庫邏輯最終要進入實現,所以我只在設計中含有最基礎最原子的那些功能和接口,比如圖形方面的邏輯,動畫啊什麼的,(這是業務領域的輪子,,外來輪子纔是另外輪子,源程序樹應該另外輪子各佔一文件夾,主就放在根文件夾內),更高階部分才發展業務領域邏輯,發展更高階的邏輯,比如場景管理,資源管理,,

13.4 範型設計與實作
那麼以上多範型設計圖中,“面向用戶和開發者”的高階接口應提供什麼呢,這並不是一個有標準答案的問題(這不可能是一個形式的活動,只能是一個感性的活動),根據“設計的方法學”,我們只需做列舉題與選擇題,比如以下:
1, 是不是應提供一個“”,我以前想到用Actor封裝,Stage封裝
2, 如果要提供播放CS的邏輯中間件,(因爲CS可能要獲取網絡)那麼是不是應提供一個類,這個類接受網絡參數,這樣就有引用到Yake 的Net,不,,絕對不能這樣,,,這樣會增加表現與邏輯的聯繫,,這樣做出的類(集)接口就是不好的接口設計,,,想要什麼樣的應用邏輯總是可以用邏輯互飪的方法達到,,而基礎的中間邏輯件部分只應提供屬於它自己職責的獨立邏輯。最好不要與其它邏輯交叉(往往將遠離應用的中間邏輯稱爲真正的接口,,,而靠近應用的實現邏輯,,或應用邏輯本身稱爲真正的實現,,只要不過早地將實現邏輯做進或混入接口邏輯,,那麼你就做到了實現與邏輯分開,,,也即這裏“真正的接口”主要就是發揮橋接作用的接口邏輯,因爲接口邏輯也算是中間邏輯,應在這些抽象層次上提供少量邏輯,,,,後來的大量實現邏輯靠前面的接口邏輯來擴展,,)

我們要寫什麼樣的實現呢,注意我們還得爲實現構造一個架構(實際上這個還可做到GameGeneric之上,但是這個應該屬於具體遊戲的邏輯了,因此我們還是做到NotWar3的Lua代碼構架中去)

純邏輯的庫ODE,也跟LogicGeneric一樣吧,,不需考慮平臺相關,Logic級應提供什麼功能接口,應參照WAR3的JASS的二個基本文件來設計,那裏有大部分遊戲世界邏輯(但JASS用了過程,這裏是C++用OO來實現),,應考慮到遊戲世界這個相對它的Plugable dll的“高階”中間邏輯層次應提供什麼邏輯跟接口(很明顯,這裏面應有一個能容納Plug邏輯的引入邏輯的架構(但是YAKE的引入庫邏輯是引用現有的庫,而這裏的總架構是先於PLUG的,因此更爲靈活和自由,應提供類ADDAFORCE之類的總擴展接口,才能爲PLUG留足夠好的對主LOGICL架構的擴展空間),和一個類YAKEBASEGENERIC的實現集,因爲需要用到DLL和LOG機制,),

下面設計出一個UML圖,這步開始,我們就進入設計的編碼實現了,這步過後,設計就完成了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章