如此理解面向对象编程

如此理解面向对象编程

从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的设计准则

(全文完)

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

好烂啊有点差凑合看看还不错很精彩 (63 人打了分,平均分: 4.25 )
Loading...

如此理解面向对象编程》的相关评论

  1. 严重同意,OO的高级黑啊。在产品代码里写这种代码的人,要么出于无知,要么是炫耀OO的技法。无论是哪一种都要被强烈谴责!!!

  2. 如果BoxSpecifier的每个实现里都封装一个很复杂的业务逻辑的话,我认为没什么大问题,但如果就事论事,只是现在的功能的话,我感觉Rob Pike说的做一个配置表格是最好的.
    当然了,如果那位OO大师能把那个regsiter的那块变成自动扫描类接口什么的或都变成一个配置表格就最好了

  3. 老实说,如果让我按照面向对象的方法去写程序,我估计也会像Joseph Bergin那样去写。请注意我的前提要求:“按照面向对象的方法”。我觉得第一作者的思路也是限定了这个范围。当然,Joseph Bergin刚刚完成了一本关于敏捷和模式的书;也许Ta的思路也固定到这个范围了。如果不限定这个范围,也许可能是另一种情况。
    另外,很期待Rob Pike“配置表格”的解决方案;不知道楼下是否有高手能写出来,分享一下。

  4. 其实OO也好,GP也罢,我认为都是对数据和方法的封装,减少IF-ELSE的潜逃,这样我们的有限大脑堆栈才能处理,不然就会出现栈溢出

  5. 面向对象的好处是可以增加代码的装13程度,让老板觉得这个人水平非常高

  6. 其实写程序最重要的是高内聚,松耦合,无论什么OOP,设计模式等,最终还是要落到高内聚,松耦合上面来。

  7. 感觉什么都要讲究一个度,抽象出的所有的概念和理论都是为了更好的解决问题,更好的编程,简单、解耦、易扩展、可重用。这些理论和规则一旦生搬硬套, 就失去它存在的意义了,反而引人反感。关键还是要看使用的人。大师就是大师,眼睛很毒!

  8. 模式是一种设计思维,OO只是实现这种思维的一种手段。

    而不是反过来,没有这种设计思维,单纯的OO几乎是没有任何价值的。

  9. 最后那段不同意。

    设计模式是分范式的,OO设计模式不能用在FP上。总有人在强调FP多牛时说它用来实现GoF23里面某些模式多么方面,这是笑话,因为GoF是OO设计模式。所谓设计模式,就是用某种东西解决某类“不太直接”的问题时总结出来的套路,FP可以“直接”实现一些东西,何必借助OO的模式?但FP也有模式啊,比如Monad。

    看个讨论吧:《Does Functional Programming Replace GoF Design Patterns?》 http://stackoverflow.com/questions/327955/does-functional-programming-replace-gof-design-patterns

  10. 中国Thoughtworks 那帮人很多靠这类忽悠生存吃饭,楼主你总黑他们,他们饭碗没了,会咬你的

  11. 文中这样的例子在martin fowler的《重构》中也有,非常像。。。ThoughtWorks首席。。。。

  12. 一篇非常不错的文章啊。以前这样把问题详细化的文章看得比较少。最后一种实现的方式中的hashmap应该是亮点。

  13. 第一个代码段让我想起了 Bruce Eckel 在 thinging in python 中举的例子(那章节好像是关于如何创建可维护性程序API框架的),可能该 Ph.D 用java在实现了一次吧。 最近想升入学习下设计模式,还望博主多发博文指导~

  14. 讽刺的是, OOP们精心构造来DRY的抽象机制, 因代码上的overhead已经很大程度地阻碍了DRY…有人用数据表示数据, 有人用代码表示数据; 让我们将代码和数据合体, 投奔Lisp吧~

  15. 今儿好像明白OO了,让我这个不懂面向对象的非主流程序员豁然明白其实我一直就被误导了,幸好不是专业玩这个

  16. 那要是某个实现需要读数据库, 另一个实现需要返回随机数呢?
    楼主不是查第二大的数都要实现成可以查第N大数的形式吗, 怎么这回就没想到以后的变化吗?

  17. 把一个原本很简单的东西弄得很晦涩,难道这是OO的本意?
    我觉得OO的目标应该是把复杂的逻辑业务尽量简化,而不是相反。

  18. dfewfe :
    那要是某个实现需要读数据库, 另一个实现需要返回随机数呢?
    楼主不是查第二大的数都要实现成可以查第N大数的形式吗, 怎么这回就没想到以后的变化吗?

    同意,你说的简直太对了。

    想象一下吧,要是这个东西将来还要用来查火车票的价格、火车票的余量甚至直接拿来定火车票……你要不把它实现的比12306还厉害,不说能不能对得起国家对得起党,至少也对不起你吃进去的干饭呼吸的臭屁啊……

  19. 这哥们真的在捧unix设计模式嘛?
    unix的组合和精巧完全被“OO”搞挂了啊……

  20. 其实这个OO的实现并不见得不合理, 我以前有些系统也是这样去实现的, 人家的代码只是示意而已, 如果真的只需要输出一行文本, 当然有比这简单得多的做法.

    但是, 考虑到如果你要做一个底层的库, 如果你要做的是不同的操作系统下各自不同的复杂实现, 尤其是, 你还希望使用你的库的人能够增加你现在还不知道的操作系统, 你认为怎么做会更好?

    照我的总结, 1- KISS 原则是没错的, 如果某人真的为打印一行文本而象上面那样去OO, 那么他就是一个书呆子(不过, 我们讨论问题的时候, 不应该假装自己是个书呆子吧); 2. 不要迷信面向对象, 但是不要以为那些实现复杂设计的人都是傻子.

  21. oo大湿, 不要违反迪米特法则:
    System.out.println(OSDiscriminator.getBoxSpecifier().getStatement()) ;
    不应出现两个连续的”.”

  22. C++11下,实现起来特别轻松。

    使用 {} 初始化 std::map , 使用 [] 直接取内容。

  23. 面向对象只是编程的一种解决思路,它是程序语言上来约束大家要高内聚、低耦合。。。
    像C这类非OOP语言,一样可以写出优秀且大型的系统。

  24. 这个OO的实现本质上也是一个三行的表格啊。。。只不过用代码层层包装了一下

    话说回来,设计师一个就够了。。其他人都能按照标准化流程来工作不就行了,理不理解系统有什么关系呢?

  25. 个人觉得,他只是在初学者显摆下OO而已
    在限定“用OO实现”这个框架下,如果我,恐怕也只能这么做。

    感觉外面讲OO的书,大多这么折腾的,都麻木了

    abc :
    Hacker 也指 “业余高端电脑使用者”

    正解,在此处,Hacker可以理解为“计算机爱好者”,或者“菜鸟”

  26. Walkerinwind :个人觉得,他只是在初学者显摆下OO而已在限定“用OO实现”这个框架下,如果我,恐怕也只能这么做。
    感觉外面讲OO的书,大多这么折腾的,都麻木了

    abc :Hacker 也指 “业余高端电脑使用者”

    正解,在此处,Hacker可以理解为“计算机爱好者”,或者“菜鸟”

    @Walkerinwind
    菜鸟又不恰当,应该是自以为老手的新手

  27. OO认为万物都是对象,但这个前提是正确的么?
    描述一个快递的业务流程的时候需要把开门的门这个概念对象化么?
    现代计算机的编程无非是基于及格图灵等价概念上
    图灵机的基础元素是寄存器
    lambda演算的基础元素是lambda
    我觉得这才是编程的基础,耳所谓OO只是一种分类处理的方法,在编程实践中真的有必要完全贯彻么?真心不觉得rob pike黑OO了

  28. 一直有2个迷惑:
    1、在MIS领域,java的开发效率怎么会那么低(所以成本会那么高),而java系统的运行效率又那么低
    2、java的代码,里面的函数都是这么短,在推崇短函数的今天,的确非常令人佩服

    今天看此文,好像突然把两者联系起来了

  29. 这个例子其实有误导性。因为例子中的实现逻辑的代码过于简单,使得架构代码数量比逻辑实现代码多的多,潜意识里会让人觉得oo的设计是臃肿的。例子中大师的设计,只不过是大材小用,把一个在正规应用中的做法放在了一个print的例子中,oo被吐槽也很正常。
    反过来讲,可能很多oo粉喜欢将ifelse极致的转换成继承,这个度没把握好肯定也是不好的。
    另一方面,本质上来说,这个现象只不过是:熟悉A的人会吐槽B,熟悉B的人会吐槽A。一个真正理解了A和B的人,不会肤浅的去吐槽。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注