封装——既然setter已经是公共的,为什么还需要它呢?(复制)

时间:2021-02-28 22:25:01

This question already has an answer here:

这个问题已经有了答案:

Encapsulation is hiding the data. I would like to hear some really interesting answers here.

封装就是隐藏数据。我想在这里听到一些非常有趣的答案。

What is the point behind keeping variables as private when we already declare public setter methods for variables.

当我们已经为变量声明公共setter方法时,保持变量为私有的原因是什么?

I understand the usage of encapsulation but when we are making the setters as public what is the point behind keeping the variables as private, we can directly use public access modifiers

我理解封装的用法,但是当我们使setter成为公有时,保持变量为私有的原因是什么,我们可以直接使用公共访问修饰符

Is it because we do not want others to know the exact way we are storing data or managing data on the back-end?

是因为我们不希望别人知道我们在后端存储数据或管理数据的确切方式吗?

9 个解决方案

#1


30  

Is it because we do not want others to know the exact way we are storing data or managing data on the back-end?

是因为我们不希望别人知道我们在后端存储数据或管理数据的确切方式吗?

Yes, that's the point. It is related to the concepts of abstraction and information hiding too.

是的,这就是重点。它也与抽象和信息隐藏的概念有关。

You provide a public setter that when invoked by the class client will have the effect that you have documented. It is none of the client's business how this effect is actually achieved. Are you modifying one of the class attributes? Ok, let the client know that, but not the fact that you are actually modifying a variable. In the future, you could want to modify your class so that instead of a simple backup variable it uses something completely different (a dictionary of attributes? An external service? Whatever!) and the client will not break.

您提供了一个公共setter,当类客户端调用时,它将具有您所记录的效果。这个效果是如何实现的,与客户无关。您正在修改一个类属性吗?好的,让客户知道这个,但不要告诉他你实际上在修改一个变量。将来,您可能想要修改类,以便它使用完全不同的东西(属性字典?外部服务吗?无论如何!)这样客户就不会崩溃。

So your setter is an abstraction that you provide to the client for "modify this class attribute". At the same time you are hiding the fact that you are using an internal variable because the client doesn't need to know that fact.

你的setter是一个抽象,你为客户端提供“修改这个类属性”。与此同时,您隐藏了使用内部变量的事实,因为客户不需要知道这个事实。

(Note: here I'm using the word "attribute" as a generic concept, not related to any concrete programming language)

(注意:这里我使用“属性”这个词作为通用概念,与任何具体的编程语言都没有关系)

#2


23  

I fully agree with Konamiman's answer, but I'd like to add one thing:

我完全同意科纳曼的回答,但我想补充一点:

There are cases where you really don't want that abstraction. And that's fine.

在某些情况下,您确实不希望抽象。这很好。

A simple example I like to use here is a class for a 3-dimensional float vector:

我喜欢在这里使用的一个简单的例子是一个三维浮点向量的类:

class Vector3f {
public:
    float x;
    float y;
    float z;
};

Could you make those fields private and provide setters instead? Sure, you could. But here you might argue that the class is really just supposed to provide a tuple of floats and you don't want any additional functionality. Thus adding setters would only complicate the class and you'd rather leave the fields public.

能否将这些字段设置为private并提供setter ?当然,你可以。但这里你可能会说,这个类实际上只是提供了一个浮点数,而你不需要任何额外的功能。因此,添加setter只会使类复杂化,您宁愿将字段保留为公有。

Now, you can easily construct scenarios where that might bite you later on. For instance, you might one day get a requirement that Vector3fs are not allowed to store NaNs and should throw an exception if anyone tries to do so. But such a hypothetical future problem should not be enough to justify introducing additional abstractions.

现在,您可以很容易地构建一些场景,这些场景稍后可能会对您产生影响。例如,您可能有一天会得到一个要求,即Vector3fs不允许存储NaNs,如果有人试图存储NaNs,那么应该抛出一个异常。但这种假设的未来问题不应足以证明引入额外的抽象。

It's your call as a programmer to decide which abstractions make sense for the problem at hand and which ones would only get in your way of getting the job done. Unnecessary abstractions are over-engineering and will hurt your productivity just as much as not abstracting enough.

作为一名程序员,你需要决定哪些抽象是对手边的问题有意义的,哪些是只会妨碍你完成工作的。不必要的抽象是过度工程化的,会损害您的生产力,就像抽象不够一样。

Bottom line: Don't blindly use setters everywhere just because someone claimed that's good practice. Instead, think about the problem at hand and consider the tradeoffs.

底线:不要盲目地到处使用setter,仅仅因为某人声称这是一个很好的实践。相反,考虑一下手头的问题,考虑一下权衡。

#3


11  

Because by encapsulation we provide single point of access. Suppose you define a variable and its setter as follows

因为通过封装,我们提供了一个访问点。假设您定义一个变量及其setter如下所示

String username; 

public void setUsername(String username){
this.username = username;
}

Later you like to add some validation before setting username property. If you are setting the username at 10 places by directly accessing the property then you don't have single point of access and you need to make this change at 10 places. But if you have one setter method then by making a change at one place you can easily achieve the result.

稍后,您希望在设置username属性之前添加一些验证。如果你通过直接访问属性将用户名设置在10个位置那么你就没有一个访问点你需要在10个位置进行修改。但是如果您有一个setter方法,那么通过在一个地方进行更改,您可以轻松地实现结果。

#4


5  

Although Konamiman's answer is spot on, I'd like to add that, in the particular case of public setters versus directly exposing public fields you are asking, there is another very important distinction to keep in mind apart from information hiding and decoupling implementation from the public surface, or API, of a class; validation.

虽然Konamiman的回答是现货,我想补充的是,在特定情况下的公共盘与直接公开公共字段你问,要记住还有一个非常重要的区别除了信息隐藏和分离实现公众的表面,或API,类的;验证。

In a public field scenario, there is no way to validate the field's value when it's modified. In case of a public setter (be it a Foo {get; set;} property or a SetFoo(Foo value)) method you have the possibility to add validation code and launch required side-effects and this way ensure that your class is always in a valid or predictable state.

在公共字段场景中,当字段被修改时,没有办法验证它的值。对于公共setter(无论是Foo {get;set;}属性或SetFoo(Foo值)方法可以添加验证代码并启动所需的副作用,这样可以确保类始终处于有效的或可预测的状态。

#5


4  

Think about this : I'm representing a real life object, a Lion through a class. I'd do something like this.

想想这个:我代表的是一个真实存在的对象,一个类中的狮子。我会做这样的事情。

class Lion {
    public int legs;
} 

Now my class is needed by some other developer to create an object and set its legs field. He'd do something like this

现在其他开发人员需要我的类来创建一个对象并设置它的legs字段。他会做这样的事

Lion jungleKing = new Lion();
jungleKing.legs=15;

Now the question is, Java won't restrict him to setting any number more than 4 as the number of legs for that object. It's not an error, and it'll run just fine. But it's a logical blunder, and the compiler won't help you there. This way a Lion may have any number of legs. But if we write the code this way

现在的问题是,Java不会限制他将任何超过4的数字设置为该对象的分支数。这不是一个错误,它会运行得很好。但是这是一个逻辑上的错误,编译器不会帮你。这样,狮子可以有任意数目的腿。但是如果我们这样写代码

class Lion {
    private int legs;
    public void setLegs(int legs){
        if(legs>4)
            this.legs=4;
        else this.legs=legs;
    } 
} 

Now you won't have any Lion with more than 4 legs because the policy of updating the fields of the class has been defined by the class itself and there's no way anyone not knowing the policy is going to update the legs field because the only way to update the legs field is through the setLegs() method and that method knows the policy of the class.

现在你不会有任何狮子有超过4条腿,因为更新的政策领域的类已经定义的类本身,没有任何人不知道政策将更新腿领域因为腿更新字段的唯一途径是通过setLegs()方法,这个方法知道类的政策。

#6


3  

What if you do want to a range check before assignment? That's one of the cases I use setters and getters

如果你真的想在分配前进行范围检查呢?这是我使用setter和getter的一种情况

#7


2  

More or less simple and realistic example I encountered in practice is an Options class, which has a lot of setters and getters. At some point you might want to add new option which depends on others or has side effects. Or even replace group of options with Enum. In this case setA function will not just modify a field, but will hide some additional configuration logic. Similarly getA will not just return value of a, but something like config == cStuffSupportingA.

我在实践中遇到的或多或少的简单和现实的例子是一个选项类,它有很多setter和getter。在某种程度上,你可能想要添加新的选项,这取决于其他人或者有副作用。甚至用Enum替换一组选项。在这种情况下,setA函数不仅会修改一个字段,还会隐藏一些额外的配置逻辑。类似地,getA不仅返回a的值,还返回config == cStuffSupportingA之类的值。

#8


2  

Wikipedia has a good overview of [mutator methods(https://en.wikipedia.org/wiki/Mutator_method), which is what setter methods are and how they work in different languages.

Wikipedia对[mutator方法(https://en.wikipedia.org/wiki/Mutator_method)有一个很好的概述,它是什么setter方法以及它们如何在不同的语言中工作。

The short version: if you want to introduce validation or other logic that gets executed on object modification it is nice to have a setter to put that logic in. Also you may want to hide how you store things. So, those are reasons for having getters/setters. Similarly, for getters, you might have logic that provides default values or values that are dependent on e.g. configuration for things like Locale, character encoding, etc. There are lots of valid reasons to want to have logic other than getting or setting the instance variable.

简短的版本:如果您想引入在对象修改时执行的验证或其他逻辑,最好有一个setter来放入该逻辑。你也可以隐藏你如何储存东西。所以,这些就是有getter /setter的原因。类似地,对于getter,您可能具有提供默认值或值的逻辑,这些值依赖于诸如Locale、字符编码等的配置。

Obviously, if you have getters and setteres, you don't want people bypassing them by manipulating the object state directly, which is why you should keep instance variables private.

显然,如果您有getter和setteres,您不希望人们通过直接操作对象状态绕过它们,这就是为什么您应该保持实例变量为private的原因。

Other things to consider include whether you actually want your objects to be mutable at all (if not, make fields final), whether you want to make modifying the object state threadsafe with e.g. locks, synchronized, etc.

其他需要考虑的事项包括:您是否真正希望您的对象是可变的(如果不是,则将字段设置为final),您是否希望使用例如lock、synchronized等来修改对象状态threadsafe。

#9


2  

Setting fields as private documents a powerful fact: these private fields are only directly used within the current class. This helps maintainers by not having to track down field usage. They can reason better on the code by looking at the class and determining that the effects on and from these fields with the class' environment go through public and protected method calls. It limits the exposure surface on the class.

将字段设置为私有文档是一个很重要的事实:这些私有字段只在当前类中直接使用。这有助于维护人员不必跟踪字段使用情况。通过查看类并确定这些字段对类环境的影响通过公共和受保护的方法调用,可以更好地对代码进行推理。它限制了类上的暴露面。

In turn, defining a "setter" for a private field is not about giving it publicity again. It is about declaring another powerful fact: an object belonging to this class has a property that can be modified from the outside. (The terms object and property are used in the sense of a bounded part of the whole and an observable fact about this part, not in the OOP sense)

反过来,为私有字段定义“setter”并不是要再次进行公开。它是关于声明另一个强大的事实:属于这个类的对象具有可以从外部修改的属性。(对象和属性这两个词是用在整体有界部分的意义上的,而不是用在OOP的意义上

Why then declare a "setter" on a field when making the field public would suffice? Because declaring a field not only binds a name to a property of the objects of the class, but also commits to use memory storage for this property.

那么为什么要在字段公开时声明一个字段的“setter”就足够了呢?因为声明一个字段不仅将一个名称绑定到类对象的属性,而且还承诺使用该属性的内存存储。

Therefore, if you declare a "private field with a setter", you declare three things:

因此,如果您声明一个“带有setter的私有字段”,则声明以下三件事:

  • You declare that the name you gave to the field/setter cluster represents a property of the object which is of interest when the object is seen as a black box.
  • 您声明给字段/setter集群的名称表示对象的属性,当对象被视为黑框时,该属性是相关的。
  • You declare that the value of this property is modifiable by the environment of the object.
  • 您声明该属性的值可以被对象的环境修改。
  • You declare that in this particular concrete class, the property of the object is realized by committing some memory storage to it.
  • 您声明在这个特定的具体类中,对象的属性是通过提交一些内存存储来实现的。

I advocate that you never make your fields private with getters and setters indiscriminately. Fields are for describing storage. Methods are for interactions with the environment. (And the particular case of "getters" and "setters" are for describing properties of interest)

我主张,你绝不能不加区分地将你的领域与getters和setters放在一起。字段用于描述存储。方法用于与环境的交互。(“getter”和“setter”用于描述所感兴趣的属性)

#1


30  

Is it because we do not want others to know the exact way we are storing data or managing data on the back-end?

是因为我们不希望别人知道我们在后端存储数据或管理数据的确切方式吗?

Yes, that's the point. It is related to the concepts of abstraction and information hiding too.

是的,这就是重点。它也与抽象和信息隐藏的概念有关。

You provide a public setter that when invoked by the class client will have the effect that you have documented. It is none of the client's business how this effect is actually achieved. Are you modifying one of the class attributes? Ok, let the client know that, but not the fact that you are actually modifying a variable. In the future, you could want to modify your class so that instead of a simple backup variable it uses something completely different (a dictionary of attributes? An external service? Whatever!) and the client will not break.

您提供了一个公共setter,当类客户端调用时,它将具有您所记录的效果。这个效果是如何实现的,与客户无关。您正在修改一个类属性吗?好的,让客户知道这个,但不要告诉他你实际上在修改一个变量。将来,您可能想要修改类,以便它使用完全不同的东西(属性字典?外部服务吗?无论如何!)这样客户就不会崩溃。

So your setter is an abstraction that you provide to the client for "modify this class attribute". At the same time you are hiding the fact that you are using an internal variable because the client doesn't need to know that fact.

你的setter是一个抽象,你为客户端提供“修改这个类属性”。与此同时,您隐藏了使用内部变量的事实,因为客户不需要知道这个事实。

(Note: here I'm using the word "attribute" as a generic concept, not related to any concrete programming language)

(注意:这里我使用“属性”这个词作为通用概念,与任何具体的编程语言都没有关系)

#2


23  

I fully agree with Konamiman's answer, but I'd like to add one thing:

我完全同意科纳曼的回答,但我想补充一点:

There are cases where you really don't want that abstraction. And that's fine.

在某些情况下,您确实不希望抽象。这很好。

A simple example I like to use here is a class for a 3-dimensional float vector:

我喜欢在这里使用的一个简单的例子是一个三维浮点向量的类:

class Vector3f {
public:
    float x;
    float y;
    float z;
};

Could you make those fields private and provide setters instead? Sure, you could. But here you might argue that the class is really just supposed to provide a tuple of floats and you don't want any additional functionality. Thus adding setters would only complicate the class and you'd rather leave the fields public.

能否将这些字段设置为private并提供setter ?当然,你可以。但这里你可能会说,这个类实际上只是提供了一个浮点数,而你不需要任何额外的功能。因此,添加setter只会使类复杂化,您宁愿将字段保留为公有。

Now, you can easily construct scenarios where that might bite you later on. For instance, you might one day get a requirement that Vector3fs are not allowed to store NaNs and should throw an exception if anyone tries to do so. But such a hypothetical future problem should not be enough to justify introducing additional abstractions.

现在,您可以很容易地构建一些场景,这些场景稍后可能会对您产生影响。例如,您可能有一天会得到一个要求,即Vector3fs不允许存储NaNs,如果有人试图存储NaNs,那么应该抛出一个异常。但这种假设的未来问题不应足以证明引入额外的抽象。

It's your call as a programmer to decide which abstractions make sense for the problem at hand and which ones would only get in your way of getting the job done. Unnecessary abstractions are over-engineering and will hurt your productivity just as much as not abstracting enough.

作为一名程序员,你需要决定哪些抽象是对手边的问题有意义的,哪些是只会妨碍你完成工作的。不必要的抽象是过度工程化的,会损害您的生产力,就像抽象不够一样。

Bottom line: Don't blindly use setters everywhere just because someone claimed that's good practice. Instead, think about the problem at hand and consider the tradeoffs.

底线:不要盲目地到处使用setter,仅仅因为某人声称这是一个很好的实践。相反,考虑一下手头的问题,考虑一下权衡。

#3


11  

Because by encapsulation we provide single point of access. Suppose you define a variable and its setter as follows

因为通过封装,我们提供了一个访问点。假设您定义一个变量及其setter如下所示

String username; 

public void setUsername(String username){
this.username = username;
}

Later you like to add some validation before setting username property. If you are setting the username at 10 places by directly accessing the property then you don't have single point of access and you need to make this change at 10 places. But if you have one setter method then by making a change at one place you can easily achieve the result.

稍后,您希望在设置username属性之前添加一些验证。如果你通过直接访问属性将用户名设置在10个位置那么你就没有一个访问点你需要在10个位置进行修改。但是如果您有一个setter方法,那么通过在一个地方进行更改,您可以轻松地实现结果。

#4


5  

Although Konamiman's answer is spot on, I'd like to add that, in the particular case of public setters versus directly exposing public fields you are asking, there is another very important distinction to keep in mind apart from information hiding and decoupling implementation from the public surface, or API, of a class; validation.

虽然Konamiman的回答是现货,我想补充的是,在特定情况下的公共盘与直接公开公共字段你问,要记住还有一个非常重要的区别除了信息隐藏和分离实现公众的表面,或API,类的;验证。

In a public field scenario, there is no way to validate the field's value when it's modified. In case of a public setter (be it a Foo {get; set;} property or a SetFoo(Foo value)) method you have the possibility to add validation code and launch required side-effects and this way ensure that your class is always in a valid or predictable state.

在公共字段场景中,当字段被修改时,没有办法验证它的值。对于公共setter(无论是Foo {get;set;}属性或SetFoo(Foo值)方法可以添加验证代码并启动所需的副作用,这样可以确保类始终处于有效的或可预测的状态。

#5


4  

Think about this : I'm representing a real life object, a Lion through a class. I'd do something like this.

想想这个:我代表的是一个真实存在的对象,一个类中的狮子。我会做这样的事情。

class Lion {
    public int legs;
} 

Now my class is needed by some other developer to create an object and set its legs field. He'd do something like this

现在其他开发人员需要我的类来创建一个对象并设置它的legs字段。他会做这样的事

Lion jungleKing = new Lion();
jungleKing.legs=15;

Now the question is, Java won't restrict him to setting any number more than 4 as the number of legs for that object. It's not an error, and it'll run just fine. But it's a logical blunder, and the compiler won't help you there. This way a Lion may have any number of legs. But if we write the code this way

现在的问题是,Java不会限制他将任何超过4的数字设置为该对象的分支数。这不是一个错误,它会运行得很好。但是这是一个逻辑上的错误,编译器不会帮你。这样,狮子可以有任意数目的腿。但是如果我们这样写代码

class Lion {
    private int legs;
    public void setLegs(int legs){
        if(legs>4)
            this.legs=4;
        else this.legs=legs;
    } 
} 

Now you won't have any Lion with more than 4 legs because the policy of updating the fields of the class has been defined by the class itself and there's no way anyone not knowing the policy is going to update the legs field because the only way to update the legs field is through the setLegs() method and that method knows the policy of the class.

现在你不会有任何狮子有超过4条腿,因为更新的政策领域的类已经定义的类本身,没有任何人不知道政策将更新腿领域因为腿更新字段的唯一途径是通过setLegs()方法,这个方法知道类的政策。

#6


3  

What if you do want to a range check before assignment? That's one of the cases I use setters and getters

如果你真的想在分配前进行范围检查呢?这是我使用setter和getter的一种情况

#7


2  

More or less simple and realistic example I encountered in practice is an Options class, which has a lot of setters and getters. At some point you might want to add new option which depends on others or has side effects. Or even replace group of options with Enum. In this case setA function will not just modify a field, but will hide some additional configuration logic. Similarly getA will not just return value of a, but something like config == cStuffSupportingA.

我在实践中遇到的或多或少的简单和现实的例子是一个选项类,它有很多setter和getter。在某种程度上,你可能想要添加新的选项,这取决于其他人或者有副作用。甚至用Enum替换一组选项。在这种情况下,setA函数不仅会修改一个字段,还会隐藏一些额外的配置逻辑。类似地,getA不仅返回a的值,还返回config == cStuffSupportingA之类的值。

#8


2  

Wikipedia has a good overview of [mutator methods(https://en.wikipedia.org/wiki/Mutator_method), which is what setter methods are and how they work in different languages.

Wikipedia对[mutator方法(https://en.wikipedia.org/wiki/Mutator_method)有一个很好的概述,它是什么setter方法以及它们如何在不同的语言中工作。

The short version: if you want to introduce validation or other logic that gets executed on object modification it is nice to have a setter to put that logic in. Also you may want to hide how you store things. So, those are reasons for having getters/setters. Similarly, for getters, you might have logic that provides default values or values that are dependent on e.g. configuration for things like Locale, character encoding, etc. There are lots of valid reasons to want to have logic other than getting or setting the instance variable.

简短的版本:如果您想引入在对象修改时执行的验证或其他逻辑,最好有一个setter来放入该逻辑。你也可以隐藏你如何储存东西。所以,这些就是有getter /setter的原因。类似地,对于getter,您可能具有提供默认值或值的逻辑,这些值依赖于诸如Locale、字符编码等的配置。

Obviously, if you have getters and setteres, you don't want people bypassing them by manipulating the object state directly, which is why you should keep instance variables private.

显然,如果您有getter和setteres,您不希望人们通过直接操作对象状态绕过它们,这就是为什么您应该保持实例变量为private的原因。

Other things to consider include whether you actually want your objects to be mutable at all (if not, make fields final), whether you want to make modifying the object state threadsafe with e.g. locks, synchronized, etc.

其他需要考虑的事项包括:您是否真正希望您的对象是可变的(如果不是,则将字段设置为final),您是否希望使用例如lock、synchronized等来修改对象状态threadsafe。

#9


2  

Setting fields as private documents a powerful fact: these private fields are only directly used within the current class. This helps maintainers by not having to track down field usage. They can reason better on the code by looking at the class and determining that the effects on and from these fields with the class' environment go through public and protected method calls. It limits the exposure surface on the class.

将字段设置为私有文档是一个很重要的事实:这些私有字段只在当前类中直接使用。这有助于维护人员不必跟踪字段使用情况。通过查看类并确定这些字段对类环境的影响通过公共和受保护的方法调用,可以更好地对代码进行推理。它限制了类上的暴露面。

In turn, defining a "setter" for a private field is not about giving it publicity again. It is about declaring another powerful fact: an object belonging to this class has a property that can be modified from the outside. (The terms object and property are used in the sense of a bounded part of the whole and an observable fact about this part, not in the OOP sense)

反过来,为私有字段定义“setter”并不是要再次进行公开。它是关于声明另一个强大的事实:属于这个类的对象具有可以从外部修改的属性。(对象和属性这两个词是用在整体有界部分的意义上的,而不是用在OOP的意义上

Why then declare a "setter" on a field when making the field public would suffice? Because declaring a field not only binds a name to a property of the objects of the class, but also commits to use memory storage for this property.

那么为什么要在字段公开时声明一个字段的“setter”就足够了呢?因为声明一个字段不仅将一个名称绑定到类对象的属性,而且还承诺使用该属性的内存存储。

Therefore, if you declare a "private field with a setter", you declare three things:

因此,如果您声明一个“带有setter的私有字段”,则声明以下三件事:

  • You declare that the name you gave to the field/setter cluster represents a property of the object which is of interest when the object is seen as a black box.
  • 您声明给字段/setter集群的名称表示对象的属性,当对象被视为黑框时,该属性是相关的。
  • You declare that the value of this property is modifiable by the environment of the object.
  • 您声明该属性的值可以被对象的环境修改。
  • You declare that in this particular concrete class, the property of the object is realized by committing some memory storage to it.
  • 您声明在这个特定的具体类中,对象的属性是通过提交一些内存存储来实现的。

I advocate that you never make your fields private with getters and setters indiscriminately. Fields are for describing storage. Methods are for interactions with the environment. (And the particular case of "getters" and "setters" are for describing properties of interest)

我主张,你绝不能不加区分地将你的领域与getters和setters放在一起。字段用于描述存储。方法用于与环境的交互。(“getter”和“setter”用于描述所感兴趣的属性)