Если я смогу добавить зерно соли через несколько месяцев:
Очень неприлично иметь общедоступные свойства. Все должно быть инкапсулировано, просто потому что (среди прочих причин) использование прямой манипуляции с атрибутами не дает вам способов легко реорганизовать или выполнить (более) контрольные проверки, когда какой-либо внешний источник изменяет поле. Например, предположим, у вас есть класс со многими полями, который используется в проекте несколько раз, и этот проект содержит несколько тысяч файлов; это проект, который работает и расширяется уже несколько лет. Допустим, что компания меняет свою бизнес-модель или что обнаружена проблема с некоторыми типами данных поля, и теперь требуется некоторая проверка; Будете ли вы дублировать эту проверку во всех тех тысячах исходного кода, который имеет прямой доступ к общедоступному члену? В PHP решение может быть простым, но не в большинстве языков программирования ОО (например, Java). Дело в том, что OO основано на инкапсуляции. Короче говоря, инкапсуляция не только дает чистый код, но и очень легко обслуживаемый (не говоря уже о рентабельном и связном) код.
Ваше предложение о том, чтобы __get / __set управлял приватным членом (массивом), хорошо. Таким образом, если вам нужна дополнительная проверка по дороге, просто создайте свой установщик и / или ваш получатель, и это будет конец. Некоторые могут поспорить с тем, что это контрпродуктивно, так как завершение кода не может включиться в __get / __set. ИМХО, полагаться на завершение кода - это просто ленивое кодирование. Но опять же, наличие каждого члена имеет свой собственный метод получения и / или установки позволяет вам написать более полную документацию по API. Лично я обычно использую эту технику для внутренних занятий или занятий общего назначения. Если все ваши поля не требуют какой-либо проверки, или, как вы сказали, их несколько десятков, то, по моему мнению, использование магических методов было бы приемлемо.
Суть в том, чтобы избежать прямого доступа членов к экземплярам классов, точка. Как вы решите достичь этого, зависит только от вас. Просто убедитесь, что API хорошо документирован, тем более абстрактным вы его сделаете.
В заключение, в PHP, если у вас уже есть используемые классы, которые не инкапсулируют свои поля, например что-то вроде
class SomeObject {
public $foo;
public $bar;
public $baz;
//...
}
Вы можете просто исправить этот класс без необходимости что-либо реорганизовывать в нечто вроде:
class SomeObject {
private $_foo; // having underscore as prefix helps to know what's private/protected
private $_bar; // inside the code.
private $_baz;
public function __get($name) {
$methodName = 'get'.ucfirst($name);
if (method_exists($this, $methodName)) {
return $this->{$methodName}();
} else {
throw new Exception("Method '{$methodName}' does not exist");
}
}
public function __set($name, $value) {
$methodName = 'set'.ucfirst($name);
if (method_exists($this, $methodName)) {
$this->{$methodName}($value);
} else {
throw new Exception("Method '{$methodName}' does not exist");
}
}
public function getFoo() { return $this->_foo; }
public function setFoo($value) { $this->_foo = $value; }
public function getBar() { return $this->_bar; }
public function setBar($value) { $this->_bar = $value; }
public function getBaz() { return $this->_baz; }
public function setBaz($value) { $this->_baz = $value; }
}
А потом
$obj = new SomeObject();
$obj->foo = 'Hello world'; // legacy code support
$obj->setFoo('Hello world'); // same thing, but preferred
И вы удовлетворяете обеим парадигмам ОО и наличию прямого доступа к атрибутам экземпляра. Вы могли бы также __call()
проверить префикс 'get' или 'set' и вызвать __get()
и __set()
соответственно, но я бы не пошел так далеко, хотя это действительно позволило бы классам общего назначения получить доступ к своим закрытым членам через ->member
и ->getMember()
/ ->setMember()