Объектно-ориентированный PHP Best Practices
Скажем, у меня есть класс, который представляет человека, переменная в этом классе будет $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 ответов
Использование явных методов получения и установки для свойств объекта (как в примере, который вы дали для set_name
) вместо прямого доступа к ним дает вам (среди прочих) следующие преимущества:
- Вы можете изменить "внутреннюю" реализацию без необходимости каких-либо внешних вызовов. Таким образом, "внешний" код не нужно так часто менять (потому что вы предоставляете согласованные средства доступа).
- Вы явно указываете, какие свойства предназначены для использования / вызова извне класса. Это окажется очень полезным, если другие люди начнут использовать ваш класс.
Приведенные выше причины являются причиной того, что это может считаться наилучшей практикой, хотя в действительности это не является необходимым (и может считаться излишним для некоторых целей; например, когда ваш объект выполняет очень мало "обработки", а просто выступает в качестве заполнителя для "данных"). ").
У вас должны быть методы установки / получения. Они - боль, но вам не обязательно писать их самим. Среда 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
Нет необходимости объявлять члены или функции установки / получения. Если в будущем вам нужно добавить функциональность в метод set/get, вы просто объявляете его, по существу переопределяя магический метод. Также, поскольку метод setter возвращает $ this, вы можете связать их так:
$object->setName('Bob')
->setHairColor('green')
->setAddress('someplace');
что делает код простым в написании и чтении.
Единственным недостатком этого подхода является то, что он усложняет распознавание структуры вашего класса. Так как вы по сути объявляете члены и методы во время выполнения, вы должны выгрузить объект во время выполнения, чтобы увидеть, что он содержит, а не читать класс. Если вашему классу нужно объявить четко определенный интерфейс (потому что это библиотека и / или вы хотите, чтобы phpdoc генерировал документацию API), я бы настоятельно рекомендовал объявлять открытые методы set/get вместе с приведенным выше кодом.
Я полностью согласен с 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 в конструкторе (обратите внимание на дублирующий код для установки пола).
Чтобы получить полный ООП, вы должны сделать что-то похожее на:
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(), и имеете другой класс для обработки всей логики сохранения.
В качестве контрапункта к ответу ChristopheD, если ваша переменная экземпляра предназначена исключительно для частного использования, я бы не стал писать геттер и сеттер, а просто объявил бы переменную экземпляра частной.
Если другие объекты нуждаются в доступе к объекту, вы всегда можете добавить геттер. (Это открывает другую проблему в том, что другие классы могут изменять объект, возвращаемый получателем. Но ваш получатель всегда может вернуть копию переменной экземпляра.)
Кроме того, использование getter / setter также защищает другие части того же класса от знания его собственной реализации, что я нашел очень полезным в некоторых случаях!
С более общей точки зрения как прямой доступ ($person->name), так и методы доступа ($person->getName) считаются вредными. В ООП объекты не должны делиться какими-либо знаниями об их внутренней структуре, а только выполнять сообщения, отправленные им. Пример:
// 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