为什么要使用 getter 和 setter/accessors?
使用 getter 和 setter(仅获取和设置)比简单地使用这些变量的公共字段有什么优势?
如果 getter 和 setter 所做的不仅仅是简单的 get/set,我可以很快弄清楚这一点,但我不是 100% 清楚如何:
public String foo;
比:
private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }
而前者需要少得多的样板代码。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
实际上有许多充分的理由考虑使用访问器而不是直接公开类的字段 - 不仅仅是封装的论据和使未来的更改更容易。
以下是我所知道的一些原因:
There are actually many good reasons to consider using accessors rather than directly exposing fields of a class - beyond just the argument of encapsulation and making future changes easier.
Here are the some of the reasons I am aware of:
因为 2 周(月、年)后,当您意识到您的 setter 需要做的不仅仅是设置值之外,您还会意识到该属性已在 238 个其他类中直接使用: -)
Because 2 weeks (months, years) from now when you realize that your setter needs to do more than just set the value, you'll also realize that the property has been used directly in 238 other classes :-)
公共字段并不比除了返回字段并为其赋值之外不执行任何操作的 getter/setter 对差。首先,很明显(在大多数语言中)不存在功能差异。任何差异都必须存在于其他因素中,例如可维护性或可读性。
getter/setter 对的一个经常提到的优点却不是。有人声称您可以更改实现,并且您的客户端不必重新编译。据说,setter 可以让您稍后添加验证等功能,而您的客户甚至不需要知道它。然而,向 setter 添加验证是对其先决条件的更改,违反了之前的合同,这很简单,“您可以在此处放置任何内容,并且稍后可以获得相同的内容来自吸气剂”。
因此,既然您违反了合同,那么更改代码库中的每个文件是您应该做的事情,而不是避免。如果你避免它,你就假设所有代码都假设这些方法的合同是不同的。
如果这不应该是合同,那么该接口就允许客户端将对象置于无效状态。 这与封装完全相反如果该字段从一开始就无法真正设置为任何内容,为什么从一开始就没有进行验证?
同样的论点也适用于这些传递 getter/setter 对的其他假定优点:如果您稍后决定更改正在设置的值,那么您就违反了合同。如果您以超出一些无害的修改(例如日志记录或其他不可观察的行为)的方式重写派生类中的默认功能,那么您就违反了基类的约定。这违反了里氏可替代性原则,该原则被视为面向对象的原则之一。
如果一个类的每个字段都有这些愚蠢的 getter 和 setter,那么它就是一个没有任何不变量的类,没有契约。这真的是面向对象设计吗?如果类只有这些 getter 和 setter,那么它只是一个愚蠢的数据持有者,而愚蠢的数据持有者应该看起来像愚蠢的数据持有者:
向这样的类添加传递 getter/setter 对不会增加任何价值。其他类应该提供有意义的操作,而不仅仅是字段已经提供的操作。这就是定义和维护有用的不变量的方式。
使用 getter 和 setter 是有原因的,但如果这些原因不存在,那么以虚假封装神的名义创建 getter/setter 对并不是一件好事。创建 getter 或 setter 的正当理由包括经常提到的稍后可以进行的潜在更改,例如验证或不同的内部表示。或者也许该值应该可由客户端读取但不可写入(例如,读取字典的大小),因此简单的 getter 是一个不错的选择。但当你做出选择时,这些理由就应该存在,而不仅仅是你以后可能想要的东西。这是 YAGNI(你不需要它)的一个实例。
A public field is not worse than a getter/setter pair that does nothing except returning the field and assigning to it. First, it's clear that (in most languages) there is no functional difference. Any difference must be in other factors, like maintainability or readability.
An oft-mentioned advantage of getter/setter pairs, isn't. There's this claim that you can change the implementation and your clients don't have to be recompiled. Supposedly, setters let you add functionality like validation later on and your clients don't even need to know about it. However, adding validation to a setter is a change to its preconditions, a violation of the previous contract, which was, quite simply, "you can put anything in here, and you can get that same thing later from the getter".
So, now that you broke the contract, changing every file in the codebase is something you should want to do, not avoid. If you avoid it you're making the assumption that all the code assumed the contract for those methods was different.
If that should not have been the contract, then the interface was allowing clients to put the object in invalid states. That's the exact opposite of encapsulation If that field could not really be set to anything from the start, why wasn't the validation there from the start?
This same argument applies to other supposed advantages of these pass-through getter/setter pairs: if you later decide to change the value being set, you're breaking the contract. If you override the default functionality in a derived class, in a way beyond a few harmless modifications (like logging or other non-observable behaviour), you're breaking the contract of the base class. That is a violation of the Liskov Substitutability Principle, which is seen as one of the tenets of OO.
If a class has these dumb getters and setters for every field, then it is a class that has no invariants whatsoever, no contract. Is that really object-oriented design? If all the class has is those getters and setters, it's just a dumb data holder, and dumb data holders should look like dumb data holders:
Adding pass-through getter/setter pairs to such a class adds no value. Other classes should provide meaningful operations, not just operations that fields already provide. That's how you can define and maintain useful invariants.
There are reasons to use getters and setters, but if those reasons don't exist, making getter/setter pairs in the name of false encapsulation gods is not a good thing. Valid reasons to make getters or setters include the things often mentioned as the potential changes you can make later, like validation or different internal representations. Or maybe the value should be readable by clients but not writable (for example, reading the size of a dictionary), so a simple getter is a nice choice. But those reasons should be there when you make the choice, and not just as a potential thing you may want later. This is an instance of YAGNI (You Ain't Gonna Need It).
很多人都在谈论 getter 和 setter 的优点,但我想唱反调。现在我正在调试一个非常大的程序,程序员决定将所有内容都设置为 getter 和 setter。这看起来不错,但它是逆向工程的噩梦。
假设您正在查看数百行代码,并且发现了这一点:
这是一段非常简单的代码,直到您意识到它是一个 setter。现在,您跟踪该 setter 并发现它还设置了 person.firstName、person.lastName、person.isHuman、person.hasReallyCommonFirstName,并调用 person.update(),该更新将查询发送到数据库等。哦,那就是发生内存泄漏的地方。
乍一看就理解本地代码是良好可读性的一个重要属性,但 getter 和 setter 往往会破坏这一属性。这就是为什么我尽可能避免使用它们,并在使用它们时尽量减少它们的作用。
Lots of people talk about the advantages of getters and setters but I want to play devil's advocate. Right now I'm debugging a very large program where the programmers decided to make everything getters and setters. That might seem nice, but its a reverse-engineering nightmare.
Say you're looking through hundreds of lines of code and you come across this:
It's a beautifully simply piece of code until you realize its a setter. Now, you follow that setter and find that it also sets person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName, and calls person.update(), which sends a query out to the database, etc. Oh, that's where your memory leak was occurring.
Understanding a local piece of code at first glance is an important property of good readability that getters and setters tend to break. That is why I try to avoid them when I can, and minimize what they do when I use them.
在纯粹的面向对象世界中,getter 和 setter 是一种可怕的反模式。阅读这篇文章:Getters/Setters。邪恶的。期间。简而言之,他们鼓励程序员将对象视为数据结构,而这种类型的思维是纯粹的过程性(就像 COBOL 或 C 中的那样)。在面向对象的语言中,没有数据结构,只有公开行为的对象(不是属性/属性!)
您可以在 优雅对象(我关于面向对象编程的书)。
In a pure object-oriented world getters and setters is a terrible anti-pattern. Read this article: Getters/Setters. Evil. Period. In a nutshell, they encourage programmers to think about objects as of data structures, and this type of thinking is pure procedural (like in COBOL or C). In an object-oriented language there are no data structures, but only objects that expose behavior (not attributes/properties!)
You may find more about them in Section 3.5 of Elegant Objects (my book about object-oriented programming).
原因有很多。我最喜欢的是当您需要更改行为或规范可以在变量上设置的内容时。例如,假设您有一个 setSpeed(int speed) 方法。但您希望只能将最大速度设置为 100。您会执行以下操作:
现在,如果您代码中的所有位置都在使用公共字段,然后您意识到需要上述要求,该怎么办?享受寻找公共字段的每一个用法的乐趣,而不是仅仅修改你的设置器。
我的 2 美分:)
There are many reasons. My favorite one is when you need to change the behavior or regulate what you can set on a variable. For instance, lets say you had a setSpeed(int speed) method. But you want that you can only set a maximum speed of 100. You would do something like:
Now what if EVERYWHERE in your code you were using the public field and then you realized you need the above requirement? Have fun hunting down every usage of the public field instead of just modifying your setter.
My 2 cents :)
访问器和修改器的优点之一是您可以执行验证。
例如,如果
foo
是公共的,我可以轻松地将其设置为null
,然后其他人可以尝试调用该对象的方法。但它已经不在那里了!使用setFoo
方法,我可以确保foo
永远不会设置为null
。访问器和修改器还允许封装 - 如果您不应该在设置后看到该值(也许它在构造函数中设置,然后由方法使用,但永远不会被更改),那么任何人都不会看到它。但是,如果您可以允许其他类查看或更改它,则可以提供适当的访问器和/或修改器。
One advantage of accessors and mutators is that you can perform validation.
For example, if
foo
was public, I could easily set it tonull
and then someone else could try to call a method on the object. But it's not there anymore! With asetFoo
method, I could ensure thatfoo
was never set tonull
.Accessors and mutators also allow for encapsulation - if you aren't supposed to see the value once its set (perhaps it's set in the constructor and then used by methods, but never supposed to be changed), it will never been seen by anyone. But if you can allow other classes to see or change it, you can provide the proper accessor and/or mutator.
谢谢,这确实澄清了我的想法。现在这里有(几乎)10 个(几乎)不使用 getter 和 setter 的好理由:
最后三个我只是离开(N/A 或 D/C)...
Thanks, that really clarified my thinking. Now here is (almost) 10 (almost) good reasons NOT to use getters and setters:
The last three I'm just leaving (N/A or D/C)...
取决于您的语言。您已将其标记为“面向对象”而不是“Java”,因此我想指出 ChssPly76 的答案取决于语言。例如,在 Python 中,没有理由使用 getter 和 setter。如果需要更改行为,可以使用属性,它围绕基本属性访问封装了 getter 和 setter。像这样的东西:
Depends on your language. You've tagged this "object-oriented" rather than "Java", so I'd like to point out that ChssPly76's answer is language-dependent. In Python, for instance, there is no reason to use getters and setters. If you need to change the behavior, you can use a property, which wraps a getter and setter around basic attribute access. Something like this:
编辑:我回答这个问题是因为有很多学习编程的人问这个问题,而且大多数答案在技术上都非常有能力,但如果你是新手,它们就不那么容易理解了。我们都是新手,所以我想我应该尝试一个对新手更友好的答案。
主要的两个是多态性和验证。即使它只是一个愚蠢的数据结构。
假设我们有一个简单的类:
一个非常简单的类,其中包含其中有多少液体,以及它的容量是多少(以毫升为单位)。
当我这样做时会发生什么:
嗯,你不会指望这会起作用,对吗?
您希望进行某种健全性检查。更糟糕的是,如果我从未指定最大容量怎么办?哦,亲爱的,我们有一个问题。
但还有另一个问题。如果瓶子只是一种容器怎么办?如果我们有几个容器,每个容器都有容量和填充液体量怎么办?如果我们可以创建一个接口,我们就可以让程序的其余部分接受该接口,瓶子、塑料桶和各种东西就可以互换使用。那不是更好吗?由于接口需要方法,这也是一件好事。
我们最终会得到这样的结果:
太棒了!现在我们只需将 Bottle 更改为:
我将把 BottleOverflowException 的定义留给读者作为练习。
现在请注意这有多强大。现在,我们可以通过接受 LiquidContainer 而不是 Bottle 来处理代码中的任何类型的容器。这些瓶子处理这类东西的方式也各不相同。您可以让瓶子在状态发生变化时将其状态写入磁盘,或者将瓶子保存在 SQL 数据库或 GNU 知道的其他内容上。
所有这些都可以有不同的方式来处理各种问题。 Bottle 只是检查,如果溢出就会抛出 RuntimeException。但这可能是错误的做法。
(关于错误处理有一个有用的讨论,但我故意在这里保持非常简单。评论中的人可能会指出这种简单方法的缺陷。;))
是的,看起来我们走了从一个非常简单的想法到快速获得更好的答案。
另请注意,您无法更改瓶子的容量。现在它已经板上钉钉了。您可以通过将 int 声明为 Final 来实现此目的。但如果这是一个列表,您可以清空它,向其中添加新内容,等等。你不能限制接触内脏的机会。
还有第三件事并不是每个人都解决过:getter 和 setter 使用方法调用。这意味着它们在其他地方看起来就像普通方法一样。 DTO 之类的东西不再有奇怪的特定语法,而是到处都有相同的东西。
EDIT: I answered this question because there are a bunch of people learning programming asking this, and most of the answers are very technically competent, but they're not as easy to understand if you're a newbie. We were all newbies, so I thought I'd try my hand at a more newbie friendly answer.
The two main ones are polymorphism, and validation. Even if it's just a stupid data structure.
Let's say we have this simple class:
A very simple class that holds how much liquid is in it, and what its capacity is (in milliliters).
What happens when I do:
Well, you wouldn't expect that to work, right?
You want there to be some kind of sanity check. And worse, what if I never specified the maximum capacity? Oh dear, we have a problem.
But there's another problem too. What if bottles were just one type of container? What if we had several containers, all with capacities and amounts of liquid filled? If we could just make an interface, we could let the rest of our program accept that interface, and bottles, jerrycans and all sorts of stuff would just work interchangably. Wouldn't that be better? Since interfaces demand methods, this is also a good thing.
We'd end up with something like:
Great! And now we just change Bottle to this:
I'll leave the definition of the BottleOverflowException as an exercise to the reader.
Now notice how much more robust this is. We can deal with any type of container in our code now by accepting LiquidContainer instead of Bottle. And how these bottles deal with this sort of stuff can all differ. You can have bottles that write their state to disk when it changes, or bottles that save on SQL databases or GNU knows what else.
And all these can have different ways to handle various whoopsies. The Bottle just checks and if it's overflowing it throws a RuntimeException. But that might be the wrong thing to do.
(There is a useful discussion to be had about error handling, but I'm keeping it very simple here on purpose. People in comments will likely point out the flaws of this simplistic approach. ;) )
And yes, it seems like we go from a very simple idea to getting much better answers quickly.
Please note also that you can't change the capacity of a bottle. It's now set in stone. You could do this with an int by declaring it final. But if this was a list, you could empty it, add new things to it, and so on. You can't limit the access to touching the innards.
There's also the third thing that not everyone has addressed: getters and setters use method calls. That means that they look like normal methods everywhere else does. Instead of having weird specific syntax for DTOs and stuff, you have the same thing everywhere.
好吧,我只是想补充一点,即使有时它们对于变量/对象的封装和安全是必要的,如果我们想编写一个真正的面向对象程序,那么我们需要 停止过度使用访问器,因为有时我们在以下情况下非常依赖它们:并不是真正必要的,这几乎与我们将变量公开一样。
Well i just want to add that even if sometimes they are necessary for the encapsulation and security of your variables/objects, if we want to code a real Object Oriented Program, then we need to STOP OVERUSING THE ACCESSORS, cause sometimes we depend a lot on them when is not really necessary and that makes almost the same as if we put the variables public.
我知道现在有点晚了,但我认为有些人对表演感兴趣。
我做了一些性能测试。我编写了一个“NumberHolder”类,它保存一个整数。您可以使用 getter 方法读取该 Integer
anInstance.getNumber()
或使用anInstance.number
直接访问号码。我的程序通过两种方式读取该数字 1,000,000,000 次。该过程重复五次并打印时间。我得到以下结果:(时间 1 是直接方式,时间 2 是吸气剂)
你看,吸气剂(几乎)总是快一点。然后我尝试了不同数量的周期。我没有使用 100 万,而是使用了 1000 万和 10 万。
结果:
1000万次循环:
1000万次循环,时间几乎相同。
这里有 10 万(10 万)个循环:
同样,在不同数量的循环下,吸气剂比常规方法要快一点。我希望这对你有帮助。
I know it's a bit late, but I think there are some people who are interested in performance.
I've done a little performance test. I wrote a class "NumberHolder" which, well, holds an Integer. You can either read that Integer by using the getter method
anInstance.getNumber()
or by directly accessing the number by usinganInstance.number
. My programm reads the number 1,000,000,000 times, via both ways. That process is repeated five times and the time is printed. I've got the following result:(Time 1 is the direct way, Time 2 is the getter)
You see, the getter is (almost) always a bit faster. Then I tried with different numbers of cycles. Instead of 1 million, I used 10 million and 0.1 million.
The results:
10 million cycles:
With 10 million cycles, the times are almost the same.
Here are 100 thousand (0.1 million) cycles:
Also with different amounts of cycles, the getter is a little bit faster than the regular way. I hope this helped you.
除非当前交付需要,否则不要使用 getter setter 即不要过多考虑将来会发生什么,如果有任何事情需要更改,那么它是大多数生产应用程序、系统中的更改请求。
想得简单、容易,需要时增加复杂性。
我不会仅仅因为我认为这是正确的或者我喜欢这种方法,就利用企业主对深厚技术知识的无知。
我编写了一个庞大的系统,没有 getter setter,仅使用访问修饰符和一些方法来验证执行业务逻辑。如果你绝对需要的话。使用任何东西。
Don't use getters setters unless needed for your current delivery I.e. Don't think too much about what would happen in the future, if any thing to be changed its a change request in most of the production applications, systems.
Think simple, easy, add complexity when needed.
I would not take advantage of ignorance of business owners of deep technical know how just because I think it's correct or I like the approach.
I have massive system written without getters setters only with access modifiers and some methods to validate n perform biz logic. If you absolutely needed the. Use anything.
对于 Java 的情况,我花了相当长的时间思考这个问题,我相信真正的原因是:
换句话说,在接口中指定字段的唯一方法是提供写入新值的方法和读取当前值的方法。
这些方法就是臭名昭著的 getter 和 setter......
I spent quite a while thinking this over for the Java case, and I believe the real reasons are:
In other words, the only way you can specify a field in an interface is by providing a method for writing a new value and a method for reading the current value.
Those methods are the infamous getter and setter....
我们使用 getter 和 setter:
可重用性,getter 和 setter 方法是访问私有类成员的公共接口。
封装的口号
封装的口号是使字段私有,方法公开。
getter 和 setter 方法不会添加新功能,我们可以改变主意,稍后再回来改进该方法
任何可以使用值的地方,都可以添加返回该值的方法。而不是:
使用
通俗地说
假设我们需要存储这个
的详细信息人
。此Person
具有字段name
、age
和sex
。执行此操作涉及为name
、age
和sex
创建方法。现在,如果我们需要创建另一个人,则需要重新创建name
、age
、sex
方法。我们可以创建一个带有 getter 和 setter 方法的 bean
class(Person)
,而不是这样做。因此,明天每当我们需要添加一个新人时,我们都可以创建这个 Beanclass(Person class)
的对象(见图)。这样我们就重用了bean类的字段和方法,这样就好多了。We use getters and setters:
Getter and setter methods are public interfaces to access private class members.
Encapsulation mantra
The encapsulation mantra is to make fields private and methods public.
Even though the getter and setter methods do not add new functionality, we can change our mind come back later to make that method
Anywhere a value can be used, a method that returns that value can be added. Instead of:
use
In layman's terms
Suppose we need to store the details of this
Person
. ThisPerson
has the fieldsname
,age
andsex
. Doing this involves creating methods forname
,age
andsex
. Now if we need create another person, it becomes necessary to create the methods forname
,age
,sex
all over again.Instead of doing this, we can create a bean
class(Person)
with getter and setter methods. So tomorrow we can just create objects of this Beanclass(Person class)
whenever we need to add a new person (see the figure). Thus we are reusing the fields and methods of bean class, which is much better.它对于延迟加载很有用。假设相关对象存储在数据库中,除非需要,否则您不想获取它。如果对象是由 getter 检索的,那么内部对象可以为 null,直到有人请求它,然后您可以在第一次调用 getter 时获取它。
我在一个交给我的项目中有一个基页面类,它从几个不同的 Web 服务调用加载一些数据,但这些 Web 服务调用中的数据并不总是在所有子页面中使用。 Web 服务虽然有很多好处,但它开创了“慢”的新定义,因此如果不需要,您不会希望进行 Web 服务调用。
我从公共字段转移到 getters,现在 getters 检查缓存,如果不存在则调用 Web 服务。因此,通过一些包装,可以阻止大量 Web 服务调用。
因此,吸气剂使我无需在每个子页面上尝试找出我需要什么。如果我需要它,我会调用 getter,如果我还没有它,它就会为我找到它。
It can be useful for lazy-loading. Say the object in question is stored in a database, and you don't want to go get it unless you need it. If the object is retrieved by a getter, then the internal object can be null until somebody asks for it, then you can go get it on the first call to the getter.
I had a base page class in a project that was handed to me that was loading some data from a couple different web service calls, but the data in those web service calls wasn't always used in all child pages. Web services, for all of the benefits, pioneer new definitions of "slow", so you don't want to make a web service call if you don't have to.
I moved from public fields to getters, and now the getters check the cache, and if it's not there call the web service. So with a little wrapping, a lot of web service calls were prevented.
So the getter saves me from trying to figure out, on each child page, what I will need. If I need it, I call the getter, and it goes to find it for me if I don't already have it.
到目前为止,我在答案中错过了一个方面,即访问规范:
One aspect I missed in the answers so far, the access specification:
在不支持“属性”(C++、Java)或将字段更改为属性时需要重新编译客户端的语言(C#)中,使用 get/set 方法更容易修改。例如,向 setFoo 方法添加验证逻辑不需要更改类的公共接口。
在支持“真实”属性的语言(Python、Ruby,也许是 Smalltalk?)中,没有必要使用 get/set 方法。
In languages which don't support "properties" (C++, Java) or require recompilation of clients when changing fields to properties (C#), using get/set methods is easier to modify. For example, adding validation logic to a setFoo method will not require changing the public interface of a class.
In languages which support "real" properties (Python, Ruby, maybe Smalltalk?) there is no point to get/set methods.
OO 设计的基本原则之一:封装!
它为您带来了许多好处,其中之一是您可以在幕后更改 getter/setter 的实现,但该值的任何使用者都将继续使用只要数据类型保持不变就可以工作。
One of the basic principals of OO design: Encapsulation!
It gives you many benefits, one of which being that you can change the implementation of the getter/setter behind the scenes but any consumer of that value will continue to work as long as the data type remains the same.
您应该在以下情况下使用 getter 和 setter:
所以这很少是一个一般的面向对象问题;这是一个特定于语言的问题,不同的语言(和不同的用例)有不同的答案。
从面向对象理论的角度来看,getter 和 setter 是没有用的。类的接口是它的功能,而不是它的状态。 (如果没有,则说明您编写了错误的类。)在非常简单的情况下,类所做的只是例如表示直角坐标中的点,*属性是接口的一部分; getters 和 setters 只会让这一点变得模糊。但除了非常简单的情况外,属性、getter 和 setter 都不是接口的一部分。
换句话说:如果您认为您的类的使用者甚至不应该知道您有
spam
属性,更不用说能够随意更改它,那么给他们一个set_spam
方法是您最不想做的事情。* 即使对于这个简单的类,您也可能不一定希望允许设置
x
和y
值。如果这真的是一个类,它不应该有诸如translate
、rotate
等方法吗?如果它只是一个类,因为您的语言没有记录/结构/命名元组,那么这实际上并不是面向对象的问题......但是没有人做过一般的面向对象设计。他们用特定的语言进行设计和实现。在某些语言中,getter 和 setter 并非毫无用处。
如果您的语言没有属性,那么表示概念上是属性但实际上经过计算或验证等的唯一方法是通过 getter 和 setter。
即使您的语言确实具有属性,也可能存在不足或不合适的情况。例如,如果您希望允许子类控制属性的语义,则在没有动态访问的语言中,子类不能用计算属性替换属性。
至于“如果我以后想改变我的实现怎么办?”问题(在OP的问题和接受的答案中以不同的措辞重复多次):如果它确实是纯粹的实现更改,并且您从属性开始,则可以将其更改为属性而不影响接口。当然,除非您的语言不支持这一点。所以这实际上又是同样的情况。
此外,遵循您所使用的语言(或框架)的习惯用法也很重要。如果您用 C# 编写漂亮的 Ruby 风格代码,那么除您之外的任何有经验的 C# 开发人员都将难以阅读它,这很糟糕。有些语言比其他语言在约定方面拥有更强大的文化。Java 和 Python 在惯用的 getter 方面处于两端,但恰好拥有两种最强大的文化,这可能并非巧合。
除了人类读者之外,还会有一些库和工具希望您遵守约定,如果您不遵守约定,就会让您的生活变得更加困难。将 Interface Builder 小部件与 ObjC 属性之外的任何内容挂钩,或者使用某些没有 getter 的 Java 模拟库,只会让您的生活变得更加困难。如果这些工具对你很重要,就不要与它们对抗。
You should use getters and setters when:
So this is very rarely a general OO question; it's a language-specific question, with different answers for different languages (and different use cases).
From an OO theory point of view, getters and setters are useless. The interface of your class is what it does, not what its state is. (If not, you've written the wrong class.) In very simple cases, where what a class does is just, e.g., represent a point in rectangular coordinates,* the attributes are part of the interface; getters and setters just cloud that. But in anything but very simple cases, neither the attributes nor getters and setters are part of the interface.
Put another way: If you believe that consumers of your class shouldn't even know that you have a
spam
attribute, much less be able to change it willy-nilly, then giving them aset_spam
method is the last thing you want to do.* Even for that simple class, you may not necessarily want to allow setting the
x
andy
values. If this is really a class, shouldn't it have methods liketranslate
,rotate
, etc.? If it's only a class because your language doesn't have records/structs/named tuples, then this isn't really a question of OO…But nobody is ever doing general OO design. They're doing design, and implementation, in a specific language. And in some languages, getters and setters are far from useless.
If your language doesn't have properties, then the only way to represent something that's conceptually an attribute, but is actually computed, or validated, etc., is through getters and setters.
Even if your language does have properties, there may be cases where they're insufficient or inappropriate. For example, if you want to allow subclasses to control the semantics of an attribute, in languages without dynamic access, a subclass can't substitute a computed property for an attribute.
As for the "what if I want to change my implementation later?" question (which is repeated multiple times in different wording in both the OP's question and the accepted answer): If it really is a pure implementation change, and you started with an attribute, you can change it to a property without affecting the interface. Unless, of course, your language doesn't support that. So this is really just the same case again.
Also, it's important to follow the idioms of the language (or framework) you're using. If you write beautiful Ruby-style code in C#, any experienced C# developer other than you is going to have trouble reading it, and that's bad. Some languages have stronger cultures around their conventions than others.—and it may not be a coincidence that Java and Python, which are on opposite ends of the spectrum for how idiomatic getters are, happen to have two of the strongest cultures.
Beyond human readers, there will be libraries and tools that expect you to follow the conventions, and make your life harder if you don't. Hooking Interface Builder widgets to anything but ObjC properties, or using certain Java mocking libraries without getters, is just making your life more difficult. If the tools are important to you, don't fight them.
从面向对象设计的角度来看,这两种选择都可能削弱类的封装性,从而损害代码的维护。如需讨论,您可以查看这篇优秀的文章:http://tropicalprogrammer.com/?p=23
From a object orientation design standpoint both alternatives can be damaging to the maintenance of the code by weakening the encapsulation of the classes. For a discussion you can look into this excellent article: http://typicalprogrammer.com/?p=23
代码不断发展。
private
非常适合您需要数据成员保护。最终,所有的类都应该是某种“小程序”,它们有一个定义良好的接口你不能简单地修改其内部。也就是说,软件开发并不是要设定类的最终版本,就好像您在第一次尝试时按下某个铸铁雕像一样。当您使用它时,代码更像是粘土。 随着您开发它并了解有关您正在解决的问题领域的更多信息,它会不断发展。在开发过程中,类之间可能会发生超出应有的交互(您计划排除的依赖关系)、合并在一起或分开。所以我认为争论归结为人们不想虔诚地写
所以你有:
而不是
不仅是
getVar()
视觉上嘈杂,它给人一种错觉gettingVar()
在某种程度上是一个比实际情况更复杂的过程。如果您的类有一个 passthru setter,那么您(作为类编写者)如何看待 var 的神圣性对于您的类的用户来说尤其令人困惑 - 那么看起来您正在将这些门设置为“保护”您坚持认为有价值的东西(var
的神圣性),但即使您承认var
的保护对于任何人来说都没有多大价值in 和set
var
为他们想要的任何值,而你甚至不需要偷看他们在做什么。因此,我的编程如下(假设采用“敏捷”类型的方法 - 即,当我编写代码时不知道确切它将做什么/没有时间或经验来规划精心设计的瀑布式界面set):
1) 从具有数据和行为的基本对象的所有公共成员开始。这就是为什么在我的所有 C++“示例”代码中,您会注意到我到处都使用
struct
而不是class
。2) 当一个对象的数据成员的内部行为变得足够复杂时(例如,它喜欢以某种顺序保留内部
std::list
),就会编写访问器类型函数。因为我自己编程,所以我并不总是立即设置成员private
,但在类的演变过程中,该成员将被“提升”为protected或<代码>私有。
3)给出了完全充实的类,并且对其内部有严格的规则(即它们确切地知道自己在做什么,并且你不应该用它的内部来“操”(技术术语))
class
名称、默认私有成员以及仅允许选择的少数成员为public
。我发现,在类演化的早期阶段,当大量数据成员被迁移、移动等时,这种方法可以让我避免坐在那儿认真地编写 getter/setter。
Code evolves.
private
is great for when you need data member protection. Eventually all classes should be sort of "miniprograms" that have a well-defined interface that you can't just screw with the internals of.That said, software development isn't about setting down that final version of the class as if you're pressing some cast iron statue on the first try. While you're working with it, code is more like clay. It evolves as you develop it and learn more about the problem domain you are solving. During development classes may interact with each other than they should (dependency you plan to factor out), merge together, or split apart. So I think the debate boils down to people not wanting to religiously write
So you have:
Instead of
Not only is
getVar()
visually noisy, it gives this illusion thatgettingVar()
is somehow a more complex process than it really is. How you (as the class writer) regard the sanctity ofvar
is particularly confusing to a user of your class if it has a passthru setter -- then it looks like you're putting up these gates to "protect" something you insist is valuable, (the sanctity ofvar
) but yet even you concedevar
's protection isn't worth much by the ability for anyone to just come in andset
var
to whatever value they want, without you even peeking at what they are doing.So I program as follows (assuming an "agile" type approach -- ie when I write code not knowing exactly what it will be doing/don't have time or experience to plan an elaborate waterfall style interface set):
1) Start with all public members for basic objects with data and behavior. This is why in all my C++ "example" code you'll notice me using
struct
instead ofclass
everywhere.2) When an object's internal behavior for a data member becomes complex enough, (for example, it likes to keep an internal
std::list
in some kind of order), accessor type functions are written. Because I'm programming by myself, I don't always set the memberprivate
right away, but somewhere down the evolution of the class the member will be "promoted" to eitherprotected
orprivate
.3) Classes that are fully fleshed out and have strict rules about their internals (ie they know exactly what they are doing, and you are not to "fuck" (technical term) with its internals) are given the
class
designation, default private members, and only a select few members are allowed to bepublic
.I find this approach allows me to avoid sitting there and religiously writing getter/setters when a lot of data members get migrated out, shifted around, etc. during the early stages of a class's evolution.
考虑使用访问器的一个充分理由是没有属性继承。请参阅下一个示例:
输出:
There is a good reason to consider using accessors is there is no property inheritance. See next example:
Output:
Getters 和 setters 用于实现面向对象编程的两个基本方面,它们是:
假设我们有一个 Employee 类:
这里 Full Name 的实现细节是与公共属性不同,对用户隐藏并且用户无法直接访问。
Getters and setters are used to implement two of the fundamental aspects of Object Oriented Programming which are:
Suppose we have an Employee class:
Here the implementation details of Full Name is hidden from the user and is not accessible directly to the user, unlike a public attribute.
数据结构和对象之间存在差异。
数据结构应该暴露其内部结构而不是行为。
对象不应该暴露其内部结构,但应该暴露其行为,这也称为得墨忒耳法则
大多数 DTO 被认为更多是一种数据结构而不是对象。他们应该只暴露他们的数据而不是行为。在 DataStructure 中使用 Setter/Getter 将暴露行为而不是其中的数据。这进一步增加了违反德墨忒尔法则的机会。
鲍勃叔叔在他的《干净的代码》一书中解释了德米特法则。
所以根据这个,LoD违规的例子是:
这里,函数应该调用它的直接朋友的方法,这里是ctxt,它不应该调用它直接朋友的朋友的方法。但这条规则不适用于数据结构。所以这里如果 ctxt、option、scratchDir 是数据结构,那么为什么要用某些行为来包装它们的内部数据并违反 LoD。
相反,我们可以做这样的事情。
这满足了我们的需求,甚至不违反 LoD。
受到 Robert C. Martin(鲍勃叔叔)的《干净代码》的启发
There is a difference between DataStructure and Object.
Datastructure should expose its innards and not behavior.
An Object should not expose its innards but it should expose its behavior, which is also known as the Law of Demeter
Mostly DTOs are considered more of a datastructure and not Object. They should only expose their data and not behavior. Having Setter/Getter in DataStructure will expose behavior instead of data inside it. This further increases the chance of violation of Law of Demeter.
Uncle Bob in his book Clean code explained the Law of Demeter.
So according this, example of LoD violation is:
Here, the function should call the method of its immediate friend which is ctxt here, It should not call the method of its immediate friend's friend. but this rule doesn't apply to data structure. so here if ctxt, option, scratchDir are datastructure then why to wrap their internal data with some behavior and doing a violation of LoD.
Instead, we can do something like this.
This fulfills our needs and doesn't even violate LoD.
Inspired by Clean Code by Robert C. Martin(Uncle Bob)
如果您不需要任何验证,甚至不需要维护状态,即一个属性依赖于另一个属性,那么我们需要在一个属性发生更改时维护状态。您可以通过公开字段而不使用 getter 和 setter 来保持简单。
我认为,随着程序的增长,面向对象编程会让事情变得复杂,这对开发人员来说扩展成为噩梦。
一个简单的例子;我们从 xml 生成 C++ 标头。标头包含不需要任何验证的简单字段。但仍然像 OOPS 访问器一样,我们按如下方式生成它们。
这是非常冗长的并且不是必需的。一个简单的
就足够了并且可读。
函数式编程没有数据隐藏的概念,它们甚至不需要它,因为它们不会改变数据。
If you don't require any validations and not even need to maintain state i.e. one property depends on another so we need to maintain the state when one is change. You can keep it simple by making field public and not using getter and setters.
I think OOPs complicates things as the program grows it becomes nightmare for developer to scale.
A simple example; we generate c++ headers from xml. The header contains simple field which does not require any validations. But still as in OOPS accessor are fashion we generates them as following.
which is very verbose and is not required. a simple
is enough and readable.
Functional programming don't have the concept of data hiding they even don't require it as they do not mutate the data.
此外,这是为了让你的班级“面向未来”。特别是,从字段更改为属性是 ABI 破坏,因此如果您后来决定需要更多逻辑而不仅仅是“设置/获取字段”,那么您需要破坏 ABI,这当然会给任何事情带来问题else 已经针对您的班级进行了编译。
Additionally, this is to "future-proof" your class. In particular, changing from a field to a property is an ABI break, so if you do later decide that you need more logic than just "set/get the field", then you need to break ABI, which of course creates problems for anything else already compiled against your class.
另一种用途(在支持属性的语言中)是 setter 和 getter 可以暗示操作是不平凡的。通常,您希望避免在属性中执行任何计算成本较高的操作。
One other use (in languages that support properties) is that setters and getters can imply that an operation is non-trivial. Typically, you want to avoid doing anything that's computationally expensive in a property.
getter/setter 的一个相对现代的优点是可以更轻松地在标记(索引)代码编辑器中浏览代码。例如,如果您想查看谁设置了成员,您可以打开设置器的调用层次结构。
另一方面,如果成员是公共成员,则这些工具无法过滤对该成员的读/写访问权限。因此,您必须费力地使用该成员的所有用途。
One relatively modern advantage of getters/setters is that is makes it easier to browse code in tagged (indexed) code editors. E.g. If you want to see who sets a member, you can open the call hierarchy of the setter.
On the other hand, if the member is public, the tools don't make it possible to filter read/write access to the member. So you have to trudge though all uses of the member.
举个例子:
如果您创建一个公共变量,您可以访问该变量并在任何地方(任何类)更改值。但是,如果您创建为私有变量,则除了声明的类之外,该变量无法在任何类中查看/访问。
那么我们如何才能在外部访问该变量:
这就是 getters 和 setters 的来源。您可以将变量声明为私有,然后您可以为该变量实现 getter 和 setter。
示例(Java):
优点:
当任何人想要访问或更改/设置
balance
变量的值时,他/她必须拥有许可。您也可以在构造函数中设置值,但稍后当您需要时
要更新/更改值,您必须实现 setter 方法。
As a example:
If you create a public variable, you can access that variable and change value in anywhere(any class). But if you create as private that variable cannot see/access in any class except declared class.
So how can we access that variable outside:
This is the place getters and setters coming from. You can declare variable as private then you can implement getter and setter for that variable.
Example(Java):
Advantage:
When anyone want to access or change/set value to
balance
variable, he/she must have permision.You can set value in constructor also but when later on when you want
to update/change value, you have to implement setter method.