使用面向对象范例的最大希望可能是代码重用。有人对此表示怀疑。为什么没有实现?
OOP定义的代码重用是否会使项目更高效?
或更易于管理?还是更容易维护?还是质量更高?
也许大家都同意代码重用是一件好事,但是有几种方法可以实现这一目标。问题是关于OOP提供的代码重用方法。这是好事吗?是否有比面向对象,子分类,多态等更好的方法来实现代码重用?有什么方法更好?为什么呢
告诉我们您在OOP重用或其他范式重用方面的经验。
使用面向对象范例的最大希望可能是代码重用。有人对此表示怀疑。为什么没有实现?
OOP定义的代码重用是否会使项目更高效?
或更易于管理?还是更容易维护?还是质量更高?
也许大家都同意代码重用是一件好事,但是有几种方法可以实现这一目标。问题是关于OOP提供的代码重用方法。这是好事吗?是否有比面向对象,子分类,多态等更好的方法来实现代码重用?有什么方法更好?为什么呢
告诉我们您在OOP重用或其他范式重用方面的经验。
Answers:
代码重用是一个很好的主意。 不是一个伟大的。
我有大约30年的软件工程经验,试图“重用”。
在发现我将70年代初构建的一个OS的设计重用到70年代后期构建的另一个OS后,我开始研究80年代的“代码重用”作为研究主题。
代码重用的好处是有时可以重用诚实的现有代码。但是世界充满了代码。如何找到想要的东西?这就是我所说的“ 重用诅咒”:
我是圣诞老人(可以开源),我有10亿个软件组件。您可以有任何一个。
选择好运。
要很好地解决重用问题:
多年来发现的大多数情况是,要使代码可重用,就必须为此目的而设计代码,或者其中包含太多隐式假设。实际上,最成功的代码重用库很小。可以说,库和框架是“可重用”的代码,它们非常成功。Java和C#成功的原因不是因为它们是非常好的计算机语言,而是因为它们具有大量精心设计,实施和记录的可用库。但是人们不会在库中查看源代码。他们只是调用了一个有据可查的API(设计为通常可用)。
没有进行代码重用(也不是OOP),这在我们对系统进行编码的能力方面提供了数量级的改进。
我认为关键的缺陷在于,任何类型的代码重用从根本上受到限制,因为代码内置了太多的假设。如果使代码很小,则可以最小化假设,但是从头开始构建的成本不是很高,并且重用收益无效。如果您使代码块很大,那么在新的上下文中它们几乎没有用。像格列佛(Gulliver)一样,它们被一百万条细细的绳子绑在沙滩上,而您根本承受不了割掉它们的全部费用。
我们应该做的是重用知识来构造代码。如果我们能够做到这一点,那么我们可以运用这些知识来构造我们所需的代码,并处理当前的假设集。
为此,仍然需要相同的规范功能来表征软件组件(您仍然必须说出想要的!)。但是,然后您将此“构造”知识应用于规范以生成所需的代码。
作为一个社区,我们对此还不是很擅长。但是人们总是这样做。我们为什么不能自动化呢?有大量的研究,这表明它可以在许多情况下完成。
为此所需的一个关键机械是用于接受“组件描述”的机械工具(这些只是正式文档,可以像编程语言一样进行解析)并将程序转换应用于它们。
编译器已经做到了这一点:-}而且,他们真的很擅长解决的问题。
具有代码生成功能的UML模型是一种尝试。这不是一个很好的尝试;在大多数UML模型中,人们所说的几乎是“我有看起来像这样的数据”。如果没有功能,很难生成一个真实的程序。
我正在尝试构建实用的程序转换系统,称为DMS的工具。通过将程序转换而不是抽象规范用于生成代码,而将遗留代码用于清理代码,可以使自己分心。(这些都是抽象中的相同问题!)。(构建这样的工具需要花费很多时间;我这样做已经有15年的时间了,与此同时,您必须吃饭)。
但是DMS具有我上面描述的两个关键属性:处理任意形式规范的能力以及捕获“代码生成知识”作为转换并按需应用的能力。值得注意的是,在某些特殊情况下,我们确实会根据规范生成一些相当有趣的代码。DMS很大程度上是使用自身来构建的,以生成其实现。这至少为我们实现了(知识)重用的一些希望:极大地提高了生产率。我有大约7名技术人员组成的团队;我们已经为DMS编写了1-2 MSLOC的“规范”,但是生成了10MSLOC的代码。
简介: 重用生成知识是胜利,而不是代码重用。
Mostly what has been discovered over the years is that for code to be reusable, it sort of has to be designed for that purpose, or it contains too many implicit assumptions.
我也得出了类似的结论,但我不能这么简洁地表达出来。
代码重用在OOP中实现,但在功能编程中也实现。每当您使用一段代码并使其余代码可调用它,以便您可以在其他地方使用此功能时,就是重复使用代码。
这种类型的代码重用还使代码更易于管理,因为更改此可调用块会更改其被调用的所有位置。我想说这个结果也提高了质量和可读性。
我不确定OOP是否只是用于提供代码重用。我将OOP视为与对象进行交互并抽象出数据结构的详细信息的一种方式。
面向对象编程的起源可以追溯到1960年代。随着硬件和软件变得越来越复杂,可管理性常常成为人们关注的问题。研究人员研究了保持软件质量的方法,并开发了面向对象的编程,部分通过强烈地强调离散的,可重复使用的编程逻辑单元来解决常见问题。该技术着重于数据而不是过程,其程序由自足的模块(“类”)组成,每个模块的实例(“对象”)包含操纵其自己的数据结构(“成员”)所需的所有信息。这与多年来占据主导地位的现有模块化编程相反,后者专注于模块的功能,而不是专门针对数据,但同样提供代码重用,和自给自足的可重复使用的编程逻辑单元,从而可以通过使用链接的模块(子例程)进行协作。仍然存在的这种更常规的方法倾向于将数据和行为分开考虑。
double sqrt (double x)
?纯函数是可重用性的原型。
double sqrt(double x)
你可以定义他们中的很多,而与通用编程语言,你将不得不与它来完成。float sqrt(float x)
int sqrt(int x)
Number sqrt(Number x)
是和否
代码重用是许多不同活动的统称。
我会回答很长,但是为什么呢?Udi Dahan解释得比我好得多。
http://www.udidahan.com/2009/06/07/the-fallacy-of-reuse/
这是帖子的开头:
这个行业早已重用。
有一种信念是,如果我们仅重用更多代码,一切都会更好。
甚至有人甚至说,面向对象的重点在于重用–事实并非如此,封装才是重中之重。在面向组件之后,应该重用的事情就发生了。显然,这并不是很顺利,因为在这里,我们现在将可重用的希望寄托在面向服务上。
完整的模式书籍已就如何实现面向一天的重用进行了编写。从实体服务和活动服务,到流程服务和编排服务,服务均通过各种方式对其进行分类。组合服务已被吹捧为重用和创建可重用服务的关键。
我不妨让您了解这个肮脏的小秘密:
重用是谬论
我同意克里斯的观点,函数式编程是重用代码的好方法。
许多程序具有重复出现的代码结构。为此,在OOP世界中使用了一些设计模式,但这可以通过递归函数和功能编程语言中的模式匹配来实现。有关这方面的更多信息,请参见《Real World Functional Programming》中的第一章。
我认为OOP中的深层继承在许多情况下可能会产生误导。您有一个类,并且许多密切相关的方法在不同的文件中实现。正如Joe Armstrong关于OOP所说的那样:
面向对象语言的问题在于,它们具有随身携带的所有隐式环境。您想要香蕉,但是得到的是一只大猩猩,拿着香蕉和整个丛林。
当涉及到代码重用时,高阶函数也非常有用,例如map
,foldr
这是Google MapReduce的基础。
异步消息传递也是组织复杂软件的一种好方法,一些计算机科学家指出,就像“ 告诉,不要问 OOP”原理一样,假定对象之间可以异步通信。在“ 面向对象的编程:错误的路径?”中了解有关此内容的更多信息。是乔·阿姆斯特朗为引用:
我开始怀疑什么是面向对象的编程,我以为Erlang不是面向对象的,它是一种功能编程语言。然后,我的论文指导说:“但是你错了,Erlang是非常面向对象的”。他说面向对象的语言不是面向对象的。我可能会想,尽管我不太确定是否相信,但是Erlang可能是唯一的面向对象语言,因为面向对象编程的3个原则是,它基于消息传递,您在对象与对象之间具有隔离。有多态性。
像事件驱动系统和Erlang中那样,异步消息传递也是使系统解耦的一种好方法,而松散耦合在复杂系统中也很重要。使用充分解耦的系统,您可以在系统运行时(可能在不同节点上)对系统进行升级。Unibet对此进行了精彩的演讲:域事件驱动架构
但是我认为大多数代码重用都是通过使用库和框架来完成的。
OOP没什么特别的。您可以在有或没有OOP的情况下制作可重用的代码。纯函数特别可重用:例如,输入java.lang.math.sqrt(double)
一个数字并给出一个数字。没有OOP,但是绝对比那里的大多数代码可重复使用。
从功能编程的角度来看,OOP主要是关于状态管理。
在函数式编程中,您可以轻松拥有数百个有用的列表函数:http : //haskell.org/ghc/docs/6.12.1/html/libraries/base-4.2.0.0/Data-List.html。
List类中有数百种方法吗?公共方法被认为是您要保持内部状态的接口。
可悲的是,有些人没有(重新)使用许多小的功能,而是重复了功能。对我来说,这是因为OOP 不像功能编程那样鼓励代码重用。
OOP太开放,无法有效重用。
有太多的重用方法。每个公共班级都问:“给我一个新的实例!” ,每种公开方法都说:“给我打电话!” ,每种受保护的方法都会产生:“重写我!” -所有这些重用方式都是不同的,它们具有不同的参数,它们出现在不同的上下文中,都有各自的规则,如何调用/扩展/覆盖它。
API更好,它是OOP(或非oop)点的严格子集,但在现实生活中,API的功能过于强大且永远在增长,连接点仍然太多。同样,一个好的API可以使生活更轻松,这是为OOP提供接口的最佳方法。
Datadlow范例为组件提供了严格的接口,它们具有以下类型的端口:
根据域的不同,有一些数据包类型,因此消费者和生产者可以在它们具有相同(或兼容)端口的情况下进行连接。它最漂亮的部分是可以在视觉上完成的,因为没有参数或对连接的调整,它们实际上只是连接了消费者和生产者。
我有点不清楚,您可以看一下StackOverflow上的“ dataflow”标签,或Wikipedia的“ datafow编程”或Wikipedia的“基于流的编程”。
(此外,我已经用C ++编写了一个数据流系统。因此,OOP和DF不是敌人,DF是更高级别的组织方式。)
确实没有像人们描述它那样“重用”的东西。重用是任何事物的偶然属性。很难计划。大多数人在谈论“重用”时的意思是“使用”。这是一个远没有吸引力和令人兴奋的术语。使用库时,通常是按预期用途使用库。你是不是,除非你正在做一些与它真的疯了重新使用。
从这个意义上讲,在现实世界中的重用就是重新定义事物的用途。我可以在这里重复使用这些座椅,并重新排列它们以形成……床!不是很舒服的床,但是我可以做到。那不是他们的主要用途。我在其原始适用范围之外重用了它们。[...]明天,我将飞往英国。我不会重复使用飞机。我只会将其用于预期的目的,对此没有任何幻想或激动。
—凯夫琳·亨尼(Kevlin Henney)
我将冒着嘲笑和自白的风险,我只是最近才使用OOP。它不会自动出现在我身上。我的大部分经验涉及关系数据库,因此我认为是在表和联接中。有人声称最好从一开始就学习它,这样可以避免在编程时必须重新思考。我没有那么奢侈,拒绝因为象牙塔理论而放弃我的职业。像其他所有事情一样,我会解决的。
起初,我认为整个概念没有任何意义。这似乎是不必要的,也太麻烦了。我知道,这是疯话。显然,您需要某种程度的理解,然后才能欣赏到任何东西的好处或为了更好的方法而忽略它。
代码重用需要不重复代码,了解如何完成代码以及预先计划。确定存在不值得的情况时,是否应该避免重用代码?而且没有一种语言严格地面向对象,以至于当您认为您应该从另一个类继承代码时,它将引发错误。充其量,它们提供了一个有利于实施的环境。
我认为OOP的最大好处是人们普遍接受代码的组织方式。其他一切都是肉汁。一组程序员可能并未就如何构造所有类完全达成共识,但他们应该能够找到代码。
我已经看到了足够的过程代码来知道它可以在任何地方,有时甚至在任何地方。
OOP为您提供了更多重用代码的方法。就这些。
经典OO有时无法实现代码重用,特别是当您疯狂地继承所有继承时,因为缺少在类之间共享实际功能的更好方法。对于此问题,已经创建了水平重用机制,例如AOP,特征和嫁接。
我认为AOP是OOP缺失的一半。AOP并不是真正众所周知的,但是它已用于生产代码。
我将尝试用简单的术语进行解释:假设您可以使用称为方面的特殊结构来注入和过滤功能,这些方面具有“方法”,这些方法定义了通过反射将影响什么以及如何受到影响,但是在编译时,这个过程称为编织。
一个方面是一个例子,该方面告诉“对于以get开头的某些类的所有方法,您的程序将把获取的数据和获取的时间写入日志文件”。
如果您想更好地了解AOP,请观看以下两个讲座:
特性是用于定义补充OOP的可重用代码的另一种构造,它们类似于mixins,但更干净。
除了解释它们外,还有一个很棒的PHP RFC解释了两者。特质即将到PHP btw,它们已经提交到主干。
在我看来,OOP仍然是模块化的关键,而今天我们众所周知,OOP仍然不完整。
OOP提供了一组有用的工具,这些工具使您可以编写可以在没有这些工具的地方使用的代码。如果编写的PrintIt
函数接受任何旧对象并对其进行调用.toString()
,则在使用一种以上类型的对象调用该代码后,您将立即重新使用该代码。使用这些工具,每一行代码都能完成更多工作。
目前,功能编程非常受潮人欢迎。它为您提供了一整套单独的工具,以使每一行代码都能完成更多工作。它可能不是更好或不能用,但是在工具箱中提供了另一个工具。
(一个疯狂的想法是对整个面向对象的重用进行了额外的扩展:这个想法是我们可以定义一个Customer
类,并在编写的每个应用程序中使用它。然后,应用程序到处都是胶水。这不能正常工作。但这并不意味着OO失败,甚至不意味着重用失败。应用程序内基本的代码重用类型使得编写性能更高的应用程序以及更快地编写它们成为可能。
阅读以上文章,有几点评论:
问题是更微妙的恕我直言:
因此,从制作可重用代码的角度来看,OOP本身并不坏,但是使用OOP编写的代码种类本质上很难重用。
同样,函数式编程可能会导致更多可重用的代码。但是在截止日期前正确地编写合理的功能代码以实现抽象可能是行不通的。而且,“半右”抽象将更易于表达OOP样式。而且,它往往不会使代码易于重用 -更高级别的抽象意味着对代码的理解将需要程序员有限的认知能力进行更高的前期投资。
举一个实际的例子:游戏代码涉及许多可变状态,因为这是考虑对游戏进行编码的自然方法,除非它是一种非常困惑/算法的游戏,所以显然最终要使用OO进行构造。当然,很难重用。但是,如果没有OOP,包含相同知识的相同代码将更难重用。并且将其重写为功能样式可能需要完全改变您对代码的了解方式以及其背后的知识。是的,在OO到FP重写之后,代码背后的知识会更加清晰……但是代价可能很大,而且可能是希望重用最终获得的非常聪明和抽象的代码的人也必须支付这种费用,因此,自相矛盾的是,即使从技术上讲它更可重用,人们最终也不会重用这些代码。
...这导致了最后一个微妙之处:代码重用与People | Code接口有关,而不仅仅是代码。OOP在提供此接口方面做得不错,因为它与当今人们思考的许多代码的映射很好。FP可能更适合代码重用,但是恕我直言,因为它不容易重用如今人们实际需要编写的那种代码。这将随着我们需要编写的代码类型而改变。
PS如果有人想说“ OO不是关于可变状态,那么您也可以使OO具有不可变状态” ...我称其为“使用类作为命名空间的FP”。当它为您工作时很好,它避免了某些语言的模块系统的某些缺点,并可能导致可重复使用的代码。但这不是OO;)