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

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


当前回答

如果您想要一个只读变量,但不想让客户端改变访问它的方式,请尝试使用这个模板类:

template<typename MemberOfWhichClass, typename primative>                                       
class ReadOnly {
    friend MemberOfWhichClass;
public:
    template<typename number> inline bool   operator==(const number& y) const { return x == y; } 
    template<typename number> inline number operator+ (const number& y) const { return x + y; } 
    template<typename number> inline number operator- (const number& y) const { return x - y; } 
    template<typename number> inline number operator* (const number& y) const { return x * y; }  
    template<typename number> inline number operator/ (const number& y) const { return x / y; } 
    template<typename number> inline number operator<<(const number& y) const { return x << y; }
    template<typename number> inline number operator^(const number& y) const  { return x^y; }
    template<typename number> inline number operator~() const                 { return ~x; }
    template<typename number> inline operator number() const                  { return x; }
protected:
    template<typename number> inline number operator= (const number& y) { return x = y; }       
    template<typename number> inline number operator+=(const number& y) { return x += y; }      
    template<typename number> inline number operator-=(const number& y) { return x -= y; }      
    template<typename number> inline number operator*=(const number& y) { return x *= y; }      
    template<typename number> inline number operator/=(const number& y) { return x /= y; }      
    primative x;                                                                                
};      

示例用途:

class Foo {
public:
    ReadOnly<Foo, int> cantChangeMe;
};

记住,您还需要添加按位和一元运算符!这只是为了让你开始

其他回答

取决于你的语言。您已经将其标记为“面向对象”而不是“Java”,因此我想指出ChssPly76的答案依赖于语言。例如,在Python中,没有理由使用getter和setter。如果需要更改行为,可以使用一个属性,该属性将getter和setter包装在基本属性访问周围。类似于:

 class Simple(object):
   def _get_value(self):
       return self._value -1

   def _set_value(self, new_value):
       self._value = new_value + 1

   def _del_value(self):
       self.old_values.append(self._value)
       del self._value

   value = property(_get_value, _set_value, _del_value)

我们使用getter和setter:

可重用性在编程的后期执行验证

Getter和setter方法是访问私有类成员的公共接口。


封装咒语

封装的口头禅是将字段私有化,将方法公开化。

Getter方法:我们可以访问私有变量。Setter方法:我们可以修改私有字段。

尽管getter和setter方法没有添加新的功能,但我们可以稍后再改变主意,制作该方法

较好的更安全的;和更快。


只要可以使用值,就可以添加返回该值的方法。而不是:

int x = 1000 - 500

use

int x = 1000 - class_name.getValue();

用外行的话来说

假设我们需要存储此人的详细信息。此人具有姓名、年龄和性别字段。要做到这一点,需要创建姓名、年龄和性别的方法。现在,如果我们需要创建另一个人,就需要重新创建姓名、年龄和性别的方法。

我们可以使用getter和setter方法创建一个bean类(Person),而不是这样做。因此,明天我们只要在需要添加新人员时创建这个Bean类(Person类)的对象即可(见图)。因此,我们重用bean类的字段和方法,这要好得多。

在以下情况下,应使用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模拟库,只会让您的生活更加困难。如果这些工具对你很重要,不要与它们对抗。

在纯面向对象的世界中,getters和setters是一种可怕的反模式。阅读本文:Getters/Setters。恶毒的时期简而言之,它们鼓励程序员将对象视为数据结构,这种类型的思考是纯过程的(如COBOL或C)。在面向对象的语言中,没有数据结构,只有暴露行为的对象(不是属性/财产!)

您可以在ElegantObjects(我的面向对象编程书)的第3.5节中找到更多关于它们的信息。

考虑使用访问器是有充分理由的,因为没有属性继承。请参见下一个示例:

public class TestPropertyOverride {
    public static class A {
        public int i = 0;

        public void add() {
            i++;
        }

        public int getI() {
            return i;
        }
    }

    public static class B extends A {
        public int i = 2;

        @Override
        public void add() {
            i = i + 2;
        }

        @Override
        public int getI() {
            return i;
        }
    }

    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.i);
        a.add();
        System.out.println(a.i);
        System.out.println(a.getI());
    }
}

输出:

0
0
4