据我所知,尽管花费了数百万或数十亿美元用于OOP教育,语言和工具,但OOP并未提高开发人员的工作效率或软件可靠性,也没有降低开发成本.很少有人在任何严格的意义上使用OOP(很少有人坚持或理解LSP等原则); 人们对问题域建模所采用的方法几乎没有统一性或一致性.这个课程常常仅用于语法糖; 它将记录类型的函数放入它们自己的小命名空间中.
我为各种各样的应用程序编写了大量代码.虽然有些地方真正的可替代子类型在应用程序中发挥了重要作用,但这些都非常特殊.总的来说,虽然谈到"重复使用"的口号很多,但实际情况是,除非一段代码完全符合您的要求,否则几乎没有成本效益的"重复使用".设计类以正确的方式扩展是非常困难的,因此扩展的成本通常很高,以至于"重用"根本就不值得.
在许多方面,这并不让我感到惊讶.现实世界是不是"OO",这种想法在OO隐含的 - 我们可以用一些类分类模型的事情 - 在我看来非常根本性的缺陷(我可以坐在桌子上,一个树桩,一个车盖某人的膝盖 - 但不是其中一个 - 一把椅子).即使我们转向更抽象的领域,OO建模通常也很困难,违反直觉,最终无益(考虑圆/椭圆或正方形/矩形的经典例子).
那我在这里错过了什么?哪里是OOP的价值,为什么所有的时间和金钱,没有作出任何软件好?
现实世界不是"OO",OO隐含的想法 - 我们可以用一些类别分类法来模拟事物 - 在我看来基本上是有缺陷的
虽然这是事实并且已被其他人观察到(采用STL的发明者Stepanov),其余的都是无稽之谈.OOP可能存在缺陷,它肯定不是银弹,但它使大规模应用程序变得更加简单,因为它是减少依赖性的好方法.当然,这只适用于"好"的OOP设计.邋design的设计不会带来任何好处.但好的,解耦设计可以使用OOP很好地建模,而不是很好地使用其他技术.
有更好的,更通用的模型(Haskell的类型模型浮现在脑海中),但这些模型通常也更复杂和/或难以有效实施.OOP是极端之间的良好平衡.
OOP不是关于创建可重用的类,而是关于创建可用类.
这个课程常常仅用于语法糖; 它将记录类型的函数放入它们自己的小命名空间中.
是的,我发现这也太普遍了.这不是面向对象的编程.它是基于对象的编程和以数据为中心的编程.在与OO Languages合作的10年中,我看到人们大多数都在进行基于对象的编程.OBP很快就崩溃了,因为你基本上得到了两个词中最糟糕的一个:1)程序编程没有遵循经过验证的结构化编程方法和2)OOP而不遵守经过验证的OOP方法.
完成OOP是一件美好的事情.这使得很容易解决的问题非常困难,而且对于那些没有经验的人(不要试图在那里听起来很夸张),它几乎看起来像魔术.话虽这么说,OOP只是编程方法工具箱中的一个工具.它不是所有方法的全部.它恰好适合大型商业应用程序.
大多数使用OOP语言的开发人员都在使用他们日常使用的框架和类型中完成的OOP示例,但他们并没有意识到这一点.以下是一些非常简单的示例:ADO.NET,Hibernate/NHibernate,Logging Frameworks,各种语言集合类型,ASP.NET堆栈,JSP堆栈等......这些都是在其代码库中严重依赖OOP的所有东西.
重用不应该是OOP的目标 - 或者任何其他范例.
重用是良好设计和适当抽象级别的副作用.代码通过做一些有用的事情来实现重用,但却没有做到让它变得不灵活.代码是否为OO并不重要 - 我们重用可行的方法,而不是自己做的微不足道.这是实用主义.
将OO视为通过继承重用的新方法的思想从根本上是有缺陷的.正如您所注意到的那样,LSP违规很多.相反,OO被恰当地认为是管理问题域复杂性的一种方法.目标是随着时间的推移系统的可维护性.实现这一目标的主要工具是将公共接口与私有实现分离.这允许我们使用编译器强制执行的规则,例如"只应使用...修改",而不是代码审查.
使用它,我相信你会同意,允许我们创建和维护非常复杂的系统.这有很多价值,在其他范例中并不容易.
关于宗教信仰,但我会说你正在描绘现代OOP状态的过于严峻的画面.我认为,它实际上已经降低了成本,制作大型软件项目管理,等等.这并不意味着它解决了软件混乱的根本问题,并不意味着普通开发人员是OOP专家.但是将功能模块化为对象组件肯定减少了世界上意大利面条代码的数量.
我可以想到几十个图书馆,它们可以重复使用,节省了永远无法计算的时间和金钱.
但是,就OOP浪费时间而言,我认为这是因为缺乏程序员培训,加上学习语言特定OOP映射的陡峭学习曲线.有些人"得到"OOP而其他人永远不会.
没有经验证据表明,面向对象是人们思考世界的一种更自然的方式.在编程心理学领域有一些工作表明OO在某种程度上不比其他方法更合适.
面向对象的表示似乎没有普遍的可用性或更少的可用性.
仅仅采用OO方法并要求开发人员使用这些方法是不够的,因为这可能会对开发人员的工作效率以及开发的系统质量产生负面影响.
这是来自2000年10月ACM通讯的"关于OO表示的可用性".这些文章主要将OO与面向过程的方法进行比较.有很多关于如何使用OO方法"思考"的人的研究(Int.J. of Human-Computer Studies 2001,issue 54,或Human-Computer Interaction 1995,vol.10,OO研究中有一个完整的主题),从我读到的内容来看,没有任何迹象表明OO方法具有某种自然性,使其比传统的程序方法更适合.
我认为使用不透明的上下文对象(在Win32中的HANDLEs,在C中的FILE*,用来命名两个着名的例子 - 地狱,HANDLEs生活在内核模式障碍的另一边,它真的没有得到在程序代码中也可以找到比封装更多的封装; 我很难看到这对OOP来说是多么特别.
HANDLE
s(以及WinAPI的其余部分)是 OOP!C不能很好地支持OOP,因此没有特殊的语法,但这并不意味着它不使用相同的概念.从各个方面来说,WinAPI都是面向对象的框架.
看,这是涉及OOP或替代技术的每一次讨论的问题:没有人清楚定义,每个人都在谈论其他事情,因此无法达成共识.对我来说似乎浪费时间.
它是一种编程范例.旨在让我们的凡人更容易将问题分解成更小,更可行的部分..
如果你没有发现它有用..不要使用它,不要支付培训费用并且要开心.
另一方面,我发现它很有用,所以我会:)
相对于直接的程序编程,OOP的第一个基本原则是信息隐藏和封装的概念.这个想法导致类的概念将接口与实现分开.这些是非常重要的概念,也是建立框架的基础,以不同的方式和更好的(我认为)方式思考程序设计.你不能真正反对这些属性 - 没有做出权衡,它总是一种更简洁的方式来模块化.
包括继承和多态在内的OOP的其他方面也很重要,但正如其他人所提到的那样,这些方面通常被过度使用.即:有时人们使用继承和/或多态,因为他们可以,而不是因为他们应该.它们是强大的概念,非常有用,但需要明智地使用,而不是OOP的自动获胜优势.
相对于重复使用.我同意重复使用已超过OOP.这是明确定义的对象的可能的副作用,通常是更原始的/通用的类,并且是封装和信息隐藏概念的直接结果.它可能更容易被重用,因为定义良好的类的接口只是更简单,有点自我记录.
OOP的问题在于它是超卖的.
正如Alan Kay最初设想的那样,它是原始数据和全球惯例的先前实践的一个很好的替代品.
然后一些管理顾问类型锁定它并将其作为软件的救世主出售,并且类似于旅鼠,学术界和工业界随之而来.
现在,在其他好的想法被超卖之后,他们正在像在功能性编程中那样翻滚.
那么我会做些什么呢?很多,我写了一本关于此的书.(它已经绝版 - 我没有得到一分钱,但你仍然可以获得副本.)亚马逊
我的建设性答案是将编程看作是在现实世界中对事物进行建模的一种方式,而是作为编码需求的一种方式.
这是非常不同的,并且基于信息理论(在任何人都能理解的水平上).它说编程可以看作是定义语言的过程,这样做的技巧对于良好的编程至关重要.
它提升了领域特定语言(DSL)的概念.它强烈赞同DRY(不要重复自己).它给代码生成带来了很大的好感.它导致软件的数据结构比现代应用程序的数据结构大得多.
它旨在重新激发前进的道路在于创造性的想法,并且应该质疑即使是被广泛接受的想法.
HANDLEs(以及WinAPI的其余部分)是OOP!
不过他们呢?它们不是可继承的,它们肯定不是可替代的,它们缺乏定义明确的类......我认为它们远远没有"OOP".
你有没有用WinAPI创建一个窗口?然后你应该知道你定义了一个class(RegisterClass
),创建了一个it(的实例CreateWindow
),调用了虚方法(WndProc
)和基类方法(DefWindowProc
)等等.WinAPI甚至采用SmallTalk OOP的命名法,调用方法"消息"(Window Messages).
句柄可能不是可继承的,但是,final
在Java中.他们不缺课,他们是班级的占位符:这就是"句柄"这个词的含义.看看像MFC或.NET WinForms这样的架构,很明显,除了语法之外,与WinAPI没什么不同.
是的,OOP并没有解决我们所有的问题,对此感到抱歉.然而,我们正致力于解决所有这些问题的SOA.
在我审查代码和我一直在通过项目设计经验,OOP的价值没有完全实现,因为很多开发者已经无法正常概念化面向对象的模型在他们的头脑.因此,他们不使用OO设计编程,经常继续编写自上而下的程序代码,使类成为一个非常扁平的设计.(如果你甚至可以称之为"设计")
观察同事们对抽象类或接口是什么的了解是非常可怕的,更不用说正确设计继承层次结构以满足业务需求.
然而,当存在良好的OO设计时,阅读代码并看到代码自然地落入直观的组件/类中只是纯粹的快乐.我一直认为系统架构和设计就像设计公司中的各个部门和员工一样 - 所有这些都是为了完成宏观方案中的某项工作,从而发挥推动组织/系统前进所需的协同作用.
那当然,是相当罕见的不幸.就像世界上设计精美的设计与可怕设计的物理对象的比例一样,软件工程和设计也是如此.拥有良好的工具并不一定能带来良好的实践和结果.
OOP非常适合编程内部计算机结构,如GUI"小部件",其中例如SelectList和TextBox可以是Item的子类型,其具有诸如"move"和"resize"的常用方法.
麻烦的是,我们90%的人在业务领域工作,我们正在处理发票,员工,工作,订单等业务概念.这些不适合OOP,因为"对象"更加模糊,可能会根据业务重新设计等因素而变化.
最糟糕的情况是OO热情地应用于数据库,包括对SQL数据库的恶劣OO"增强" - 除非数据库新手认为他们必须是正确的做事方式,因为它们是较新的,否则它们被正确忽略.
也许帽子,膝盖或树不是椅子,但它们都是ISittable.
我认为那些现实世界的东西都是物体
你做?
发票有哪些方法?等一下.它不能自己付费,它不能自己发送,它无法与供应商实际交付的项目进行比较.它根本没有任何方法; 它是完全惰性和无功能的.它是一种记录类型(结构,如果您愿意),而不是对象.
同样你提到的其他事情.
仅仅因为某些东西是真实的并不能使它成为OO意义上的一个对象.OO对象是状态和行为的特殊耦合,可以自行行动.这不是现实世界中丰富的东西.
我在过去的9年左右一直在编写OO代码.除了使用消息传递之外,我很难想象其他方法.我看到的主要好处完全符合CodingTheWheel所说的:模块化.OO自然地引导我从模块化组件构建我的应用程序,这些组件具有干净的界面和明确的职责(即松散耦合,高度内聚的代码以及明确的关注点分离).
我认为OO崩溃的地方是人们创建深层嵌套的类heirarchies.这可能导致复杂性.然而,将共同的功能分解为基类,然后在其他后代类中重用它是一个非常优雅的事情,恕我直言!
首先,观察结果有点草率.我没有关于软件生产力的数据,也没有充分的理由相信它没有上升.此外,由于有许多人滥用OO,即使OO是花生酱以来最好的东西,使用OO也不一定能提高生产率.毕竟,一个不称职的脑外科医生可能会比没有人更糟糕,但一个称职的脑外科医生可能是非常宝贵的.
话虽这么说,OO是一种不同的排列方式,将过程代码附加到数据上,而不是让程序代码对数据进行操作.这应该至少是一个小胜利,因为有些情况下OO方法更自然.毕竟,没有什么可以阻止任何人在C++中编写程序性API,因此提供对象的选项使得语言更加通用.
此外,OO做得非常好:它允许旧代码自动调用新代码,无需更改.如果我有程序性地管理事物的代码,并且我添加了一个类似但与前一个类似但不相同的新东西,我必须更改过程代码.在OO系统中,我继承了功能,改变了我喜欢的内容,并且由于多态性而自动使用新代码.这增加了变化的局部性,这是一件好事.
缺点是良好的OO不是免费的:需要时间和精力才能正确学习它.由于它是一个重要的流行词,因此有很多人和产品做得很差,只是为了做到这一点.设计一个好的类接口而不是一个好的过程API并不容易,而且还有各种容易出错的错误(比如深层次的层次结构).
将其视为一种不同的工具,通常不一定更好.比如锤子和螺丝刀.也许我们最终会退出软件工程的实践,因为他们知道用什么扳手敲击螺钉.
@Sean
然而,将共同的功能分解为基类,然后在其他后代类中重用它是一个非常优雅的事情,恕我直言!
但无论如何,"程序化"开发人员已经这样做了几十年.语法和术语可能不同,但效果相同.OOP比"在基类中重复使用常用功能"还要多,我甚至可能会说,很难将其描述为OOP; 从不同的代码位调用相同的函数是一种与子过程本身一样古老的技术.
@Konrad
OOP可能存在缺陷,它肯定不是银弹,但它使大规模应用程序变得更加简单,因为它是减少依赖性的好方法
这就是教条.在这方面,我没有看到OOP明显优于旧程序编程的原因.每当我进行程序调用时,我都会将自己从实现的细节中分离出来.
对我来说,OOP语法本身有很多价值.使用试图表示真实事物或数据结构的对象通常比尝试使用一堆不同的扁平(或"浮动")函数对相同数据执行相同操作更有用.对具有良好OOP的事物有一定的自然"流动",这对于长期阅读,编写和维护更有意义.
Invoice实际上并不一定是具有可以自己执行的函数的"对象" - 对象实例只是为了对数据执行函数而不必知道实际存在哪种类型的数据.可以成功调用函数"invoice.toJson()"而无需知道"发票"是什么类型的数据 - 结果将是Json,无论它来自数据库,XML,CSV甚至是另一个JSON对象.使用过程函数,你突然间必须更多地了解你的数据,并最终得到像"xmlToJson()","csvToJson()","dbToJson()"等函数.它最终变成一个完整的混乱和一个如果您更改了基础数据类型,那将会非常头痛.
OOP的要点是通过抽象来隐藏实际的实现.要实现该目标,您必须创建一个公共接口.为了使您的工作更容易创建公共接口并保持干净,您必须使用抽象类,继承,多态和设计模式等概念.
所以对我来说,OOP真正的首要目标是使未来的代码维护和更改变得更容易.但即使超越这一点,当以程序代码永远不可能的方式正确完成时,它可以真正简化很多事情.如果它与"真实世界"不匹配并不重要 - 无论如何,用代码编程都不会与现实世界的对象交互.OOP只是一个让我的工作更轻松,更快的工具 - 我会在任何一天都去做.
@CodingTheWheel
但是,就OOP浪费时间而言,我认为这是因为缺乏程序员培训,加上学习语言特定OOP映射的陡峭学习曲线.有些人"得到"OOP而其他人永远不会.
不过,如果这真的令人惊讶,我不知道.我认为,技术上正确的措施(LSP是显而易见的事情)做难用,但如果我们不使用这种方法它使代码脆弱和不可延伸反正(因为我们可以了解它不再理由).而且我认为OOP导致我们采取违反直觉的结果会让人们不会惊讶于人们不会捡到它.
更重要的是,由于软件对于普通人来说已经基本上难以可靠和准确地编写,我们是否真的应该赞美一种一贯教导并且难以学习的技术?如果利益是明确的,那么尽管有困难,它可能值得坚持,但似乎并非如此.
@Jeff
相对于直接的程序编程,OOP的第一个基本原则是信息隐藏和封装的概念.这个想法导致类的概念将接口与实现分开.
哪个有更隐蔽的实现:C++的iostreams,或C的FILE*s?
我认为使用不透明的上下文对象(在Win32中的HANDLEs,在C中的FILE*,用来命名两个着名的例子 - 地狱,HANDLEs生活在内核模式障碍的另一边,它真的没有得到在程序代码中也可以找到比封装更多的封装; 我很难看到这对OOP来说是多么特别.
我想这可能是我努力看到好处的原因之一:明显好的部分并不是OOP特有的,而OOP特有的部分显然不是很好!(这并不是说它们一定是坏的,而是我没有看到它们广泛适用且始终有益的证据).
在我读过的唯一一个开发博客中,我是那个Joel-On-Software-based创始人,我读过很久以前OO并没有提高生产力.自动内存管理.凉.谁可以拒绝这些数据?
variable2654
就会变成variable3
你所使用的方法.或者,更好的是,它有一个你能理解的名字,如果函数很短的话,它被称为value
,这足以让人完全理解.
当没有函数的代码成为带方法的代码时,您可以删除数英里的代码.
当你重构代码是真正的面向对象,b
,c
,q
,和Z
成为this
,this
,this
和this
.由于我不相信使用该this
关键字,您可以删除数英里的代码.实际上,即使你使用,也可以做到这一点this
.
例如,当您使用机票预订系统时,您所谓的预订可能根本不符合法律/商业预订.
我确实想提一下你是对的.在大多数情况下,可重用性是一个梦想.下面是安德斯Hejilsberg关于该主题的报价(辉煌)从这里:
如果你要求初级程序员编写一个日历控件,他们常常会自己想:"哦,我将编写世界上最好的日历控件!它将在日历类型方面具有多态性.它将有显示器,和mungers,以及这个,那个和另一个." 他们需要在两个月内发送日历应用程序.他们将所有这些基础设施放在控件中,然后花两天时间在其上面编写一个糟糕的日历应用程序.他们会想,"在下一个版本的应用程序中,我将会做更多的事情."
一旦他们开始思考他们实际上是如何实现他们的抽象设计的所有其他具体化,然而事实证明他们的设计是完全错误的.现在他们把自己画成了一个角落,他们不得不抛弃整个事物.我一遍又一遍地看到了.我坚信极简主义.除非您确实要解决一般问题,否则不要尝试使用框架来解决特定问题,因为您不知道该框架应该是什么样子.