3

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

Python 在类中有“私有”变量吗?

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

如果这个问题看起来很基本或有点元循环,我深表歉意,但它总是困扰着我,因为我不太清楚封装的主要 OOP 概念之一的确切原因。

4

9 回答 9

8

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

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 没有隐私的概念。相反,按照惯例,变量前缀的单个下划线向调用者提供相同的警告:“如果你触摸它,它可能会破坏这段代码!” 这可能最类似于protected. 双下划线也会触发名称修饰,这表明即使是子类也不应该使用它;这将最类似于private. 在 Python 中,调用者有责任接受访问这些成员的风险,但鼓励程序员尽可能公开。

至于谁在 Java 中实现变量的可见性,那就是运行时本身。不过,确实有一些聪明的方法可以解决这个问题。我相信反射提供了一些方法,任何进入字节码或 JVM 本身的人都可以做一些事情。考虑 mocks 所做的事情;他们可以让 JVM 相信 mock 是一种特定类型,即使它不是。

编辑:

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

于 2013-08-11T03:33:32.420 回答
3

他们的存在是为了更容易地对程序进行推理。例如,如果您有一个带有公共设置器的私有字段,以确保维护该字段的某些属性,那么您只需要确保该字段类中的代码不会违反该属性;如果该字段是公开的,那么您需要确保所有类都不会违反该属性。(另一个类可以使用反射来违反该属性,但在这种情况下,您会在代码审查中发现它并解雇负责的程序员。)

于 2013-08-11T03:27:58.193 回答
1

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

一旦你将一个问题分解为一个可执行的算法,使用组件,你就会开始遇到这些组件如何交互的问题。程序员弄错了,导致片段交互不正确。需求变化:一个由许多执行 A 的部分组成的程序,现在必须执行 B 代替(或附加)。出于成本和时间的原因,我们希望重新利用旧代码来做新的事情。所有这些都会导致错误。要是我们能有可理解的、行为良好的大步骤,我们只是由可理解的、行为良好的小步骤组成……

许多早期的语言都是“面向动词的”:创建一个简单的机器级变量(如INTEGERor REAL)、修改它、ADD X AND Y GIVING Z等等。如果您需要构建大量状态来描述某物(例如火星着陆器),那么您有大量变量来保存所有部分。然后将错误的动词应用于错误的部分变得太容易了,例如尝试读取降落伞的温度,或者使用仅在轮子上起作用的功能旋转轨道推进器。

“类型化”语言提供了一种抑制不良交互的方法。如果rotate函数/过程只对一个wheel类型进行操作,你就不会在 RCS 系统上意外调用它。但是最终你会得到大量的动作动词,thruster_rotate并且wheel_rotate(尽管一些类型语言提供了命名空间,以便你可以做thruster.rotate(thruster_id)或类似的事情,这不会干扰wheel.rotate(wheel_number))。

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

唉,人还是会犯错。访问器为您(作为程序员)提供了一种方法,允许系统的其他部分(通常由其他人编写)以良好控制的方式与“您的对象”交互,并且可能对他们更有用。如果您有温度传感器,您可以提供两者的读数,degrees_F即使degrees_C在内部,实际读数也以微伏为单位。保护(无论执行得多么松散——有人指出,C++ 必须来自自由恋爱时代,因为它让朋友可以访问你的私人部分)至少允许你以代码形式陈述你的意图,即谁应该摆弄什么.

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

于 2013-08-11T04:20:03.467 回答
1

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

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

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

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

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

这些信息可以从任何基本的 Java 教程或书籍中轻松获得。

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

是的,可以访问所有方法,但是如果不小心这样做会导致代码极其脆弱。

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

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

于 2013-08-11T03:25:24.933 回答
0

据我了解,使用访问说明符(访问修饰符)(如 public、private、static、protected)背后的关键原因之一是尽量减少在编程中出现意外错误的可能性。

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

于 2013-08-11T03:42:17.527 回答
0

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

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

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

于 2013-08-11T04:15:17.573 回答
0

我也一直在问自己这个问题。我主要关心的是如何使对象的“所有者”(创建它的相同或不同类的对象)可以访问某些方法,而不是“旁观者”(由“所有者”提供引用但仅调用某些方法)。当然,这可以概括为超过 2 个特权类别。

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

封装的目的不是保护类的开发者(和对象),而是保护使用该类的开发者(和对象?。“私人”修饰符类似于标签上写着“危险电压。该装置只能由训练有素的人员维修。” 如果您继续找到修改变量的方法,则该类很可能会崩溃。但有了它,它就是你的应用程序崩溃了。如果你去找原作者投诉,你觉得会是什么答案?如果您向带有此类标签的机器制造商投诉会怎样?好的。你没有改变任何东西,但你只是有一个“窥视”并让你的代码阅读并依赖于该字段的值。如果在未来的版本中,原始类的开发人员决定更改字段的含义怎么办?也许它的测量单位?还是彻底删除?你的程序当然会崩溃。但同样,如果你试图抱怨,他/她会怎么回答?很可能正是妈妈多次做过的事情:“我告诉过你!”。

我确实为位于 Java 之上的对象创建了一个“安全”机制。决定没有编程语言的协作,这是不值得的。不过,这是在 AOP 和注释之前。也许我应该再试一次。

我想我在上面解释了 的含义private。相反的访问修饰符当然是public,它不值得更多解释。 protected和“包装默认值”(没有访问修饰符)对应于标签“危险电压。本装置只能由受过中等培训的人员维修。” 谁是“中等训练人员”?开发人员会意识到类是否发生变化,并在必要时调整自己的代码。从事需要了解内在性课程的工作的人。也就是说,与所讨论的类在同一包中的类的开发人员(“包默认值”),以及所讨论的类的子类或同一包中的类的开发人员(protected)。

更具体的细节,请参阅Oracle 的 Java“面向对象”教程

于 2013-08-11T04:38:57.550 回答
0

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

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

于 2013-08-12T03:42:08.740 回答
0

访问说明符就像储物柜钥匙一样。你必须有钥匙才能打开你的大门或高层管理人员的房间门,否则任何人都可以打开这些门。它是最低级别的信息安全。您的头脑所拥有的信息存储在组织的首席执行官、首席财务官等的负责人中,不用担心。

于 2019-01-22T10:07:44.563 回答