如此理解面向對象編程

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

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

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

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

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

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

過程化的方案

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

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

幼稚的面向對象編程

public class PrintOS
{
	public static void main(final String[] args)
  	{
		System.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;
 	}
}

 

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

 

public interface BoxSpecifier
{
	String getStatement() ;
}

 

public class DefaultBox implements BoxSpecifier
{
	public String getStatement()
	{
		return "This is not a box." ;
  	}
}

 

public class UNIXBox implements BoxSpecifier
{
	public String getStatement()
	{
		return "This is a UNIX box and therefore good." ;
  	}
}

 

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

public class PrintOS
{
  	public static void main(final String[] args)
  	{
		System.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;
  	}
}
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() ;
  	}
}
public interface BoxSpecifier
{
  	String getStatement() ;
}
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." ;
	}
}
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) ;
 	}
}
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) ;
	}
}
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的設計準則

發佈了216 篇原創文章 · 獲贊 11 · 訪問量 30萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章