繁体   English   中英

公共数据成员与Getters,Setters

[英]Public Data members vs Getters, Setters

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

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

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

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

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

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

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

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

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

不,它根本不是同一件事。

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


1.公开数据成员:

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


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

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


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

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

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

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

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

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

还是考虑一个BankAccount对象-我们应该让SetBalance()设置器更改当前余额吗? 不,大多数银行都不会感谢您实施这种方法。 相反,我们想要类似ApplyTransaction( Transaction & tx )

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

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

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

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

公开数据。 如果有一天(您不太可能需要)在“ getter”或“ setter”中需要逻辑,则可以将数据类型更改为重载operator=和/或operator T的代理类(其中T =无论您使用哪种类型重新使用)来实现必要的逻辑。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

考虑以下:

double premium;
double tax;

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

double myPremium = class.premium;

您的规格刚刚更改,从用户的角度来看, premium + tax必须是premium + tax

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

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

double premium;
double tax;

double GetPremium(){return premium;};

您所有的代码都将使用GetPremium()并且您的tax更改将为一行:

double premium;
double tax;

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM