为什么我们需要在类中使用私有变量?

我读过的每本编程书籍都说这是一个私有变量,这是您定义它的方式,但仅在此处停止。

在我看来,这些解释的措辞总是像我们对职业的信任危机。这些解释听起来总是像其他程序员要弄乱我们的代码一样。但是,有许多编程语言没有私有变量。


私有变量可以帮助防止什么?
您如何确定某个特定属性应为私有属性还是
不为私有属性?如果默认情况下每个字段都应该是私有的,那么为什么在类中有
公共数据成员?
在什么情况下应该公开变量?


评论

您的问题听起来与语言无关,但是您已将其标记为Java和C ++。如果您对这两种语言之外的观点感兴趣,您可能应该这样说。

根据记录,创建私有方法不会增加“安全性”。应用程序的其他部分仍可以使用反射访问私有成员。

私有变量和公共变量的用法和区别以及我们为什么拥有它们,只能在通用的面向对象原理的上下文中才能理解。不能单独理解。您可能需要从这个角度来看它,事情可能会变得更加清晰。

何时有理由使用Getter和Setter进行重复

如果您抓住任何在车上系上安全带的人,则应该拿走他们的驾驶执照,因为他们显然不信任自己的驾驶能力。

#1 楼

与其说是信任,不如说是管理复杂性之一。

可以从班级外部访问公共成员,出于实际考虑,这意味着“可能在任何地方”。如果公共领域出现问题,罪魁祸首可以在任何地方,因此要查明错误,您可能必须查看很多代码。

私有成员,作者:相反,只能从同一类内部进行访问,因此,如果出现问题,通常只需要查看一个源文件。如果您的项目中有一百万行代码,但是您的类很小,那么可以将错误跟踪的工作量减少1000倍。

另一个优点与“耦合”概念有关。 '。由另一个类m使用的类A的公共成员B引入了一个依赖关系:如果更改m中的A,还必须检查mB的用法。更糟糕的是,类A中的任何内容都不能告诉您在哪里使用了m,因此,您必须再次搜索整个代码库。如果它是您正在编写的库,则您甚至必须确保项目外的代码不会由于更改而中断。在实践中,无论多么痛苦,库都倾向于尽可能长时间地保留其原始方法签名,然后通过主要版本更新引入一系列重大更改。相比之下,使用私有成员时,您可以立即排除依赖项-无法从外部访问它们,因此所有依赖项都包含在类内部。

在这种情况下,“其他程序员”包括您的未来和过去的自我。您现在很可能不应该使用变量Y来执行X操作,但是当客户迫切需要您实现某些功能时,您注定会忘记三个月的时间,而您想知道为什么X会中断Y以晦涩的方式出现。

因此,关于何时应将其设为私有:我会说默认情况下将所有内容设为私有,然后仅公开那些绝对必须公开的部分。您可以将更多的内容设为私有,更好。

评论


+1是解决问题的核心,尽管我个人发现,当我停止遍历篮球只是为了确保经常使用的变量保持私有状态时,我的代码更易于维护。当然,将来可能会出现错误,但是要筛选的代码行少了60行,而引导的变量和函数却少了;)

–杰弗里·斯威尼(Jeffrey Sweeney)
2012年4月10日下午5:57

现代计算的圣杯正在降低复杂性。

–user1249
2012年4月10日上午9:58

我总是喜欢说调试的一部分不仅是直接朝着“出了什么问题”迈进,还包括绕过找出“没出什么问题”的回旋过程,并将研究重点放在剩下的问题上。如果我可以让编译器通过诸如私有变量之类的工具来帮助我定义“不可能发生的事情”,那么可以节省大量时间。仅在极少数情况下,我证明发生了错误的事情是不可能发生的,我才必须挖掘更可怕的假设,例如缓冲区溢出可能会覆盖我的私有数据。

–Cort Ammon
15年9月22日在23:37

就是这样当人们听到“隐藏信息”时,他们认为应该对加密密钥,数据库凭据等使用私有变量。

–Wayne Werner
16年6月17日在14:31

@AdamZerner参见“告诉,不要问”(martinfowler.com/bliki/TellDontAsk.html)有关首先使用getter / setter的信息,但否则,是的,因为您应该可以随时更改值的内部表示形式你希望。与往常一样,也有例外...(例如DTO)

– doubleYou
18年1月18日在18:17

#2 楼

私有变量有助于防止人们依赖代码的某些部分。例如,假设您要实现一些数据结构。您希望数据结构的用户不在乎如何实现它,而只是通过定义良好的界面使用实现。原因是,如果没有人依赖于您的实现,则可以随时更改它。例如,您可以更改后端实现以提高性能。依赖于您的实现的任何其他开发人员都会崩溃,而界面用户会没事的。拥有在不影响类用户的情况下更改实现的灵活性,这是使用私有变量(更广泛地说是封装)可以为您带来的巨大好处。

这也不是真正的“信任危机”。如果公开一条数据,则不能确保没有人依赖它。依赖某些特定于实现的变量通常非常方便,而不是通过公共接口,尤其是在截止期限之内。此外,开发人员不会总是意识到自己依赖于可能会发生变化的事物。

所以我希望这种回答可以回答您的其他问题。您的所有实现细节都应该是私有的,公共部分应该是一个小巧,简洁,定义明确的接口,以供您使用类。

评论


IMO也是关于lib作者和lib使用者之间的通信。公共接口就像“使用这个”,而私有接口就像“不使用那个”,除了在Java中,如果将它设为私有,您真的不能偶然使用它,这样更加安全和清晰。

–chakrit
2012年10月10日下午5:55

@chakrit和其他人:请注意,那些真正想使用您的私有字段和方法的人可以这样做(通过反射)。私有小于“不是主要用于安全性”,它没有提供任何“安全性”。这只是一个很强的暗示(通过编译器提供),提示不要访问它。

–user7043
2012年10月10日10:35



@delnan请注意“偶然”一词,是的,也许我对此应该更加清楚。

–chakrit
2012年4月10日10:39



@chakrit是的,我并不是要暗示您错了。我只是想宣传这一点。

–user7043
2012年4月10日10:41



@delnan:私有字段确实以某些语言提供某些形式的安全性。例如,请参阅Eric Lippert对访问级别和修饰符(私有,密封等)是否出于C#中的安全性目的的回答。

–布赖恩
2013年6月27日14:31



#3 楼

此处的关键字是封装。在OOP中,您想使用私有变量来强制对对象/类进行正确的封装。

虽然其他程序员并没有吸引您,但它们确实与您的代码进行交互。如果您不将变量设为私有,则他们很可能会在自己的代码中引用它,而又不会造成任何伤害。但是,如果您需要回到班级并进行一些更改,则不再需要知道谁在哪个位置使用哪个变量。封装的目的是使类的外部接口显式,以便您仅知道这些(通常)方法可以被其他人使用。


因此,私有变量可确保相应的变量仅保留在定义类中。如果需要更改,则更改是类本身的。
在传统语言(如C ++或Java)中,通常将所有内容设为私有,并且只能由相应的getter和setter进行访问。真的不需要做很多决定。
有时,f.ex。在C ++结构中,您只需要一个类即可将多种事物组合在一起。例如,考虑一个Vector类,它仅具有一个x和一个y属性。在这些情况下,您可以通过将它们声明为公共属性来允许直接访问这些属性。特别是,您不必担心外部的某些代码是否通过直接将新值写入xy来修改类的对象。另外,请注意,此问题与其他代码略有不同语言。例如,扎根于功能编程的语言强调数据不变性,即数据值根本无法更改。在这种情况下,您不必关心其他程序员(或更确切地说是他们的代码)对数据的处理方式。他们根本无法做任何可能影响您代码的事情,因为所有数据都是不可变的。

因此,在这些语言中,您会得到一种称为统一访问原则的知识,该原则不会故意区分诸如getter和setter之类的方法,而是提供对变量/函数的直接访问。因此,您可能会说私有声明在面向对象的场景中非常流行。

这也表明,如何扩大知识范围以包括其他领域,可以使您以全新的方式查看现有概念。

评论


+1“虽然其他程序员并没有吸引您,但他们确实会与您的代码进行交互。”程序员使用您的代码并不是天生的邪恶。通过使用私有变量,您可以确保它们(以及您将来)不会受到使用代码的伤害。它还可以帮助您设计更好的API并记录下来。

–rszalski
13-10-5 7:00



#4 楼

私有变量可确保以后在对象或函数范围之外的引用不会无意中影响其他变量。对于大型编程项目,这可以帮助避免许多有趣的问题(通常不会被编译器捕获)。

例如,像Javascript这样的原型语言可以让用户在自己认为合适的地方粘贴变量:

function SomeObject() {
    this.a = 2; //Public (well, actually protected) variable

    this.showA = function() {
        alert(this.a);
    }
}

//Some lines down...

var obj = new SomeObject();
obj.a = 3;
obj.showA(); //Will alert 3. Uh oh!


如果该变量是私有变量,则无法从外部进行更改:

function SomeObject() {
    var a = 2; //Private variable

    this.showA = function() {
        alert(a);
    }
}

//Some lines down...

var obj = new SomeObject();
obj.a = 3;
obj.showA(); //Will alert 2


也就是说,并非所有变量都必须是私有的,过多使用私有变量实际上会使代码更加麻烦。不会严重影响对象的琐碎字段不需要get()set()方法。

私有变量还使将来更容易扩展代码,而不必依赖繁杂的文档公共变量呢。如果可以覆盖较少的变量,则不会意外破坏功能。

在对象/函数将要访问其他对象/函数时,应使用公共变量,因为通常,私有变量可以不这样做。您可能有几个公共变量,也有很多公共变量,如果正确使用它们,则不是一件坏事。

评论


警报3为何会是“哦,哦!”的原因并不明显。在这种情况下。也许这就是程序员想要发生的事情。

–user253751
19年5月9日,2:18

@immibis也许,也许我应该在答案中详细说明。但是,访问属性可以为违反某些OOP原则(如开闭原则或LoD)敞开大门,因为您可能会暴露实现细节。根据您的观点,这取决于确切的属性。大多数语言都使用对象作为引用,并且当对象的引用被传递并且同伴开发者悄悄地更改属性时,调试起来真的很难。 IMO如果您有一个类实例,则使用方法要容易得多且可扩展得多。

–杰弗里·斯威尼(Jeffrey Sweeney)
19年5月9日在20:45

#5 楼

有一些很好的答案指出了类的未来维护者和用户的利益。但是,原始设计也有很多好处。它提供了一个清晰的分界点,介于您的目标是使您自己的事情变得容易的时候,还是您的目标是使类用户的事情变得容易的时候。当您在公共和私人之间进行选择时,它会发出信号通知您的大脑切换到一种模式或另一种模式,最终您会获得更好的API。

不是没有隐私的语言就不可能实现这种设计,可能性较小。人们倾向于以为不需要编写吸气剂,而是倾向于写foo.getBar(),而不是被提示写类似foo.bar的东西,但是当您后来遇到像obj.container[baz].foo.bar这样的较长怪物时,就不会再考虑该决定。从未使用过严格语言的程序员甚至可能都不知道这是怎么回事。

,这就是为什么在没有隐私的语言中,人们通常会采用模仿它的命名标准,例如在所有“私人”成员。即使语言不强制执行,这也是一个有用的信号。

#6 楼

所有变量都应该是私有的,除非它们绝对需要公开(这几乎是绝对不应该的,您应该使用属性/获取器和设置器)。

变量在很大程度上提供了对象的状态,而私有变量则防止了其他状态进入并更改对象的状态。

评论


对主题的看法过于狭窄。在某些情况下,甚至可以优先使用公共区域

–罗兰·特普(Roland Tepp)
2012年4月11日19:59

当然可以设计状态不能更改的对象(不可变对象),但这不是适用于所有类的一般规则。以我的经验,大多数对象使其他人很容易更改对象的状态。

– David K
2014年7月12日23:41

@DavidK也许bbb的意思是“私有变量阻止其他人进入并随意更改对象的状态。”公共方法可以更改对象的私有状态,但只能以对对象功能必需且一致的方式进行。

– Max Nanasy
2014年7月17日在20:57



@Max Nanasy:是的,当一个人仅在公共接口中提供方法时,就可以控制状态的改变。例如,成员之间可能存在必须满足的关系才能使对象“自洽”,并且您可以允许将自洽状态仅更改为另一个自洽状态。如果所有这些变量都是公共的,则您不能强制执行此操作。但是也可能有一些您不希望进行任何更改的对象,因此弄清楚我们正在谈论的是哪些是很重要的。

– David K
14年7月17日在21:30

#7 楼


私有变量有什么作用?

是要弄清楚哪些属性和方法是接口,哪些实际上是核心功能。公共方法/属性是关于其他代码的,通常是其他使用您的对象的开发人员的代码。我从未在同一项目中由100多人组成的团队工作过,但是根据我的经验,也许是由3-5人组成的团队以及多达20个其他开发人员使用我编写的内容,其他问题似乎很愚蠢。

注意:我主要是一名JavaScript开发人员。我通常不担心其他开发人员查看我的代码,并且我充分意识到他们可以随时重新定义我的内容。我认为他们有足够的能力先知道他们在做什么。如果不是这样,我不太可能会在不使用源代码控制的团队中工作。


如何确定一组特定的属性是否应为私有?如果默认情况下每个字段都应该是私有的,那为什么在类中有公共数据成员呢?

我以前认为当您实际上没有执行任何操作时,将getter和setter放在私有属性上是很愚蠢的对要设置的属性的一种确认或其他特殊处理。我仍然认为并非总是必要的,但是当您处理大规模,高复杂性时,但最重要的是,许多其他开发人员都以一致的方式依赖于您的对象,因此以一致和统一的方式。当人们看到称为getThatsetThat的方法时,他们确切地知道了它们的意图,并且可以编写对象与您的对象进行交互,并期望他们将始终得到西红柿而不是tomahtos。如果他们不这样做,他们就知道您的对象正在提供可能不应该给他们的东西,这意味着它允许其他对象对可能不应该使用的数据做一些事情。您可以根据需要进行控制。

另一个重要的优点是,根据某种变化的状态上下文,使对象的传递或解释与getter或setter不同的值更容易。因为他们使用一种方法访问您的东西,所以在不破坏依赖于该对象的其他代码的情况下,更改对象的操作方式要容易得多。重要的原则是,只有您的对象实际上会更改您使它负责的数据。这对于使代码保持松散耦合特别重要,这在可移植性和易于修改方面是一个巨大的胜利。


在什么情况下应该公开变量?

使用一流的函数(您可以将函数作为参数传递),除了在较小规模的项目中不需要这样做以外,我没有想到很多重要的原因。如果没有它,我想您可能会有一些成员被其他对象大量且定期地处理,以至于不断调用get和set来处理实际上并没有真正接触到数据本身的对象似乎有点麻烦。本身会让我想知道为什么对象负责该数据。一般来说,在决定是否需要公共数据属性之前,我倾向于希望重新检查体系结构中的缺陷。 IMO,让您执行通常是个坏主意的语言没有错。某些语言不同意。

评论


恕我直言,公开类的唯一目的不是公开变量,这没有什么特别的错误。确实,JVM为此目的有许多内置类:int [],double [],Object []等。但是,应该非常谨慎地注意如何公开此类的实例。 void CopyLocationTo(Point p);的含义[从调用方接受一个Point]比Point location()更清晰,因为还不清楚Point pt = foo.location()的作用是什么; pt.x ++;将位于foo的位置。

–超级猫
2013年12月23日20:39在

#8 楼

看到我的这篇帖子中有关SO的一个相关问题。

简短之处在于,变量作用域使您可以向代码使用者显示应该和不应该使用的东西。私有变量可以保存通过使用属性设置器或处理方法“审核”的数据,以确保整个对象处于一致状态。直接更改私有变量的值可能会导致对象变得不一致。通过使其私有化,某人必须努力工作并具有很高的运行时权限才能对其进行更改。

因此,正确的成员范围是自文档代码的关键。

#9 楼

我将使用一个真实的例子从另一个角度讨论封装的价值。相当早以前(80年代初),为Radio Shack彩色计算机编写了一款名为Daggorath的地下城游戏。几年前,Richard Hunerlach将其从纸面汇编器列表移植到C / C ++(http://mspencer.net/daggorath/dodpcp.html)。一段时间后,我得到了代码,并开始对其进行重构以提供更好的封装(http://dbp-consulting.com/stuff/)。

代码已经被分解为不同的代码了对象来处理计划,视频,用户输入,地牢创建,怪物等,但是您绝对可以确定它是从汇编程序移植的。我最大的任务是增加封装。我的意思是要获得代码的不同部分,以使彼此的业务脱颖而出。例如,有很多地方可以直接更改视频变量以产生一些效果。有些人通过错误检查来做到这一点,有些没有,对改变事物的含义有不同的想法。有很多重复的代码。相反,视频部分需要具有一个界面来完成所需的任务,并且执行该操作的代码可以全部集中在一个地方,一个想法,一个地方进行调试。那种事情很猖.。每个对象都可以直接访问其他对象,并且代码随处可见。

随着代码开始被人们弄乱,甚至未被诊断出的错误也消失了。代码变得更高的质量。每个任务都变成了代码中一个地方的责任,只有一个地方可以正确地完成它。 (每次通过该代码再次获得更多信息。)

关于代码的所有可见内容都是您的界面。无论您是真的还是不是真的。如果您尽可能地限制可见性,那么以后就不会将代码放在错误的位置。可以清楚地看出代码的哪一部分负责哪些数据。它使设计更好,更清洁,更简单,更优雅。

如果您使一个对象负责其自己的数据,并为需要发生任何事情的其他任何人提供接口,那么您的代码将变得更简单。它只是掉出来。您有只做一件事的小型简单例程。更少的复杂性==更少的错误。

评论


在“我们的lib用户的自由”与“给我们减少麻烦”之间的硬平衡。从某种意义上讲,我会开始相信封装会更好,因为这样可以防止我自己的编码出现错误,并且我还将帮助最终用户防止出现错误。但是缺乏自由可能会简单地把它们扔掉,寻找替代方案……可能尚未使用编码,但是通过公共方法可以预见的功能可以帮助平衡,但需要更多时间。

–水瓶座力量
16年6月17日在2:11

不幸的是,重构版本不在github上。

– jmoreno
18年1月19日在11:42

#10 楼

它与信任或对攻击的恐惧无关,仅与封装有关,而不是在类的用户上强制使用不必要的信息。

考虑私有常量-它们不应该包含秘密值(那些值应该存储在其他地方),不能被更改,不需要将其传递给您的类(否则它们将具有公开)。它们的唯一可能用途是在OTHER类中作为常量。但是,如果您这样做,那么这些类现在将取决于您的类,以进行与您的类无关的工作。如果更改常数,其他类可能会中断。这对双方都是不利的-作为班级的作家,您希望自由地尽可能地改变,并且不想担心无法控制的事情。班级的使用者希望能够依赖班级的公开细节,而不必担心更改它和破坏他们的代码。

班级的使用者希望了解进行交互所需要的一切与您的班级在一起,不想对您的班级一无所知,这不会改变他们的工作方式:这是无用的琐事。如果您使用了带反射的语言,那么您有多少次使用它来学习不是某个类是如何做的,也不是在何处意外地抛出异常,而是仅学习私有字段和方法的名称?我打赌永远不会。因为您没有使用该知识。

#11 楼

OOP概念具有继承性,它具有其功能之一(Java或C ++)。因此,如果我们要继承(意味着要访问继承的类的变量),则有可能影响这些变量。因此,我们必须确定变量是否可以更改。

仅出于此目的,我们在OOP中使用访问修饰符。修饰符之一是私有的,这意味着只能由该类访问。任何其他类都不会影响这些变量。

我们知道,protected意味着将被继承的类可以访问它。

为什么有修饰符概念在OOP中,由于这些原因(任何类都可以使用OOP概念访问其他类变量)。如果没有修饰符概念,则意味着在继承类或使用其他OOP概念时会很困难。

#12 楼

正如其他人所指出的那样,私有变量可以避免误操作导致对象进入不一致状态,并且难以跟踪错误和不可预见的异常。

但是,另一方面,大多数情况下却被忽略其他对象与受保护字段有关。

扩展子类将具有对受保护字段的完全访问权限,从而使对象像这些字段是公共的一样脆弱,但该脆弱性仅限于扩展类。 -self(除非它进一步暴露了此类字段)。

因此,很难将公共字段视为好字段,迄今为止,使用它们的唯一原因是将类用作配置参数(非常简单的类,具有许多字段且没有逻辑,因此该类仅作为参数传递给某些方法。)但是另一方面,私有字段降低了代码对其他用户的灵活性。

灵活性vs麻烦,利弊:

用p在vanilla类中的代码实例化的对象旋转的字段是安全的,并且是您的唯一责任。

另一方面,由代码用户实例化的具有受保护字段扩展类的对象是他们的责任,而不是您的责任。

因此,没有充分记录的受保护字段/方法,或者如果用户不太了解应如何使用此类字段和方法,则很有可能给自己和您造成不必要的麻烦。

另一方面,将大多数事物设为私有将降低用户的灵活性,甚至可能使他们放弃寻找可维护的替代方案,因为他们可能不想创建和维护分支只是为了让事情按自己的方式进行。

因此,真正重要的是私有,受保护和公共之间的良好平衡。

现在,决定私有和受保护之间的真正问题。

什么时候使用保护的?

每次您了解一个字段具有高度的灵活性时,都应将其编码为受保护的。
灵活性是:从变为null(始终检查null并将其识别为有效状态而不抛出异常),再到由类ex使用之前具有约束。 > = 0,<100等,并会自动修正为上溢/下溢的值,最多抛出一条警告消息。

因此,对于这种受保护的字段,您可以创建一个吸气剂并仅使用它(而不是直接使用field变量),而其他用户可能不使用它,以防万一他们希望对自己的特定代码具有更大的灵活性,在我的示例中可能是:如果他们希望负值在扩展类中可以正常工作。 />

#13 楼

imo @tdammers是不正确的,并且实际上具有误导性。

存在私有变量来隐藏实现细节。您的班级A可能正在使用array来存储分数。明天您可能要改用treepriority queue。您班上所有用户的需要是一种输入分数和名称addScore()的方法,以及一种确定谁是getTop(n)的前十名的方法。

他们不需要访问基础数据结构。听起来不错吗?嗯,有一些警告。

无论如何,您不应该存储很多状态,大多数状态都是不需要的。即使将状态“隔离”到特定的类中,存储状态也会使您的代码复杂化。考虑一下,私有变量可能已更改,因为另一个对象称为该类的方法。您仍然需要弄清楚该方法的调用时间和位置,并且理论上可以在任何地方调用该公共方法。

最好的办法是限制程序包含的状态量并使用纯尽可能的发挥作用。

#14 楼


访问变量意味着在程序运行时访问其值,
将变量私有化可在代码运行时保护其值。
所谓的“数据隐藏”的目的是保持内部对使用该类的其他类隐藏的数据
。那些其他类应该
仅通过调用类上的方法来访问行为,而不是直接更改
变量的值。
通过将变量设为私有数据成员,您可以更轻松地
确保该值永远不会被修改或更改。另一方面,
如果变量是公共的,则另一个类可以修改或更改
值,这可能导致代码的其他部分崩溃。


评论


在先前的17个答案中,这似乎并没有提供任何实质性的解释。

– gna
19年5月8日在5:16

#15 楼

您首先应该了解面向对象编程的概念。它具有抽象,封装等。

抽象-您无需了解实现的下划线详细信息即可了解逻辑。

封装-您看不到下划线对象的实现。只有您可以看到对象的公共接口。

现在在特定的实现中,使用诸如C#,Java,C ++的面向对象程序之一,就可以实现这些概念。

private-应该对外界隐藏的实现。这样您就可以更改它,并且不会影响类的用户。

public-这是一个可以使用您的对象的接口。

评论


protected-受子类(扩展程序)(java)直接访问。

–水瓶座力量
16年6月17日在2:13