面向对象访问说明符的推理

时间:2013-08-11 03:19:15

标签: java python oop

我有一个关于面向对象访问说明符的原因的一般性问题。我从来没有完全理解为什么它们存在的原因,只是认为它们是作为一种非常基本的代码安全形式存在但是在查看了这个线程的讨论之后

Does Python have “private” variables in classes?

我明白我完全错了,他们根本没有帮助安全。那些访问说明符只是被认为是面向对象设计中的良好编程实践吗?当我们说私人或受保护时,究竟是谁保护数据字段或类或方法?人们是否仍然可以访问已经知道它的方法?通过反思和其他方法?

我很抱歉,如果这个问题看起来很基本或有点元抄袭,但它总是让我感到困扰,因为我不太了解封装的主要OOP概念之一的确切推理。

9 个答案:

答案 0 :(得分:7)

访问修饰符用于指示您打算如何使用您的代码。当你保持内部状态时,它们尤为重要。考虑一个保持计数的类:

public class Thing {
    public int count = 0;
    public void doSomething() { count++; }
    public int getHowManyTimesDone() { return count; }
}

此代码有什么问题?如果来电者修改了count,我的代码就违反了合同:

Thing x = new Thing();
x.doSomething();
x.doSomething();
x.count = 0;
System.out.println(x.getHowManyTimesDone());

我的班级合同说明应打印2,但会打印0,因为调用者修改了count。通过使count成为private变量,我告诉来电者,“嘿,你不应该触摸这个!这样做可能会使代码中断!”

Python doesn't have a concept of privacy。相反,按照惯例,前缀为变量的单个下划线向调用者提供相同的警告:“如果触摸它,它可能会破坏此代码!”这可能与protected最相似。双下划线也会触发名称修改,这表明即使是子类也不应该使用它;这最类似于private。在Python中,如果他们无论如何都要访问这些成员,那么调用者有责任接受风险,但鼓励程序员尽可能公开。

至于谁在Java中实现变量的可见性,它就是运行时本身。不过,确实有一些巧妙的方法可以解决这个问题。我相信反射提供了一些手段,任何进入字节码或JVM本身的人都可以做些什么。考虑一下模拟做的事情;他们可以说服JVM模拟是一种特殊的类型,即使它不是。

编辑:

还有一件事。在Java中,鼓励程序员保留所有实例变量private并使用方法来访问和改变它们。这是为了可维护性,而不是隐藏细节的哲学原因。由于Java没有C#或类似Python的属性机制,如果需要在获取或设置实例变量时添加逻辑,则需要修改依赖于该实例变量的所有代码以使用这些方法。通过始终使用方法来访问变量,可以最大限度地减少通过进行此类更改而中断的相关代码。换句话说,它是处理语言缺点的一个障碍。

答案 1 :(得分:3)

他们可以更容易地推断该计划。例如,如果你有一个带有公共setter的私有字段,它确保维护字段的某些属性,那么你只需要确保字段类中的代码不违反该属性;如果该字段是公开的,那么您需要确保所有类都不违反该属性。 (另一个类可以使用反射来破坏属性,但在这种情况下,你会在代码审查中捕获它并解雇负责的程序员。)

答案 2 :(得分:1)

  

我有一个关于面向对象访问说明符的原因的一般性问题。我从来没有完全理解为什么它们存在的原因,只是认为它们是作为一种非常基本的代码安全形式存在但是在查看了这个线程的讨论之后

在其他原因中,它们允许控制对类字段/属性的访问,并且它们允许绑定属性,以允许监听器收到对它们的任何更改的通知。

  

我明白我完全错了,他们根本没有帮助安全。

在我看来,这是一个过于宽泛和模糊的陈述。它们允许控制对财产的访问,并且因此本身增加了“安全性”。如果您不同意,那么为了进一步帮助您,请考虑告诉我们您的安全功能定义。

  

当我们说私人或受保护时,究竟是谁保护数据字段或类或方法?

可以从任何基础Java教程或书籍中获取此信息。

  

人们是否仍然可以访问已经知道它的方法?通过反思和其他方法?

是的,可以访问所有方法,但这样做不需要特别小心,可能会导致代码非常脆弱。

  

但它总是困扰我,我不太清楚封装的主要OOP概念之一的确切推理。

您将继续学习(我们都必须这样做),包括有关该主题的开始和高级文本。

答案 3 :(得分:1)

在“你想要发生什么” - 抽象设计 - 和“你如何实现这一目标”之间的编程中存在着持续的紧张关系,这是实现某些结果的规则的具体表达。如果我们有一台计算机Oracle(不要与Oracle-the-company混淆),我们可能会问“生命的意义是什么?”并得到答案(当然是42)。但我们不这样做:我们必须告诉计算机以繁琐的小步骤做事。

只要将问题分解为可执行算法,并使用组件,您就会开始遇到与这些部分交互的问题。程序员弄错了,导致碎片交互不正确。需求变更:一个程序由许多做A的部分组成,现在必须做B(或者另外)。出于成本和时间的原因,我们希望重新使用旧代码来做新事物。所有这些导致了错误。如果我们能够拥有可理解的,乖巧的大步骤,我们只能用可理解的,乖巧的小步骤来构建......

许多早期语言都是“动词导向的”:创建一个简单的机器级变量(如INTEGERREAL),修改它,ADD X AND Y GIVING Z等等。如果您需要构建大量的状态来描述某些内容,例如Mars Lander,那么您拥有大量包含所有部分的变量。然后,将错误的动词应用于错误的部分变得非常容易,例如尝试读取降落伞的温度,或者使用仅适用于车轮的功能旋转轨道推进器。

“类型化”语言提供了一种抑制不良交互的方法。如果rotate函数/过程仅在wheel类型上运行,则不能在RCS系统上意外调用它。但最后你会得到大量动作动词,thruster_rotatewheel_rotate(尽管某些类型语言提供了名称空间,因此您可以thruster.rotate(thruster_id)或类似,但不会干扰{ {1}})。

面向对象编程提供了一种不同的方法:程序的状态可以根据各个“对象状态”保存,访问者可以对这些对象进行操作。现在wheel.rotate(wheel_number)可以在RCS推进器上工作,而thruster.rotate()可以在车轮上工作。它是,或者至少可以更清楚什么是在做什么。 (请注意,OOPL也可以并且通常也具有花哨的命名空间。)

唉,人们仍然会犯错误。访问者为您(作为程序员)提供了一种方法,允许系统的其他部分(通常由其他人编写)以一种良好控制的方式与“您的对象”进行交互,并且可能对他们更有用。如果您有温度传感器,即使在内部,您也可以同时提供wheel.rotate()degrees_F的读数,实际读数为微伏。保护(无论松散执行 - 已经注意到C ++必须来自Free Love时代,因为它让朋友可以访问您的私人部分)至少允许您以代码形式说明您的意图关于谁应该摆弄什么。

最后,编程通常与抽象有关:暴露需要暴露的东西,但同时隐藏任何不应暴露的东西。访问器和保护使您可以直接控制暴露的内容和隐藏的内容。即使它们主要是建议性的(如在Python中),它们仍然是一个直接的声明:“如果你没有使用这个模块,你就不应该使用或修改这个东西。它只是一个实现细节,而不是一部分接口。“

答案 4 :(得分:0)

据我了解,使用访问说明符(访问修饰符)(如public,private,static,protected)背后的一个主要原因是最大限度地减少了编程中出现意外错误的可能性。

在面向对象编程中存在这些是由于封装的概念。当您开发大型软件时,访问说明符非常方便,无论是安全性还是最大限度地降低在不需要时对数据/类/方法进行不必要访问的风险。

答案 5 :(得分:0)

访问说明符有很多原因,但通常归结为易于创建和维护代码。其他好的答案也在这里,我只是放下一些额外的想法。

考虑到许多开发,特别是在Java中,使用某种IDE。一个好的IDE足够聪明,只能在实际看到的小弹出列表中显示变量或方法。因此,如果我只使用一个类而不是编辑它,每当我编写该类的对象时,只会显示一个已过滤的方法列表,从而删除所有内部变量和方法;这使编码更容易。

访问说明符允许你做的另一件很酷的事情就是搞砸了getter和setter;基本上,如果一个成员是私人的,你只能通过一个getter或setter访问它;但这意味着您可以将巧妙的附加代码放入这些方法中。也许一个制定者也会召唤一个事件来警告其他人已经设定了价值;或者一个getter比获取变量的值更复杂,也许它会进行一些计算。如果您不必使用该setter并且只能直接访问该变量,那么一些开发人员可能会因为上述原因而错过他们应该使用setter的事实,这显然会产生问题。

答案 6 :(得分:0)

我一直在问自己这个问题。我主要关心的是如何使对象的“所有者”(创建它的相同或不同类的对象)可以访问某些方法,而不是“观众”(由“所有者”提供引用的对象)只能调用某些方法)。当然,这可以推广到2个以上的权限类别。

我认为大多数(如果不是全部)编程语言提供的机制与实现这样的目标相差甚远。它们更不稳定,并且在许多情况下在运行时不可执行(或有效实施)。但是,作为评论/文档,它们非常有价值。任何愿意尊重类的预期封装的开发人员都会在IDE的帮助下听取这些指示。

封装的目的不是保护类的开发人员(和对象),而是开发人员(和对象?)使用该类。 “私人”修改器类似于标签“危险电压。该设备仅由经过严格培训的人员提供服务”。如果您继续寻找修改变量的方法,那么该类很可能会崩溃。但有了它,你的应用程序崩溃了。如果你去原来的autor抱怨,你认为会有什么回答?如果您向带有此类标签的机器制造商投诉会怎么样?好。你没有改变任何东西,但你只是有一个“偷看”并让你的代码读取并依赖于该字段的值。如果在未来版本中原始类的开发人员决定更改字段的含义,该怎么办?也许它的测量单位?还是完全删除它?当然,你的程序会崩溃。但是,如果你试图抱怨,他/她会回答什么?最可能的确是妈妈做了很多次:“我告诉你了!”。

我确实为坐在Java上的对象创建了一个“安全”机制。决定如果没有编程语言的合作,那就不值得付出努力了。不过,这是在AOP和注释之前。也许我应该尝试一下。

我想我在上面解释了private的含义。相反的访问修饰符当然是public,它不值得更多解释。 protected和“包默认”(缺少访问修饰符)对应标签“危险电压。此单元仅由经过适当培训的人员进行维修”。谁是“训练有素的人员”?开发人员将了解类是否更改并在必要时调整自己的代码。执行工作的人必然需要了解内部性课程。这是与相关类相同的包中的类的开发人员(“包默认”),以及相关类的子类的开发人员或同一包中的类的开发人员(protected)。

有关更具体的详细信息,请参阅Oracle's Java "Object Orientation" tutorial

答案 7 :(得分:0)

我将把封装的优点留给其他答案(不同的编程语言对此有不同的理解,程序员也是如此)。

但是,我将解决安全性的含义。正如在SO上的许多答案一样,只是因为它得到了很多赞成,并没有使它正确(在OP中链接的问题)。虽然 有许多方法可以解决普通jvm中的访问“限制”,但意味着访问修饰符没有真正的安全隐患。如果您曾经运行过“不受信任”的applet,那么您已经依赖于java语言的访问修饰符的安全性。这是因为applet在带有 SecurityManager 的jvm中运行。当配置SecurityManager时,运行代码不能绕过访问修饰符限制,这些限制具有非常真实的安全隐患。例如,如果不信任可以修改String实例的内容,那么绕过jvm SecurityManager并打破安全沙箱(并删除你的硬盘)将是相当简单的。

答案 8 :(得分:0)

访问说明符就像储物柜钥匙。您必须具有钥匙才能打开最高管理的正门或房间门,否则任何人都可以打开这些门。它是最低级别的信息安全。您的负责人拥有的信息存储在组织的CEO,CFO等人的负责人中。