公共数据成员与Getters,Setters


77

我目前在Qt等C ++中工作。我正在使用具有私有数据成员和公共成员函数的类。我为班级中的数据成员提供了公共获取器和设置器。

现在我的问题是,如果我们在类中具有数据成员的getter和setter方法,那么将那些数据成员设置为私有的意义何在?我同意在基类中拥有私有数据成员听起来很合逻辑。但是除此之外,对我来说,拥有私人成员以及其获取者和设置者也是如此。

或者相反,我们是否可以将所有变量都公开,这样就根本不需要getter和setter?拥有这些是一种好习惯吗?我知道拥有私有成员可以确保数据抽象,但是拥有getter和setter实际上可以非常轻松地访问那些变量。欢迎对此提出任何建议。


1
为了控制另一类中一个类的变量的修改,通过将变量设为私有,我们可以控制值的修改方式,例如,如果在另一类中,如果使用setter将变量修改为负,则可以将异常抛出设为负数。为此,必须将变量转换为private,要访问该私有变量,我们可以使用getter。
karthik gorijavolu

Answers:


78

都不行 您应该具有执行操作的方法。如果这些事情之一恰好与一个特定的内部变量相对应,那很好,但是应该没有什么能传达给班级用户的。

私有数据是私有的,因此您可以随时替换实现(并且可以进行完全重建,但这是另一个问题)。将精灵从瓶子中取出后,您将发现无法将其推回。

编辑:在发表评论后,我对另一个答案。

我的意思是,您问的是错误的问题。关于使用getter / setter或拥有公共成员,没有最佳实践。只有什么才是最适合您的特定对象的,以及它如何对某些特定的现实世界事物(或游戏中的虚构事物)建模。

个人获得者/设定者是两个弊端中的较小者。因为一旦您开始制作吸气剂/吸气剂,人们就会停止对哪些数据应该可见以及哪些数据不应该可视化地进行设计。对于公众成员而言,情况甚至更糟,因为这种趋势已使所有内容公开化。

相反,请检查对象的功能以及将其作为对象的含义。然后创建为该对象提供自然接口的方法。自然接口涉及使用getter和setter公开一些内部属性,即使如此。但是重要的是,您需要提前考虑一下,并出于设计合理的原因创建了吸气剂/吸气剂。


4
另一个有用的区别:您可以轻松地在成员函数上放置调试断点,但是对于类的所有实例化,将调试断点放置在内存位置上要困难得多。这开始引起诸如以下的问题:我是否需要知道何时进行更改或访问?这些数据成员之间是否必须维护任何关系?多线程安全要求是什么?
杰森·哈里森

37

不,它甚至不是同一件事。

通过不同的类接口方法,可以实现不同级别的保护/实现隐藏:


1.公开数据成员:

  • 提供对数据成员的读取和写入(如果不是const)访问权限
  • 揭示数据对象实际上存在并且实际上是该类的成员的事实(允许人们创建指向该数据成员的指针到成员类型的指针)
  • 提供对数据成员的左值访问(允许创建指向该成员的普通指针)


2.一种返回对一条数据(可能是对私有数据成员)的引用的方法:

  • 提供对数据的读写访问(如果不是const)
  • 公开数据对象实际上存在但不公开它实际上是此类的成员的事实(不允许人们创建指向数据的指针到成员类型的指针)
  • 提供对数据的左值访问(允许创建指向它的普通指针)


3. Getter和/或Setter方法(可能访问私有数据成员):

  • 提供对该属性的读取和/或写入访问权限
  • 不会暴露数据对象实际存在的事实,更不用说物理上存在于此类中的事实(不允许人们创建指向该数据的成员类型的指针或与此相关的任何类型的指针)
  • 不提供对数据的左值访问(不允许创建指向它的普通指针)

getter / setter方法甚至没有公开该属性是由物理对象实现的事实。也就是说,在getter / setter对的后面可能没有物理数据成员。

综上所述,看到有人声称getter和setter对与公共数据成员相同是很奇怪的。实际上,它们没有共同之处。

当然,每种方法都有变化。例如,getter方法可能会返回对数据的const引用,这会将其放置在(2)和(3)之间。


1
@ AndreyT,确实是一个好人……但是,为私人成员设置吸气剂和吸气剂是一种好习惯吗?至少对于那些可能经常被检索和修改的成员...
liaK 2010年

3
它们可能不相同,但是它们用于相同的目的-提供对对象属性的公共访问。
Mark Ransom 2010年

3
您正在从C ++语言的角度看这种表格-我的回答是从应用程序设计的角度来看的。

1
+1指出获取器不需要返回对数据的引用或指针。能够获得副本与能够获得左值有很大不同。
tloach 2010年

1
@liaK-它与良好实践无关,与对象模型无关。请参阅上面的答案。您在问错问题。
jmucchiello 2010年

24

如果每个数据项都有获取程序和设置程序,则将数据设为私有是没有意义的。这就是为什么为每个数据项都具有吸气剂和设置剂是一个坏主意的原因。考虑一下std :: string类-它(可能)具有一个getter,size()函数,并且根本没有二传手。

还是考虑一个BankAccount对象-我们应该让 SetBalance()二传手改变当前余额吗?不,大多数银行都不会感谢您实施此类操作。相反,我们想要类似的东西ApplyTransaction( Transaction & tx )


1
您的意思是说我们应该避免为数据成员使用getter和setter方法?
liaK 2010年

2
+1您希望通过“任务” /“操作”来获得界面,而不是获取器/设置器或公共数据。
Mark B 2010年

3
尼尔,我认为这不是一个好建议。获取器和设置器对于Qt的财产制度至关重要。没有它们,您将无法拥有属性。此外,getter和setter可以做其他必要的事情,例如锁定互斥锁,查询数据库,隐式共享,引用计数,延迟加载等。
CMircea

2
你的第一句话让我失望了。还有就是仍然在使数据的私有性,即使你有这方面的getter和setter点。这似乎与您的观点正交,即没有私有数据(没有getter和setter的情况)也可以(甚至是理想的)。
比尔蜥蜴

2
@比尔你是对的-夸张效果。我的观点是获取/设置功能的普遍弊端。

11

使用Getter和Setter,您可以将逻辑应用于私有成员的输入/输出,从而控制对数据的访问(对知道其OO术语的人进行封装)。

公共变量使您的类的数据向公众开放,以进行不受控制和未经验证的操纵,这几乎总是不希望的。

您还必须长期考虑这些事情。您可能现在没有验证(这就是为什么公共变量似乎是个好主意的原因),但是有可能在以后添加它们。提前添加它们会离开框架,因此减少了重构的范围,更不用说验证不会以这种方式破坏相关代码了。

但是请记住,这并不意味着每个私有变量都需要自己的getter / setter。尼尔(Neil)在他的银行业案例中提出了一个很好的观点,即有时Getters / Setters毫无意义。


11

公开数据。如果您有朝一日确实需要“ getter”或“ setter”中的逻辑,这可能会导致数据类型更改为过载的代理类operator=和/或operator T(其中T =您现在使用的任何类型)实施必要的逻辑。

编辑:控制对数据的访问构成封装的想法基本上是错误的。封装是关于隐藏实现的细节(通常!),而不是控制对数据的访问。

封装是对抽象的补充:抽象处理对象的外部可见行为,而封装则处理隐藏该行为的实现细节。

使用getter或setter实际上会降低抽象级别并公开实现-它要求客户端代码意识到该特定类将逻辑上的“数据”实现为一对函数(getter和setter)。使用如我上面提出的代理提供真正的封装-除了一个不起眼的角落情况下,完全隐藏的事实,是什么逻辑上的一段数据通过对功能的实际执行。

当然,这需要保留在上下文中:对于某些类,“数据”根本不是一个很好的抽象。一般来说,如果您可以提供更高级别的操作来代替数据,那是可取的。尽管如此,对于某些类来说,最有用的抽象是读取和写入数据,在这种情况下,(抽象的)数据应该像其他任何数据一样可见。获取或设置值可能涉及比简单的位复制更多的事实,这是应向用户隐藏的实现细节。


2
“您可以将数据类型更改为代理类”-是的,可以,但是这是一个创可贴,可能会带来更多麻烦,而不是值得的。
Mark Ransom 2010年

3
吸气器/吸气器是否是好的设计,“没有可破解的解决方法”不是争论,而是借口。
BlueRaja-Danny Pflughoeft,2010年

3
@BlueRaja:废话。参见编辑。事实是,要么数据应该是完全不可见的(不是公共的,也不是getter或setter的),否则它应该作为data可见,在这种情况下,getter和setter暴露了错误的抽象。
杰里·科芬

3
@Rob:简单的统计信息-即使在已经维护了数十年的代码中,绝大多数的getter / setter方法都不过是简单的传递,它们根本无法完成任何有用的工作。
杰里·科芬

4
@杰里是对的。公开数据,或使类抽象并将数据移到子类。中间点-带有公共获取者和设置者的私人数据-是黑客。
John Dibling 2010年

5

如果您确定自己的逻辑很简单,并且在读取/写入变量时无需执行任何其他操作,那么最好将数据公开。在C ++情况下,我更喜欢使用struct而不是class来强调数据是公共的事实。

但是,通常在访问数据成员时您需要做一些其他事情,或者您想给自己自由,以便以后添加此逻辑。在这种情况下,getter和setter是个好主意。您的更改将对代码的客户透明。

一个简单的附加功能示例-您可能希望在每次访问变量时都记录一个调试字符串。


5

除了封装方面的考虑(这是足够的理由)之外,在有getter / setter方法的情况下,只要设置/访问变量,就很容易设置断点。


4

使用公共字段而不是获取和设置方法的原因包括:

  1. 没有非法值。
  2. 客户端应该对其进行编辑。
  3. 为了能够写诸如object.XY = Z之类的东西。
  4. 要坚决保证价值只是价值,并且没有与之相关的副作用(将来也不会)。

根据您所使用的软件类型,这些可能都是非常例外的情况(如果您认为遇到过这种情况,可能是错误的),或者它们可能始终存在。真的要看

(摘自基于价值的编程的十个问题。)


3

在严格的实践基础上,我建议您首先将所有数据成员设为私有,并且使他们的getter和setter私人。当您发现世界其他地方(即您的“(l)用户社区”)的实际需求时,就可以公开适当的getter和/或setter或编写适当控制的公共访问器。

同样(出于对Neil的好处),在调试期间,有时在读取或写入特定数据成员时有一个方便的位置挂起调试打印和其他操作很有用。使用getter和setter方法,这很容易。对于公共数据成员,这是后路的巨大痛苦。


我发现私有的get / set函数在开发过程中非常有用,即使它们在公共接口中没有业务。
丹尼斯·齐克福斯

2

我一直认为,在大多数编程语言中,getter和setter都是故意冗长的,专门使您对使用它们感到三思-为什么调用者需要了解类的内部工作原理,这才是您的首要问题。 。


2

我相信,仅使用getter和setter来获取和设置值是没有用的。通过这种方法,公共成员和私人成员之间没有区别。仅当需要以某种方式控制值或在将来认为它可能有用时才使用getter和setter(添加一些逻辑不会使您编辑其余代码)。

作为参考,请阅读C ++准则(C.131)


1

我建议您没有公共数据成员(POD结构除外)。我也不建议您为所有数据成员都使用getter和setter方法。而是为您的班级定义一个干净的公共接口。这可能包括获取和/或设置属性值的方法,并且那些属性可以实现为成员变量。但是,请勿为您的所有成员做吸气剂和装夹器。

这个想法是您将接口与实现分开,从而允许您修改实现,而类的用户不必更改其代码。如果通过getter和setter公开所有内容,则在使用公共数据方面没有任何改善。


PODS(结构)是“无公共数据成员”规则的重要例外;太糟糕了,Microsoft在某些.net值类型的设计中没有为它们做例外。.net中的读/写结构属性具有古怪的行为,而暴露的结构字段则没有。
2012年

1

使用getter和setter可以允许您修改将值​​提供给用户的方式。

考虑以下:

double premium;
double tax;

然后,您可以使用此premium值在各处编写代码以获得溢价:

double myPremium = class.premium;

您的规格刚刚更改,从用户的角度来看,溢价需要 premium + tax

您将必须premium在代码中使用该值的所有地方进行修改,并添加tax

如果相反,您是这样实现的:

double premium;
double tax;

double GetPremium(){return premium;};

您所有的代码都将使用,GetPremium()而您的tax更改将是一行:

double premium;
double tax;

double GetPremium(){return premium + tax;};

1
然后,当您需要不含税的保费时,您将其称为新的吸气剂?而是创建一种方法来计算对象上的真实世界,然后让其决定是否要结合溢价和税收。
jmucchiello 2010年

是的,我想我的例子不是一个真正的getter / setter。我只是试图说明,使用getter和setter可以独立于后台进行编写实现。我想一个更好的例子是,如果变量premium更改为m_dPremium或其他内容。
Ben Burnett

0

返回值还会影响getter和setter的使用。获取变量的值或访问私有数据成员变量是不同的。按值保持完整性,按引用或按指针的程度不高。


0

Getter和Setter主要存在,以便我们可以控制如何获取成员以及如何设置成员。Getter和Setter并不仅仅作为访问特定成员的方式而存在,而是要确保在尝试设置成员之前,它可能满足某些条件,或者如果我们获取它,我们可以控制我们返回该成员的副本。非基本类型的成员。总的来说,当您想要流水化如何与数据成员进行交互时,应该尝试使用g / s'ers,而没有它们将导致该成员以特定方式使用。

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.