面向对象的PHP最佳实践

时间:2010-03-09 09:14:58

标签: php oop

假设我有一个代表一个人的类,该类中的变量将是$ 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

}

然后发送一个数组?这是最好的做法吗?或者有人可以推荐最佳做法吗?

6 个答案:

答案 0 :(得分:29)

你应该有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 :(得分:21)

对对象的属性使用显式getter和setter(就像您为set_name提供的示例)而不是直接访问它们,可以为您(以及其他)提供以下优势:

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

以上原因是为什么这可以被认为是最佳实践,尽管它并非真正必要这样做(并且可能被认为是某种用途的过度杀伤;例如当你的对象做很少的'处理时' '但仅仅充当'数据'的占位符。)

答案 2 :(得分:6)

我完全同意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方法(注意性别设置器的重复代码)。

答案 3 :(得分: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(),并使用另一个类来处理所有保存逻辑。

答案 4 :(得分:2)

作为对ChristopheD答案的对照,如果您的实例变量仅供私人使用,我不会费心去写一个getter& setter,只是将实例变量声明为private。

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

此外,使用getter / setter还可以保护同一类的其他部分不知道自己的实现,我偶尔发现它非常有用!

答案 5 :(得分:1)

从更一般的角度来看,直接访问($ 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