类的方法应该调用自己的getter和setter吗?


53

在我工作的地方,我看到很多班级都做了这样的事情:

public class ClassThatCallsItsOwnGettersAndSetters {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        setField("value");
        //do stuff
        String localField = getField();
        //do stuff with "localField"
    }
}

如果我是从头开始写的,那我应该写成methodWithLogic()这样的:

public class ClassThatUsesItsOwnFields {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public void methodWithLogic() {
        field = "value";
        //do stuff            
        //do stuff with "field"
    }
}

我感到,当类调用其自己的getter和setter时,它将使代码更难阅读。在我看来,这几乎意味着在该方法调用中正在发生复杂的逻辑,即使在我们看来,它从来没有发生过。当我调试一些不熟悉的代码时,谁会说该错误不是该方法的副作用?换句话说,这使我在理解代码的过程中走了很多路。

第一种方法有好处吗?第一种方法实际上更好吗?


当我调试一些不熟悉的代码时,谁说该错误不是该方法的副作用?您的单元测试。:)
约书亚·泰勒

1
如果在内部代码中使用getter / setter,则可以通过调试器在代码中创建断点。它还使您可以确保在设置设置/获取值时如果出现任何问题,您知道这是由于该方法而不是由于某些无效的访问。总体而言,它提供了更多的一致性代码。
callyalater

Answers:


46

我不会说哪个更好或更坏,因为这部分取决于您的情况(我认为)。但是请考虑您的getter和setter稍后可能会更改实现,而绕过它们将跳过该逻辑。

例如,如果以后在某些设置器字段中添加“脏”标志会怎样?通过在内部代码中调用设置器,您将在不更改任何其他代码的情况下设置脏标志。在许多情况下,这将是一件好事。


但是,当您在后端初始化数据并且不希望将其解释为脏数据时该怎么办。如果您setField()从一开始就打电话,您只是介绍了一个错误。
Daniel Kaplan 2013年

6
是的,这就是为什么我说这部分取决于情况。也许您的数据初始化应跳过设置方法,但其他逻辑代码则不应。选择合适的规则并坚持下去。
Matt S

3
@DanielKaplan:关键是在大多数情况下,调用getter和setter是正确的事情,而仅在特定情况下才是正确的。但是,在现实世界的代码中,当您以后更改现有的getter / setter实现以引入一些有意的副作用时,您很可能必须检查类中对getter或setter的每个调用,以及每个对getter或setter的直接访问。领域。这就是为什么您应该尝试使类尽可能小。
布朗

33

直接调用设置器本身不是问题。问题实际上应该是:为什么我们的代码到处都有setter?

可变类是一种危险的游戏,尤其是在类本身管理自己的状态的情况下。考虑真正需要多少个二传手。可以在构造函数中设置多少个,然后完全由类本身封装呢?

如果必须在外部设置该字段,请问自己是否应该使用带有逻辑的方法。您的课程本身真的只是数据/状态课程吗?这堂课有多个责任吗?

不要误会我的意思,在某些情况下这很好。我并不是说您应该完全消除带有逻辑的类的设置方法。但是这些应该是例外,而不是规则。并且,在那种情况下,直接访问哪个应该很明显。


1
我完全同意/实践这种思路。我还认为您提出的观点实际上比我的问题更重要。就是说,我不觉得它直接回答了我原来的问题。除非您说的是,“在大问题上,您的问题无关紧要”。这也可能是正确的:)
Daniel Kaplan 2013年

@DanielKaplan:我的意思就是这样。:)在这个问题的答案和评论之间,我感到困惑,但我真的不认为有一个正确的答案不会问更大的问题。
pdr

9

是的,您的类的方法应调用getter和setter。编写吸气剂和吸气剂的全部要点是面向未来。您可以将每个属性都设为一个字段,并将数据直接提供给该类的用户。构建getter和setter的原因未必一定是因为现在存在复杂的逻辑,但是如果将来必须添加接口,该接口在将来不会中断。


1
我看不到您的第一句话和其余段落之间的关系。第一句话说,一个类应该调用自己的 getter和setter方法。本段的其余部分说明了为什么客户端代码(即使用类的代码)应该使用getter和setter而不是直接访问字段。但是我不知道这是为什么类不应该直接访问其自己的字段的原因。
Daniel Kaplan

1
@DanielKaplan我的观点是原因是相同的。如果以后再添加setter逻辑,则它对内部代码的影响与对外部代码的影响相同(可能)。
2013年

2
@Michael:类经常有很好的理由不使用自己的getter / setter方法。一种常见的情况是存在许多格式的设置程序,someField=newValue; refresh(); 如果方法允许设置多个字段,则调用设置程序写入这些字段将导致冗余的“刷新”操作。编写所有字段,然后调用refresh()一次可能会产生更高效,更流畅的操作。
supercat

6

一个单词回答您的问题,是的。

让类调用其自己的getter和setter可以增加可扩展性,并为将来的代码提供更好的基础。

假设您有这样的事情:

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        this.year = year;
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

调用setter for year和make当前可能不会添加任何功能,但是如果您想向setter添加诸如输入验证之类的东西怎么办?

public class Vehicle
{
    private int year;
    private String make;

    public Vehicle(int year, String make)
    {
        setYear(year);
        setMake(make);
    }

    public void setYear(int year)
    {
        if(year > 0)
        {
            this.year = year;
        }
        else
        {
            System.out.println(year + " is not a valid year!");
        }
    }

    public void setMake(String make)
    {
        this.make = make;
    }
}

5

尚未提及的一件事是,getter和setter(与所有方法一样)在Java中是虚拟的。这为始终在代码中使用它们添加了另一个功能。另一个用户可以扩展您的课程,覆盖您的获取器和设置器。然后,您的基类将使用子类的数据,而不是其自身的数据。在显式标记虚拟函数的语言中,这更加方便,因为您可以预测并声明可以使用哪些函数。在Java中,这是您始终必须意识到的事情。如果需要这样做,那么在您的代码中使用它们是一件好事,否则就不那么多了。


3

如果您要完成公共接口提供的某些操作,请使用getter / setter方法。

但是,作为课程的所有者/开发人员,您可以(当然在课程中)访问代码的私有部分,但是您还应负责减轻危险。

因此,也许您有一个遍历某些内部列表的getter,并且想要获得当前值而不增加迭代器。在这种情况下,请使用私有变量。

public class MyClass
{
    private int i;
    private List<string> list;
    public string getNextString()
    {
        i++;
        return list[i];
    }

    private void getString()
    {
        // Do not increment
        string currentString = list[i];

        // Increment
        string nextString = getNextString();
    }
}

你能给出这个理由吗?
Daniel Kaplan 2013年

1

是。Getter和Setter表示状态,所以转过来解决这个问题-除非必须这样做,否则是否要跟踪以相同方式更改对象状态的多种方式?

您需要跟踪的事物越少越好-这就是为什么不可变对象更易于处理的原因。

考虑一下,没有什么阻止您同时拥有公共场所和公共获取者/设定者的,但是您会得到什么呢?

它可在occassion是可取的,甚至必要直接访问现场的一个或多个原因,你不应该,如果它发生避而远之。但是,只有在这样做具有明确的好处时,您才应该这样做。


我要问的问题是,我只想问直接访问同一类中的字段。我已经了解了客户端代码的getter和setter方法的目的。
Daniel Kaplan

@DanielKaplan:我理解这一点,我的意思是,在实际操作中,您应该将它们相同。
jmoreno

@DanielKaplan:您可以有一个私人二传手和一个公共二传手,它们至少在装修方面具有完全相同的结果(所有副作用都相同),但是除了事情可能会发散之外,那还能给您带来什么呢?这种情况与您所描述的情况之间的区别在于,您无法避免能够访问getter / setter之外的状态,但可以避免实际这样做。除非必须这样做,否则不要使您的代码复杂化。
jmoreno

1

两种方法都有其用例。由于公共设置器使字段值(和/或绑定值)保持一致,因此,当方法逻辑不干扰此一致性逻辑时,应使用设置器。如果您只是设置“属性”,请使用setter。另一方面,在某些情况下,您需要直接访问某些字段,例如具有大量设置器的批量操作或设置器概念过于简单的操作。

保持一致是您的责任。塞特通过定义来做到这一点,但不能涵盖复杂的情况。


1

我会说不。

如果您的getter / setter方法只是获取并设置(没有延迟的初始化,没有检查等),则只需使用您的变量即可。如果将来要更改吸气剂/设置器,则可以很好地更改您methodWithLogic()的吸气剂/设置器(因为您的班级正在更改),并且可以调用吸气剂/设置器,而不是直接分配。您可以记录对getter / setter的调用(因为当您的其余类代码直接使用变量时,调用getter / setter会很奇怪)。

JVM将内联频繁调用getter / setter。因此,这绝不是性能问题。使用变量的好处是可读性和恕我直言,我会坚持下去。

希望这可以帮助..

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.