如此理解面向?qū)ο缶幊?/h1>
從Rob Pike 的 Google+上的一個(gè)推看到了一篇叫《Understanding Object Oriented Programming》的文章,我先把這篇文章簡(jiǎn)述一下,然后再說(shuō)說(shuō)老牌黑客Rob Pike的評(píng)論。
先看這篇教程是怎么來(lái)講述OOP的。它先給了下面這個(gè)問(wèn)題,這個(gè)問(wèn)題需要輸出一段關(guān)于操作系統(tǒng)的文字:假設(shè)Unix很不錯(cuò),Windows很差。
這個(gè)把下面這段代碼描述成是Hacker Solution。(這幫人覺(jué)得下面這叫黑客?我估計(jì)這幫人真是沒(méi)看過(guò)C語(yǔ)言的代碼)
- public class PrintOS
- {
- public static void main(final String[] args)
- {
- String osName = System.getProperty("os.name") ;
- if (osName.equals("SunOS") || osName.equals("Linux"))
- {
- System.out.println("This is a UNIX box and therefore good.") ;
- }
- else if (osName.equals("Windows NT") || osName.equals("Windows 95"))
- {
- System.out.println("This is a Windows box and therefore bad.") ;
- }
- else
- {
- System.out.println("This is not a box.") ;
- }
- }
- }
然后開(kāi)始用面向?qū)ο蟮木幊谭绞揭徊揭徊降剡M(jìn)化這個(gè)代碼。
先是以過(guò)程化的思路來(lái)重構(gòu)之。
過(guò)程化的方案
- public class PrintOS
- {
- private static String unixBox()
- {
- return "This is a UNIX box and therefore good." ;
- }
- private static String windowsBox()
- {
- return "This is a Windows box and therefore bad." ;
- }
- private static String defaultBox()
- {
- return "This is not a box." ;
- }
- private static String getTheString(final String osName)
- {
- if (osName.equals("SunOS") || osName.equals("Linux"))
- {
- return unixBox() ;
- }
- else if (osName.equals("Windows NT") ||osName.equals("Windows 95"))
- {
- return windowsBox() ;
- }
- else
- {
- return defaultBox() ;
- }
- }
- public static void main(final String[] args)
- {
- System.out.println(getTheString(System.getProperty("os.name"))) ;
- }
- }
然后是一個(gè)幼稚的面向?qū)ο蟮乃悸贰?/p>
幼稚的面向?qū)ο缶幊?/h4>
PrintOS.java
- public class PrintOS
- {
- public static void main(final String[] args)
- {
- System.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;
- }
- }
OSDiscriminator.java
- public class OSDiscriminator // Factory Pattern
- {
- private static BoxSpecifier theBoxSpecifier = null ;
- public static BoxSpecifier getBoxSpecifier()
- {
- if (theBoxSpecifier == null)
- {
- String osName = System.getProperty("os.name") ;
- if (osName.equals("SunOS") || osName.equals("Linux"))
- {
- theBoxSpecifier = new UNIXBox() ;
- }
- else if (osName.equals("Windows NT") || osName.equals("Windows 95"))
- {
- theBoxSpecifier = new WindowsBox() ;
- }
- else
- {
- theBoxSpecifier = new DefaultBox () ;
- }
- }
- return theBoxSpecifier ;
- }
- }
BoxSpecifier.java
- public interface BoxSpecifier
- {
- String getStatement() ;
- }
DefaultBox.java
- public class DefaultBox implements BoxSpecifier
- {
- public String getStatement()
- {
- return "This is not a box." ;
- }
- }
UNIXBox.java
- public class UNIXBox implements BoxSpecifier
- {
- public String getStatement()
- {
- return "This is a UNIX box and therefore good." ;
- }
- }
WindowsBox.java
- public class WindowsBox implements BoxSpecifier
- {
- public String getStatement()
- {
- return "This is a Windows box and therefore bad." ;
- }
- }
他們覺(jué)得上面這段代碼沒(méi)有消除if語(yǔ)句,他們說(shuō)這叫代碼的“logic bottleneck”(邏輯瓶頸),因?yàn)槿绻阋黾右粋€(gè)操作系統(tǒng)的判斷的話,你不但要加個(gè)類,還要改那段if-else的語(yǔ)句。
所以,他們整出一個(gè)叫Sophisticated的面向?qū)ο蟮慕鉀Q方案。
#p#
OO大師的方案
注意其中的Design Pattern
- public class PrintOS
- {
- public static void main(final String[] args)
- {
- System.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;
- }
- }
OSDiscriminator.java
- public class OSDiscriminator // Factory Pattern
- {
- private static java.util.HashMap storage = new java.util.HashMap() ;
- public static BoxSpecifier getBoxSpecifier()
- {
- BoxSpecifier value = (BoxSpecifier)storage.get(System.getProperty("os.name")) ;
- if (value == null)
- return DefaultBox.value ;
- return value ;
- }
- public static void register(final String key, final BoxSpecifier value)
- {
- storage.put(key, value) ; // Should guard against null keys, actually.
- }
- static
- {
- WindowsBox.register() ;
- UNIXBox.register() ;
- MacBox.register() ;
- }
- }
BoxSpecifier.java
- public interface BoxSpecifier
- {
- String getStatement() ;
- }
DefaultBox.java
- public class DefaultBox implements BoxSpecifier // Singleton Pattern
- {
- public static final DefaultBox value = new DefaultBox () ;
- private DefaultBox() { }
- public String getStatement()
- {
- return "This is not a box." ;
- }
- }
UNIXBox.java
- public class UNIXBox implements BoxSpecifier // Singleton Pattern
- {
- public static final UNIXBox value = new UNIXBox() ;
- private UNIXBox() { }
- public String getStatement()
- {
- return "This is a UNIX box and therefore good." ;
- }
- public static final void register()
- {
- OSDiscriminator.register("SunOS", value) ;
- OSDiscriminator.register("Linux", value) ;
- }
- }
WindowsBox.java
- public class WindowsBox implements BoxSpecifier // Singleton Pattern
- {
- public static final WindowsBox value = new WindowsBox() ;
- private WindowsBox() { }
- public String getStatement()
- {
- return "This is a Windows box and therefore bad." ;
- }
- public static final void register()
- {
- OSDiscriminator.register("Windows NT", value) ;
- OSDiscriminator.register("Windows 95", value) ;
- }
- }
MacBox.java
- public class MacBox implements BoxSpecifier // Singleton Pattern
- {
- public static final MacBox value = new MacBox() ;
- private MacBox() { }
- public String getStatement()
- {
- return "This is a Macintosh box and therefore far superior." ;
- }
- public static final void register()
- {
- OSDiscriminator.register("Mac OS", value) ;
- }
- }
作者還非常的意地說(shuō),他加了一個(gè)“Mac OS”的東西。老實(shí)說(shuō),當(dāng)我看到最后這段OO大師搞出來(lái)的代碼,我快要吐了。我瞬間想到了兩件事:一個(gè)是以前酷殼上的《面向?qū)ο笫莻€(gè)騙局》和 《各種流行的編程方式》中說(shuō)的“設(shè)計(jì)模式驅(qū)動(dòng)編程”,另一個(gè)我想到了那些被敏捷洗過(guò)腦的程序員和咨詢師,也是這種德行。
于是我去看了一下第一作者Joseph Bergin的主頁(yè),這個(gè)Ph.D是果然剛剛完成了一本關(guān)于敏捷和模式的書。
Rob Pike的評(píng)論
(Rob Pike是當(dāng)年在Bell lab里和Ken一起搞Unix的主兒,后來(lái)和Ken開(kāi)發(fā)了UTF-8,現(xiàn)在還和Ken一起搞Go語(yǔ)言。注:不要以為Ken和Dennis是基友,其實(shí)他們才是真正的老基友!)
Rob Pike在他的Google+的這貼里評(píng)論到這篇文章——
他并不確認(rèn)這篇文章是不是搞笑?但是他覺(jué)得這些個(gè)寫這篇文章是很認(rèn)真的。他說(shuō)他要評(píng)論這篇文章是因?yàn)樗麄兪且幻鸋acker,至少這個(gè)詞出現(xiàn)在這篇文章的術(shù)語(yǔ)中。
他說(shuō),這個(gè)程序根本就不需要什么Object,只需要一張小小的配置表格,里面配置了對(duì)應(yīng)的操作系統(tǒng)和你想輸出的文本。這不就完了。這么簡(jiǎn)單的設(shè) 計(jì),非常容易地?cái)U(kuò)展,他們那個(gè)所謂的Hack Solution完全就是笨拙的代碼。后面那些所謂的代碼進(jìn)化相當(dāng)瘋狂和愚蠢的,這個(gè)完全誤導(dǎo)了對(duì)編程的認(rèn)知。
然后,他還說(shuō),他覺(jué)得這些OO的狂熱份子非常害怕數(shù)據(jù),他們喜歡用多層的類的關(guān)系來(lái)完成一個(gè)本來(lái)只需要檢索三行數(shù)據(jù)表的工作。他說(shuō)他曾經(jīng)聽(tīng)說(shuō)有人在他的工作種用各種OO的東西來(lái)替換While循環(huán)。(我聽(tīng)說(shuō)中國(guó)Thoughtworks那幫搞敏捷的人的確喜歡用Object來(lái)替換所有的if-else語(yǔ)句,他們甚至還喜歡把函數(shù)的行數(shù)限制在10行以內(nèi))
他還給了一個(gè)鏈接http://prog21.dadgum.com/156.html,你可以讀一讀。最后他說(shuō),OOP的本質(zhì)就是——對(duì)數(shù)據(jù)和與之關(guān)聯(lián)的行為進(jìn)行編程。便就算是這樣也不完全對(duì),因?yàn)椋?/p>
Sometimes data is just data and functions are just functions.
我的理解
我覺(jué)得,這篇文章的例子舉得太差了,差得感覺(jué)就像是OO的高級(jí)黑。面向?qū)ο缶幊套⒅氐氖牵?strong>1)數(shù)據(jù)和其行為的打包封裝,2)程序的接口和實(shí)現(xiàn)的解耦。你那怕,舉一個(gè)多個(gè)開(kāi)關(guān)和多個(gè)電器的例子,不然就像STL中,一個(gè)排序算法對(duì)多個(gè)不同容器的例子,都比這個(gè)例子要好得多得多。老實(shí)說(shuō),Java SDK里太多這樣的東西了。
我以前給一些公司講一些設(shè)計(jì)模式的培訓(xùn)課,我一再提到,那23個(gè)經(jīng)典的設(shè)計(jì)模式和OO半毛錢關(guān)系沒(méi)有,只不過(guò)人家用OO來(lái)實(shí)現(xiàn)罷了。設(shè)計(jì)模式就三個(gè)準(zhǔn)則:1)中意于組合而不是繼承,2)依賴于接口而不是實(shí)現(xiàn),3)高內(nèi)聚,低耦合。你看,這完全就是Unix的設(shè)計(jì)準(zhǔn)則。