1

我了解它们之间的区别(至少在 C# 中)。我知道它们对分配给它们的元素的影响。我不明白为什么实施它们很重要——为什么不把所有东西都公开?

我读到的关于这个主题的材料通常是关于类和方法不应该对其他人进行不必要的访问,但我还没有遇到一个例子说明为什么/如何这将是一件坏事。这似乎是一件安全的事情,但我是程序员;我创建方法并定义它们将(或不会)做什么。我为什么要花费所有精力编写一个函数来尝试更改它不应该更改的变量,或者尝试读取另一个类中的信息,如果那样会很糟糕?

如果这是一个愚蠢的问题,我很抱歉。这只是我在我读过的关于 OOP 的第一篇文章中遇到的,我从来没有觉得它真的很成功。

4

7 回答 7

10

I'm the programmer仅当您是唯一的程序员时才是正确的假设。

在许多情况下,其他程序员使用第一个程序员的代码。他们通过摆弄他们不应该使用的字段值来以他不打算的方式使用它,并且他们创建了一个有效的 hack,但是当原始代码的生产者更改它时会中断。

OOP 是关于创建具有明确定义的合约的库。如果您的所有变量都是公开的并且可供其他人访问,那么“合同”理论上包括对象(及其子对象)中的每个字段,因此构建仍然遵守原始合同的新的不同实现变得更加困难。

此外,对象的“移动部分”暴露得越多,类的用户就越容易错误地操作它。


你可能不需要这个,但这里有一个我认为很有趣的例子:

假设您出售的汽车在发动机舱上方没有引擎盖。到了晚上,司机打开灯。他到了目的地,下了车,然后记得他把灯开着了。他懒得打开车门,所以他把连接到灯的电线从连接电池的地方拉出来。这工作正常 - 灯熄灭了。然而,因为他没有使用预期的机制,所以下次他在黑暗中开车时会发现自己遇到了问题。

住在美国(去吧,给我投反对票!),他拒绝为他对汽车内脏的不正确使用负责,并起诉你,制造商制造了一种在黑暗中驾驶不安全的产品,因为灯不能关机后能可靠开机。

这就是为什么所有汽车的发动机舱都有引擎盖 :)


一个更严重的例子:你创建了一个Fraction类,它有一个分子和分母字段以及一堆操作分数的方法。您的构造函数不允许其调用者创建分母为 0 的分数,但由于您的字段是公开的,因此用户很容易将现有(有效)分数的分母设置为 0,然后就会产生欢闹。

于 2009-12-11T14:50:26.693 回答
7

首先,该语言中没有任何内容强制您使用访问修饰符——如果您愿意,您可以自由地将所有内容公开在您的班级中。但是,使用它们有一些令人信服的理由。这是我的观点。

  1. 隐藏类如何操作的内部结构可以保护该类不被意外使用。虽然您可能是该类的创建者,但在许多情况下,您不会是唯一的消费者——甚至不是维护者。隐藏内部状态可以保护那些可能不像你一样理解它的工作的人。当类没有按照您可能想要的方式运行时,将所有内容公开会产生“调整”内部状态或内部行为的诱惑 - 而不是实际纠正内部实现的公共接口。这是毁灭之路。

  2. 隐藏内部有助于整理命名空间,并允许 Intellisense 等工具仅显示相关且有意义的方法/属性/字段。不要小看 Intellisense 之类的工具——它们是开发人员快速确定他们可以对您的类做什么的强大手段。

  3. 隐藏内部允许您构建适合类正在解决的问题的接口。暴露所有的内部(通常大大超过暴露的接口)使得以后很难理解这个类试图解决什么。

  4. 隐藏内部结构使您可以将测试集中在适当的部分- 公共接口上。当一个类的所有方法/属性都是公共的时,您必须潜在测试的排列数量会显着增加 - 因为任何特定的调用路径都成为可能。

  5. 隐藏内部结构可以帮助您控制(强制执行)通过您的类的调用路径。这样可以更轻松地确保您的消费者了解可以要求您的班级做什么以及何时做。通常,您的代码中只有几条路径是有意义且有用的。允许消费者采取任何方式使他们更有可能无法获得有意义的结果——并将其解释为您的代码有问题。限制你的消费者如何使用你的类实际上可以让他们正确地使用它。

  6. 隐藏内部实现可以让您自由地更改它,因为它不会对您的类的消费者产生不利影响——只要您的公共接口保持不变。如果您决定在内部使用字典而不是列表 - 没有人应该关心。但是,如果您使类的所有内部都可用,那么有人可以编写代码,这取决于您在内部使用列表这一事实。想象一下,当您想要更改有关您的实施的此类选择时,必须更改所有消费者。黄金法则是:一个类的消费者不应该关心这个类是如何做的。

于 2009-12-11T14:54:27.970 回答
3

它主要是一种隐藏和共享的东西。您可以生成和使用所有自己的代码,但其他人提供库等以更广泛地使用。

将事物设为非公开允许您显式定义类的外部接口。非公开的东西不是外部接口的一部分,这意味着您可以在内部更改任何您想要的内容,而不会影响使用外部接口的任何人,

于 2009-12-11T14:51:32.433 回答
2

您只想公开 API 并隐藏其他所有内容。为什么?好的,假设您想制作一个很棒的 Matrix 库,因此您制作

class Matrix {
   public Object[][] data //data your matrix storages
   ...
   public Object[] getRow()
}

默认情况下,使用您的库的任何其他程序员都希望通过利用底层结构来最大化其程序的速度。

//Someone else's function
Object one() {data[0][0]}

现在,您发现使用 list 来模拟矩阵会提高性能,因此您可以从

Object[][] data => Object[] data

导致Object one()破裂。换句话说,通过更改您的实现,您破坏了向后兼容性:-(

通过封装,您可以将内部实现与外部接口(通过private修饰符实现)分开。这样你就可以在不破坏向后兼容性的情况下尽可能多地改变实现:D 利润!!!

当然,如果您是唯一会修改或使用该类的程序员,您最好将其公开。

注意:封装你的东西还有其他主要好处,这只是众多好处之一。有关详细信息,请参阅封装

于 2009-12-11T14:59:54.370 回答
1

我认为最好的理由是为您的代码提供抽象层。

随着应用程序的增长,您将需要让您的对象与其他对象交互。拥有可公开修改的字段会使您更难围绕整个应用程序进行思考。

限制您在类上公开的内容可以更轻松地抽象您的设计,以便您可以理解代码的每一层。

于 2009-12-11T14:55:13.443 回答
1

对于某些类来说,拥有私有成员可能看起来很荒谬,并且有一堆方法只是设置和获取这些值。其原因是,假设您有一个成员是公共且可直接访问的类:

class A 
{
    public int i;

   ....
}

现在你继续在你写的一堆代码中使用它。现在在编写了一堆直接访问 i 的代码之后,现在您意识到 i 应该对其有一些限制,例如 i 应该始终 >= 0 并且小于 100(为了论证)。现在,您可以检查所有使用 i 的代码并检查此约束,但您可以添加一个公共的 setI 方法来为您执行此操作:

class A
{
    private int i;
    public int I 
    {
        get {return i;}
        set 
        {
            if (value >= 0 && value < 100)
                i = value;
            else
                throw some exception...
         }
    }
}

这隐藏了所有错误检查。虽然这个例子很老套,但像这样的情况却经常出现。

于 2009-12-11T14:57:43.873 回答
1

它根本与安全无关。

访问修饰符和范围都与结构、层、组织和通信有关。

如果你是唯一的程序员,那么在你有这么多甚至不记得的代码之前,这可能没问题。在这一点上,它就像一个团队环境 - 访问修饰符和代码结构引导您留在架构中。

于 2009-12-11T14:57:52.783 回答