【问题标题】:Does each public property has to have accessor methods?每个公共属性都必须有访问器方法吗?
【发布时间】:2010-08-11 16:36:18
【问题描述】:

我正在进入 OOP,但遇到了以下两难境地。我有一个类:

class Recipe {

    var $title;
    var $time;
    var $ingredients = array();
    var $instructions;
    var $category;

    function __construct($title, $time, $ingredients, $instructions, $category) {

        $this->title = $title;
        ...
    }

    function getTitle() {
        return $this->title;
    }

}

所有属性都是公开的(默认情况下)。我是否必须为所有这些属性(例如 getTitle)定义访问器方法,或者我可以直接引用这些属性,如下所示:

...
$recipe = new Recipe($title, $time, $ingredients, $instructions, $category);
echo $recipe->title; // versus $recipe->getTitle();

看起来我将节省大量时间而不必定义访问器方法。但是我想知道这种方法的优缺点是什么?

【问题讨论】:

    标签: php oop object


    【解决方案1】:

    OOP 的黄金法则是始终将您的属性设为私有!在极少数情况下允许使用公共属性,但即便如此,也可能有替代解决方案。

    原因是:如果您公开您的属性,任何人都可以将它们更改为他们想要的任何内容。大多数属性不能只是任何值。你的 $title 属性应该是一个整数吗?我对此表示高度怀疑。那么,如果您或其他人不小心将其设置为整数怎么办?你不会发现它。它将设置该值,并且您的程序将继续运行,直到它失败,因为在某个地方需要一个字符串。此外,您的属性可能应该在设置之前以某种方式进行验证。您可以在属性的设置器中包含所有这些验证。

    即使您不需要验证属性,您仍然最好将它们放在 getter 和 setter 之后,以防最终您确实需要验证它。

    将您的属性设为私有可确保您的对象在不应该发生的情况下不会乱七八糟,从而避免由此导致的任何错误。 :)

    有时您会想“好吧,只有我会编辑我的代码,所以什么都不会发生”。但是,您现在应该练习这样做。养成这样做的习惯。以后你会避免任何麻烦。

    【讨论】:

    • 这很有意义!那么为什么在PHP中默认所有属性都设置为public而不是private?
    • 我想首先是因为这意味着你不必在每个方法前面都加上“public”。此外,可能存在需要保持公开的向后兼容性问题。尽管我似乎找不到任何文档说明为什么它们默认是公开的。注意,在 C++ 中,属性默认是私有的。只需确保您知道开始使用特定语言时的方式。 :)
    • Please read up on PHP5-era OO,其中包括可访问性关键字。您当前的风格是 PHP4 时代,没有人应该将其用于新代码。
    • @Charles,可访问性关键字是什么?我无法在 php doc 中找到参考。您是指像 _set_get 这样的魔术方法吗?
    • @mario,我认为应该强调最佳实践,因为很明显 OP 是初学者。教他们封装的价值以及如何实现它,然后说明何时可以弯曲/打破规则而不是相反。知道什么时候不要强调封装总比完全不熟悉这个概念要好。
    【解决方案2】:

    我会说不应该使用不必要的 setter/getter 负载,尤其是在 PHP 中,否则您的应用程序可能会变得非常慢。 这是一个简单的例子:

    <?php
    class Recipe {
    
        public $title;
    
        function __construct($title){
            $this->title = $title;
        }
    
        function getTitle(){
            return $this->title;
        }
    
    }
    
    $a = new Recipe('potatoes');
    $t1 = microtime(true);
    for($i=0;$i<1000000;$i++){
        $x = $a->title;
    }
    $t2 = microtime(true) - $t1;
    echo $t2.'s<br/>';
    
    $a = new Recipe('potatoes');
    $t1 = microtime(true);
    for($i=0;$i<1000000;$i++){
        $x = $a->getTitle();
    }
    $t2 = microtime(true) - $t1;
    echo $t2.'s<br/>';
    
    ?> 
    

    回声:

    0.25662112236023s

    1.0309250354767s

    使用 getter 慢 4 倍!

    【讨论】:

    • 如果我正确理解了您的代码,则相反...使用 getter 时速度快 4 倍。
    • 对不起,我以错误的顺序复制了结果,让我编辑我的帖子 - 并随时自己尝试测试:)
    【解决方案3】:

    Getter/Setter 在大多数脚本语言中是不受欢迎的。它们最初是专门与 Java 和 Java Bean 一起引入的。强封装在静态编译的代码中是有意义的。在脚本语言中,所有访问都通过解释器,而不是直接访问内存地址;因此有魔法方法。在 PHP 中,您可以通过 __get 路由所有内容,从而使包装器变得多余。

    getter/setter 的用例是详细过滤和验证方案。除非你想出一个具体的属性,否则准备空心的 setter 是没有意义的(无论如何,getter 很少转换值)。

    【讨论】:

      【解决方案4】:

      公共属性不需要 getter/setter 方法,但它使您更容易出错。使用访问器还可以让您强制验证数据,而直接设置公共属性可以允许设置任何数据。

      如果您利用 PHP 的神奇功能,您可以编写一个动态的 getter/setter 方法来访问私有/受保护的属性。

      <?php
      
      
      /**
       * Implements auto get/set
       * class Entity extends GetterSetter {}
       * $name = $entity->getName(); // return $this->name;
       * $name = $entity->getFullName(); // return $this->full_name;
       * $entity->setFullName("Ian"); // $this->full_name = "Ian";
       */
      
      class GetterSetter {
          public function __call($name, $args) {
              if (substr($name, 0, 3) == "get") {
                  $key = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', (substr($name, 3, strlen($name)-3))));
                  if (property_exists($this, $key)) {
                      //print "/* GET " . $this . "->" . $key . " = " . $this->$key . "\n */";
                      return $this->$key;
                  }
              } elseif (substr($name, 0, 3) == "set") {
                  $key = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', (substr($name, 3, strlen($name)-3))));
                  if (property_exists($this, $key)) {
                      //print "/* SET " . $this . "->" . $key . " = " . $args[0] . "\n */";
                      return ($this->$key = $args[0]);
                  } else {
                      print "Key not found: " . $this . "->" . $key;
                  }
              }
          }
      
      }
      ?>
      

      【讨论】:

      • 它通过防止设置任意数据来帮助执行验证并减少错误。
      • 我不能只使用 _get_set 代替吗?
      【解决方案5】:

      使用 getter/setter:

      • 您可以更改实施。例如,稍后您可能会更改 getTime 以计算 $instructions 的数量,而不是拥有一个成员 $time。
      • Setter 可以抛出异常、验证输入或新状态、更改其他数据、记录...

      将您的对象想象成其他对象希望如何使用它或它应该做什么。对象不仅仅是数据类型的列表。

      【讨论】:

        猜你喜欢
        • 2011-06-29
        • 1970-01-01
        • 2022-11-18
        • 1970-01-01
        • 1970-01-01
        • 2011-11-22
        • 1970-01-01
        • 2013-06-25
        • 1970-01-01
        相关资源
        最近更新 更多