简体   繁体   English

直接访问成员或始终使用getter

[英]Access members directly or always use getters

Note: C++ specific question 注意:C ++特定问题

I personally find it weird/ugly when a class uses a getter to access its own member data. 当一个类使用getter访问自己的成员数据时,我个人觉得它很奇怪/丑陋。 I know the performance impact is none but I just don't like to see all those method calls. 我知道性能影响是没有但我只是不喜欢看到所有这些方法调用。 Are there any strong arguments either way, or is it just one of those things that's personal preference and should be left to each coder, or arbitrarily controlled in a coding standard? 是否有任何强烈的争论,或者它只是个人喜好的事情之一,应留给每个编码人员,还是在编码标准中任意控制?

Update: I'm meaning simple getters, specifically for a class' non-public members. 更新:我的意思是简单的吸气剂,特别是对于一个班级的非公共成员。

The reason you might want to use a getter/setter is because it conceals the implementation. 您可能想要使用getter / setter的原因是因为它隐藏了实现。 You won't have to rewrite all of your code if you are using getters/setters in case the implementation does change, because those members can continue to work. 如果您正在使用getter / setter以防实现确实发生更改,则不必重写所有代码,因为这些成员可以继续工作。

EDIT based on the many clever comments: 编辑基于许多聪明的评论:

As for a class using setters and getters on itself, that may depend on the particulars. 至于使用自己的吸气剂和吸气剂的类,这可能取决于具体情况。 After all, the implementation of a particular class is available to the class itself. 毕竟,类本身可以使用特定类的实现。 In the cases where a class is normally instantiated, the class should use the member values directly for its own members (private or otherwise) and its parent classes (if they are protected) and only use getters/setters in the case that those members are private to the parent class. 在通常实例化类的情况下,类应该直接将成员值用于其自己的成员(私有或其他)及其父类(如果它们受到保护),并且仅在这些成员使用时使用getter / setter。私有父类。

In the case of an abstract type, which will usually not contain any implementation at all, it should provide pure virtual getters and setters and use only those in the methods it does implement. 在抽象类型的情况下,它通常根本不包含任何实现,它应该提供纯虚拟的getter和setter,并且只使用它实现的方法中的那些。

Willingness to use getters/setters within class member implementation is the canary in the mine telling that your class is growing unreasonably. 在集体成员实施中愿意使用getter / setter是矿井中的金丝雀,告诉你的班级正在不合理地增长。 It tells that your class is trying to do too many different things, that it serves several purposes where it should serve one instead. 它告诉你的班级正在尝试做太多不同的事情,它有几个目的,它应该服务于一个

In fact, this is usually encountered when you are using one part of your class to store or access your data, and another part to make operations on it. 实际上,当您使用类的一部分来存储或访问数据时,通常会遇到这种情况,而另一部分则是对其进行操作。 Maybe you should consider using a standalone class to store and give access to your data, and another one to provide a higher view, with more complex operations with your data. 也许你应该考虑使用一个独立的类来存储和访问你的数据,另一个用来提供更高的视图,以及更复杂的数据操作。

THE OBVIOUS 显而易见

getters and setters for protected members makes as much sense as for public... derived classes are just another form of client code, and encapsulating implementation details from them can still be useful. 受保护成员的getter和setter与public一样有意义......派生类只是客户端代码的另一种形式,从它们封装实现细节仍然很有用。 I'm not saying always do it, just to weight pros and cons along the normal lines. 我并不是说总是这样做,只是为了减轻正常线条的利弊。

getters and setters for private members is rarely a net benefit, though: 私人成员的吸气剂和制定者很少是净收益,但是:

  • it does provide the same kind of encapsulation benefits 它确实提供了相同类型的封装优势

    • single place for breakpoints/logging of get/set + invariant checks during dev (if used consistently) 在开发期间断点/记录get / set +不变检查的单个位置(如果一致地使用)
    • virtual potential 虚拟潜力
    • etc... 等等...

    but only to the presumably relatively small implementation of the same struct/class. 但仅限于可能相对较小的相同结构/类的实现。 In enterprise environments, and for public/protected member data, those benefits can be substantial enough to justify get/set methods: a logging function may end up having millions of lines of code depedent on it, and hundreds or thousands of libraries and apps for which a change to a header may trigger recompilation. 在企业环境中,对于公共/受保护的成员数据,这些好处可以足以证明获取/设置方法的合理性:日志记录功能最终可能依赖于数百万行代码,以及数百或数千个库和应用程序对标头的更改可能会触发重新编译。 Generally a single class implementation shouldn't be more than a few hundred (or at worst thousand) lines - not big or complex enough to justify encapsulating internal private data like this... it could be said to constitute a "code smell". 一般来说,单个类的实现不应该超过几百(或最差千)行 - 不足以证明封装内部私有数据的大小或复杂性......可以说它构成了“代码味道”。

THE NOT-SO OBVIOUS 不那么显眼

  • get/set methods can very occasionally be more readable than direct variable access (though more often less readable) get / set方法偶尔比直接变量访问更具可读性(尽管通常不太可读)
  • get/set methods may be able to provide a more uniform and convenient interface for code-generated member or friend methods (whether from macros or external tools/scripts) get / set方法可能能够为代码生成的成员或朋友方法(无论是来自宏还是外部工具/脚本)提供更加统一和方便的界面
  • less work required to transition between being a member or friend to a freestanding helper function should that become possible 如果可能的话,在成为成员或朋友之间转换为独立的帮助函数所需的工作量减少
  • implementation may be rendered more understandable (and hence maintainable) to people who're normally only users of the class (as more operations are expressed via, or in the style of, the public interface) 对于通常只是该类用户的人(因为更多操作通过公共接口或以公共接口的方式表达),可以使实现更容易理解(并因此可维护)

It's a bit out of scope for the question, but it's worth noting that classes should generally provide action-oriented commands, event-triggered callbacks etc. rather than encouraging a get/set usage pattern. 这个问题有点超出范围,但值得注意的是,类通常应提供面向操作的命令,事件触发的回调等,而不是鼓励获取/设置使用模式。

It seems most people didn't read your question properly, the question is concerning whether or not class methods accessing its own class' members should use getters and setters; 似乎大多数人没有正确地阅读你的问题,问题是关于访问自己班级成员的班级方法是否应该使用getter和setter; not about an external entity accessing the class' members. 不是关于访问班级成员的外部实体。

I wouldn't bother using getter and setter for accessing a class' own members. 我不打算使用getter和setter访问类自己的成员。

However, I also keep my classes small (typically about 200-500 lines), such that if I do need to change the fields or change its implementations or how they are calculated, search and replace wouldn't be too much work (indeed, I often change variable/class/function names in the early development period, I'm picky name chooser). 但是,我也保持我的类很小(通常约200-500行),这样如果我确实需要更改字段或更改其实现或如何计算它们,搜索和替换不会太多工作(事实上,我经常在早期开发期间更改变量/类/函数名称,我是挑剔的名字选择器。

I only use getter and setters for accessing my own class members when I am expecting to change the implementation in the near future (eg if I'm writing a suboptimal code that can be written quickly, but plans to optimize it in the future) that might involve radically changing the data structure used. 当我期望在不久的将来改变实现时,我只使用getter和setter来访问我自己的类成员(例如,如果我正在编写一个可以快速编写的次优代码,但计划将来优化它)可能涉及从根本上改变所使用的数据结构。 Conversely, I don't use getter and setter before I already have the plan; 相反,在我已经有计划之前,我不使用getter和setter; in particular, I don't use getter and setter in expectation of changing things I'm very likely never going to change anyway. 特别是,我不会使用getter和setter来期待改变我无论如何都不会改变的东西。

For external interface though, I strictly adhere to the public interface; 对于外部接口,我严格遵守公共接口; all variables are private, and I avoid friend except for operator overloads; 所有变量都是私有的,除了运算符重载之外,我避免使用friend ; I use protected members conservatively and they are considered a public interface. 我保守地使用protected成员,他们被认为是一个公共接口。 However, even for public interface, I usually still avoid having direct getters and setters methods, as they are often indicative of bad OO design (every OO programmers in any language should read: Why getter and setter methods are Evil ). 然而,即使对于公共接口,我通常仍然避免使用直接的getter和setter方法,因为它们通常表示不良的OO设计(任何语言的每个OO程序员都应该阅读: 为什么getter和setter方法是邪恶的 )。 Instead, I have methods that does something useful, instead of just fetching the values. 相反,我有一些方法可以做一些有用的事情,而不仅仅是获取值。 For example: 例如:

class Rectangle {
    private:
        int x, y, width, height;
    public:
        // avoid getX, setX, getY, setY, getWidth, setWidth, getHeight, setHeight
        void move(int new_x, int new_y);
        void resize(int new_width, int new_height);
        int area();
}

The only advantage is that it allows changing internal representation without changing external interface, permitting lazy evaluation, or why not access counting. 唯一的优点是它允许在不改变外部接口的情况下改变内部表示,允许惰性评估,或者为什么不访问计数。

In my experience, the number of times I did this is very, very low. 根据我的经验,我这样做的次数非常非常低。 And it seems you do, I also prefer to avoid the uglyness and weightyness of getter/setters. 而且你似乎也这样做了,我也更愿意避免吸气剂/安装者的丑陋和沉重。 It is not that difficult to change it afterwards if I really need it. 如果我确实需要它,那么事后就不难改变它。

As you speak about a class using its own getter/setters in its own implementation functions, then you should consider writing non-friend non-member functions where possible. 当您在自己的实现函数中使用自己的getter / setter来讨论类时,您应该考虑尽可能编写非朋友非成员函数。 They improve encapsulation as explained here . 他们提高封装的解释在这里

An argument in favor of using getters is that you might decide one day to change how the member field is calculated. 支持使用getter的一个论点是你可能决定有一天改变成员字段的计算方式。 You may decide that you need it to be qualified with some other member, for instance. 例如,您可能决定需要它与其他成员合格。 If you used a getter, all you have to do is change that one getter function. 如果你使用了一个getter,你所要做的就是改变一个getter函数。 If you didn't you have to change each and every place where that field is used currently and in the future. 如果不这样做,则必须更改当前和将来使用该字段的每个位置。

Just a crude example. 只是一个粗略的例子。 Does this help? 这有帮助吗?

struct myclass{
    int buf[10];
    int getAt(int i){
        if(i >= 0 && i < sizeof(buf)){
            return buf[i];
        }
    }

    void g(){
        int index = 0;
        // some logic
        // Is it worth repeating the check here (what getAt does) to ensure
              // index is within limits
        int val = buf[index];
    }
};

    int main(){}

EDIT: 编辑:

I would say that it depends. 我会说这取决于。 In case the getters do some kind of validation, it is better to go through the validation even if it means the class members being subjected to that validation. 如果getter进行某种验证,最好通过验证,即使它意味着类成员正在进行验证。 Another case where going through a common entry point could be helpful is when the access needs to be essentially in a sequential and synchronized manner eg in a multithreaded scenario. 通过公共入口点的另一种情况可能是有用的,即访问需要基本上以顺序和同步的方式,例如在多线程场景中。

this is actually for supporting the object oriented-ness of the class by abstracting the way to get(getter). 这实际上是通过抽象get(getter)的方式来支持类的面向对象。 and just providing its easier access. 并提供更容易的访问。

Protecting a member variable by wrapping its access with get/set functions has its advantages. 通过使用get / set函数包装其访问来保护成员变量有其优点。 One day you may wish to make your class thread-safe - and in that instance, you'll thank yourself for using those get/set functions 有一天你可能希望让你的类是线程安全的 - 在这种情况下,你会感谢你使用那些get / set函数

Simple answer. 简单回答。 If you are writing a one shoot program, that will never change, you can leave the getters at peace and do without any. 如果你正在编写一个永不改变的单一拍摄程序,那么你可以让游戏者安静下来而不做任何事。

However if you write a program that could change or been written over time, or others might use that code, use getters. 但是,如果您编写的程序可能会随时间更改或编写,或者其他人可能会使用该代码,请使用getter。

If you use getters it helps change the code faster later on, like putting a guard on the property to verify correctness of value, or counting access to the property(debugging). 如果使用getter,它有助于以后更快地更改代码,例如在属性上设置保护以验证值的正确性,或计算对属性的访问(调试)。

Getters to me are about easy possibilities(free lunch). 吸毒者对我来说很简单(免费午餐)。 The programmer who write the code does not need getters, he wants them. 编写代码的程序员不需要getter,他想要它们。

hope that help. 希望有所帮助。

My thoughts are as follows. 我的想法如下。

Everything should be static, constant, and private if possible. 如果可能的话,一切都应该是静态的,恒定的和私人的。

  • As you need a variable to be instanced meaning more than one unique copy you remove static. 由于您需要一个要实例化的变量,这意味着您需要删除一个以上的唯一副本。

  • As you need a variable to be modifiable you remove the const. 因为您需要一个可修改的变量,所以删除const。

  • As you need a class/variable to be accessed by other classes you remove the private. 由于您需要其他类访问的类/变量,因此您将删除私有。

The Usage of Setters/Getters - General Purpose. 塞特犬/吸气剂的用法 - 通用。

  • Getter's are okay if the value is to ONLY be changed by the class and we want to protect it. 如果该值仅由类改变并且我们想要保护它,那么Getter是可以的。 This way we can retrieve the current state of this value without the chance of it's value getting changed. 通过这种方式,我们可以检索此值的当前状态,而不会更改其值。
  • Getter's should not be used if you are planning to provide a Setter with it. 如果您计划使用Setter提供Setter,则不应使用Getter。 At this point you should simply convert the value to public and just modify it directly. 此时,您只需将值转换为public即可直接修改它。 Since this is the intent with a Get/Set. 因为这是Get / Set的意图。

  • A Setter is plain useless if you are planning to do more then simply "this.value = value". 如果你打算做更多的事情,那么Setter就没那么简单了“this.value = value”。 Then you shouldn't be calling it "SetValue" rather describe what it is actually doing. 那么你不应该称它为“SetValue”而是描述它实际上在做什么。

  • If let's say you want to make modifications to a value before you "GET" it's value. 如果让我们说你想要在“获取”它的值之前修改一个值。 Then DO NOT call it "GetValue". 然后不要将其称为“GetValue”。 This is ambiguous to your intent and although YOU might know what's happening. 虽然你可能知道发生了什么,但这与你的意图含糊不清。 Someone else wouldn't unless they viewed the source code of that function. 除非他们查看该函数的源代码,否则其他人不会。

  • If let's say you are indeed only Getting/Setting a value, but you are doing some form of security. 如果让我们说你确实只是获取/设置一个值,但你正在做某种形式的安全性。 Ie Size check, Null Check, etc.. this is an alternative scenario. 即大小检查,空检查等。这是一种替代方案。 However you should still clarify that in the name Eg "SafeSetValue" , "SafeGetValue" or like in the "printf" there is "printf_s". 但是你仍然应该澄清,在名称为“SafeSetValue”,“SafeGetValue”或类似“printf”的名称中有“printf_s”。

Alternatives to the Get/Set situations 获取/设置情况的替代方案

  • An example that I personally have. 我个人拥有的一个例子。 Which you can see how I handle a Get/Set scenario. 您可以在其中看到我如何处理Get / Set方案。 Is I have a GameTime class which stores all kinds of values and every game tick these values get changed. 我有一个GameTime类存储各种值,每个游戏都会记录这些值的变化。

    https://github.com/JeremyDX/DX_B/blob/master/DX_B/GameTime.cpp https://github.com/JeremyDX/DX_B/blob/master/DX_B/GameTime.cpp

  • As you will see in the above my "GETS" are not actually "GETS" of 正如您将在上面看到的,我的“GETS”实际上并不是“GETS”
    values except in small cases where modification wasn't needed. 值,除非在不需要修改的小情况下。 Rather they are descriptions of values I am trying to retrieve out of this 相反,它们是我试图从中检索的值的描述
    GameTime class. GameTime课程。 Every value is "Static Private". 每个值都是“静态私有”。 I cannot do Const 我不能做Const
    given the information is obtained until runtime and I keep this 鉴于信息是在运行时获得的,我保留了这一点
    static as there is no purpose to have multiple instances of Timing. 静态,因为没有目的有多个Timing实例。

  • As you will also see I don't have any way of performing a "SET" on any of this data, but there are two functions "Begin()" and "Tick()" which both change the values. 您还将看到我没有任何方法可以对任何此类数据执行“SET”,但有两个函数“Begin()”和“Tick()”都会更改这些值。 This is how ALL "setters" should be handled. 这就是应该处理所有“设置者”的方式。 Basically the "Begin()" function resets all the data and loads in our constants which we CANT set as constants since this is data we retrieve at runtime. 基本上,“Begin()”函数重置我们常量中的所有数据和加载,我们将其设置为常量,因为这是我们在运行时检索的数据。 Then TICK() updates specific values as time passes in this case so we have fresh up to date information. 然后TICK()在这种情况下随着时间的推移更新特定值,因此我们有最新的信息。

  • If you look far into the code you'll find the values "ResetWindowFrameTime()" and "ElapsedFrameTicks()". 如果你仔细研究代码,你会发现值“ResetWindowFrameTime()”和“ElapsedFrameTicks()”。 Typically I wouldn't do something like this and would have just set the value to public. 通常我不会做这样的事情,只会将值设置为公开。 Since as you'll see I'm retrieving the value and setting the value. 因为你会看到我正在检索值并设置值。 This is another form of Set/Get, but it still uses naming that fits the scenario and it uses data from private variables so it didn't make sense to pull another private variable and then multiply it by this rather do the work here and pull the result. 这是Set / Get的另一种形式,但它仍然使用适合场景的命名,并且它使用来自私有变量的数据,因此拉出另一个私有变量然后将其乘以此而不是在这里工作并拉动它是没有意义的结果。 There is also NO need to edit the value other then to reset it to the current frame index and then retrieve the elapsed frames. 也无需编辑其他值,然后将其重置为当前帧索引,然后检索已用帧。 It is used when I open a new window onto my screen so I can know how long I've been viewing this window for and proceed accordingly. 当我在屏幕上打开一个新窗口时使用它,这样我就可以知道我已经查看了这个窗口多长时间并继续相应的操作。

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

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