多態(tài)手报,OO與FP

注:本文原名《OO NOT SUCKS, YOU DO》盒卸。

緣起

Erlang之父Joe Armstrong曾經(jīng)寫過一篇《Why OO Sucks》,被很多反OO的程序員——尤其是erlang社區(qū)的程序員——當(dāng)作了大旗碉哑。

文中指出了OO的四大問題:

  1. 數(shù)據(jù)結(jié)構(gòu)和函數(shù)不應(yīng)被綁在一起(Data structure and functions should not be bound together)挚币;
  2. 所有事物都不得不是對象(Everything has to be an object);
  3. 在面向?qū)ο笳Z言里扣典,數(shù)據(jù)類型定義散播在各處(In an OOPL data type definitions are spread out all over the place);
  4. 對象有私有狀態(tài)(Objects have private state)妆毕。

做為一個(gè)已經(jīng)關(guān)注OOFP都將近10年的老鳥(包括erlang),我對此的看法是:這四大問題里贮尖,除了第二條尚可商榷之外笛粘,其它幾條都只是證明了Joe并不明白什么是面向?qū)ο?/a>,甚至都不明白什么叫高內(nèi)聚湿硝,低耦合薪前。

一個(gè)例子

Joe在其著作《Programming Erlang》里,給出一個(gè)下面的例子:

area({rectangle, Width, Ht}) -> Width * Ht;
area({circle, R})            -> 3.14159 * R * R;
area({square, X})            -> X * X.

這個(gè)例子充分反映了Joe對于程序設(shè)計(jì)的看法:

  1. 數(shù)據(jù)結(jié)構(gòu)和算法是分離的图柏;
  2. 沒有私有狀態(tài)序六,所有的數(shù)據(jù)結(jié)構(gòu)信息都是公開的;
  3. 對于不同類型數(shù)據(jù)的類似操作應(yīng)該被放在一起蚤吹。

接著例诀,為了證明Erlang的簡練,Joe給出了一個(gè)C語言的實(shí)現(xiàn):

enum ShapeType { Rectangle, Circle, Square }; 

struct Shape {  
  enum ShapeType kind; 
  union {     
    struct { int width, height; } rectangleData; 
    struct { int radius; } circleData;
    struct { int side;   }  squareData
  } shapeData; 
};
   
double area(struct Shape* s) { 
  if( s->kind == Rectangle ) { 
    int width, ht;    width = s->shapeData.rectangleData.width; 
    ht = s->shapeData.rectangleData.ht; 
    return width * ht; 
  } else if ( s->kind == Circle ) {
    // ...
  } else if ( s->kind == Square ) {
    // ...
  }
}

Joe認(rèn)為這是一個(gè)與Erlang版本思想一致的C實(shí)現(xiàn)裁着。都是使用模式匹配繁涂。差別只在于,Erlang的版本要比C版本要簡練的多二驰。

然后Joe又給出了一個(gè)Java的版本:

abstract class Shape { 
  abstract double area(); 
} 

class Circle extends Shape {
  final double radius;
  Circle(double radius) { this.radius = radius; }
  double area() { return Math.PI * radius*radius; } 
} 

class Rectangle extends Shape { 
  final double ht;
  final double width;
  
  Rectangle(double width, double height) { 
    this.ht = height;    this.width = width; 
  }
  
  double area() { return width * ht; } 
}
 
class Square extends Shape {
  final double side;
  
  Square(double side) { 
    this.side = side;
  } 
  
  double area() { return side * side; } 
} 

通過這個(gè)例子扔罪,Joe除了想說明Java版本比Erlang版本更為繁瑣之外,更是為了證明之前批評OO的理由桶雀,在面向?qū)ο笳Z言里矿酵,數(shù)據(jù)類型定義散播在各處Java版本的三個(gè)類定義被放在三個(gè)不同的文件里。

JoeOO的所有批評都是集中在現(xiàn)象級(包括他對OOPL的批評矗积,錯(cuò)誤的把某種OO語言的現(xiàn)象當(dāng)做OOPL本身)全肮,卻沒有深入到軟件設(shè)計(jì)的挑戰(zhàn),目標(biāo)和解決方案一級來探討棘捣。這也是他寫出那篇充滿謬誤的文章的最根本原因辜腺。

回到這三個(gè)實(shí)現(xiàn),事實(shí)上是三種不同的設(shè)計(jì)思路(不僅僅是語言造成的實(shí)現(xiàn)差異)乍恐。這一切评疗,還需要從本源說起。

選擇問題

軟件開發(fā)中茵烈,存在著大量的選擇問題百匆。所以,結(jié)構(gòu)化的語言都提供了諸如if-else呜投,switch-case的語法結(jié)構(gòu)加匈;C/C++的預(yù)處理則提供了#if寄症,#ifdef#else等控制結(jié)構(gòu)矩动;而其它語言有巧,比如 ErlangHaskell等則提供了模式匹配作為選擇方式悲没;而 Makefile也提供了if-else結(jié)構(gòu)篮迎,讓程序的構(gòu)建者可以選擇不同的構(gòu)建方式。

選擇問題恰恰是程序的復(fù)雜度和管理難度所在示姿。很多時(shí)候甜橱,如果程序過于依賴這些if-else式的控制結(jié)構(gòu),會(huì)讓一段代碼過于依賴具體的細(xì)節(jié)栈戳,從而造成這些代碼需要不斷修改岂傲,無法做到開放封閉,也有害于代碼的重用子檀。

隔離細(xì)節(jié)(也意味著隔離變化)的方法當(dāng)然是抽象镊掖。程序員們試圖通過抽象的手段,將 if-else, switch-case所表達(dá)的不同事物褂痰,統(tǒng)一為某個(gè)一致的概念亩进。然后,當(dāng)前代碼就可以對那些不同事物進(jìn)行一致的操作缩歪,從而將if-else式的選擇過程踢出當(dāng)前代碼归薛。

但這并不意味著選擇的過程消失了,它們只是被從當(dāng)前代碼中被分離出去(分離不同變化方向)匪蝙。而這種分離就保證了當(dāng)前代碼不再受選擇過程的干擾主籍。

這種處理問題的思路,被稱為多態(tài)逛球。其定義為: 同一外表之下的多種形態(tài)千元。結(jié)合之前的描述,可以總結(jié)出多態(tài)思想的四個(gè)關(guān)鍵元素:

  1. 客戶代碼(用戶)
  2. 同一外表(抽象)
  3. 不同形態(tài)(細(xì)節(jié))
  4. 形態(tài)選擇(映射)
多態(tài)四要素
多態(tài)四要素

從之前的描述及這幅圖也可以清晰的看出:

  1. 形態(tài)的變化需忿,是一個(gè)選擇問題诅炉,因而與其它選擇形式蜡歹,比如if-else屋厘,switch-case,模式匹配等月而,可以等價(jià)轉(zhuǎn)換汗洒;
  2. 多態(tài)通過同一外表,封裝了形態(tài)的變化父款;讓客戶代碼不再受形態(tài)變化的影響溢谤,從而讓系統(tǒng)具備更好的正交性瞻凤。(多種不同形態(tài)間也是正交的)
  3. 形態(tài)選擇的過程,其實(shí)是一個(gè)組合的過程:把客戶代碼與某種具體形態(tài)進(jìn)行組合世杀。這是組合式設(shè)計(jì)的源泉之一阀参。

多態(tài)與組合式設(shè)計(jì)

我一直認(rèn)為,FP語言給程序員提供的最強(qiáng)大武器在于:極其便利的計(jì)算組合手段瞻坝。比如map函數(shù)(下面例子給出的是一個(gè)haskell版本的類型注解)蛛壳。

map :: (a -> b) -> [a] -> [b]

這個(gè)函數(shù),和任何一個(gè)類型為a->b的函數(shù)進(jìn)行組合所刀,就能把一個(gè)類型為[a]的列表轉(zhuǎn)化為一個(gè)類型為[b]的列表衙荐。

C程序員則會(huì)使用callback function的方式達(dá)到類似的效果。

而基于單個(gè)函數(shù)的組合浮创,正是一種最為樸素的運(yùn)行時(shí)多態(tài)實(shí)現(xiàn)忧吟。

另外,Haskell的這個(gè)實(shí)現(xiàn)還包括了另外一種多態(tài):參數(shù)化多態(tài)斩披。其中a溜族,b都是泛化的類型變量,在具體的調(diào)用時(shí)垦沉,編譯器會(huì)根據(jù)實(shí)際傳入的參數(shù)類型對a,b進(jìn)行實(shí)例化斩祭。

Erlang由于是弱類型,所以并不存在參數(shù)化多態(tài)的問題乡话。

C++程序員摧玫,可以將其看作范型編程中的模版。事實(shí)上绑青,模版也是一種參數(shù)化多態(tài)诬像。

運(yùn)行時(shí)多態(tài)解決的是算法變化問題;而參數(shù)化多態(tài)解決的是類型變化問題闸婴。

FP以參數(shù)化多態(tài)坏挠,以及樸素的運(yùn)行時(shí)多態(tài)為基礎(chǔ),構(gòu)成了FP自底向上的組合設(shè)計(jì)方法:通過將一個(gè)個(gè)可以適應(yīng)類型變化的小函數(shù)逐級組合邪乍,最后得到更為強(qiáng)大的計(jì)算功能降狠。

OO ROCKS!!!

Erlang作為一門FP語言,想必Joe一定是認(rèn)可基于運(yùn)行時(shí)多態(tài)的設(shè)計(jì)思想的庇楞。

而運(yùn)行時(shí)多態(tài)榜配,作為OO關(guān)鍵特征之一,則提供了更為強(qiáng)大的運(yùn)行時(shí)多態(tài)的支持吕晌。這里談到的強(qiáng)大蛋褥,指的是更為便捷的隔離變化的手段。

  1. OOclass為單位睛驳,可以定義一個(gè)接口集烙心。
  2. 如果數(shù)據(jù)是一種實(shí)現(xiàn)細(xì)節(jié)膜廊,OO可以將數(shù)據(jù)進(jìn)行信息隱藏。

對于第一點(diǎn):如果這組接口集正是一個(gè)概念應(yīng)該提供的高內(nèi)聚完整集合淫茵,我們就應(yīng)該有一種直觀便利的手段來表達(dá)爪瓜。

比如,Transaction DSL的公共抽象Action匙瘪,其定義如下:

struct Action
{
   virtual Status exec(TransactionInfo&) = 0;
   virtual Status handleEvent(TransactionInfo&, const Event&) = 0;
   virtual Status stop(TransactionInfo&) = 0;
   virtual void kill(TransactionInfo&) = 0;
   
   virtual ~Action() {}
};

我們知道钥勋,所謂單一職責(zé),不僅包含前半部分:DO ONE THING辆苔,還包含后半部分DO IT WELL算灸。如果一個(gè)概念,必須要包含多個(gè)接口才完整驻啤,那么我們沒有任何理由非要將其分離為一個(gè)個(gè)孤零零的函數(shù)菲驴;而是恰恰相反,語言需要提供一種直觀便利的手段骑冗,讓我們可以完整的定義赊瞬。

而第二點(diǎn),Joe認(rèn)為對象有私有狀態(tài)OO Sucks的重要原因之一贼涩。但是對實(shí)現(xiàn)細(xì)節(jié)進(jìn)行信息隱藏巧涧,是構(gòu)成了OO對提升系統(tǒng)正交性,促進(jìn)系統(tǒng)局部化影響的重要手段遥倦。如下圖所示:

私有數(shù)據(jù)帶來的正交性
私有數(shù)據(jù)帶來的正交性

對于這一點(diǎn)谤绳,Joe堅(jiān)持程序應(yīng)該與數(shù)據(jù)分離,可是按照高內(nèi)聚原則:關(guān)聯(lián)緊密的事物應(yīng)該被放在一起袒哥。如果數(shù)據(jù)確實(shí)是某種算法的特定細(xì)節(jié)時(shí)缩筛,這種強(qiáng)制分離只會(huì)降低內(nèi)聚度,提高耦合度堡称。如下圖所示:

數(shù)據(jù)與程序分離帶來的耦合
數(shù)據(jù)與程序分離帶來的耦合

從這兩幅圖瞎抛,可以清晰的看出,當(dāng)數(shù)據(jù)是一種不穩(wěn)定的實(shí)現(xiàn)細(xì)節(jié)時(shí)却紧,對象有私有狀態(tài)數(shù)據(jù)與程序分離對于系統(tǒng)耦合度的不同影響桐臊。

所以,如果站在Joe對于OO批評的論據(jù)出發(fā)晓殊,不僅不會(huì)得到OO Sucks的結(jié)論断凶,而是恰恰相反:OO ROCKS!挺物!

AD-HOC多態(tài) vs. SUBTYPE多態(tài)

現(xiàn)在懒浮,讓我們回到Joe在其著作中給出的例子:

area({rectangle, Width, Ht}) -> Width * Ht;
area({circle, R})            -> 3.14159 * R * R;
area({square, X})            -> X * X.

這是個(gè)模式匹配的版本飘弧。模式匹配也是一種多態(tài):ad-hoc多態(tài)识藤。

而之前例子中Java的版本砚著,則是我們熟知的subtype多態(tài)

按照Joe的觀點(diǎn)痴昧,Erlang的版本稽穆,優(yōu)于Java的版本。但這是否就意味著ad-hoc多態(tài)優(yōu)于sub-type多態(tài)呢赶撰?

如果這個(gè)系統(tǒng)的需求復(fù)雜度就是這個(gè)樣子舌镶,不再變化,那么毫無疑問:erlang的版本明顯更簡潔豪娜。

可現(xiàn)實(shí)是餐胀,對于很多長生命周期的大型項(xiàng)目,需求變化是家常便飯瘤载。如果現(xiàn)在增加一個(gè)需求:對于所有Shape否灾,增加一個(gè)求周長的需求

這難不倒Erlang程序員鸣奔,很快墨技,他們就在同一個(gè)文件里(這正是Joe所倡導(dǎo)的),寫下了如下代碼:

perimeter({rectangle, Width, Ht}) -> 2 * (Width + Ht);
perimeter({circle, R})            -> 2 * 3.14159 * R;
perimeter({square, X})            -> 4 * X.

而使用Java挎狸,則修改如下:

interface Shape { 
  // untouched code
  double perimeter();
} 

class Circle extends Shape {
  // untouched code
  double perimeter() { return Math.PI * 2 * radius; }
} 

class Rectangle extends Shape { 
  // untouched code
  double perimeter() { return (width + ht) * 2; } 
}
 
class Square extends Shape {
  // untouched code 
  double perimeter() { return 4 * side; }
} 

我們可以看出扣汪,從工作量的角度,兩者差不多锨匆。區(qū)別在于崭别,Erlang版本,只需要在一個(gè)地方羅列三種實(shí)現(xiàn)恐锣;而Java版本則需要到四個(gè)文件進(jìn)行同步修改紊遵。好在,如果某個(gè)子類在修改時(shí)被遺漏侥蒙,編譯器會(huì)報(bào)錯(cuò)暗膜。這會(huì)強(qiáng)迫程序員對所有子類都進(jìn)行修改,不會(huì)遺忘鞭衩。這一局学搜,Erlang繼續(xù)保持優(yōu)勢。

現(xiàn)在论衍,我們考慮另外一個(gè)變化方向:如果現(xiàn)在增加一個(gè)新的Shape瑞佩,比如Triangle

Erlang需要做的修改是:

area({rectangle, Width, Ht}) -> Width * Ht;
area({circle, R})            -> 3.14159 * R * R;
area({square, X})            -> X * X;
area({triangle, Data})       -> ... .

perimeter({rectangle, Width, Ht}) -> 2 * (Width + Ht);
perimeter({circle, R})            -> 2 * 3.14159 * R;
perimeter({square, X})            -> 4 * X;
perimeter({triangle, Data})       -> ... .

需要修改areaperimeter兩個(gè)函數(shù)的實(shí)現(xiàn)坯台。

Java則無需改動(dòng)任何原有代碼炬丸,只需要增加一個(gè)新的類:

class Triangle extends Shape {
  // Data

  double area() { ... }
  double perimeter() { ... }
} 

在這個(gè)變化方向上,Java版本對于系統(tǒng)的影響程度,要小于Erlang版本稠炬。

由此焕阿,我們可以知道,從兩者受變化影響程度角度看:

  1. 如果擴(kuò)展一個(gè)操作首启,ad-hoc多態(tài)優(yōu)于subtype多態(tài)暮屡;
  2. 如果擴(kuò)展一個(gè)類型,subtype多態(tài)優(yōu)于ad-hoc多態(tài)毅桃。

到目前為止褒纲,兩者平分秋色。

我們再看看其它的變化:如果某個(gè)Shape的數(shù)據(jù)發(fā)生了變化钥飞,比如莺掠,Triange的數(shù)據(jù)從Data變化為Data1。則Erlang的代碼需要到兩個(gè)地方修改:

area({rectangle, Width, Ht}) -> Width * Ht;
area({circle, R})            -> 3.14159 * R * R;
area({square, X})            -> X * X;
area({triangle, Data1})       -> ... .

perimeter({rectangle, Width, Ht}) -> 2 * (Width + Ht);
perimeter({circle, R})            -> 2 * 3.14159 * R;
perimeter({square, X})            -> 4 * X;
perimeter({triangle, Data1})       -> ... .

Java版本則只需要修改一個(gè)類:

class Triangle extends Shape {
  // Data1

  double area() { ... }
  double perimeter() { ... }
} 

subtype多態(tài)再勝一局读宙。

更何況汁蝶,subtype版本的四個(gè)Shape都各自隱藏了自己的數(shù)據(jù)格式÷坫玻客戶代碼不可能對這些數(shù)據(jù)產(chǎn)生任何依賴掖棉。客戶只依賴Shape所提供的API膀估。

Erlang版本的設(shè)計(jì)幔亥,所有數(shù)據(jù)對于客戶均是公開的,客戶可以自由的訪問它們察纯。而一旦數(shù)據(jù)格式發(fā)生了變化帕棉,BANG!!!,所有相關(guān)的代碼都需要修改饼记。

因而香伴,無論站在縮小依賴范圍,還是向著穩(wěn)定方向依賴原則來看具则,subtype多態(tài)都完勝ad-hoc多態(tài)即纲。

ADT 與 Existential Quantification

對于上面這個(gè)例子,由于erlang是弱類型博肋,尚可以那么實(shí)現(xiàn)低斋。如果使用強(qiáng)類型語言比如haskell,如果要使用ad-hoc多態(tài)匪凡,則必須首先讓它們類型一致化膊畴。其中一種手段是代數(shù)數(shù)據(jù)類型(ADT),比如:

data Shape = Circle Float
           | Rectangle Float Float
           | Square Float
           
area :: Shape -> Float
area (Circle r)      = PI * r * r
area (Rectangle w h) = w * h
area (Square s)      = s * s

perimeter :: Shape -> Float
perimeter (Circle r)      = PI * r * 2
perimeter (Rectangle w h) = (w + h) * 2
perimeter (Square s)      = s * 4 

這樣的實(shí)現(xiàn)病游,是典型的在一處窮舉所有Shape的設(shè)計(jì)方式唇跨。每次增加,修改,刪除一個(gè)Shape买猖,都需要來修改這里所有的代碼糕簿。

這很明顯不符合開放封閉原則梗劫。但如果這樣的情況是可窮舉且穩(wěn)定的卿啡,那么這樣的設(shè)計(jì)倒也無可厚非庙楚。

但假如我們現(xiàn)在設(shè)計(jì)了一個(gè)框架慧库,需要由框架的用戶自己定義和擴(kuò)展具體的Shape强法,然后注冊給我們的框架灌危。這時(shí)就需要一個(gè)與具體Shape無關(guān)的公共抽象尺栖。此時(shí)讽挟,ad-hoc多態(tài)不再能解決我們的問題懒叛。而subtype多態(tài)正是解決這類問題的靈丹妙藥。

在這種情況下耽梅,Joe所批評的數(shù)據(jù)類型定義散播在各處薛窥,不僅不是一個(gè)問題,而恰恰是一個(gè)最為正交的合理選擇眼姐。(當(dāng)然诅迷,如果不需要將類型定義在多處時(shí),只有Java這類的具體OO語言才有這樣的約束众旗,而其它支持OO的語言罢杉,比如C++,完全可以將多個(gè)類型定義在一個(gè)文件里贡歧。)

而在缺乏subtype多態(tài)的強(qiáng)類型的系統(tǒng)下滩租,這類問題很難簡單的解決。因而GHC引入了一個(gè)擴(kuò)展:Existential Quantification利朵,用來模擬subtype多態(tài)律想,才終于別扭的部分解決了這類問題。

關(guān)于Existential Quantification绍弟,由于篇幅問題技即,不再具體展開。感興趣的可查閱相關(guān)資料樟遣。

枚舉 vs. subtype多態(tài)

Joe的例子姥份,還給出了一個(gè)C語言的使用Union枚舉的實(shí)現(xiàn),這是一種典型的模擬ADT的實(shí)現(xiàn)方式(UnionADT Sum TypeC語言中的具體實(shí)現(xiàn))年碘。因而其帶來的問題也和ADT一樣澈歉。

而我們也可以從中清晰的看出:基于枚舉的實(shí)現(xiàn),與基于subtype多態(tài)的實(shí)現(xiàn)方式之間存在著轉(zhuǎn)換關(guān)系屿衅。

正像模式匹配一樣埃难,如果一套枚舉對應(yīng)的switch-case在系統(tǒng)中多處存在,那就意味著,每次增加涡尘,刪除一個(gè)新的枚舉常量忍弛,都需要多處修改代碼;另外考抄,如果某個(gè)枚舉值對應(yīng)的數(shù)據(jù)結(jié)構(gòu)發(fā)生了變化细疚,也需要到多處修改代碼。

而為了做到局部化影響川梅,則最好將枚舉修改為subtype多態(tài)的方式來解決問題疯兼。從而讓系統(tǒng)對于這類變化可以做到開放封閉

結(jié)論

多態(tài)是用來應(yīng)對變化贫途,提高可重用性的重要手段吧彪。而不同多態(tài),各自有各自的適用場景及問題丢早。而如何取舍姨裸,則還是要用《簡單設(shè)計(jì)原則》來作為標(biāo)尺。

erlang確實(shí)有其非常出色的設(shè)計(jì):比如其虛擬機(jī)提供的輕量級進(jìn)程怨酝,以及Actor Model對于高并發(fā)傀缩,分布式,高可靠的支持农猬,可復(fù)用庫OTP對于快速構(gòu)建通信類應(yīng)用的支持等等都很不錯(cuò)赡艰。但所有這些都并不能構(gòu)成OO Sucks的理由。

事實(shí)上盛险,完全可以存在一個(gè)系統(tǒng)瞄摊,同樣提供相同的機(jī)制,唯獨(dú)編程語言被設(shè)定為OO語言苦掘,或者更強(qiáng)大的混合范式語言换帜,這并不會(huì)削弱Erlang平臺(tái)+OTP所提供的價(jià)值,反而可能會(huì)有利于構(gòu)建更加高效的鹤啡,易于應(yīng)對變化的系統(tǒng)惯驼。

而被某種具體范式的某個(gè)具體語言的某些具體特性在某些具體場景下漂亮應(yīng)用所吸引,在不明覺歷递瑰、或者并未明白設(shè)計(jì)的根本出發(fā)點(diǎn)的情況下祟牲,狂熱的推崇一種范式,并轉(zhuǎn)而攻擊另外一種自己并不真正了解的范式抖部,并不是一種可取的態(tài)度说贝。(難道這個(gè)問題真的是非此即彼,非黑即白么慎颗?)

比如乡恕,C++對于ad-hoc多態(tài)言询,subtype多態(tài),參數(shù)化多態(tài)傲宜,duck typing多態(tài)运杭,預(yù)處理時(shí)多態(tài),編譯時(shí)多態(tài)函卒,鏈接時(shí)多態(tài)辆憔,等等各種隔離變化、有助于提高系統(tǒng)正交性的手段全部支持报嵌,卻總有那么一群完全沒搞懂C++的人卻在不明就里虱咧,人云亦云的批評它。

語言的復(fù)雜本身并不是壞事情沪蓬,因?yàn)檫@個(gè)世界的問題就是那么復(fù)雜多樣彤钟,你必須擁有足夠多的手段才能高效應(yīng)對来候。只要這些復(fù)雜度都是在幫助程序員正確高效的解決問題跷叉,那么再復(fù)雜都是好事情。只有那些沒有價(jià)值营搅,只帶來麻煩的偶發(fā)復(fù)雜度才是問題云挟。而讓一群沒有編程素養(yǎng)的人使用一門強(qiáng)大的工具,以至于誤用濫用转质,也是一個(gè)問題(問題在于园欣,為何我們要讓沒有編程素養(yǎng)的程序員單獨(dú)工作?難道讓沒有設(shè)計(jì)素養(yǎng)的程序員使用簡單語言就會(huì)做出好設(shè)計(jì)休蟹,寫出好代碼沸枯?)。而作為追求高效開發(fā)的程序員赂弓,使用一門極其簡單绑榴,卻對很多問題缺乏高效手段的乏味工具,這才是最浪費(fèi)生命盈魁,也會(huì)縮短職業(yè)生命的糟糕選擇翔怎。

因此,作為解決實(shí)際問題的程序員杨耙,首先應(yīng)該深刻去理解設(shè)計(jì)本身的挑戰(zhàn)與目的赤套。然后盡可能豐富自己的工具箱,讓自己持有多種武器珊膜,以便與在解決具體問題時(shí)容握,可以找到更為合適的手段。而不是手里持有一把錘子车柠,則到處都是釘子剔氏。

寫在最后

在今天寫完這篇文章之時(shí)脖旱,為了添加相關(guān)的鏈接引用,無意中發(fā)現(xiàn)了這個(gè)消息介蛉,Joe Armstrong承認(rèn)他之前那篇Why OO Sucks的文章是不成熟的萌庆,轉(zhuǎn)而宣稱Erlang才是唯一的OO語言:

Erlang has got all these things. It's got isolation, it's got polymorphism and it's got pure messaging. From that point of view, we might say it's the only object oriented language and perhaps I was a bit premature in saying that object oriented languages are about. You can try it and see it for yourself.
——Ralph Johnson, Joe Armstrong on the State of OOP

我說Joe叔,咱能別這么從一個(gè)極端走向另外一個(gè)極端好么币旧?你讓之前跟著你喊OO sucks的人情何以堪啊…… :D

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末践险,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吹菱,更是在濱河造成了極大的恐慌巍虫,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鳍刷,死亡現(xiàn)場離奇詭異占遥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)输瓜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門瓦胎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人尤揣,你說我怎么就攤上這事搔啊。” “怎么了北戏?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵负芋,是天一觀的道長。 經(jīng)常有香客問我嗜愈,道長旧蛾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任蠕嫁,我火速辦了婚禮锨天,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拌阴。我一直安慰自己绍绘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布迟赃。 她就那樣靜靜地躺著陪拘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纤壁。 梳的紋絲不亂的頭發(fā)上左刽,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音酌媒,去河邊找鬼欠痴。 笑死迄靠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的喇辽。 我是一名探鬼主播掌挚,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼菩咨!你這毒婦竟也來了吠式?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤抽米,失蹤者是張志新(化名)和其女友劉穎特占,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體云茸,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡是目,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了标捺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片懊纳。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宜岛,靈堂內(nèi)的尸體忽然破棺而出长踊,到底是詐尸還是另有隱情功舀,我是刑警寧澤萍倡,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站辟汰,受9級特大地震影響列敲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜帖汞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一戴而、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧翩蘸,春花似錦所意、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至郎任,卻和暖如春秧耗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背舶治。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工分井, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留车猬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓尺锚,卻偏偏與公主長得像珠闰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子瘫辩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容