注:本文原名《OO NOT SUCKS, YOU DO》盒卸。
緣起
Erlang
之父Joe Armstrong
曾經(jīng)寫過一篇《Why OO Sucks》,被很多反OO
的程序員——尤其是erlang
社區(qū)的程序員——當(dāng)作了大旗碉哑。
文中指出了OO
的四大問題:
- 數(shù)據(jù)結(jié)構(gòu)和函數(shù)不應(yīng)被綁在一起(
Data structure and functions should not be bound together
)挚币; - 所有事物都不得不是對象(
Everything has to be an object
); - 在面向?qū)ο笳Z言里扣典,數(shù)據(jù)類型定義散播在各處(
In an OOPL data type definitions are spread out all over the place
); - 對象有私有狀態(tài)(
Objects have private state
)妆毕。
做為一個(gè)已經(jīng)關(guān)注OO
與FP
都將近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ì)的看法:
- 數(shù)據(jù)結(jié)構(gòu)和算法是分離的图柏;
- 沒有私有狀態(tài)序六,所有的數(shù)據(jù)結(jié)構(gòu)信息都是公開的;
- 對于不同類型數(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è)不同的文件里。
Joe
對OO
的所有批評都是集中在現(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)矩动;而其它語言有巧,比如 Erlang
,Haskell
等則提供了模式匹配作為選擇方式悲没;而 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)鍵元素:
- 客戶代碼(用戶)
- 同一外表(抽象)
- 不同形態(tài)(細(xì)節(jié))
- 形態(tài)選擇(映射)
從之前的描述及這幅圖也可以清晰的看出:
- 形態(tài)的變化需忿,是一個(gè)選擇問題诅炉,因而與其它選擇形式蜡歹,比如
if-else
屋厘,switch-case
,模式匹配等月而,可以等價(jià)轉(zhuǎn)換汗洒; - 多態(tài)通過同一外表,封裝了形態(tài)的變化父款;讓客戶代碼不再受形態(tài)變化的影響溢谤,從而讓系統(tǒng)具備更好的正交性瞻凤。(多種不同形態(tài)間也是正交的)
- 形態(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)大蛋褥,指的是更為便捷的隔離變化的手段。
-
OO
以class
為單位睛驳,可以定義一個(gè)接口集烙心。 - 如果數(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)局部化影響的重要手段遥倦。如下圖所示:
對于這一點(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)聚度,提高耦合度堡称。如下圖所示:
從這兩幅圖瞎抛,可以清晰的看出,當(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}) -> ... .
需要修改area
和perimeter
兩個(gè)函數(shù)的實(shí)現(xiàn)坯台。
而Java
則無需改動(dòng)任何原有代碼炬丸,只需要增加一個(gè)新的類:
class Triangle extends Shape {
// Data
double area() { ... }
double perimeter() { ... }
}
在這個(gè)變化方向上,Java
版本對于系統(tǒng)的影響程度,要小于Erlang
版本稠炬。
由此焕阿,我們可以知道,從兩者受變化影響程度角度看:
-
如果擴(kuò)展一個(gè)操作首启,
ad-hoc
多態(tài)優(yōu)于subtype
多態(tài)暮屡; -
如果擴(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)方式(Union
是ADT Sum Type
在C
語言中的具體實(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