【问题标题】:What is the advantage of having a private attribute with getters and setters? [duplicate]拥有 getter 和 setter 的私有属性有什么好处? [复制]
【发布时间】:2013-09-17 09:07:28
【问题描述】:

在面向对象编程中,我曾经有过这个问题,现在仍然有:

如果我们将一个类成员声明为 private 会有什么好处? 会为它创建一个public getter和一个public setter吗?

我看不出上述情况与将类成员声明为公共的情况在安全级别上有任何区别。

谢谢!

【问题讨论】:

  • 如果你的 setter 只包括返回你的字段,那么没有区别。但有时您可能希望在计算后返回您的字段。
  • 它可以帮助保持接口随着时间的推移保持稳定(封装)。一个明显的情况是,如果您以后想更改为惰性求值。

标签: java c++ oop


【解决方案1】:

封装提供数据隐藏和对成员变量的更多控制。如果一个属性是公开的,那么任何人都可以访问它并可以为其分配任何值。但是如果你的成员变量是私有的并且你已经为它提供了一个 setter。然后,您始终可以选择在 setter 方法中进行一些约束检查,以避免设置不合逻辑的值。

例如一个只有公共成员的类:

class MyClass {
    public int age;
}

public MyClassUser {

    public static void main(String args[]) {
        MyClass obj = new MyClass();
        obj.age = -5 // not a logical value for age
    }
}

具有私有成员和 setter 的同一类:

 class MyClass {
     private int age;
     public void setAge(int age) {
         if(age < 0) {
            // do not use input value and use default
         } else { 
            this.age = age;
         }
     }
 }

【讨论】:

  • 哇!我发誓我添加答案时没有看到你的例子
【解决方案2】:

如果您的类没有要维护的不变量,那么为私有数据成员编写公共 getter 和 setter 是没有意义的;您应该只使用公共数据成员。

另一方面,如果您确实需要维护不变量,那么使用 setter 可以让您限制可以分配给数据成员的值。

请注意,仅仅因为您有一个数据成员并不意味着您必须为它编写任何 getter 或 setter。

烦心:“但如果内部结构发生变化怎么办?”没关系。您有一个返回 std::string const&amp;getName 函数。 Getter 减少封装,因为它们约束您在以后更改实现时的选择。

【讨论】:

  • 如果你的类将来可能需要维护不变量,并且你对调用者没有任何控制权,你也应该使用 getter 和 setter。
【解决方案3】:

快速(有点傻)的例子:

class Foo {

    private int age = -1;  // unset value

    public setAge(int a) {
        if (a < 0) {
            throw new IllegalArgumentException("Invalid age "+a);
        }
        age = a;
    }

    public getAge() {
       if (age < 0) {
           throw new InvalidStateException("Age was not previously set.")
       }
       return age;
    }    
}

简而言之:您获得了控制权,并且可以确保值是正确的。这叫封装。

【讨论】:

  • +1 您可能一开始不会这样做,但以后可能想添加这样的逻辑,而不必使用此类更改所有代码。
  • +1 ,添加到@PeterLawrey 这只是代码集中,我想说。
【解决方案4】:

http://en.wikipedia.org/wiki/Encapsulation_%28object-oriented_programming%29

您可以稍后更改类成员的内部表示,向 getter 和 setter 添加功能(例如通知观察者),所有这些都无需更改接口(公共 getter 和 setter)。

【讨论】:

    【解决方案5】:

    您的问题确实是字段和属性之间的区别。归档通常是私有的,并且属性会公开它们。贝娄是对 SO 的精彩回答的引述:

    属性公开字段。应该(几乎总是)保留字段 私有的类并通过 get 和 set 属性访问。特性 提供一个抽象级别,允许您在更改字段的同时 不影响外部事物访问它们的方式 使用你的类。

    What is the difference between a Field and a Property in C#?

    在 C# 中,自动属性会为您创建一个文件,而无需手动声明:

    public Prop { get; set; }
    public Prop { public get; private set; }
    public Prop { private get; public set; }
    // etc, you can specify access modifier as per your need
    

    【讨论】:

    • +1 C# 允许您隐藏样板代码。
    • 我不熟悉 C#,但我可以看到它是一种快速开发的语言。 +1
    【解决方案6】:

    我看不出上述情况与将类成员声明为公共的情况在安全级别上有什么区别。

    直接的问题是:

    1)如果要检查一些条件,同时设置值怎么办?

    2) 如果子类想要通过 ovveridng 方法返回或设置其他东西怎么办?

    其他原因:Why getter and setter are better than public fields in Java

    【讨论】:

    • +1 表示覆盖
    【解决方案7】:

    如果您有一个范围有限的数据传输对象,并且按照设计它应该没有与之关联的逻辑,那么我在 getter 和 setter 中看不到值。

    但是,如果您的组件可能有也可能没有相关的逻辑,或者它可能被广泛使用,那么隐藏数据存储方式的细节是有意义的。最初看起来所有的 getter 和 setter 都是微不足道的,只是填满了你的类,但随着时间的推移,你可能会向 setter 添加验证,甚至更改 getter。例如您可能会删除一个字段(并在将来返回一个常量),将数据存储在委托对象中或从其他字段计算值。

    【讨论】:

      【解决方案8】:

      除了封装,考虑一下你的setter 不是简单地设置一个值的情况。
      如果您在许多课程中使用它怎么办?现在你意识到你想改变它的功能了吗?您必须在手动设置它的整个地方进行更改。而如果你有一个 setter 生活会更轻松。

      【讨论】:

        【解决方案9】:

        与任何封装一样:它隐藏了实现细节。这使您可以控制访问并提供稳定的界面,即使内部发生变化。

        Setter 控制访问

        class Person  //version 1.0
        {
          std::string name;
        
        public:
          std::string getName() const { return name; }
        
          void setName(const std::string &newName)
          {
            if (!newName.empty())  //disallow empty names
              name = newName;
          }
        };
        

        Getter 在 API 演进过程中很有用

        class Person  //version 1.1
        {
          std::string firstName;
          std::string lastName;
        
        public:
          std::string getFirstName() const { return firstName; }
        
          void setFirstName(const std::string &newFirstName)
          {
            firstName = newFirstName;
          }
        
          std::string getLastName() const { return lastName; }
        
          void setLastName(const std::string &newLastName)
          {
            if (!newLastName.empty())  //disallow empty last names
              firstName = newFirstName;
          }
        
          std::string getName() const
          {
            std::ostringstream s;
            if (!firstName.empty())
              s << fistName << ' ';
            s << lastName;
            return s.str();
          }
        
          void setName(const std::string &newName)
          {
            setFirstName(splitUntilLastSpace(newName));
            setLastName(splitFromLastSpace(newName));
          }
        };
        

        【讨论】:

          【解决方案10】:

          访问器方法为给定字段提供单点更新。这是有益的,因为可以通过单一方法控制验证逻辑或对字段的其他修改,而不是在整个代码库中直接访问字段。

          请参阅此 IBM 文档,了解更多好处:http://www.ibm.com/developerworks/java/library/ws-tip-why.html

          【讨论】:

            【解决方案11】:

            如果公共 getter 和公共 setter 只是返回私有属性的值并改变它的值,那么我看不出有什么区别。

            但是,您正在实现封装,因此稍后您可以实现不同的行为,例如,包括对 setter 或只写/只读属性的参数检查。

            【讨论】:

              【解决方案12】:

              其实,如果你一个人在一个小项目上开发,而且你不会重用你的代码,那有点没用,但它主要是一个好习惯。

              但是,在团队开发中,您可能需要对修改进行一些控制,您可以通过 getter 和 setter 来实现。

              此外,在某些类中,您将只有 getter,因为 setter 将由构造函数或通过其他一些函数完成。

              【讨论】:

                【解决方案13】:

                将变量声明为private 称为Encapsulation in Java
                以下是在使用 Java 或任何面向对象的编程语言编写代码时使用封装的几个优点:

                1. 封装代码更灵活,更易于根据新要求进行更改。
                2. Java 中的封装使单元测试变得容易。
                3. Java 中的封装允许您控制谁可以访问什么。
                4. 封装还有助于在 Java 中编写不可变类,这在多线程中是一个不错的选择 环境。
                5. 封装减少了模块的耦合并增加了模块内部的凝聚力,因为所有部分都是一个东西 被封装在一个地方。
                6. 封装允许您更改代码的一部分而不影响代码的其他部分。

                另一个优点是

                在 java 中将变量设为私有并为它们提供 getter 和 setter 使您的类兼容 Java bean 命名约定

                【讨论】:

                  【解决方案14】:

                  我只有一件事要补充到这篇文章迄今为止的优秀答案。

                  有时一个类属性可能有多个 Getter 或 Setter,让我们用一个愚蠢的简短示例来说明:

                  class Angle
                  {
                  public:
                      void Set(MyAngleTypedef a_value) { m_angle = a_value; }
                      // Note the 'Radians' type
                      void SetRadians(Radians a_value) { m_angle = ConvertRadiansToOurUnit(a_value); }
                      // Note the 'Degrees' type
                      void SetDegrees(Degrees a_value) { m_angle = ConvertDegreesToOurUnit(a_value); }
                  
                      void Get(MyAngleTypedef a_value) const { return m_angle; }
                      // Note the 'Radians' type
                      Radians GetRadians(Radians a_value) const { return ConvertOurUnitToRadians(m_angle); }
                      // Note the 'Degrees' type
                      Degrees GetDegrees(Degrees a_value) const { return ConvertOurUnitToDegrees(m_angle); }
                  private:
                      // Raw value of the angle in some user-defined scale.
                      MyAngleTypedef m_angle;
                  }
                  

                  为您想要工作的每个单元类型多次存储值是没有意义的,因此 Getter 和 Setter 将提供一个接口,使该类能够使用不同的单元。

                  恕我直言,当一个对象包含活动属性(在分配或访问它之后或之前必须做一些工作的属性)时,它必须是一个class,只有基本的 Getter 和 Setter(不需要的私有属性)在类外访问,显然不需要公共的 Getter 和 Setter)。

                  另一方面,如果一个对象只包含被动属性(在分配或访问时不需要额外工作的属性)它必须是struct,因此他的所有属性都是公开的无需 Getter 和 Setter 即可访问。

                  请注意,此答案是从 的角度来看的,请查看this question 了解更多信息。

                  【讨论】:

                    【解决方案15】:

                    面向对象编程最重要的概念之一是封装。您将数据和作用于该数据的方法封装在一起。理想情况下,数据应仅通过其相关方法访问。并且数据的状态应该被其他对象通过这些方法“查询”。将变量公开将导致该变量可直接用于所有其他对象,从而破坏封装。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2017-06-27
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-05-16
                      相关资源
                      最近更新 更多