it-swarm.cn

OOP能否实现代码重用的承诺?有哪些替代方法可以实现代码重用?

使用面向对象范例的最大希望可能是代码重用。有人对此表示怀疑。为什么没有实现?

代码重用是否等于OOP定义了代码,从而使项目更具生产力?

或更易于管理?还是更容易维护?还是质量更高?

也许大家都同意代码重用是一件好事,但是有几种方法可以实现这一目标。问题是关于OOP提供的代码重用方法。这是好事吗? 是否有比实现面向对象,子分类,多态性等更好的方法来实现代码重用?有什么方法更好? 为什么

告诉我们您使用OOP重用或其他范式重用的经验。

56
Maniero

代码重用是一个很好的主意。 不是很好

我有大约30年的软件工程经验,试图“重用”。

在发现我将70年代初构建的一个OS的设计重用到70年代后期构建的另一个OS后,我开始研究80年代的“代码重用”作为研究主题。

代码重用的好处是有时可以重用诚实的现有代码。但是世界是full代码;如何找到想要的东西?这就是我所说的重复使用诅咒

我是圣诞老人(好吧,开源),我有10亿个软件组件。您可以有任何一个。

选择好运。

要很好地解决重用问题:

  • 重用者需要以某种方式指定他需要的内容(功能,性能,目标语言,环境假设等)
  • 必须有一个“可重用”代码库,并已通过这些潜在标准以各种方式建立了索引
  • 必须存在某种机制来挑选候选元素(十亿个元素,您不能亲自查看它们)
  • 必须有一种方法来表征所选择的候选者离规范有多远
  • 应该存在一些常规过程,以允许重用者修改所选的可重用代码(这是OOP的最大贡献:您可以通过覆盖其插槽来编辑现有组件/对象。OOP不提供任何内容其他帮助)。
  • 所有这一切显然必须比简单地重新编码便宜

多年来发现的大多数情况是,要使代码可重用,就必须为此目的而设计代码,或者其中包含太多隐式假设。实际上,最成功的代码重用库很小。可以说,库和框架是“可重用”的代码,它们非常成功。 Java和C#成功的原因不是因为它们是非常好的计算机语言,而是因为它们具有大量经过精心设计,实现和记录的库。但是,人们不会在Java中查看源代码。库;它们只是调用记录良好的API(设计为通常可用)。

没有进行代码重用(也不是OOP),这在我们对系统进行编码的能力方面提供了数量级的改进。

我认为关键缺陷是任何类型的代码重用都受到根本限制,因为代码内置了太多的假设。如果使代码很小,则可以最小化假设,但是从头开始构建的成本不是很高,并且重用收益无效。如果您使代码块很大,那么在新的上下文中它们几乎没有用。像格列佛(Gulliver)一样,它们被一百万条细细的绳子绑在沙滩上,而您根本承受不起全部砍掉的负担。

我们应该做的是重复使用知识来构造代码。如果我们能够做到这一点,那么我们可以运用这些知识来构造我们所需的代码,并处理当前的假设集。

为此,仍然需要相同的规范功能来表征软件组件(您仍然必须说出想要的!)。但是,然后您将此“构造”知识应用于规范即可generate您想要的代码。

作为一个社区,我们对此还不是很擅长。但是人们总是这样做。我们为什么不能自动化呢?有大量的研究,这表明它可以在许多情况下完成。

为此所需的一个关键机械是用于接受“组件描述”的机械工具(这些只是正式文档,可以像编程语言一样进行解析)并将 程序转换 应用于他们。

编译器已经做到了这一点:-}而且,他们真的很擅长解决的问题。

具有代码生成功能的UML模型是一种尝试。这不是一个很好的尝试;在大多数UML模型中,人们所说的几乎是“我有看起来像这样的数据”。如果没有功能,很难生成一个真实的程序。

我正在尝试构建 实用程序转换系统,称为DMS的工具 。通过将程序转换应用于抽象规范来生成代码,而不是对遗留代码进行清理,从而将注意力分散了很多。 (这些都是抽象中的相同问题!)。 (构建这样的工具需要花费很多时间;我这样做已经有15年的时间了,与此同时,您必须吃饭)。

但是DMS具有我上面描述的两个关键属性:处理任意形式规范的能力以及捕获“代码生成知识”作为转换并按需应用的能力。值得注意的是,在某些特殊情况下,我们确实会根据规范生成一些相当有趣的代码。 DMS很大程度上是使用自身来构建的,以生成其实现。这至少为我们实现了(知识)重用的一些希望:极大地提高了生产率。我有大约7名技术人员组成的团队;我们已经为DMS编写了1-2 MSLOC的“规范”,但是生成了10MSLOC的代码。

简介:重用生成知识是胜利,而不是重用代码

35
Ira Baxter

代码重用是通过OOP,但也可以通过函数式编程实现。任何时候,如果您使用一块代码,并使其可由其余代码调用,则可以使用此功能其他地方是代码重用。

这种类型的代码重用还使代码更易于管理,因为更改此可调用块会更改其被调用的所有位置。我想说这个结果也提高了质量和可读性。

我不确定OOP是否只是为了提供代码重用。我将OOP作为与对象进行交互并从中提取细节的更多方式)数据结构。

来自Wikpedia:

面向对象编程的起源可以追溯到1960年代。随着硬件和软件变得越来越复杂,可管理性常常成为人们关注的问题。研究人员研究了保持软件质量的方法,并开发了面向对象的编程,部分通过强烈强调编程逻辑的离散,可重用单元来解决常见问题。该技术着重于数据而不是过程,其程序由自足的模块(“类”)组成,每个模块的实例(“对象”)包含操纵其自己的数据结构(“成员”)所需的所有信息。这与多年来占据主导地位的现有模块化编程相反,后者专注于模块的功能,而不是特定于数据,但同样提供了代码重用和编程逻辑的自足可重用单元,从而实现了协作通过使用链接的模块(子例程)。这种仍然存在的更传统的方法倾向于将数据和行为分开考虑。

36
Chris

是和否

代码重用是许多不同活动的统称。

  1. 在单个项目中重复使用代码。 OO非常适合此操作,一个设计良好的应用程序将紧密映射建模世界的关系,从而消除重复的代码但是,您可以争辩说,OO之前的技术可以实现相同的目标,这是事实,但是OO在许多方面都更方便。
  2. 第三方库无论有没有OO,这似乎同样有效。
  3. 跨用途代码重用 OO)最大的代码重用承诺是,一旦为一个应用程序编写了代码,以后就可以将其重用于另一个应用程序了当OO]的概念在高层管理部门的大门中过滤掉,而OO)完全无法实现这一目标时,这就是风行一时。事实证明,目的是OO)设计(可能还有所有过程代码,但这仅是我的理论)的关键方面,而重新使用代码的尝试却因维护灾难而告终。一个旧的框架,没人敢修改,它的朋友,对于每个应用来说,略有不同的框架通常源于此。)
15
biziclop

我同意克里斯的观点,函数式编程是重用代码的好方法。

许多程序具有重复出现的代码结构。为此,在OOP世界中使用了某些设计模式,但这可以通过递归函数函数式编程语言中的模式匹配有关更多信息,请参见 真实世界函数式编程 中的第一章。

我认为OOP)中的深度继承在许多情况下可能会产生误导。您拥有一个类,并且许多紧密相关的方法在不同的文件中实现。Joe Armstrong关于OOP说:

面向对象语言的问题在于,它们具有随身携带的所有隐式环境。您想要香蕉,但是得到的是一只大猩猩,拿着香蕉和整个丛林。

高阶函数在代码重用方面也非常有用,例如mapfoldr是Google的 MapReduce 的基础。

异步消息传递也是组织复杂软件的一种好方法,一些计算机科学家指出,假定对象之间相互异步通信,如告诉我,不要问OOP=原理。请参阅 面向对象编程:错误的路径? 被引用Joe Armstrong

我开始怀疑什么是面向对象的编程,我以为Erlang不是面向对象的,而是一种功能编程语言。然后,我的论文指导说:“但是你错了,Erlang是非常面向对象的”。他说面向对象的语言不是面向对象的。我可能会想,尽管我不太确定是否相信,但Erlang可能是唯一的面向对象语言,因为面向对象编程的3个原则是基于消息的传递,您在对象之间具有隔离并且具有多态性

像事件驱动系统和Erlang中那样,异步消息传递也是使系统解耦的一种很好的方法,而松散耦合在复杂系统中很重要。使用充分解耦的系统,您可以在系统运行时(也许在不同的节点上)对系统进行升级。 Unibet对此进行了精彩的演讲: 域事件驱动的体系结构

但是我认为大多数代码重用都是通过使用库和框架来完成的。

13
Jonas

我会回答很长,但是为什么呢? Udi Dahan解释得比我好得多。

http://www.udidahan.com/2009/06/07/the-fallacy-of-reuse/

这是帖子的开头:

这个行业已经被重复使用所占据。

有一种信念是,如果我们仅重用更多代码,一切都会更好。

甚至有人甚至说,面向对象的重点在于重用–并不是,封装是一件大事。在面向组件之后,应该重用的事情就发生了。显然,这并不是很顺利,因为在这里,我们现在将重用希望寄托在面向服务上。

完整的模式书籍已就如何实现面向一天的重用进行了编写。从实体服务和活动服务,到流程服务和编排服务,对服务进行分类的每种方式都试图实现这一目标。组合服务被吹捧为重用和创建可重用服务的关键。

我不妨让您了解这个肮脏的小秘密:

重用是谬论

13
Tony

不起眼的unix管道在代码重用方面所做的工作比已经过去和结束的其他事情要多。当对象出现时,对象只是碰巧的一种结构化代码的直观方式,后来人们开始在对象上添加所有内容。通常,对象是用于封装而不是代码重用的,代码重用需要更多的东西,并且类继承层次结构不能很好地替代代码重用机制的真正含义。

6
davidk01

OOP没什么特别的。您可以在有或没有OOP的情况下制作可重用的代码。 纯函数特别可重用:例如,Java.lang.math.sqrt(double)接受数字并给出数字。没有OOP,但绝对比那里的大多数代码可重用。

4
Joonas Pulakka

从功能编程的角度来看,OOP主要是关于管理状态。

在函数式编程中,您可以轻松拥有数百个有用的列表函数: http://haskell.org/ghc/docs/6.12.1/html/libraries/base-4.2.0.0/Data-List.html

List类中有数百种方法吗?公共方法被认为是您要保持内部状态的接口。

可悲的是,有些人没有(重新)使用许多小的功能,而是重复了功能。对我来说,这是因为OOP)不像功能编程那样鼓励代码重用

4
LennyProgrammers

对我来说,是的,但并非总是如此,它可以通过其他方式完成。

大多数时候,通过创建抽象基类并创建该类的具体实现。

还有许多框架利用继承来提供代码重用(Delphi,Java,.Net只是立即想到的一些)。

这并不是说许多实用程序库和代码片段也不能很好地完成这项工作,但是精心设计的对象层次结构让人有些满意。

3
Chris Buckett

以我的经验,与通过使用OOP)诸如继承层次结构的原理)相比,通过通用编程工具(如C++模板)利用“可重用”代码取得了更大的成功。

3
Charles Salvia

OOP太开放,无法有效重用。

有太多的重用方法。每个公共类都会问:“创建我的新实例!”,每个公共方法都说:“叫我!”,每个受保护的方法都会产生:“重写我!”-所有这些重用方式都是不同,它们具有不同的参数,它们出现在不同的上下文中,都有各自的规则,以及如何调用/扩展/覆盖它。

[〜#〜] api [〜#〜]更好,它是OOP(或非-oop)点,但在现实生活中,API的功能过于强大且在不断增长,连接点仍然太多,而且,一个好的API可以使生活更轻松,这是为OOP提供接口的最佳方法。


Datadlow范例为组件提供了严格的接口,它们具有以下类型的端口:

  • 消费者(输入),和
  • 生产者(产出)。

根据域的不同,有一些数据包类型,因此消费者和生产者可以在它们具有相同(或兼容)端口的情况下进行连接。它最漂亮的部分是它可以在视觉上完成,因为没有参数或对连接进行了调整,它们实际上只是连接了消费者和生产者。

我有点不清楚,您可以看看 StackOverflow上的“ dataflow”标签Wikipedia“ datafow编程” 或Wikipedia “基于流的编程” “

(此外,我已经用C++编写了一个数据流系统。所以OOP和DF不是敌人,DF是较高级别的组织方式。)

2
ern0

在CommonLisp中,有很多方法可以实现重用:

  • 动态类型,默认情况下让您的代码通用

  • 命令式抽象,即子例程

  • 具有多个继承的面向对象and多个调度

  • 语法抽象,能够定义新的语法结构或简化样板代码

  • 功能抽象,闭包和高阶函数

如果尝试将CommonLisp体验与其他语言进行比较,您会发现简化代码重用的主要功能是两者面向对象和功能抽象。它们比其他方法更具互补性:没有它们,您就不得不以笨拙的方式重新实现缺少的功能。例如,请参阅用作闭包和模式匹配的函子类,以获取不可扩展的方法。

2
Andrea

OOP为您提供more重用代码的方式。就这些。

1
Steven A. Lowe

确实没有像人们描述它那样“重用”的东西。重用是任何东西的偶然属性。很难计划。大多数人在谈论“重用”时的意思是“使用”。这是一个远没有吸引力和令人兴奋的术语。使用库时,通常是按预期用途使用库。您正在not重复使用它,除非您对其进行了真正疯狂的操作。

从这个意义上讲,在现实世界中的重用就是重新定义事物的用途。我可以在这里重复使用这些座椅,并重新排列它们以形成……床!不是很舒服的床,但是我可以做到。那不是他们的主要用途。我在其原始适用范围之外重用了它们。 [...]明天,我将飞往英国。我将重新使用飞机。我只会将它用于预期的目的,对此没有任何幻想或激动。

—凯夫琳·亨尼(Kevlin Henney)

1
fredoverflow

横向复用:方面,特征,移植

经典OO有时无法实现代码重用,尤其是当您疯狂地继承所有继承时,因为缺少在类之间共享实际功能的更好方法。对于此问题,已经创建了水平重用机制,例如AOP,特征和嫁接。

面向方面的编程

我认为AOP是OOP缺失的一半。 AOP并不是真正众所周知的,但是它已经用于生产代码。

我将尝试用简单的术语进行解释:假设您可以使用称为方面的特殊结构来注入和过滤功能,这些方面具有“方法”,这些方法定义通过 反射 将影响什么以及如何受到影响,但是在编译时,此过程称为 编织

一个方面是一个示例,该方面告诉“对于以get开头的某些类的所有方法,您的程序将把获取的数据和获取的时间写入日志文件”。

如果您想更好地了解AOP,请观看以下两个讲座:

特性与嫁接

特性 是用于定义补充OOP的可重用代码的另一种构造,它们类似于 mixins ,但更干净。

除了解释它们外,还有 很好的PHP RFC解释了这两者 。特性即将到PHP btw,它们已经提交到主干了。

综上所述

在我看来,OOP仍然是模块化的关键,正如我们今天众所周知的那样OOP仍不完整

1
dukeofgaming

我冒着嘲笑和自白的风险,我最近才刚使用OOP。它不会自动传给我。我的大部分经验都涉及关系数据库,所以我认为有人说最好从一开始就学习它,这样可以避免在编程时重新思考您的想法,我没有那么奢侈,也不想因为象牙塔理论而放弃我的职业。其他的,我会解决的。

起初,我认为整个概念没有任何意义。这似乎是不必要的,也太麻烦了。我知道,这是疯话。显然,您需要一定程度的理解,然后才能体会到任何好处或为了更好的方法而忽略它。

代码重用需要不重复代码,了解如何完成代码以及预先计划。确定存在不值得的情况时,是否应该避免重用代码?而且没有一种语言如此严格OO)以至于它认为您应该从另一个类继承代码时会抛出错误,充其量它们提供了一个有利于实现它的环境。

我认为OOP)的最大好处是人们普遍接受代码的组织方式。其他所有内容都是肉汁。一群程序员可能不会就如何构造所有类完全达成共识,但是他们应该能够找到代码。

我已经看到了足够的过程代码来知道它可以在任何地方,有时甚至在任何地方。

1
JeffO

阅读以上文章,有几点评论:

  • 许多人认为OOP中的代码重用表示继承。我不同意。接口和契约是OOP系统中的代码重用。)OOP是创建组件技术的灰盒尝试。
  • 作为重用主题的领域特定和通用“框架”之间的区别,我认为太抽象了。以我的观点,只有很好地理解它所解决的问题,才能完成一个组件(简洁,最少和可重用的接口协定及其背后的实现)。特定于域的组件是(可重复使用的)组件,它使非领域专家可以在对领域知之甚少的情况下完成工作。用户需要了解界面,而不是问题域的复杂性。
  • 经常会忘记重用级别:构想重用,规范重用,体系结构/设计重用,接口重用,测试用例重用。重用代码并不总是有利的。但这通常可以节省大量时间,而坚持使用特定的体系结构来开发新的类似产品。
  • 我眼中的OOP设计模式(Gamma等))阐述了战术实现技术,而不是在大规模代码重用的情况下有意义。它们有助于编写具有以下内容的应用程序: OOP元素,但是我不会将它们视为解决单个应用程序之外的“代码重用”问题的解决方案。
  • 也许这是不公平的:20年的C/C++/C#经验和6个月的函数式编程(F#)。启用重用的主要要素之一是:人们需要轻松地找到“接口”,对其进行研究,理解,然后再使用它。纯粹的函数式编程使我不容易看到结构,重用的候选对象,或者从哪里开始以及从哪里结束。如此赞誉的“语法糖”通常是我眼中的盐,使我无法轻易看到发生的事情。因此,我不太可能尝试重用某个功能(它是什么-一堆功能?),它可能具有我什至看不到的隐藏副作用(懒惰的评估,monads等)。不要误会我的意思,函数式编程有很酷的一面,但是我宣称的所有优点都带有很大的疑问。我很好奇后职能的未来会带来什么,并希望在退休之前能看到它;)
  • 规范,设计,实现是耦合的,但对于“相同的事物”却不容易遍历。与提高编程效率,缩小差距,增加(自动推理,可追溯性)这些视图之间的共同利益相比,对于新的编程范例而言,提高未来的生产力至关重要。规范化的规范语言,标准化的测试符号(例如ttcn3)以及支持对接口和合同进行规范验证而又不加注释的编程语言可能是我们最迫切需要的。
0
BitTickler

问题是更微妙的恕我直言:

  1. OOP是一种用于构造具有可变状态的代码的绝佳方法。通过将状态封装到对象中,强制性的全状态代码变得更易于理解,因为例如,如果将一条状态表示为类的私有字段,则您至少知道这条特定的状态只能通过此类的方法进行修改。(并且可以通过滥用继承轻松地破坏此好处,顺便说一句。)现在已经足够了,但是比没有这个要好.
  2. 具有可变状态的代码本来就很难重用。比使用不可变数据结构的代码更难。

所以OOP本身从制作可重用代码的角度看还不错,但是使用OOP编写的代码本质上很难重用

此外,函数式编程可能会导致更多可重用的代码。但是,在截止日期前正确地编写抽象的功能代码可能并不可行。而且“半右”抽象将更易于表达OOP样式。并且也不会导致易于重用代码-更高的抽象水平意味着对代码的理解将需要程序员有限的认知能力进行更高的前期投资。

作为一个实际示例:游戏代码涉及许多可变状态,因为这是考虑对游戏进行编码的自然方法,除非它是一个非常难题/算法的游戏,所以显然它最终使用哦当然,很难重用。但是,包含相同知识的相同代码如果没有OOP,将更难重用。并且将其重写为功能样式可能需要完全改变您对代码的思考方式,其背后的知识。是的,结果是代码后面的知识在OO到FP重写...但是的成本可能是巨大的,也可能是这种也必须由人们支付的成本希望重用您最终会得到的非常聪明和抽象度高的代码,因此,自相矛盾的是,即使从技术上讲它更可重用,人们最终也不会重用该代码。

...这导致了最后一个微妙之处:代码重用与People | Code接口有关,而不仅仅是代码。 OOP在提供此接口方面做得不错,因为它可以很好地与当今人们思考的多种代码进行映射。FP对于代码来说可能更好重用,但是恕我直言:轻松重用当今人们实际需要编写的那种代码。这将随着我们需要的代码类型而改变写更改。

附言而且,如果有人想说“ OO不是关于可变状态,那么您也可以使用OO具有不可变状态”)...我称其为“ FP使用类作为命名空间”。它为您工作,并且避免了某些语言的模块系统的某些缺点,并可能导致可重用的代码。但这不是OO;)

0
NeuronQ

OOP提供了一组有用的工具,这些工具使您可以编写可以在没有这些工具的地方使用的代码。如果编写一个PrintIt函数以接受任何旧对象并在其上调用.toString(),则一旦使用多种类型的对象调用该代码,就将重新使用该代码。使用这些工具,每行代码执行更多操作。

函数式编程现在在行家中非常热。它为您提供了一套单独的工具,可让每一行代码执行更多操作。它可能不是更好或不能用,但是在工具箱中提供了另一个工具。

(一个疯狂的想法是对面向对象的重用进行了额外的扩展:这个想法是我们可以定义一个Customer类,并将其用于我们编写的每个应用程序中。到处都是胶水。这是行不通的,但这并不意味着OO失败,甚至重用都失败了。)应用程序内基本类型的代码重用使其成为可能编写性能更高的应用程序,并更快地编写它们。)

0
Sean McMillan