面向对象编程中的重要概念之一是封装。但是,最近,软件世界似乎倾向于使用其他范例,例如函数式编程。
这让我思考,封装和其他OOP原则如何?他们错了吗?
OOP应用错误吗?例如,艾伦·凯(Alan Kay)在OOPSLA'97主题演讲中说:“我发明了术语面向对象,我可以告诉你我没有C ++。”
乔·阿姆斯特朗(Joe Armstrong)-“对象将功能和数据结构以不可分割的单位绑定在一起。我认为这是一个根本性的错误,因为功能和数据结构属于完全不同的世界。”
面向对象编程中的重要概念之一是封装。但是,最近,软件世界似乎倾向于使用其他范例,例如函数式编程。
这让我思考,封装和其他OOP原则如何?他们错了吗?
OOP应用错误吗?例如,艾伦·凯(Alan Kay)在OOPSLA'97主题演讲中说:“我发明了术语面向对象,我可以告诉你我没有C ++。”
乔·阿姆斯特朗(Joe Armstrong)-“对象将功能和数据结构以不可分割的单位绑定在一起。我认为这是一个根本性的错误,因为功能和数据结构属于完全不同的世界。”
Answers:
我认为您陷入的陷阱是,在任何编程范例(结构化,面向对象,功能等)上都有一个严格的定义。
如果您问两个不同的开发人员OOP是什么意思,您将得到两个不同的答案。是的,作为一个职业,我们同意大学中的任何OOP软件工程课程都涉及一些通用主题,例如封装,数据隐藏等。
但是,在现实世界中,事情并没有那么干and,这就是为什么两个开发人员会给出两个不同的答案的原因。也许他们是用不同语言表达OOP概念的专家。语言范式倾向于重叠。截止到2013年左右,最新的风潮是通过闭包或lambda将函数式编程集成到面向对象的语言中。Java 8是面向对象的还是功能性的?我将其称为带有一些功能的面向对象。其他人可能会以不同的方式描述它。
OOP应用错误吗?
不,问题在于各种语言对编程概念的表达方式有所不同。也许一种语言遗漏了一个OOP概念,而另一种语言却包含了它,但遗漏了一个不同的OOP概念。语言通常是出于以下目的而设计的:使某种类型的任务变得容易,但以使其他任务更加困难为代价。这是非非非,只是在现实世界中的折衷,是无法避免的。
现实世界不是教室。我们要么需要在抽象级别上讨论概念上的编程范例,要么我们需要讨论被迫进行权衡才能真正有用的真实编程语言。只要编程语言主要由OOP的抽象定义定义,我们就可以将其包含在该存储桶中。
但是,最近,软件世界似乎倾向于使用其他范例,例如函数式编程。
这值得商.。首先,除了OOP和函数式编程之外,我没有看到其他广泛讨论的范式,因此我想我们可以忘记“其他范式”这句话,让我们谈论FP,仅此而已。
在最近的其他问题中,深入讨论了函数式编程之所以如此流行的原因,我不再赘述(例如,参见此处或此处)。但是,在我看来,这并不是因为“ OOP是一个大错误”,也不是“功能与OOP是互斥的”,它更像是人们在扩展工具箱并试图同时兼顾两者。好的,肯定有专家,他们是强硬派,一个人又一个人,但您会发现双方都有。
这让我思考,封装和其他OOP原则如何?他们错了吗?
封装具有许多不同的风味。函数式编程语言和语言构造提供了某些封装形式,其他面向对象。如果要查找使用功能性方法进行封装的示例,请从闭包开始。
关于“其他原则”:不,它们没有错,但是对于某些情况,例如大规模并行化,功能方法可能会更好地扩展。对于其他场景,例如创建设计良好的UI框架,OOP方法可能会更好地扩展(YMMV,我手头没有一个更好的示例)。而且,我确信对于大多数实际场景,这取决于团队的知识和经验以及最喜欢的编程范例,该系统将如何扩展。
OOP应用错误吗?例如,艾伦·凯(Alan Kay)在OOPSLA'97主题演讲中说:“我发明了术语面向对象,我可以告诉你我没有C ++。”
当然,很多人经常将OOP应用于错误,但是我确信FP也是如此。如果John Mc Carthy(Lisp的设计师)在考虑函数式编程时想到了类似Javascript的话,我会感到惊讶(对我的怜悯,不要为这个比较而发怒;-)
乔·阿姆斯特朗(Joe Armstrong)-“对象将功能和数据结构以不可分割的单位绑定在一起。我认为这是一个根本性的错误,因为功能和数据结构属于完全不同的世界。”
我想Erlang的发明家有一些很好的论据,但他也有他自己的观点,所以让他发表自己的看法并建立自己的观点。还有很多其他专家对此有不同的想法。
当然:
struct Foo
{
string bar;
int bux;
}
我知道您要说的是:“但这还不能封装行为!” 好吧,我与Joe Armstrong在一起:您可以编写整个程序或操作系统,而无需一流的对象。Linux证明了这一点。
Javascript程序员通常将状态和行为封装在函数和闭包中,而不是类中。
这里的主要问题是封装不是严格定义的概念,也不是有用的。做一些研究表明,人们对封装的看法受到了高度评价,许多人将其与抽象混淆了。
您要找到的第一个定义是
封装是将数据和操纵数据的功能绑定在一起的概念。
如果这是您的定义,那么大多数语言都可以将数据和对该数据进行操作的功能分组为类,模块,库,名称空间等。
但是我认为这不是封装的主要目的,因为该定义仍在继续:
...,这可以确保不受外界干扰和滥用。
一种语言机制,用于限制直接访问对象的某些组件。
但是现在,我们需要问什么是“干扰和滥用”,以及为什么应该限制对数据的直接访问。我相信有两个原因。
首先,限制数据可以突变的范围是开发人员的最大利益。通常,在设置值之前/之后需要逻辑。而且只有数量有限的地方可以设置价值是非常有价值的。在OOP语言中,可以使用类来完成。在“可变”功能语言中,闭包具有相同的目的。并且因为我们知道class =闭包,所以甚至比可变功能语言与OOP的不同“范式”更值得争论。
但是不可变语言呢?它没有变异变量的问题。这是第二个问题所在:绑定功能和数据允许将数据保持在有效状态。假设您的拥有不变的结构Email
。此结构具有单个string
字段。我们有要求,如果您具有类型的值,Email
则该字段包含有效地址。在OOP的封装中,只需声明该字段private
,仅提供Get
方法并具有constructor method
仅当传入的字符串是有效地址时成功。闭包也是如此。现在,对于不可变的语言,可能需要说结构只能通过特定的函数进行初始化,而该函数可能会失败。而且我不知道有什么语言可以满足该标准(也许有人在评论中可以启发我)。
最后一个问题是语言“支持”封装的含义。一方面,存在允许强制执行封装的语言,因此如果破坏了封装,则代码不会编译。另一方面,该语言可能提供了进行封装的方法,但它没有强制执行封装,而让开发人员自行执行。对于第二种情况,任何具有结构和功能的语言都可以使用。动态语言和Haskell浮现在脑海。我想说的是,没有另一种语言可供选择。即使是C#(实际上非常擅长于对其对象进行封装),也可以使用反射来绕过它。但是看到在C#中将被认为是大量的代码气味,并且没有理智的C#开发人员愿意这样做。