可能我的看法有些简单粗暴。但是我希望表达一下我的「一家之言」。
面向对象编程有很多用途,很多用法,但是我们会发现「设计模式驱动」成为了非常有代表性的流派。有时我们会疑惑,为什么会有设计模式,为什么面向对象编程会出现如此恐怖的这么复杂的类关系。
我认为这是为了满足「增量式开发」。它假定几个前提:
1,将一份代码测试调试稳定所需要花的时间,远远大于撰写代码的时间。
2,已经通过测试的旧代码永远不修改,只对新代码进行新增,是最可靠的开发方式。
3,你经常需要重用没有源代码的库,并且扩展和修改其功能。
很多面向对象的设计,其实是为了满足一个很基本的目的:不修改旧代码,只新增代码。代码只增不改,所以才会出现「继承」这种东西。因为你不需要原有的源代码也不需要修改原有的类,而是派生一个类增加新的方法。「继承」的本来目的看起来就是为了解决这个问题。
因此,很多类层次关系的设计,不是为了更高的效率,不是为了代码看起来更清晰,而是为了「保证旧代码不需要被修改」这个目的。
不过,这是不是现实呢?在某些公司,这是事实,在很多公司,以上的假定不是现实。
1,很多代码并没有经过长时间的充分的测试,因而没有必要为了不浪费原有测试资源而拒绝修改旧代码。
2,修改旧代码在大多数公司是不可避免的。
3,很多时候我们提倡读懂库的源代码,而不会盲目使用无源代码的库。
4,现在流行的敏捷开发模型中宣传要拥抱变化。在敏捷模型中,测试案例是被固化的,而代码与架构都可以经常被修改,换句话说,不修改旧代码,以及通过类层次关系设计系统架构,这些特性对敏捷来说都不重要,甚至背道而驰。
我的看法与 酷壳「如此理解面向对象编程」恰恰相反,他认为过度 OO 设计是敏捷带来的后果,而我认为敏捷开发恰恰使得过度 OO 设计变得毫无意义。——那些过度 OO 设计的人并没有真正的懂敏捷。真正的敏捷是在保证测试案例通过的前提下,直接简单粗暴的修改源代码,而不是为了避免修改三行源代码制造出十多个类。
如果我们的开发基于「旧代码就是需要被经常修改的」,那么面向对象中的一部分特性其实变成了毫无意义的累赘。当然这并意味着 OOP 完全无用,在这个前提下,其实我们需要重新审视面向对象的程序设计,取其精华去其糟粕。
封装性,继承性,多态性,这些特性在敏捷开发的情况下还那么重要吗?
封装性:在所有代码都公开,随时可能被修改被重构的情况下,封装性意义没有想象的那么大,虽然它仍然有价值。它的价值在于使程序行为更可预期。
继承性:继承的本目的是在不修改代码的情况下扩展代码的功能,但如果我们能够自由修改代码,那继承在绝大多数时候并不是最佳选择。事实上,巨大的类设计开销往往都是继承性带来的。继承性在今天不但没有太大的价值,反而常常有害。(这里所说的继承性指的是 invalid s 所说的第一种情况,对他说的第二种情况,我不认为那算作继承性,而应该被算作多态性。)
多态性:多态性要求一个函数在接受不同的类型对象作为参数时自动表现出不同的行为。或者说要求不同类型的对象都可以调用相同名字的方法,并且自己处理应该有的不同行为。——这个特性在今天依然重要,而且是最重要的特性,它的存在意味着 OOP 的价值仍然存在——即便对于非 OOP 的程序设计语言。只要应用了多态性思想,那么其实它就是 OOP 的。考虑一下 Linux 中的「一切皆文件」的概念,其实换句话说就是对一切对象皆可调用用于文件的那些方法,这妥妥的是「多态性」。图形界面的编程 API 中,一切对象皆可 Paint(),一切对象皆可 toString(),这妥妥的也是「多态性」。
从某种意义上说,「模板」这个一般认为不属于 OOP 的特性,却非常好的体现了多态性。模板允许同名函数接收不同类型的参数自动表现出不同的行为(虽然编译器实际上是生成了多个函数,但从编程的角度来讲它其实是一个函数)。因此我认为懂得模板技术在 OOP 设计中也非常重要,他用更小的开销更好的可读性实现了 OOP 思想。
结语:很多时候,巨大的类关系设计开销是为了避免修改旧代码而不是为了使代码更可读。而在修改旧代码不可避免或者根本不需要避免的时候,很多类关系设计开销是不必要的,我们在使用面向对象设计方法时,最好是取其精华去其糟粕,避免那些额外的不必要的类设计开销,以提升代码的可读性为目标来进行设计,而不要以避免修改代码为目标进行设计,在这种情况下 OOP 仍然有其存在的价值。
— 完 —
本文作者:pansz
【知乎日报】
你都看到这啦,快来点我嘛 Σ(▼□▼メ)
此问题还有 67 个回答,查看全部。
延伸阅读:
面向对象编程的重要性在哪?
怎样才是纯面向对象编程语言?