OOP如何演变为包含属性的概念


12

我来自C ++背景,在目前的工作中全力以赴使用C#,并且我刚刚读了很多问答,内容涉及公共领域和属性之间的区别以及此变化和体现的所有来回变化。基本问题(例如此SO帖子和所有相关的链接问题)。所有这些问题都是根据实际存在的差异解决的,这些差异应理所当然地认为存在财产制度,但是我认为最好从所有语言的设计者首先决定支持财产的角度来解决这个问题。地方正在思考(在此处查看 Wikipedia文章中的列表))。OOP是如何从C ++ / Java扩展到Wikipedia文章有趣地标识为方法和成员数据之间的中间地带的:

“也就是说,属性位于类的成员代码(方法)和成员数据(实例变量)之间,并且属性提供比公共字段更高的封装级别。”

MSDN进一步增加了背景:

“尽管从技术上讲,属性在本质上与方法非常相似,但是它们的使用场景却大不相同。应将它们视为智能字段。它们具有字段的调用语法和方法的灵活性。”

我想知道这种中间封装水平对于一般的编程证明是有用的。我假设在表达OOP范式的编程语言的第一个化身中没有这个概念。


8
裸语言功能只是用于getter和setter方法的便捷语法糖。我不知道解释和术语背后是否还有更深层次的原因。

在面向对象的专家小组中,您的问题标题可能会令人发指,并可能分散您的注意力。并不是说我自己是OO专家,我只是几年的“ OO用户”。
布朗

从Python的“无参数方法”到C ++,我真是想念它了。BMI = bob.weight/sq(bob.height)没有()IMO的情况下阅读效果更好。
OJFord 2014年

我认为属性位于原始(非.net)Visual Basic中,作为配置Active X(COM)对象的一种方式。该工具中的属性网格可能与语言属性的采用有关。请注意,原始的Visual Basic在许多方面都不是面向对象的,但是它具有创建类之类的功能。
Frank Hileman 2014年

继续:属性窗口是一种无需编写代码即可配置对象的方法。它被称为RAD开发。此链接包含VB6属性窗口的屏幕截图:microsoft.com/mspress/books/WW/sampchap/4068.aspx
Frank Hileman 2014年

Answers:


5

全部与封装和统一访问原则有关。

一个对象应该能够通过返回现有数据或运行一个方法来响应消息,但是发送者不能分辨哪个是哪个。或者,如果您从发件人的角度进行查看:发件人应该能够通过统一的界面访问现有数据或运行方法。

有几种方法可以实现此目的:

  • 完全摆脱数据,只有方法(Newspeak做到了)

    • 上面的一种不太基本的形式:删除公共接口中的数据,即使数据始终私有,在公共接口中,仅公开方法(Smalltalk,Ruby为此)
  • 完全摆脱方法,只拥有数据(自我完成,“方法”只是Method分配给实例变量的对象,使用虚拟调度查找实例变量)

  • 在方法和数据之间没有句法或语义上的区别(Scala这样做,访问字段与调用没有参数列表的方法在语法上是无法区分的(foo.bar),分配给字段与调用特殊命名的方法在语法上是无法区分的(foo.bar = baz相同)作为foo.bar_=(baz)即调用一个方法命名foo_=,和只读值可以被重写或方法而无需参数列表(即实施val foo)在一个超类可以被覆盖(或abstract val在子类与方法来实现)def foo

但是,Java不遵循统一访问原则。在Java中,可以区分访问数据和运行方法。foo.bar与有所不同foo.bar()。原因是字段和方法在语义和语法上是不同的。

C#尝试通过向语言添加属性(基本上是类似于字段的方法)来解决此问题。但是,方法调用的外观仍然不同于字段和属性访问。字段和属性现在具有统一访问权限,但方法仍然没有。

因此,这实际上并不能解决问题:您无法通过添加第三种访问方式来解决具有两种不同的访问方式!即使第三种方式看起来像其他两种方式中的一种,您仍然(至少)有两种不同的方式。您只能通过摆脱一种以外的所有其他方式来解决它,或者摆脱差异。

拥有一种带有方法,属性和字段的语言是完全可以的,但是这三个语言应该具有统一的访问权限。


1
为什么您(和其他人)认为维护统一访问如此重要?使用一种方法,您可能希望为其提供参数以更改其作用或作用。对于字段或属性,您没有此功能,通常只返回数据(尽管可以将属性实现为运行方法,而不仅仅是公开私有字段)。对我来说,这似乎很直观,也许仅仅是因为我已经习惯了,但是通过强制使用统一访问权限,您将在优雅或生产力上得到什么呢?您是否担心会在课堂之外泄漏实现细节?
Mike支持Monica

1
UAP旨在保留在不破坏公共接口的情况下更改实施细节的自由。通常的示例是添加日志记录。如果我提供通过方法的访问权限,则可以微不足道地更改方法的内部内容以写入日志。对于公共领域,如果不首先将日志转换为方法,就无法添加日志,这会破坏所有客户端代码。如果无法冒险进行重大更改,则必须使用方法。属性是最佳折衷方案,因为它们是完整的方法,但看起来像字段。粘附到UAP可以改善封装并减少紧密耦合。
阿蒙2014年

这个问题有很多很好的答案,但是我认为,通过拉出编程语言的本质的更基本方面并详细阐述UAP的相关正式概念,这一问题引起了人们的广泛关注。固体
jxramos

18

好吧,我不确定100%,但是我想事情可能比您预期的要简单。从90年代的OO建模学校开始,就需要对具有封装的成员属性的类进行建模,并且当以C ++或Java之类的语言实现时,这通常会导致代码包含大量的getter和setter,因此会产生很多“噪音”相对简单的代码。请注意,您链接的Wikipedia文章中列出的大多数语言(可能都是未检查),都是在90年代末或90年代末开始引入对象的“属性”。

我想这是语言设计师决定添加一些语法糖以减少噪音的主要原因。虽然属性肯定是没有“核心理念OO”,他们至少有事情做与面向对象的。它们没有显示“ OOP的演变”(正如您的问题标题所假定的那样),但是它们在编程语言级别支持OO 建模,从而使实现更容易。


它与面向对象编程无关,只是在某种意义上,属性是字段的抽象,而字段是面向对象编程的一部分。但是,正如您正确指出的那样,面向对象的编程不需要属性。
Frank Hileman 2014年

2
@FrankHileman:“一个属性是一个字段的抽象和字段都是面向对象编程的一部分” -嗯,这听起来像你对我同意这个概念具有实际至少东西做OOP。那就是你为什么拒绝我的原因?
Doc Brown

3
大声笑。你能怪他吗?他从厕所上滑下来,把头撞在水槽上。无论如何...我一直看到的方式是,属性并不是严格意义上的面向对象。它们有助于封装,而封装则有助于OO。
MetaFight 2014年

1
哈!对我来说,这是对程序员.stackexchange的介绍。大声笑,比我在普通ol stackoverflow中的同等经验要热得多:-p混合一天的好方法!
jxramos 2014年


11

您将其向后(有点)。Lambda微积分几乎是编程语言的核心正式基础,并且已经存在了数十年。它没有字段。

要对可变状态建模,您需要进行两个抽象。一种表示设置某种状态,另一种表示设置该状态(我的TaPL版本第13章供参考)。听起来有点熟?从理论上讲,OO并没有发展为拥有这些东西。OO阅读了《编程语言101》,迈出了一步。

实践的角度来看,有两个相当明确的动机。您来自C ++背景,因此,如果您有一个公共字段,那么会发生什么-例如说...文本框中的文本。当您想要更改设计以使“每当此文本框发生更改时,都会执行”会发生什么?因为您不能信任开发人员自己调用“ UpdateTextbox”,所以您不得不杀死该字段,创建一个或两个函数并连接该逻辑。这是对您的api 的非常重大的更改(不幸的是,.NET的属性实现仍然是一个重大更改)。Windows API中到处都有这种行为。由于这对Microsoft来说意义重大,因此C#可能希望减轻这种痛苦。

另一个主要动机是Java Beans及其近亲。建立了许多框架来使用Java反射来查找GetXSetX配对并有效地将它们视为现代属性。但是由于它们不是实际的语言构造,所以这些框架非常脆弱且不灵活。如果您打错名字,事情就会悄无声息地打破。如果有人被重构,则财产的另一端也没有动静。而且,完成所有field / get / set样板工作都是冗长而乏味的(即使对于Java!)。由于C#在很大程度上是作为“吸取了教训的Java”而开发的,因此,其中的痛苦就是其中之一。

但是最大的事情是财产概念已经成功。易于理解,易于使用。这些极大地帮助采用,并且作为一种工具,程序员发现与功能或字段相比,属性可以更干净地解决问题的子集。


7
我主要是对你投反对票,以至于过多引用了无关紧要的废话。编程语言的实际实现要考虑的事情比其模型是否包含在lambda微积分中要严重得多。
DeadMG

9
@DeadMG-的确是这样,但是另一方面,编程语言的实现者总是会熟悉那些无关紧要的形式废话。认为这不会影响他们的设计是天真的。
Telastyn 2014年

3
这绝对不是“无关紧要的废话”。但是,对于早期编程语言,lambda演算可能没有被考虑太多。如果是这样,CPU可能会更倾向于这种思路。
Frank Hileman 2014年

3
@FrankHileman-我很确定Lisp会考虑...
Telastyn 2014年

4
@Telastyn-是的-Lisp最初不应该是一种编程语言,还记得吗?
Frank Hileman 2014年

3

特别是在.net中,属性起源于Visual Basic的旧时代,实际上它并不是按照我们今天所考虑的方式面向对象的。它很大程度上是围绕当时的新COM系统构建的,该系统表面化地将所有东西(不一定是类)视为组件,而将组件视为暴露了既可以在代码中也可以在图形编辑器中访问的属性。当VB和新创建的C#合并到.net中时,VB获得了很多OOP功能,并且保留了属性,因为删除它们就像倒退了一步-想象一下他们在Visual Studio中使用的自动代码更新工具是否已经用getter和setter替换所有属性,并破坏了与所有COM库的兼容性。全面支持他们是合乎逻辑的。


我认为您不应该忽略来自Delphi以及之前的Object Pascal和Turbo Pascal with Objects的C#血统。这些都是面向对象的,并且确实具有属性(尽管我不记得OP是否具有TP5.5中的属性或是否已添加它们;大概是因为它们在实用上是个好主意,所以它们由Anders延续到了C#中。)
amaca

非常有趣的是,您碰巧是这个问题的唯一方面。我刚刚在我的问题中添加了一条评论,链接到一个网站,该网站列出了一些属性,这些属性是C#满足的某些属性方法事件模型的一部分。然后,我再次在问题页面上搜索“组件”,发现了一些误报,但我认为您的答案实际上与我链接的内容重叠。
jxramos 2015年

3

属性与面向对象的编程无关,因为属性只是语法糖。属性从表面上看起来像字段,并且在.net世界中,建议它们在某些方面表现得像字段,但从任何意义上说,它们都不是字段。属性是一种或两种方法的语法糖:一种是获取值,另一种是设置值。可以省略set方法或get方法,但不能两者都省略。可能没有字段存储get方法返回的值。因为他们与字段共享语法,并且因为他们经常使用字段,所以人们将属性与字段相关联。

属性比字段具有优势:

  • 在属性设置方法中,在存储属性值之前,可以对照一组前提条件或不变量来检查新值或对象的状态。
  • 如果可以在逻辑上将属性值的检索视为等同于字段值的检索,则为方便起见,可以使用属性获取方法。
  • 属性设置方法可以执行其他操作,例如将属性值的更改通知父对象或侦听对象。

因为属性是字段的抽象,并且为了语法上的方便,所以诸如C#之类的语言对属性采用了字段语法。


2
维基百科文章的第一行。“在某些面向对象的编程语言中,属性是一种特殊的类成员……”。因此,您的第一句话是完全错误的。其余部分未回答问题。
Doc Brown

1
@DocBrown:有趣的回应。许多维基百科文章显然是错误的。我认为您是为本文的作者辩护吗?
Frank Hileman 2014年

1
并非面向对象编程语言的所有功能都与面向对象的概念有关。
Frank Hileman 2014年

1
这不会改变您没有回答问题的事实。
布朗

3
应该注意的是,不仅仅是setter是可选的。您只能具有setter属性。
Telastyn 2014年

2

这是实现暗示的问题。在C ++或Java出现之前,属性就在OOP中(在Simula中,属性在那里,并且边缘有些粗糙,这对于Smalltalk是至关重要的)。具有属性的实体在概念上与带有代码的值不同。在某些语言约定中,get&set前缀只会使内容混乱。它们使您意识到字段和属性之间的区别,假设可以直接访问字段而无需以语言特有的方式进行获取/设置,这很容易出错。

OOP的全部要点是将事物视为“真实”世界中的实体,而不仅仅是将某些代码混入其中的结构。另一个程序员应该几乎不需要了解我实现事物的方式,并且完全不应该考虑它们被允许获取和/或设置的各种值中的哪个是真实的,而哪个是虚拟的。如果您遇到我的矢量,则无需知道我是存储矢量对象内部的角度和大小还是实际和虚部。如果我更改了库的V2.0中的表示形式,则它根本不会影响您的代码(尽管您可能希望利用这些炫酷的新功能)。同样,实体可能具有的属性取决于实体外部的数据,但是从词汇的角度来看,这无疑是属性。您问的是人们“您几岁”,而不是“请进行计算以向我透露您的年龄”,即使您知道该“对象”可用的数据是出生日期(不可变的私人成员)和当今的日期(公共的,自动递增的环境属性,取决于时区,夏时制和国际日期限制)。年龄是一种属性,而不是一种方法,即使到达该年龄也需要一些计算,并且不能(作为人工寿命有限的事物的玩具计算机表示形式)存储为字段。即使您知道该“对象”可用的数据是出生日期(私人不变成员)和今天的日期(公共的,自动递增的环境财产,取决于时区,夏时制和国际日期变更线) )。年龄是一种属性,而不是一种方法,即使到达该年龄也需要一些计算,并且不能(作为人工寿命有限的事物的玩具计算机表示形式)存储为字段。即使您知道该“对象”可用的数据是出生日期(私人不变成员)和今天的日期(公共的,自动递增的环境财产,取决于时区,夏时制和国际日期变更线) )。年龄是一种属性,而不是一种方法,即使到达该年龄也需要一些计算,并且不能(作为人工寿命有限的事物的玩具计算机表示形式)存储为字段。

与其将属性视为字段和方法的混蛋,不如将方法事物视为一种特殊的属性,它满足于您的实体可以做的事情,而不是它们所能做的事情。否则,您不是在概念上处理对象/实体,而是处理恰好附加了代码的数据集合。所述implementaions可以是相同的,但是影响是不同的。

不用说,这种抽象是有代价的。如果使用类的程序员无法确定存储的是访问数据还是获取/设置需要计算的值,那么该语言在某种程度上也必然是不确定的(因此可能要求所有内容都要求代码介于访问器/选择器和值之间)。“带代码的结构”在概念上没有错-它们当然可以提高效率-但它们在各处泄漏实现,而这就是OOP应该消除的事情之一。


我同意某些观点,不同意其他观点,但是总体信息是一个很好的信息:有关对象的某些信息需要进行计算,但从技术上讲仍然信息,而不是可以执行的操作
法拉普2014年

0

绝对没有任何东西。属性和OOP彼此无关。属性不过是函数调用的语法糖,因此属性具有与函数调用do完全相同的OOP附件-也就是说,没有。

顺便说一句,维基百科是完全不正确的。您在Java中看到的getMember / setMember模式与C#中的属性具有完全相同的优点(和缺点)。如果愿意,您甚至可以在C中复制此模式。

C#中的属性不过是语言支持的语法糖。


3
您是否曾经在类或对象上下文之外的任何编程语言中看到过属性概念?我还没有。
布朗

1
@DocBrown:我已经实现了。事实是,对于语言作者而言,属性是非常低价值的目标,因为与常规函数调用相比,属性的改进较低。
DeadMG

1
OP一直在问属性的历史,这是流行编程语言中的一个概念。实际上,从历史的角度来看,属性和OOP之间存在联系。
布朗

2
将这种属性的概念归因于OOP范式本身也许让我有些草率,但是作为一个概念,它肯定超越了单个语言及其功能。也许我正在努力阐明两者之间的适当本体。我并不是通过问题的标题暗示属性是构成或破坏OOP形式主义的OOP的基本特征/概念,但是我确实感觉到它们仅在OOP空间中才是现实,这就是为什么我这样说这个问题。
jxramos 2014年

4
它们不仅是C#中的语法糖,它们还以不同的成员的形式存在于程序集元数据中,并且您可以使用某些属性(例如数据绑定)来处理某些事情。
安迪2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.