公共数据成员与 Getters、Setters
我目前在 Qt 和 C++ 领域工作。我的类具有私有数据成员和公共成员函数。我为类中可用的数据成员提供了公共 getter 和 setter。
现在我的问题是,如果我们的类中的数据成员有 getter 和 setter,那么将这些数据成员设为私有有什么意义? 我同意在基类中拥有私有数据成员听起来很合乎逻辑。但除此之外,拥有私人成员以及他们的 getter 和 setter 对我来说似乎并不符合逻辑。
或者我们可以将所有变量设为公共变量,这样就根本不需要 getter 和 setter 了吗?拥有这些是一个好的做法吗? 我知道让私有成员确保数据抽象,但拥有 getter 和 setter 实际上可以很容易地访问这些变量。欢迎任何与此相关的指示。
I am currently working in Qt and so C++. I am having classes that has private data members and public member functions. I have public getters and setters for the data members available in the class.
Now my question is, if we have getters and setters for data members in our classes then what's the point in making those data members as private? I agree having private data members in Base classes sounds logical. But besides that, having private members and so do their getters and setters doesn't seem to be of a logical one for me.
Or instead can we make all variables as public so that no need for getters and setters at all? Is it a good practice to have those? I know having private members ensure data abstraction but having getters and setters actually lets access to those variables quite easily. Any pointers regarding this are welcome.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
两者都不。你应该有做事的方法。如果其中一件事情恰好与特定的内部变量相对应,那就太好了,但不应该有任何东西可以将这一点传达给类的用户。
私有数据是私有的,因此您可以随时替换实现(并且可以进行完全重建,但这是一个不同的问题)。一旦你把精灵从瓶子里拿出来,你会发现不可能把它推回去。
编辑:根据我对另一个答案的评论。
我的观点是你问了错误的问题。关于使用 getter/setter 或拥有公共成员,没有最佳实践。只有最适合您的特定对象以及它如何模拟某些特定的现实世界事物(或者在游戏中可能是虚构的事物)。
就个人而言,getter/setter 是两害相权取其轻。因为一旦开始制作 getter/setter,人们就不再以挑剔的眼光来设计对象,哪些数据应该可见,哪些数据不应该可见。对于公众成员来说情况更糟,因为倾向于将一切都公开。
相反,检查该对象的作用以及该对象对于某些事物意味着什么。然后创建为该对象提供自然接口的方法。自然接口涉及使用 getter 和 setter 公开一些内部属性,就这样吧。但重要的是,您提前考虑了这一点,并出于设计合理的原因创建了 getter/setter。
Neither. You should have methods that do things. If one of those things happens to correspond with a specific internal variable that's great but there should be nothing that telegraphs this to the users of your class.
Private data is private so you can replace the implementation whenever you wish (and can do full rebuilds but that's a different issue). Once you let the Genie out of the bottle you will find it impossible to push it back in.
EDIT: Following a comment I made to another answer.
My point here is that you are asking the wrong question. There is no best practice with regard to using getters/setters or having public members. There is only what is best for your specific object and how it models some specific real world thing (or imaginary thing perhaps in the case of game).
Personally getters/setters are the lesser of two evils. Because once you start making getters/setters, people stop designing objects with a critical eye toward what data should be visible and what data should not. With public members it is even worse because the tendency becomes to make everything public.
Instead, examine what the object does and what it means for something to be that object. Then create methods that provide a natural interface into that object. It that natural interface involves exposing some internal properties using getters and setters so be it. But the important part is that you thought about it ahead of time and created the getters/setters for a design justified reason.
不,这根本不是同一件事。
可以通过类接口的不同方法来实现不同级别的保护/实现隐藏:
1. 公共数据成员:
2. 返回对一段数据(可能是私有数据成员)的引用的方法:
< br>
3. getter 和/或 setter 方法(可能访问私有数据成员):
getter/setter 方法甚至没有暴露属性是由物理对象实现的事实。即,getter/setter 对后面可能没有物理数据成员。
考虑到上述情况,看到有人声称 getter 和 setter 对与公共数据成员相同是很奇怪的。事实上,他们没有任何共同点。
当然,每种方法都有其变体。例如,getter 方法可能会返回对数据的 const 引用,这会将其放置在 (2) 和 (3) 之间的某个位置。
No, it is not even remotely the same thing.
There are different levels of protection/implementation hiding that can be achieved by different approaches to a class interface:
1. Public data member:
2. A method that returns a reference to a piece of data (possibly to a private data member):
3. Getter and/or setter methods (possibly accessing a private data member):
The getter/setter approach does not even expose the fact that the property is implemented by a physical object. I.e. there might be no physical data member behind the getter/setter pair.
Taking above into the account, it is strange to see someone claim that a getter and setter pair is the same as a public data member. In fact, they have nothing in common.
Of course, there are variations of each approach. A getter method, for example, might return a const reference to the data, which would place it somewhere between (2) and (3).
如果每个数据项都有 getter 和 setter,则将数据设为私有是没有意义的。这就是为什么为每个数据项设置 getter 和 setter 是一个坏主意。考虑 std::string 类 - 它(可能)有一个 getter、size() 函数,并且根本没有 setter。
或者考虑一个
BankAccount
对象 - 我们是否应该使用SetBalance()
setter 来更改当前余额?不,大多数银行不会感谢你实施这样的事情。相反,我们想要像ApplyTransaction( Transaction & tx )
这样的东西。If you have getters and setters for each of your data items, there is no point in making the data private. That's why having getters and setters for each of your data items is a bad idea. Consider the std::string class - it (probably) has ONE getter, the size() function, and no setters at all.
Or consider a
BankAccount
object - should we haveSetBalance()
setter to change the current balance? No, most banks won't thank you for implementing such a thing. Instead, we want something likeApplyTransaction( Transaction & tx )
.公开数据。如果有一天您确实需要“getter”或“setter”中的逻辑(不太可能),您可以将数据类型更改为重载
operator=
和/或的代理类运算符 T
(其中 T=您现在使用的任何类型)来实现必要的逻辑。编辑:控制对数据的访问构成封装的想法基本上是错误的。封装是关于隐藏实现的细节(一般来说!)不控制对数据的访问。
封装是对抽象的补充:抽象处理对象的外部可见行为,而封装则处理隐藏该行为如何实现的细节。
使用 getter 或 setter 实际上降低抽象级别并公开实现——它要求客户端代码知道这个特定的类将逻辑上的“数据”实现为一对函数(getter和二传手)。正如我上面建议的那样,使用代理可以提供真正的封装——除了一个模糊的极端情况之外,它完全隐藏这样一个事实:逻辑上的数据实际上是通过一对函数来实现。
当然,这需要放在上下文中:对于某些类,“数据”根本不是一个好的抽象。一般来说,如果您可以提供更高级别的操作而不是数据,那就更好了。尽管如此,有些类最有用的抽象是读取和写入数据——在这种情况下,(抽象的)数据应该像任何其他数据一样可见。获取或设置值可能不仅仅涉及简单的位复制,这一事实是一个应该对用户隐藏的实现细节。
Make the data public. In the (rather unlikely) event that you do someday need logic in the "getter" or "setter", you can change the data type to a proxy class that overloads
operator=
and/oroperator T
(where T=whatever type you're using now) to implement the necessary logic.Edit: the idea that controlling access to the data constitutes encapsulation is basically false. Encapsulation is about hiding the details of the implementation (in general!) not controlling access to data.
Encapsulation is complementary to abstraction: abstraction deals with the object's externally visible behavior, while encapsulation deals with hiding the details of how that behavior is implemented.
Using a getter or setter actually reduces the level of abstraction and exposes the implementation -- it requires client code to be aware that this particular class implements what is logically "data" as a pair of functions (the getter and setter). Using a proxy as I've suggested above provides real encapsulation -- except for one obscure corner case, it completely hides the fact that what is logically a piece of data is actually implemented via a pair of functions.
Of course, this needs to be kept in context: for some classes, "data" isn't a good abstraction at all. Generally speaking, if you can provide higher level operations instead of data, that's preferable. Nonetheless, there are classes for which the most usable abstraction is reading and writing data -- and when that's the case, the (abstracted) data should be made visible just like any other data. The fact that getting or setting the value may involve more than simple copying of bits is an implementation detail that should be hidden from the user.
Getters 和 Setters 允许您将逻辑应用于私有成员的输入/输出,从而控制对数据的访问(对那些了解 OO 术语的人进行封装)。
公共变量使类的数据向公众开放,以进行不受控制和未经验证的操作,这几乎总是不可取的。
你也必须长期考虑这些事情。您现在可能没有验证(这就是为什么公共变量似乎是一个好主意),但它们有可能会在以后添加。提前添加它们会留下框架,因此减少了重构,更不用说验证不会以这种方式破坏依赖代码)。
但请记住,这并不意味着每个私有变量都需要自己的 getter/setter。 Neil 在他的银行业示例中提出了一个很好的观点,即有时 Getters/Setters 根本没有意义。
Getters and Setters let you apply logic to the input/output from the private members therefore controlling access to the data (Encapsulation to those who know their OO terms).
Public variables leave your class' data open to the public for uncontrolled and non-validated manipulation which is almost always un-desirable.
You have to think about these things long term as well. You may not have validation now (which is why public variables seem to be a good idea) but there's a chance they'll be added down the road. Adding them ahead of time leaves the framework so there's less re-factoring down the raod not to mention the validation won't break dependent code this way).
Keep in mind, though, that doesn't mean Each and Every private variable needs its own getter/setter. Neil brings up a good point in his banking example that sometimes Getters/Setters just don't make sense.
如果您非常确定您的逻辑很简单,并且在读取/写入变量时不需要执行其他操作,那么最好将数据保持公开。在 C++ 情况下,我更喜欢使用结构而不是类来强调数据是公共的这一事实。
但是,在访问数据成员时,您经常需要执行一些其他操作,或者您希望稍后可以自由地添加此逻辑。在这种情况下,getter 和 setter 是个好主意。您的更改对于代码的客户端来说是透明的。
附加功能的一个简单示例 - 您可能希望在每次访问变量时记录调试字符串。
If you are quite sure your logic is simple, and you never need to do something else when reading/writing a variable, it's better to keep the data public. In C++ case, I prefer to use struct instead of class to emphasize the fact that the data is public.
However, quite often you need to do some other things when accessing data members, or you want to give yourself freedom to add this logic later. In this case, getters and setters are good idea. Your change will be transparent to the clients of your code.
A simple example of additional functionality - you may want log a debug string every time you access a variable.
除了封装问题(这是足够的理由)之外,当您有 getter/setter 时,只要设置/访问变量,就很容易设置断点。
Aside from the encapsulation concerns (which are reason enough), it is very easy to set a breakpoint whenever the variable is set/accessed when you have getters/setters.
使用公共字段而不是 getter 和 setter 的原因包括:
根据您使用的软件类型,这些可能都是非常特殊的情况(如果您认为自己遇到过这种情况,那么您可能是错的)或者它们可能一直发生。这确实取决于。
(摘自关于基于值的编程的十个问题 .)
Reasons to use public fields rather than getters and setters include:
Depending on what sort of software you work on, these might all be really exceptional cases (and if you think you've come across one you're probably wrong) or they might occur all the time. It really depends.
(From Ten Questions on Value-Based Programming.)
在严格的实践基础上,我建议您首先将所有数据成员设为私有,并将其 getter 和 setter 设为私有。当您发现世界其他地方(即您的“(l)用户社区”)实际需要什么时,您可以公开适当的 getter 和/或 setter,或者编写适当控制的公共访问器。
另外(为了尼尔的利益),在调试期间,当读取或写入特定数据成员时,有一个方便的位置来挂起调试打印和其他操作有时很有用。有了 getter 和 setter,这很容易。对于公共数据成员来说,这是一个巨大的后路痛苦。
On a strictly practical basis, I'd suggest you start by making all of your data members private, AND make their getters and setters private. As you find out what the rest of the world (i.e., your "(l)user community") actually needs, you can expose the appropriate getters and/or setters, or write appropriately-controlled public accessors.
Also (for Neil's benefit), during debugging time, it is sometimes useful to have a convenient place to hang debug prints, and other actions, when a particular data member is read or written. With getters and setters, this is easy. With public data members, it is a huge pain in the posterior.
我一直认为,在大多数编程语言中,getter 和 setter 都是故意冗长的,特别是为了让你在使用它们时三思而后行 - 为什么你的调用者需要了解你的类的内部工作原理应该是你首先想到的问题。
I've always thought that getters and setters are deliberately verbose in most programming languages specifically to make you think twice about using them - why does your caller need to know about the inner workings of your class should be the question at the front of your mind.
我相信仅仅使用 getter 和 setter 来获取和设置值是没有用的。使用这种方法,公共成员和私有成员没有区别。仅当您需要以某种方式控制值或您认为它在将来可能有用时才使用 getter 和 setter(添加一些逻辑不会让您编辑其余代码)。
作为参考,请阅读 C++指南(C.131)
I believe that using getters and setters simply for getting and setting the value is useless. There is no difference between a public member and private one with such methods. Use getters and setters only when you need to control the values somehow or when you think that it might be useful in the future (adding some logic won't make you edit the rest of the code).
As a reference, read C++ guidelines (C.131)
我建议您不要拥有公共数据成员(POD 结构除外)。我也不建议您为所有数据成员设置 getter 和 setter。相反,为您的类定义一个干净的公共接口。这可能包括获取和/或设置属性值的方法,并且这些属性可以被实现为成员变量。但不要为所有成员创建 getter 和 setter。
这个想法是将接口与实现分开,允许您修改实现,而无需类的用户更改其代码。如果您通过 getter 和 setter 公开所有内容,那么与使用公共数据相比,您并没有任何改进。
I suggest that you don't have public data members (except for POD structs). I also don't recommend that you have getters and setters for all of your data members. Rather, define a clean public interface for your class. This may include methods that get and/or set property values, and those properties may be implemented as member variables. But don't make getters and setters for all of your members.
The idea is that you separate your interface from your implementation, allowing you to modify the implementation without the users of the class having to change their code. If you expose everything through getters and setters, you've not improved anything over using public data.
使用 getter 和 setter 将允许您修改向用户提供值的方式。
请考虑以下事项:
然后,您使用此
溢价
值在各处编写代码以获得溢价:您的规格刚刚更改,从用户的角度来看,溢价需要是
溢价+税
您必须修改代码中使用该
premium
值的所有位置,并向其中添加tax
。相反,如果您这样实现它:
您的所有代码都将使用
GetPremium()
并且您的tax
更改将是一行:Using getters and setters will allow you to modify the way that you give the values to the user.
Consider the following:
You then write code all over the place using this
premium
value to get the premium:Your specs just changed and premium from the user's standpoint needs to be
premium + tax
You will have to modify everywhere that that
premium
value is being used in your code, and addtax
to it.If instead you implemented it as such:
All of your code would be using
GetPremium()
and yourtax
change would be one line:返回值也会影响 getter 和 setter 的使用。获取变量的值或访问私有数据成员变量是有区别的。按值保持完整性,按引用或按指针则不然。
The return value also effects the use of getters and setters. It's a difference to get the value of a variable or to get access to private data member variable. By-value keeps integrity, by-reference or by-pointer not so much.
Getter 和 Setter 的存在主要是为了我们可以控制如何获取成员以及如何设置成员。 getter 和 setter 不仅仅作为访问特定成员的方式而存在,而是为了确保在我们尝试设置成员之前,它可能满足某些条件,或者如果我们获取它,我们可以控制返回该成员的副本成员(在非原始类型的情况下)。总的来说,当您想要通过管道方式与数据成员交互时,您应该尝试使用 g/s'ers,否则会导致以临时方式使用该成员。
Getters and Setters exist primarily so that we can control how members are fetched, and how they are set. Getters and setters dont exist only as a way to access a specific member, but to ensure that before we try and set a member, that it perhaps meets certain conditions, or if we fetch it, we could control that we return a copy of that member in the case of a non-primitive type. Overall, you should try and use g/s'ers when you want to pipeline how a data member is to be interacted with, without them would cause the member is be used in an adhoc fashion.