如此理解面向對象編程

從Rob Pike 的 Google+上的一個推看到了一篇叫《Understanding Object Oriented Programming》的文章,我先把這篇文章簡述一下,而後再說說老牌黑客Rob Pike的評論。html

先看這篇教程是怎麼來說述OOP的。它先給了下面這個問題,這個問題須要輸出一段關於操做系統的文字:假設Unix很不錯,Windows不好。java

這個把下面這段代碼描述成是Hacker Solution。(這幫人以爲下面這叫黑客?我估計這幫人真是沒看過C語言的代碼)程序員

1算法

2shell

3編程

4windows

5設計模式

6函數

7oop

8

9

10

11

12

13

14

15

16

17

18

19

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.") ;

        }

    }

}

而後開始用面向對象的編程方式一步一步地進化這個代碼。

先是以過程化的思路來重構之。

 

過程化的方案

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

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"))) ;

    }

}

而後是一個幼稚的面向對象的思路。

幼稚的面向對象編程

PRINTOS.JAVA

1

2

3

4

5

6

7

public class PrintOS

{

    public static void main(final String[] args)

    {

        System.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;

    }

}

 

OSDISCRIMINATOR.JAVA

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

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

1

2

3

4

public interface BoxSpecifier

{

    String getStatement() ;

}

 

DEFAULTBOX.JAVA

1

2

3

4

5

6

7

public class DefaultBox implements BoxSpecifier

{

    public String getStatement()

    {

        return "This is not a box." ;

    }

}

 

UNIXBOX.JAVA

1

2

3

4

5

6

7

public class UNIXBox implements BoxSpecifier

{

    public String getStatement()

    {

        return "This is a UNIX box and therefore good." ;

    }

}

 

WINDOWSBOX.JAVA

1

2

3

4

5

6

7

public class WindowsBox implements BoxSpecifier

{

    public String getStatement()

    {

        return "This is a Windows box and therefore bad." ;

    }

}

他們以爲上面這段代碼沒有消除if語句,他們說這叫代碼的「logic bottleneck」(邏輯瓶頸),由於若是你要增長一個操做系統的判斷的話,你不但要加個類,還要改那段if-else的語句。

因此,他們整出一個叫Sophisticated的面向對象的解決方案。

OO大師的方案

注意其中的Design Pattern

PRINTOS.JAVA

1

2

3

4

5

6

7

public class PrintOS

{

    public static void main(final String[] args)

    {

        System.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;

    }

}

OSDISCRIMINATOR.JAVA

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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

1

2

3

4

public interface BoxSpecifier

{

    String getStatement() ;

}

DEFAULTBOX.JAVA

1

2

3

4

5

6

7

8

9

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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

1

2

3

4

5

6

7

8

9

10

11

12

13

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) ;

    }

}

做者還很是的意地說,他加了一個「Mac OS」的東西。老實說,當我看到最後這段OO大師搞出來的代碼,我快要吐了。我瞬間想到了兩件事:一個是之前酷殼上的《面向對象是個騙局》和 《各類流行的編程方式》中說的「設計模式驅動編程」,另外一個我想到了那些被敏捷洗過腦的程序員和諮詢師,也是這種德行。

因而我去看了一下第一做者Joseph Bergin的主頁,這個Ph.D是果真剛剛完成了一本關於敏捷和模式的書。

Rob Pike的評論

(Rob Pike是當年在Bell lab裏和Ken一塊兒搞Unix的主兒,後來和Ken開發了UTF-8,如今還和Ken一塊兒搞Go語言。注:不要覺得Ken和Dennis是基友,其實他們纔是真正的老基友!)

Rob Pike在他的Google+的這貼裏評論到這篇文章——

他並不確認這篇文章是否是搞笑?可是他以爲這些個寫這篇文章是很認真的。他說他要評論這篇文章是由於他們是一名Hacker,至少這個詞出如今這篇文章的術語中。

他說,這個程序根本就不須要什麼Object,只須要一張小小的配置表格,裏面配置了對應的操做系統和你想輸出的文本。這不就完了。這麼簡單的設計,很是容易地擴展,他們那個所謂的Hack Solution徹底就是笨拙的代碼。後面那些所謂的代碼進化至關瘋狂和愚蠢的,這個徹底誤導了對編程的認知。

而後,他還說,他以爲這些OO的狂熱份子很是懼怕數據,他們喜歡用多層的類的關係來完成一個原本只須要檢索三行數據表的工做。他說他曾經據說有人在他的工做種用各類OO的東西來替換While循環。(我據說中國Thoughtworks那幫搞敏捷的人的確喜歡用Object來替換全部的if-else語句,他們甚至還喜歡把函數的行數限制在10行之內)

他還給了一個連接http://prog21.dadgum.com/156.html,你能夠讀一讀。最後他說,OOP的本質就是——對數據和與之關聯的行爲進行編程。便就算是這樣也不徹底對,由於:

Sometimes data is just data and functions are just functions.

個人理解

我以爲,這篇文章的例子舉得太差了,差得感受就像是OO的高級黑。面向對象編程注重的是:1)數據和其行爲的打包封裝,2)程序的接口和實現的解耦。你那怕,舉一個多個開關和多個電器的例子,否則就像STL中,一個排序算法對多個不一樣容器的例子,都比這個例子要好得多得多。老實說,Java SDK裏太多這樣的東西了。

我之前給一些公司講一些設計模式的培訓課,我一再提到,那23個經典的設計模式和OO半毛錢關係沒有,只不過人家用OO來實現罷了。設計模式就三個準則:1)中意於組合而不是繼承,2)依賴於接口而不是實現,3)高內聚,低耦合。你看,這徹底就是Unix的設計準則

(全文完)

相關文章
相關標籤/搜索