使用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; }

而前者需要的样板代码要少得多。


当前回答

Getter和setter用于实现面向对象编程的两个基本方面,即:

抽象封装

假设我们有一个Employee类:

package com.highmark.productConfig.types;

public class Employee {

    private String firstName;
    private String middleName;
    private String lastName;

    public String getFirstName() {
      return firstName;
    }
    public void setFirstName(String firstName) {
       this.firstName = firstName;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
         this.middleName = middleName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName(){
        return this.getFirstName() + this.getMiddleName() +  this.getLastName();
    }
 }

与公共属性不同,全名的实现细节对用户隐藏,用户无法直接访问。

其他回答

getter/setter的一个相对现代的优点是,它使在标记(索引)代码编辑器中浏览代码变得更容易。例如,如果您想查看谁设置了成员,可以打开setter的调用层次结构。

另一方面,如果成员是公共的,则这些工具无法过滤对该成员的读/写访问。因此,您必须费力地使用该成员。

我花了很长时间来思考Java案例,我相信真正的原因是:

接口的代码,而不是实现接口只指定方法,不指定字段

换句话说,在接口中指定字段的唯一方法是提供一个用于写入新值的方法和一个用于读取当前值的方法。

这些方法是臭名昭著的getter和setter。。。。

它可以用于延迟加载。假设所讨论的对象存储在数据库中,除非您需要,否则您不想获取它。如果该对象由getter检索,则内部对象可以为空,直到有人请求它,然后您可以在第一次调用getter时获取它。

我在交给我的一个项目中有一个基本页面类,它从几个不同的web服务调用加载一些数据,但这些web服务调用中的数据并不总是用于所有子页面。Web服务具有所有的优点,它开创了“慢”的新定义,因此如果不需要,您不想进行Web服务调用。

我从公共字段转到getter,现在getter检查缓存,如果缓存不存在,则调用web服务。因此,通过一点包装,许多web服务调用被阻止了。

因此,getter使我不必在每个子页面上找出我需要什么。如果我需要它,我打电话给吸气器,如果我还没有,它会帮我找到它。

    protected YourType _yourName = null;
    public YourType YourName{
      get
      {
        if (_yourName == null)
        {
          _yourName = new YourType();
          return _yourName;
        }
      }
    }

在以下情况下,应使用getter和setter:

您处理的是概念上属于属性的东西,但是:您的语言没有财产(或类似的机制,如Tcl的变量跟踪),或您的语言的属性支持不足以满足此用例,或者您的语言(或有时您的框架)惯用惯例鼓励此用例的getter或setter。

因此,这很少是一个通用的OO问题;这是一个特定于语言的问题,对于不同的语言(和不同的用例)有不同的答案。


从OO理论的角度来看,getter和setter是无用的。你的类的接口是它所做的,而不是它的状态。(如果不是,你写错了类。)在非常简单的情况下,类所做的只是,例如,表示直角坐标中的一个点,*属性是接口的一部分;getter和setter只是为这一点添油加醋。但在非常简单的情况下,属性、getter和setter都不是接口的一部分。

换一种说法:如果你认为你的类的消费者甚至不应该知道你有一个垃圾属性,更不用说随意更改它了,那么给他们一个set_spam方法是你最不想做的事情。

*即使对于这个简单的类,您也不一定希望设置x和y值。如果这真的是一个类,它不应该有translate、rotate等方法吗。?如果它只是一个类,因为您的语言没有记录/结构/命名元组,那么这实际上不是OO的问题…


但从来没有人做过一般的面向对象设计。他们用特定的语言进行设计和实现。在某些语言中,getter和setter远非一无是处。

如果您的语言没有财产,那么表示概念上是属性但实际上是计算或验证的东西的唯一方法是通过getter和setter。

即使你的语言确实有财产,也可能有不足或不合适的情况。例如,如果要允许子类控制属性的语义,在没有动态访问的语言中,子类不能用计算属性代替属性。

至于“如果我想稍后更改我的实现怎么办?”这个问题(在OP的问题和公认的答案中以不同的措辞重复了多次):如果它真的是一个纯实现更改,并且您从一个属性开始,您可以将其更改为一个属性而不影响接口。当然,除非你的语言不支持这一点。所以这真的又是同样的情况。

此外,遵循所使用语言(或框架)的习惯用法也很重要。如果你用C#编写了漂亮的Ruby风格的代码,那么除了你之外,任何有经验的C#开发人员都会很难读懂它,这很糟糕。有些语言的习俗文化比其他语言更为浓厚-Java和Python在习惯用法方面处于对立的两端,它们恰好拥有两种最强的文化,这可能不是巧合。

除了人类读者之外,还会有图书馆和工具要求你遵守惯例,如果你不遵守惯例,会让你的生活变得更艰难。将Interface Builder小部件挂接到ObjC财产以外的任何东西上,或者在没有getter的情况下使用某些Java模拟库,只会让您的生活更加困难。如果这些工具对你很重要,不要与它们对抗。

OO设计的基本原则之一:封装!

它给您带来了许多好处,其中之一是您可以在幕后更改getter/setter的实现,但只要数据类型保持不变,任何具有该值的使用者都将继续工作。