反对面向对象编程的理由被过分夸大了。

2020-07-31 22:28:19

你不可能在不招致一些敌人的情况下统治发展世界几十年。而为数十种新旧语言提供概念基础的面向对象编程当然也有一些敌人。

也许这就是为什么我们经历了一系列关于OOP的无休止的热门话题。他们将其描述为一场破坏生产力的灾难,一套欺骗性的编程模式,以及一个旨在帮助贫穷的程序员隐藏自己无能的平庸工具。OOP甚至被宣布死亡(14年前,所以对这件事持保留态度)。

所有这些咆哮的共同之处在于,他们(正确地)指出了现代软件设计中的一些陷阱,然后(错误地)得出结论,这表明编程世界的核心存在可怕的腐烂。是的,如果您将面向对象编程与草率的设计实践和模糊的架构思想混为一谈,那么它看起来就不那么棒了。但是,这些犯罪真的是OOP不可避免的一部分吗?或者他们只是我们作为编程新手有时走的一条错误的道路,带着太多的自信和好奇心?

问题开始于一些OOP支持者和几乎所有的批评者所做的一个脆弱的假设-OOP旨在对现实世界进行建模。这是OOP的原罪,这是一个腐蚀性的想法,导致了无数膨胀的代码库。

尽管OOP理论中没有要求编程对象与现实世界并行的内容,但是很多善意的教师使用这个想法来降低新学生的复杂性曲线。以下是Oracle官方Java文档中的问题插图:

“对象是理解面向对象技术的关键。现在环顾四周,你会发现许多真实世界的物体:你的狗,你的桌子,你的电视机,你的自行车…。软件对象在概念上类似于现实世界的对象。“。

这不是一个孤立的例子。许多介绍性文本模糊了代码构造和现实世界对象之间的界限,它们提供了带有汽车和车轮对象的示例,或者无可救药地捆绑在一起的Person和Family对象组。这太疯狂了。

这种想法还会导致反模式,比如使用对象关系映射将数据库分解为链接类的迷雾。

这种设计并不是每个人都适合的。但是,有很多不高兴的人被ORM系统束缚住了,没完没了地生成远远低于他们想要的效率和比他们需要的更复杂的数据类样板。OOP是否鼓励了这一点?也许吧,但真正的罪魁祸首是一种疯狂的想法,即每一个可识别的东西都应该有自己的对象表示。

如果我们忘记了我们的设计应该由代码的需要来引导,而不是由对象模型的完整性来引导,那么就没有什么好的事情发生。

对物体的更好的描述--就像许多诚实的回答一样--有点含糊。大概是这样的:

对象是一种编程构造,它允许您将数据和功能打包在某种程度上可重用的包中。某些对象可能是另一个名称的结构。其他对象可以仅仅是相关功能库。决定如何将编程问题分解为对象是OOP艺术的一部分。

“某些东西听起来像对象并不自动意味着它应该是程序中的对象。反身为应用程序中的每个概念编写类往往会给您留下一个互连的对象集合,每个对象都有自己的内部不断变化的状态。这类程序往往很难理解,因此很容易被破解。“。

有经验的程序员知道,在选择面向对象程度较低的解决方案和更面向对象的解决方案时,应该选择满足项目需要的最简单的方法。

如果OOP很难做对,那至少部分是因为无论您使用什么工具,软件设计都很难做对。

事实上,OOP在设计上的说明性比许多人认为的要少得多。面向对象的语言为您提供了一组使用对象的工具(形式化它们与接口的交互,用继承来扩展它们,等等)。但它们并没有详细说明您应该如何将这些对象应用于问题。这是一个很大的、刻意的模棱两可。

理论与实践之间的差距激发了人们对设计模式的兴趣。随着OOP变得越来越流行,程序员向他们寻求架构方面的帮助。不幸的是,设计模式很容易成为在受人尊敬的外表下偷偷引入过于复杂的OOP设计的一种方式。

你怎么避免这个陷阱呢?把重点放在优秀编程的坚如磐石的原则上,这些原则经常被引用,有些已经变成了缩写。这包括Dry(不要重复自己)、YAGNI(如果不需要就不要构建它)、Demeter法则(限制类之间必须知道什么)、持续重构以及将简单性和可读性放在首位的原则。从这些坚实的原则开始--编码的哲学--让你的设计在那个环境中成形。

对OOP目标继承发起的一些最尖锐的攻击。批评家指出了非常脆弱的基类问题,由于子类和其父类之间的微妙依赖,代码库在时间上变得冻结。

脆弱基类问题和其他继承遗留问题的解决方案出奇地简单-不要使用它。你听到的所有警示故事都是真的。

当继承在框架设计中有意义时-换句话说,作为构建您使用的工具的人的工具。如果没有严格的继承层次结构将事物捆绑在一起,.NET或Java类库将是一个更差、组织更差的地方。但是创建和维护这种类型的框架是一项巨大的架构任务。如果您是一个快速发展、以客户为中心的敏捷开发人员团队,那么这不是您想要承担的事情。这里有一个肮脏的秘密-除非你先做错几次,否则你可能不会做对。

换句话说,当您间接使用继承时,继承是一个很好的特性,但是当您使用它来扩展您自己的类时,继承是一个很好的特性。你甚至不需要它。如果您想要一种重用功能的方法,包容和委托可以很好地工作。如果您需要标准化不同的类,这就是接口的用途。

这就给我们带来了OOP的真正局限性。这并不能阻止您将错误的解决方案应用于问题。这并不妨碍你在紧迫的最后期限之前,在饥饿的鳄鱼旁边,把自己设计成一个狭小的角落。它为你提供了一套可以享受或滥用的工具。剩下的就看你的了。

有一种批评是针对OOP的,这可能是真的。OOP可能还没有消亡,但它统治世界的时刻正在消退。函数式编程继续与OOP并驾齐驱(尽管它起步非常缓慢)。纯OOP正在转向为所谓的多范例语言(如Go和Rust)腾出空间,这些语言具有更精简的面向对象特性集,并避免了一些传统的OOP包袱。在接下来的十年中的某个时候,当我们看到这些语言出现在它们自己的开发者删减中时,我们就会知道这些语言已经真正到来了。在此之前,请享受您的OOP,并保持代码的整洁。