你們看到這個標題確定會歡呼雀躍了,覺得功能少的語言就容易學。其實徹底不是這樣的。功能少的語言若是還適用範圍廣,那全部的概念一定是正交的,最後就會變得跟數學同樣。數學的概念很正交吧,正交的東西都特別抽象,一點都不直觀的。不信?出門轉左看Haskell,還有抽象代數。所以刪減語言的功能是須要高超的技巧的,這跟你們想的,還有跟go那幫人想的,能夠判定徹底不同。javascript
首先,咱們要知道到底爲何須要刪減功能。在這裏咱們首先要達成一個共識——人都是很賤的。一方面在發表言論的時候光面堂皇的表示,要以需求變動和可維護性位中心;另外一方面本身寫代碼的時候又老是不惜「後來的維護者所支付的代價代價」進行偷懶。有些時候,人就是被語言慣壞的,因此須要對功能進行刪減的同時,又不下降語言的表達能力,從而讓作很差的事情變得更難(徹底不讓別人作很差的事情是不可能的),這樣你們纔會傾向於寫出結構好的程序。java
因而,語法糖究竟是不是須要被刪減的對象呢?顯然不是。一個好的語言,採用的概念是正交的。儘管正交的概念能夠在拼接處咱們須要的概念的時候保持可維護性和解耦,可是每每這麼作起來卻不是那麼舒服的,因此須要語法糖。那若是不是語法糖,到底須要刪減什麼呢?程序員
這一集咱們就來討論面向對象的語言的事情,看看有什麼是能夠去掉的。算法
在面向對象剛剛流行起來的時候,你們就在討論什麼搭積木編程啊、is-a、has-a這些概念啊、面向接口編程啊、爲何硬件的互相插就這麼容易軟件就不行呢,而後就開始搞什麼COM啊、SOA啊這些的確讓插變得更容易,可是部署起來又很麻煩的東西。究竟是什麼緣由形成OO沒有想象中那麼好用呢?express
之因此會想起這個問題,實際上是由於最近在咱們研究院的工位上出現了一個相機的三腳架,這個太三腳架用來固定一個手機乾點邪惡的事情,因而你們就圍繞這個事情展開了討論——譬如說爲何手機和三腳架是正交的,中間只要一個前凸後凹的用來插的小鐵塊就能夠搞定,而軟件就不行呢?編程
因而我就在想,這不就是跟所謂的面向接口編程同樣,只要你所有東西都用接口,那軟件組合起來就很簡單了嗎。這樣就算恰好對不上,只要寫個adaptor,就能夠搞定了。其實這種作法咱們如今仍是很常見的。舉個例子,有些時候咱們須要Visual C++ 2013這款全球最碉堡的C++ IDE來開發世界上最好的複雜的軟件,不過自帶的那個cl.exe實在是算不上最好的。那怎麼辦,爲了用一款更好的編譯器,放棄這個IDE嗎?顯然不是。正確的解決方法是,買intel的icc,而後換掉cl.exe,而後一切照舊。設計模式
其實那個面向接口編程就有點這個意思。有些時候一個系統大部分是你所須要的,別人又不能知足,可是恰好這個系統的一個重要部分你手上又有更好的零件能夠代替。那你是選擇更好的零件,仍是選擇大部分你須要的周邊工具呢?爲何就非得二選一呢?若是你們都是面向接口編程,那你只須要跟cl.exe換成icc同樣,寫個adaptor就能夠上了。ruby
好了,那接口是什麼?其實這並無什麼深奧的理解,接口指的就是java和C#裏面的那個interface,是很直白的。不知道爲何後來傳着傳着這條建議就跟一些封裝偶合在一塊兒,而後各類非面嚮對象語言就把本身的某些部分曲解爲interface,成功地把「面向接口編程」變成了一句廢話。框架
不過在說interface以前,有一個更簡單可是能夠類比的例子,就是函數和lambda expression了。若是一個語言同時存在函數和lambda expression,那麼其實有一個是多餘的——也就是函數了。一個函數老是能夠被定義爲初始化的時候給了一個lambda expression的只讀變量。這裏並不存在什麼性能問題,由於這種典型的寫法,編譯器每每能夠識別出來,最終把它優化成一個函數。當咱們把一個函數名字當成表達式用,得到一個函數指針的時候,其實這個類型跟lambda expression並無任何區別。一個函數就只有這兩種用法,所以實際上把函數去掉,留下lambda expression,整個語言根本沒有發生變化。因而函數在這種狀況下就屬於能夠刪減的功能。函數
那class和interface呢?跟上面的討論相似,我主張class也是屬於能夠刪減的功能之一,並且刪減了的話,程序員會由於人類的本性而寫出更好的代碼。把class刪掉其實並無什麼區別,我能想到的惟一的區別也就是class自己今後不再是一個類型,而是一個函數了。這有關係嗎?徹底沒有,你用interface就好了。
class和interface的典型區別就是,interface全部的函數都是virtual的,並且沒有局部變量。class並非全部的函數都是virtual的——java的函數默認virtual可是能夠改,C++和C#則默認不virtual可是能夠改。就算你把全部的class的函數都改爲virtual,那你也會所以留下一些狀態變量。這有什麼問題呢?假設C++編譯器是一個接口,而Visual C++和周邊的工具則是依賴於這個class所創造出來的東西。若是你想把cl.exe替換成icc,實際上只要new一個新的icc就能夠了。而若是C++編譯器是一個class的話,你就不能替換了——就算class全部的函數都是virtual的,你也不可能給出一個規格相同而實現不一樣的icc——由於你已經被class所聲明的構造函數、析構函數以及寫好的一些狀態變量(成員變量)所綁架了!
那咱們能夠想到的一個迫使你們都寫出傾向於比之前更能夠組合的程序,要怎麼改造語言才能夠呢?其實很簡單,只須要不把class的名字當作一個類型,而把他當作一個函數就能夠了。class自己有多個構造函數,其實也就是這個意思。這樣的話,全部本來要用到class的東西,咱們就會去定義一個接口了。並且這個接口每每會是最小化的,由於徹底沒有必要去聲明一些用不到的函數。
因而跟去掉函數而留下匿名函數(也就是lambda expression)相似,咱們也能夠去掉class而留下匿名class的。Java有匿名class,因此咱們徹底不會感到這個概念有多麼的陌生。因而咱們能夠來檢查一下,這樣會不會讓咱們喪失什麼表達方法。
首先,是關於類的繼承。咱們有四種方法來使用類的繼承。
一、相似於C#的Control繼承出Button。這徹底是接口規格的繼承。咱們繼承出一個Button,不是爲了讓他去實現一個Control,而是由於Button比Control多出了一些新東西,並且直接體如今成員函數上面。所以在這個框架下,咱們須要作的是IControl繼承出IButton。
二、相似於C#的TextReader繼承出StreamReader。StreamReader並非爲了給TextReader添加新功能,而是爲了給TextReader指定一個來源——Stream。所以這更相似於接口和實現的區別。所以在這個框架下,咱們須要的是用CreateStreamReader函數來建立一個ITextReader。
三、相似於C#的XmlNode繼承出XmlElement。這純粹是數據的繼承關係。咱們之因此這麼作,不是由於class的用法是設計來這麼用的,而是由於C++、Java或者C#並無別的辦法可讓咱們來表達這些東西。在C裏面咱們能夠用一個union加上一個enum來作,並且你們基本上都會這麼作,因此咱們能夠看到這其實是爲了拿到一個tag來讓咱們知道如何解釋那篇內存。可是C語言的這種作法只有大腦永遠保持清醒的人可使用,並且咱們能夠看到在函數式語言裏面,Haskell、F#和Scala都有本身的一種獨有的強類型的union。所以在這個框架下,咱們須要作的是讓struct能夠繼承,而且提供一個Nullable<T>(C#也能夠寫成T?)的類型——等價於指向struct的引用——來讓咱們表達「這裏是一個關於數據的union:XmlNode,他只多是XmlElement、XmlText、XmlCData等有限幾種可能」。這徹底不關class的事情。
四、在Base裏面留幾個純虛函數,讓Derived繼承自Base而且填補他們充當回調使用——臥槽都知道是回調了爲何還要用class?設計模式幫咱們準備好了Template Method Pattern,咱們徹底能夠把這幾個回調寫在一個interface裏面,讓Base的構造函數接受這個interface,效果徹底沒有區別。
所以咱們能夠看到,幹掉class留下匿名class,根本不會對語言的表達能力產生影響。並且這讓咱們能夠把全部須要的依賴都從class轉成interface。interface是很好adapt的。仍是用Visual C++來舉例子。咱們知道cl.exe和icc均可以裝,那gcc呢?cl.exe和icc是兼容的,而gcc徹底是另外一套。咱們只須要簡單地adapt一下(儘管有可能不那麼簡單,但總比徹底不能作強多了),就可讓VC++使用gcc了。class和interface的關係也是相似的。若是class A依賴於class B,那這個依賴是綁死的。儘管class A咱們很欣賞,可是因爲class B實現得太傻比從而致使咱們必須放棄class A這種事情簡直是不能接受的。若是class A依賴於interface IB,就算他的缺省實現CreateB()函數咱們不喜歡,咱們能夠本身實現一個CreateMyB(),從而吧咱們本身的IB實現給class A,這樣咱們又能夠提供更好的B的同時不須要放棄咱們很須要的A了。
不過其實每次CreateA(CreateMyB())這種事情來獲得一個IA的實現也是很蠢得,優勢化神奇爲腐朽的意思。不過這裏就是IoC——Inverse of Control出場的地方了。這徹底是另外一個話題,並且Java和C#的一些類庫(包括個人GacUI)已經深刻的研究了IoC、正確使用了它而且發揮得淋漓盡致。這就是另外一個話題了。如何用好interface,跟class是否必須是類型,沒什麼關係。
可是這樣作還有一個小問題。假設咱們在寫一個UI庫,定義了IControl而且有一個函數返回了一個IControl的實現,那咱們在開發IButton和他的實現的時候,要如何利用IControl的實現呢?本質上來講,其實咱們只須要創造一個IControl的實現x,而後把IButton裏面全部本來屬於IControl的函數都重定向到這個x上面去,就等價於繼承了。不過這個寫起來就很痛苦了,所以咱們須要一個語法糖來解決它,傳說中的Mixin就能夠上場了。不知道Mixin?這種東西跟prototype很接近可是實際上他不是prototype,因此相似的想法常常在javascript和ruby等動態語言裏面出現。相信你們也不會陌生。
上面基本上論證了把class換成匿名class的可能性(徹底可能),和他對語言表達能力的影響(毫無影響),以及他對系統設計的好處(更容易經過人類的人性的弱點來引導咱們寫出比如今更加容易解耦的系統)。儘管這不是銀彈,但顯然比如今的作法要強多了。最重要的是,由於class不是一個類型,因此你沒辦法從IX強轉成XImpl了,因而咱們只可以設計出不須要知道到底誰實現了IX的算法,可靠性迅速提升。若是IY繼承自IX的話,那IX能夠強轉成IY就相似於COM的QueryInterface同樣,從「查看究竟是誰實現的」昇華到了「查看這個IX是否具備IY所描述的功能」,不只B格提升了,並且會讓你整個軟件的質量都獲得提升。
所以把class換成匿名class,讓本來正確使用OO的人更容易避免無心識的偷懶,讓本來不能正確使用OO的人迅速掌握如何正確使用OO,封死了一大堆由於偷懶而破壞質量的後門,具備至關的社會意義(哈哈哈哈哈哈哈哈)。
我之因此寫這篇文章是爲了告訴你們,經過刪減語言的功能來讓語言變得更好徹底是可能的。但這並不意味着你能經過你本身的口味、偷懶的習慣、B格、由於智商低而學不會等各類奇怪的理由來衡量一個語言的功能是否應該被刪除。只有冗餘的東西在他帶來危害的時候,咱們應該果斷刪除它(譬如在有interface前提下的class)。並且一般咱們爲了不正交的概念所本質上所不可避免的增長理解難度所帶來的問題,咱們還須要相應的往語言裏面加入語法糖或者新的結構(匿名class、強類型union等)。讓語言變得更簡單歷來不是咱們的目標,讓語言變得更好用纔是。並且一個語言不容易學會的話,咱們有各類方法能夠解決——譬如說增長常見狀況下能夠解決問題的語法糖、免費分享知識、經過努力提升本身的智商(雖然有一部分人會所以感到絕望不過反正社會上有那麼多職業何須非得跟死程死磕)等等有效作法。
因而在我本身設計的腳本里面,我打算全面實踐這個想法。