为什么要使用 OOP 来隐藏信息?

Why would I use OOP to hide information?

提问人:Aaron 提问时间:2/6/2011 最后编辑:maurisAaron 更新时间:2/7/2011 访问量:343

问:

为什么我要使用 PHP 来隐藏信息?

<?php

function foo($bar) {
    echo $bar;
}

foo('foobar');

?>

<?php

class foo {
    private $bar;

    function __construct($b){
        $this->bar = $b;
    }

    function display() {
        echo $this->bar;
    }
}

$foobar = new foo('bar');
$foobar->display();

?>
php 哎呀

评论

2赞 Murilo Vasconcelos 2/6/2011
en.wikipedia.org/wiki/......
0赞 yankee 2/6/2011
在你的例子中,隐藏在哪里?
0赞 mpen 2/6/2011
@yankee:他保护了......我想这就是他所指的。$bar
0赞 mario 2/6/2011
它最初是静态编译语言(C++,Java)的一个特性,允许在不破坏ABI的情况下更改内部。一些脚本语言模仿它来实现命名奇偶校验 (PHP)。具有第一类对象的语言实际上并不需要它(Python、Javascript、Lua)。
0赞 David 2/6/2011
老实说,+1 本质上是一个关于 OOP 的相当简单的问题,它产生了相当多的良好回应。

答:

4赞 paxdiablo 2/6/2011 #1

信息隐藏(封装)是一件好事,因为它减少了客户使用“未记录”信息的可能性。

封装的全部意义在于最小化代码的接口,以便您可以随时更改未发布的位,而不会影响它的客户端。

例如,假设您有一个字典类,它将一个字符串映射到另一个字符串。让我们进一步假设您将其实现为一个快速而肮脏的双阵列解决方案,因为您落后于计划 - 显然有时会发生这种情况:-)而且你没有封装使用它的数组和方法。

因此,它已经在该领域使用了一段时间,数百万客户已经购买了它。他们中有一半人抱怨速度太快,因为他们的字典有十万个词条。

另一半则通过背着你直接使用你的内部但未隐藏的数据结构来使用它。

你是做什么工作的?如果你什么都不做,你的五十万客户将不会续签合同,他们会说你的产品坏话。

如果你把你的代码改成使用平衡的树或哈希,其他五十万客户会抱怨你的决定。

这是你最终要做的事情。你最终会维护两个版本,一个是针对大数据人员的,另一个是针对偷偷摸摸的人的。这大大增加了您的成本和工作量。

如果您正确地封装了您的数据和方法,您可以批量更换内部结构,而不会对您的任何客户产生任何影响。

7赞 9000 2/6/2011 #2

无论您发布什么内容,都会成为您的公共合同。其他人开始依赖你的公共方法,使用它们并期望它们在那里并工作。“其他人”也许是半年后的你自己。

因此,在公开内容方面要非常保守。它使您可以自由地发展代码并修复其中的错误,而不会破坏公共接口。

评论

2赞 David 2/6/2011
+1 表示“'其他人',也许是半年后的你自己。伊格森定律:你自己的代码,如果你六个月没有看过,也可能是别人写的。
2赞 mpen 2/6/2011 #3

例如,在开发库时,您可能希望对 API 的用户隐藏类的内部结构,这样,如果您决定更改类的工作方式,您不必担心破坏每个人的程序,因为他们一直在访问他们不应该访问的成员。您只显示您知道不会更改的界面,这样您就可以安全地修改这些函数的工作方式。

3赞 Michael Parkin 2/6/2011 #4

这样做是为了封装功能并保护使用代码免受更改。举个例子:

class Person
{
    protected $legs;

    public function setLegs($number){
         $this->legs = $number;
    }
}

让我们假设我们现在有一些使用这个类的代码......

if ($_POST['legs'])
{
   $person = new Person;
   $person->setLegs($_POST['legs']);
}

Ofcours 您可以使用以下类来执行此操作

class Person
{
   public $legs;
}

$person = 新人; $person->腿 = $_POST['腿'];

如果您需要向腿提供任何验证,或者您想稍后进行更改,则会出现问题。假设你必须对一个人的规则进行更改(例如,一个人可以拥有的最大腿数是十条)——如果你还没有封装逻辑,你现在需要遍历你的应用程序,找到它的用途......

class Person
{
    protected $legs;

    public function setLegs($number){ 
         if ( ! is_int($number) || $number > 10){
              throw new Exception('incorrect user input');
         }
         $this->legs = $number;
    }
}

封装很重要,原因如下:

  1. 它允许重用应用程序逻辑
  2. 这样以后可以更轻松地进行更改
3赞 David 2/6/2011 #5

你的例子似乎没有清楚地说明你在问什么。你是否想知道为什么人们会对类成员使用各种保护限定词,而不仅仅是公开所有内容?

从本质上讲,这是封装的概念。将一个类的外部可见的“足迹”视为其“契约”。该类已定义且在外部可见,它提供了一组功能。确切地说,该类如何在内部实现功能,与该类之外的任何内容都无关。它不仅不需要在类外为人所知,而且明确地不应该为人所知。

这导致了面向对象设计的进一步关键概念,称为关注点分离、依赖关系倒置、开放/封闭原则等。如果其他类/对象/等可以直接访问该特定类的所有内部结构,则可以编写代码来使用这些内部结构。私人成员、私人方法等。

这会在该外部代码和类的内部逻辑之间创建依赖关系,而该类不应公开该内部逻辑。这使得重新实现该类变得更加困难,将其换成另一个实现相同“契约”(尽管具有不同的内部功能),在不更改“契约”的情况下透明地添加额外的内部功能,等等。

编辑:您可能还想知道为什么人们会创建一个私有成员变量,然后公开一个公共 getter 和 setter 来操作该变量。当您可以首先公开变量时,为什么要经历额外的麻烦,对吧?

好吧,在某些情况下,这可能没问题。但请记住封装和维护合同的概念。如果您想更改类的某些内部实现并且变量受到影响怎么办?也许您需要稍微更改其类型?如果变量是公开的,特别是在静态类型的环境中(不是 PHP,但你明白了),任何这样的更改都将是合约的“重大更改”。

相反,如果有公共 getter/setter 方法,您可以根据需要更改私有变量,并在方法中执行任何必要的转换或逻辑,基本上保持更改透明。

或者,您可能想审核对该变量的更改...只需在 setter 方法中执行此操作(记录它,引发事件,无论您需要做什么),而不是在访问公共变量的代码中的任何地方(包括尚未编写的代码)。

将变量私有化并将它们包装在公共 getter/setter 中的习惯(必要时......并非每个私有成员变量都需要公共访问)是一个需要保持的好习惯。完全有可能,甚至很可能,你的绝大多数班级成员永远不会超越这个简单的逻辑。但是在相对罕见的情况下,如果 getter/setter 到位,它会容易得多

基本上,它是一种设计,它基本上只添加了很少的前期代码(一次性创建 getter/setter,而不是持续维护成员变量访问),以允许您自由地对类进行更改,而不会破坏对象的二进制兼容性并破坏其“契约”。

编辑:@mario在他的评论中提出了一个很好的观点,我想澄清一些事情。总是为所有私人成员编写 getters/setter 绝对不是一个好习惯。这个想法是将 getters/setter 用于逻辑上应该是公共的成员。例如,如果类在显示中定义了一段文本,并且 getter/setter 修改的成员是字体、大小、粗细等。从逻辑上讲,您将看到访问方法(在不同语言中以不同的方式处理)来设置类的这些属性。

这些属性可以在内部由单个私有成员维护,也可以由一个或多个成员组合维护,也可以通过其他方式等维护。这是不应该在课堂外暴露的部分。课堂之外的所有内容都知道“我正在告诉这个对象更改其字体粗细”。“我告诉这个对象将其变量设置为此值。”_font

另请注意,在外部,通过访问器被视为成员的内容可能根本不是访问私有成员,或者至少不是以相同的方式访问。一个很好的例子是只读访问器(geters),它表示对象的某个给定状态。 例如,可以在模型内部执行各种业务验证逻辑,并返回表示对象状态的 a。它不会返回某些私人成员的内容。IsValidboolbool

评论

1赞 mario 2/7/2011
+1 表示对不良设计习惯的努力和冗长的解释。javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html
0赞 David 2/7/2011
@mario:好读。我想我应该澄清一下,特别是我说过并非所有成员都应该有访问器的地方。我想更准确地说,大多数成员可能不需要访问器。许多类将具有可设置的属性,如果这些类在对象构造后逻辑上是可更改的,则这些属性应该具有访问器。我绝对主张盲目地为所有成员编写访问器。我绝对可以在我的回答中更清楚地说明这一点。