定义访问修饰符有什么意义?

时间:2009-12-11 14:45:49

标签: oop accessor

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

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

如果这是一个愚蠢的问题,我道歉。这是我在OOP上读过的第一篇文章中遇到的问题,我从来没有觉得它真的被点击了。

7 个答案:

答案 0 :(得分:10)

只有当你是唯一的程序员时,

I'm the programmer才是正确的假设。

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

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

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


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

假设您在发动机舱内销售没有发动机罩的汽车。到了晚上,司机开灯。他到达目的地,下车,然后记得他把灯打开了。他太懒了,无法解锁车门,所以他将电线拉到灯的外面,并将灯连接到电池上。这很好 - 光线不亮了。然而,因为他没有使用预期的机制,所以下次他在黑暗中驾驶时会发现自己有问题。

住在美国(继续,向我投票!),他拒绝承担错误使用汽车内部的责任,并起诉你,制造商用于制造一种在黑暗中驾驶不安全的产品,因为灯光关闭后无法可靠地打开。

这就是为什么所有汽车的引擎舱都有引擎盖的原因:)


一个更严重的例子:您创建一个Fraction类,其中包含分子和分母字段以及一组操作分数的方法。您的构造函数不允许其调用者创建具有0分母的分数,但由于您的字段是公共的,因此用户很容易将现有(有效)分数的分母设置为0,并且随之而来的是欢闹。

答案 1 :(得分:7)

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

  1. 隐藏课程操作的内部结构可以保护课程免受意外使用。虽然您可能是课程的创建者,但在很多情况下您不会是唯一的课程消费者 - 甚至是维护者。隐藏内部状态可以为那些可能不了解其工作原理的人提供保护。让一切公开会产生诱惑,当类没有按照你想要的方式行事时“调整”内部状态或内部行为 - 而不是实际纠正内部实现的公共接口。这是毁灭的道路。

  2. 隐藏内部有助于解除命名空间,并允许Intellisense等工具仅显示相关且有意义的方法/属性/字段。不要对Intellisense等工具进行折扣 - 它们是开发人员快速确定他们可以为您的班级做些什么的有力手段。

  3. 隐藏内部结构允许您构建适合该类正在解决的问题的界面。暴露所有内部(通常大大超过暴露的界面)使得以后很难理解该课程试图解决的问题。

  4. 隐藏内部使您可以将测试重点放在适当的部分 - 公共界面上。当一个类的所有方法/属性都是公共的时,您必须测试的排列数量会显着增加 - 因为任何特定的调用路径都可以实现。

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

  6. 隐藏内部实施可以让您在知道不会对您的班级消费者产生负面影响的情况下进行更改 - 只要您的公共接口保持不变即可。如果您决定在内部使用字典而不是列表 - 没有人应该关心。但是如果你使你的班级的所有内部都可用,那么有人可以编写代码,这取决于你内部使用列表的事实。想象一下,当您想要更改有关实施的选择时,必须更改所有消费者。黄金法则是:一个班级的消费者不应该关心班级如何做它的作用。

答案 2 :(得分:3)

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

使事物非公开允许您明确定义类的外部接口。非公共内容不是外部接口的一部分,这意味着您可以在内部更改任何内容,而不会影响使用外部接口的任何人,

答案 3 :(得分: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 Profit !!!

当然,如果您是唯一一位修改或使用该课程的程序员,您可以将其公之于众。

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

答案 4 :(得分:1)

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

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

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

答案 5 :(得分:1)

对于某些类,拥有私有成员可能看起来很荒谬,有一堆方法可以设置并获取这些值。这样做的原因是,假设你有一个成员公开且可直接访问的课程:

class A 
{
    public int 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...
         }
    }
}

这隐藏了所有错误检查。虽然这个例子很陈旧,但这些情况经常出现。

答案 6 :(得分:1)

根本与安全无关。

访问修改器和范围都是关于结构,层,组织和通信。

如果你是唯一的程序员,那么在你有这么多代码甚至你不记得之前它可能会很好。那时,它就像一个团队环境 - 访问修饰符和代码结构引导您保持在体系结构中。