【问题标题】:Object Oriented PHP Best Practices面向对象的 PHP 最佳实践
【发布时间】:2011-01-25 08:30:24
【问题描述】:

假设我有一个代表一个人的类,该类中的一个变量将是 $name。

以前,在我的脚本中,我会创建一个对象的实例,然后使用以下方法设置名称:

$object->name = "x";

但是,有人告诉我这不是最佳做法?我应该有一个函数 set_name() 或类似的东西:

function set_name($name)
{
    $this->name=$name;
}

这对吗?

如果在这个例子中我想在数据库中插入一个新的“人”记录,我如何将关于这个人的所有信息,即 $name、$age、$address、$phone 等传递给类,以便插入它,我应该这样做:

function set($data)
{
    $this->name= $data['name'];
    $this->age = $data['age'];
    etc
    etc

}

然后给它发送一个数组?这会是最佳实践吗?或者有人可以推荐最佳做法吗?

【问题讨论】:

    标签: php oop


    【解决方案1】:

    你应该有 setter/getter 方法。它们很痛苦,但您不一定要自己编写它们。只要您提供类成员,IDE(例如 Eclipse 或 Netbeans)就可以自动为您生成这些。但是,如果您根本不想处理这个问题并且您使用的是 PHP5,那么您可以使用它的神奇方法来解决这个问题:

       protected $_data=array(); 
       public function __call($method, $args) {
            switch (substr($method, 0, 3)) {
                case 'get' :
                    $key = strtolower(substr($method,3));
                    $data = $this->_data[$key];
                    return $data;
                    break;
                case 'set' :
                    $key = strtolower(substr($method,3));
                    $this->_data[$key] = isset($args[0]) ? $args[0] : null;
                    return $this;
                    break;
                default :
                    die("Fatal error: Call to undefined function " . $method);
            }
        } 
    

    每次您使用以 set 或 get 开头的不存在的方法时,都会运行此代码。所以你现在可以像这样设置/获取(并隐式声明)变量:

    $object->setName('Bob');
    $object->setHairColor('green');
    
    echo $object->getName(); //Outputs Bob
    echo $object->getHairColor(); //Outputs Green
    

    无需声明成员或 setter/getter 函数。如果将来您需要向 set/get 方法添加功能,您只需声明它,本质上就是覆盖魔术方法。 此外,由于 setter 方法返回 $this,您可以像这样 chain 他们:

     $object->setName('Bob')
            ->setHairColor('green')
            ->setAddress('someplace');
    

    这使得代码既易于编写又易于阅读。

    这种方法的唯一缺点是它使你的类结构更难辨别。由于您实际上是在运行时声明成员和方法,因此您必须在执行期间转储对象以查看它包含的内容,而不是读取类。 如果您的类需要声明一个明确定义的接口(因为它是一个库和/或您希望 phpdoc 生成 API 文档),我强烈建议您在上面的代码中声明面向公众的 set/get 方法。

    【讨论】:

    • +1 当我看到这个问题时,我立刻就沿着这些思路思考。好答案!
    • 这是一个有趣的技术。
    • 就像您提到文档缺点的方式一样。有道理:)
    【解决方案2】:

    对对象的属性使用显式的 getter 和 setter(例如您为 set_name 提供的示例)而不是直接访问它们会给您(除其他外)以下优点:

    • 您可以更改“内部”实现,而无需修改任何外部调用。这样,“外部”代码不需要经常更改(因为您提供了一致的访问方式)。
    • 您非常明确地提供了要从类外部使用/调用的属性。如果其他人开始使用您的课程,这将非常有用。

    上述原因就是为什么这可以被认为是最佳实践,尽管这样做并不是真正必要(并且对于某些用途可能被认为是矫枉过正;例如,当您的对象执行很少的“处理”时' 但仅充当 'data' 的占位符)。

    【讨论】:

    • 使用 getter/setter 的另一个重要的事情是避免将所有方法都设置为public,从而防止开发人员在不受控制的情况下更改/访问它们。
    【解决方案3】:

    我完全同意 CristopheD 的观点(投了赞成票)。我只是在创建一个新时添加一个好的做法。

    通常,使用接受必填字段并为可选字段设置默认值的构造函数。比如:

    class Person
    {
      private $name;
      private $surname;
      private $sex;
    
      // Male is the default sex, in this case
      function Person($name, $surname, $sex='m'){
        $this->name = $name;
        $this->surname = $surname;
        $this->sex = $sex;
      }
    
      // Getter for name
      function getName()
      {
        return $this->name;
      }
    
      // Might be needed after a trip to Casablanca
      function setSex($sex)
      {
         $this->sex = $sex;
      }
    }
    

    显然,您可以在构造函数中使用 setter 方法(注意性设置器的重复代码)。

    【讨论】:

    • “去卡萨布兰卡旅行后可能需要”LOL
    • 要记住两件事:1. php 5+ 中的构造函数具有以下结构:__construct(){} 2. 在 php 中,您不能创建两个具有不同签名的构造函数,例如 Person(String $name) 和 Person(),你只能有一个。 (不过,有一些方法可以很容易地实现这一点。)
    【解决方案4】:

    要实现完整的 OOP,您应该执行以下操作:

    class User {
    
    private $_username;
    private $_email;
    
    public function getUsername() {
        return $this->_username;
    }
    public function setUsername($p) {
        $this->_username = $p;
    }
    ...
    public function __construct() {
        $this->setId(-1);
        $this->setUsername("guest");
        $this->setEmail("");
    }
    public function saveOrUpdate() {
        System::getInstance()->saveOrUpdate($this);
    }
    }
    

    如果你想保存一个用户,你只需创建一个用户,使用 Setters 分配它的值并执行 $user->saveOrUpdate(),然后让另一个类来处理所有的保存逻辑。

    【讨论】:

      【解决方案5】:

      与 ChristopheD 的回答相反,如果您的实例变量严格供私人使用,我不会费心编写 getter 和 setter,只需将实例变量声明为私有。

      如果其他对象需要访问该对象,您可以随时添加一个 getter。 (这暴露了另一个问题,因为其他类可能能够更改 getter 返回的对象。但您的 getter 总是可以返回实例变量的副本。)

      此外,使用 getter/setter 还可以防止同一类的其他部分了解它自己的实现,我发现它有时非常有用!

      【讨论】:

        【解决方案6】:

        从更一般的角度来看,直接访问($person->name)和访问方法($person->getName)都被认为是有害的。在 OOP 中,对象不应共享有关其内部结构的任何知识,而仅执行发送给它们的消息。示例:

        // BAD
        
        function drawPerson($person) {
          echo $person->name; // or ->getName(), doesn't matter
        }
        
        $me = getPersonFromDB();
        drawPerson($me);
        
        // BETTER
        
        class Person ....
           function draw() {
               echo $this->name;
            }
        
        $me = getPersonFromDB();
        $me->draw();
        

        更多阅读:http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多