课堂上私人成员的目的

时间:2010-03-03 21:11:13

标签: language-agnostic class oop private-members

在面向对象编程中拥有类/结构的私有/受保护成员的目的是什么?让所有成员公开会有什么害处?

10 个答案:

答案 0 :(得分:27)

封装。即隐藏类数据的实现。这允许您稍后更改它,而不会破坏所有客户端代码。例如。如果你有

class MyClass {
    public int foo;
}

您的客户可能会编写类似

的代码
MyClass bar = new MyClass();
bar.foo++;

现在如果您意识到foo实际上应该是双精度而不是整数,那么您可以更改它:

class MyClass {
    public double foo;
}

并且客户端代码无法编译: - (

通过精心设计的界面,内部(私有部分)的更改甚至可能包括将成员变量转换为计算,反之亦然:

class Person {
    public String getName();
    public String getStreetAddress();
    public String getZipCode();
    public String getCountryCode();
    public int hashCode();
}

(为了简单起见使用String属性 - 在现实世界的设计中,其中一些可能应该拥有自己的类型。)

通过这种设计,您可以自由地使用在内部引入Address属性,其中包含街道地址,邮政编码和国家/地区代码,并重写您的访问者以使用此私有成员的字段,而无需您的客户注意任何内容。

您还可以自由决定是每次计算哈希码,还是将其缓存到私有变量中以提高性能。但是,如果该缓存字段是公共的,那么任何人都可以更改它,这可能会破坏哈希映射行为并引入细微的错误。因此封装是保证对象内部状态一致性的关键。例如。在上面的示例中,您的setter可以轻松验证邮政编码和国家/地区代码,以防止设置无效值。您甚至可以确保邮政编码格式对实际国家/地区有效,即确保跨多个属性的有效性标准。通过精心设计的界面,您可以通过以下方式强制执行此绑定:仅提供一个setter来同时设置两个属性:

    public void setCountryCodeAndZip(String countryCode, String zipCode);

但是,对于公共字段,您根本没有这些选择。

私有字段的特殊用例是不可变对象;这在例如Java,例子是StringBigDecimal。这些类根本没有公共设置器,这保证了它们的对象一旦创建就不会改变它们的状态。这样可以实现许多性能优化,并使它们更容易在例如多线程程序,ORM等。

答案 1 :(得分:9)

您可能需要阅读维基百科上的Information Hiding主题。

从本质上讲,私有成员允许类隐藏其外部使用者的实现细节。这允许类更好地控制数据和行为的表达方式,并允许消费者不知道与课程主要目的无关的详细信息。

通过阻止类外部的代码建立对这些细节的依赖性,隐藏实现细节可以提高程序的可维护性。这允许实现独立于外部消费者而改变 - 降低了破坏现有行为的风险。当私有实现细节变为公开时,如果没有打破依赖于这些细节的类的消费者的可能性,就无法更改它们。

私有成员还允许类保护其实现免受外部滥用。通常,类的状态具有内部依赖关系,这些依赖关系定义状态何时有效 - 何时不存在。我们可以将管理状态信息有效性的规则视为不变 - 这意味着类总是期望它们是真的。公开私有细节,允许外部代码以可能违反不变量的方式修改此状态,从而危及类的有效性(和行为)。

信息隐藏的另一个好处是,它可以减少类消费者必须理解的表面区域 ,以便与类正确交互。简化通常是好事。它允许消费者专注于理解公共界面,而不是集体如何实现其功能。

答案 2 :(得分:7)

Section 7.4: Protect your Private Partsonline C++ tutorial中很好地解释了。

  

为什么要烦恼这个东西?

     

说明符允许一个类非常   复杂,具有许多成员函数   和数据成员,同时拥有   其他简单的公共接口   课程可以使用。有两个班级   一百个数据成员和一百个   成员函数可以非常   写得很复杂;但如果有的话   只有三四个公共成员   功能,其余的都是   私人,对某人来说很容易   学习如何使用该课程。他只   需要了解如何使用小   少数公共职能,以及   不需要打扰两个人   百位数据成员,因为他不是   允许访问此数据。他可以   只访问私人数据   班级的公共界面。没有   怀疑,在一个小程序中,使用这些   说明符似乎没必要。   但是,他们值得了解   如果你打算做任何节目   合理的尺寸(超过一对   百行)。总的来说,这很好   练习使数据成员保密。   必须调用的成员函数   应该是来自课外   公共和成员职能   只在班级内召唤   (也称为“助手功能”)   应该是私人的。这些   说明符在a中特别有用   大型计划涉及多个   程序员。

以上说明解释了如何使用private简化学习曲线。这是一个解释“代码破坏”方面的例子:

这是一个类ParameterIO,它读取和写入整数参数的向量

class ParameterIO
{
public:
    // Main member
    vector<int> *Params;
    string param_path;

    // Generate path
    void GeneratePath()
    {       
        char szPath[MAX_PATH];
        sprintf(szPath,"params_%d.dat",Params->size());
        param_path = szPath;
    }

    // Write to file
    void WriteParams()
    {
        assert_this(!Params->empty(),"Parameter vector is empty!");
        ofstream fout(param_path.c_str());
        assert_this(!fout.fail(),"Unable to open file for writing ...");
        copy(Params->begin(),Params->end(),ostream_iterator<int>(fout,"\n"));
        fout.close();
    }

    // Read parameters
    void ReadParams(const size_t Param_Size)
    {
        // Get the path
        Params->resize(Param_Size);
        GeneratePath();
        // Read
        ifstream fin(param_path.c_str());
        assert_this(!fin.fail(),"Unable to open file for reading ...");
        // Temporary integer
        for(size_t i = 0; i < Params->size() && !fin.eof() ; ++i) fin>>(*Params)[i];
        fin.close();
    }

    // Constructor
    ParameterIO(vector<int> * params):Params(params)
    {
        GeneratePath();
    }

    // Destructor
    ~ParameterIO()
    {
    }      

    // Assert
    void assert_this(const bool assertion, string msg)
    {
        if(assertion == false) 
        {
            cout<<msg<<endl;
            exit(1);
        }
    }
};

以下代码打破了这个类:

const size_t len = 20;
vector<int> dummy(len);
for(size_t i = 0; i < len; ++i) dummy[i] = static_cast<int>(i);
ParameterIO writer(&dummy);

// ParameterIO breaks here!
// param_path should be private because 
    // the design of ParameterIO requires a standardized path
writer.param_path = "my_cool_path.dat";
// Write parameters to custom path
writer.WriteParams();

vector<int> dunce;
ParameterIO reader(&dunce);
// There is no such file!
reader.ReadParams(len);

答案 3 :(得分:5)

隐喻地说,将私人会员公开为公众就像在车的仪表板上有一个选项,允许你调整发动机油压。

汽车应该在内部(私下)进行管理,并且由于显而易见的原因,应该避免用户直接弄乱它(encapsulation)。

答案 4 :(得分:4)

这真的取决于你的意识形态。我们的想法是隐藏因某些原因不应暴露的信息。

如果您有一个想要在线发布的图书馆,很多人会下载它,有些人可能会在他们的代码中使用它。如果您将公共API保持在最低限度并隐藏实现细节,那么当您遇到错误或想要改进代码时,您将很难更新它。

此外,例如,在Java中,您无法在不更改其可见性的情况下限制对成员变量的访问,因此您经常会发现自己过早地创建了getter和setter并使变量本身变为私有或受保护。例如,在Python中,该问题不存在,因为您可以使getter和setter的行为类似于直接访问的变量(它们在那里被称为属性)。

最后,有时你需要有一些方法要求一致的状态才有用,如果直接访问会导致问题。

经验法则是:如果你暴露某些东西,有人会使用它。大多数情况下,他们会将其用于错误的原因(即不是您打算如何使用它们)。在这种情况下,信息隐藏相当于武器柜上的儿童锁。

答案 5 :(得分:3)

要添加到Peter的答案,请说您的类存储了一个名称,并且您希望将其从使用单个名称字符串更改为名字字符串和姓氏字符串。如果您的成员是公共的,其他类可能直接读取(或写入)名称变量,并在该变量消失时中断。

更不用说您可能根本不希望其他课程能够编辑您的成员。

答案 6 :(得分:1)

有时你不想向所有人透露私人信息。例如,你不想公开告知你的年龄,但如果你年满25岁,你可能想告诉别人。

答案 7 :(得分:1)

在人体内有器官的目的是什么?将所有器官放在外面会有什么危害?

完全!

简短的回答是:因为你需要它们所以你不能没有它们,你不能让它们暴露给每个人修改和玩弄它们因为这可能会杀了你(导致你的班级无法正常运作) )。

答案 8 :(得分:1)

根据观众的受众和消费情况,完全没有任何伤害。让我再重复一次,以便它沉入其中。

完全没有任何伤害,取决于观众和课堂消费。

对于需要一个月左右的一两个人的许多小项目,完全降低所有私人和公共定义可能会大大增加工作量。然而,在大型项目中,可能有多个团队和团队在地理位置上并不在一起,在开始时正确设计所有公共接口可以大大增加整个项目成功的可能性。

因此,在你开始回答这个问题之前,你真的必须要看一下课程的消费方式。同样,软件开发生命周期将持续多长时间?几个月?年份?几十年?除了你之外还有其他人在上课吗?

课程越“公开”(即消费和使用课程的人越多),确定公共界面并坚持下去就越重要。

答案 9 :(得分:0)

一个简短的例子:您可能需要确保该值的某些条件。在这种情况下,直接设置可能会破坏这种情况。

很多人都说“你可能不希望每个人都阅读它”,但我认为设定一个值的限制是一个更有用的例子。

相关问题