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

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

其他回答

在面向对象的语言中,方法及其访问修饰符声明该对象的接口。在构造函数、访问器和赋值器方法之间,开发人员可以控制对对象内部状态的访问。如果变量被简单地声明为公共的,那么就没有办法规范这种访问。当我们使用setter时,我们可以限制用户所需的输入。这意味着这个变量的输入将通过一个适当的通道,通道是我们预先定义的。所以使用setter更安全。

在不支持“财产”(C++、Java)或在将字段更改为财产(C#)时需要重新编译客户端的语言中,使用get/set方法更容易修改。例如,向setFoo方法添加验证逻辑不需要更改类的公共接口。

在支持“真实”财产的语言中(Python、Ruby或Smalltalk?),没有必要使用get/set方法。

我只是想补充一点,即使有时它们对于变量/对象的封装和安全是必要的,如果我们想编写一个真正的面向对象的程序,那么我们需要停止过度使用附件,因为有时我们在不真正必要的情况下非常依赖它们,这几乎与我们将变量公开一样。

如果您不需要任何验证,甚至不需要维护状态,即一个属性依赖于另一个属性,那么当一个属性发生更改时,我们需要维护状态。您可以通过公开字段而不使用getter和setter来保持简单。

我认为OOP会随着程序的增长而使事情变得复杂,这对于开发人员来说是一场噩梦。

一个简单的例子;我们从xml生成c++头。标头包含不需要任何验证的简单字段。但仍然像OOPS访问器一样,我们生成它们的方式如下。

const Filed& getfield() const
Field& getField() 
void setfield(const Field& field){...} 

这是非常冗长的并且不是必需的。一个简单的

struct 
{
   Field field;
};

足够且可读。函数式编程没有数据隐藏的概念,它们甚至不需要它,因为它们不会改变数据。

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