Разумно ли иметь достаточное количество публичных объектов в классе? - PullRequest
12 голосов
/ 09 февраля 2010

Или, более конкретно, можно ли не полагаться на сеттеры и геттеры?

Я имею дело с классом, который проверяет наличие комнат и устанавливает общедоступные свойства, которых больше десятка. Такие вещи, как:

  • unitNumber
  • roomTypes (массив)
  • codeCorporate
  • CodeGroup
  • numberKids
  • numberAdults
  • numberRooms
  • валюта
  • minRate
  • Maxrate
  • SoapServer
  • единиц (массив)
  • hotelId

И после того, как объект создан, эти свойства устанавливаются с $this-> внутри различных методов. Однако код, который имеет дело с объектом, часто устанавливает открытые свойства напрямую, вместо использования методов getter / setter:

$object->something = 3;

foreach ($object->things as $thing ) { }

Если у меня будет время на рефакторинг этого класса ..

  • Должен ли я вставить все эти свойства в массив данных, который является частным свойством, и определить методы __set и __get?
  • Должен ли я создать один метод получения для каждого из свойств?

Ответы [ 5 ]

3 голосов
/ 09 февраля 2010

По моему мнению, редко бывает хорошей идеей иметь публичных участников.Это увеличивает связь между классами и делает рефакторинг очень сложным (если вам это нужно.)

Сеттеры / геттеры - это путь, и очень небольшое снижение производительности, которое вы платите за него, обычно либо оптимизируется,или превзойденный элегантностью.

Чтобы ответить на ваш вопрос о массиве или единственном получателе за переменную, это вопрос вкуса.Я склонен хранить в массиве только переменные сходного типа и отделять остальные.

3 голосов
/ 09 февраля 2010

Лично мне еще предстоит найти действительно вескую причину публичной собственности, хотя я открыт для предложений :-)

Хотя я очень предпочитаю указанные методы получения / установки для каждого свойства (будь то прокси для обобщенного get($name) или нет). Я предполагаю, что у вас уже есть другой код, который использует прямое присваивание, поэтому в этом случае я бы сказал, чтобы продолжить использовать магические __get/__set методы.

2 голосов
/ 09 февраля 2010

Я думаю, что большинство людей будут рекомендовать использовать сеттеры и геттеры. Прямо сейчас вы ограничены простой установкой и извлечением свойства, но что, если вы хотите войти, когда к этому свойству обращаются? Или, возможно, вы хотите сначала запустить значение с помощью функции проверки (электронная почта, номер телефона, почтовый индекс и т. Д.). Возможно, вам нужно будет вызвать другую функцию или установить другое свойство. Я думаю, вы видите, куда я направляюсь с этим. Используя сеттеры и геттеры, вы добавляете ценный уровень инкапсуляции в ваши классы, и в 99% случаев это стоит дополнительной типизации, которую вам нужно будет;) Представьте себе попытку выполнить приведенные выше примеры без сеттеров и геттеров. По меньшей мере, это будет большая головная боль.

Редактировать: Я забыл упомянуть Учение. Это картограф объектных отношений (ORM), который может автоматически устанавливать для вас сеттеры и геттеры (помимо прочего). Вы можете проверить это на http://www.doctrine -project.org /

1 голос
/ 23 августа 2010

Если я смогу добавить зерно соли через несколько месяцев:

Очень неприлично иметь общедоступные свойства. Все должно быть инкапсулировано, просто потому что (среди прочих причин) использование прямой манипуляции с атрибутами не дает вам способов легко реорганизовать или выполнить (более) контрольные проверки, когда какой-либо внешний источник изменяет поле. Например, предположим, у вас есть класс со многими полями, который используется в проекте несколько раз, и этот проект содержит несколько тысяч файлов; это проект, который работает и расширяется уже несколько лет. Допустим, что компания меняет свою бизнес-модель или что обнаружена проблема с некоторыми типами данных поля, и теперь требуется некоторая проверка; Будете ли вы дублировать эту проверку во всех тех тысячах исходного кода, который имеет прямой доступ к общедоступному члену? В 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()

1 голос
/ 09 февраля 2010

Я бы сделал шаг назад и задал бы несколько более общих вопросов:

  • Почему я должен выставлять столько информации; что его использует и почему?
  • Действительно ли этот класс является просто структурой данных без поведения, и в этом случае должен быть закрытым классом для какого-либо другого класса?
  • Служит ли этот класс единственной цели или он становится монолитным?

Вы можете обнаружить, что можете создавать представления экземпляра класса для экспорта в базу данных, отображения в форме и т. Д. Изучите шаблоны "Построитель" и "Ациклический посетитель", чтобы начать с. *

Что касается методов доступа, я не вижу необходимости использовать их для того, что вы описываете: для извлечения свойств класса и внутренней информации о состоянии, или структуры. Тем не менее, для атрибутов класса я мог видеть выгоду в некоторых случаях, но больше для извлечения атрибутов, а не для мутаций состояния вашего объекта.

...