Подтвердить что ты не робот

Является ли разумным иметь достаточное количество публичных объектов в классе?

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

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

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

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

$object->something = 3;

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

Если у меня есть время, чтобы реорганизовать этот класс..

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

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

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

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

Вы можете обнаружить, что вы можете создавать представления экземпляра класса для экспорта в базу данных, отображения в форме и т.д. Ознакомьтесь с шаблонами "Builder" и "Acyclic Visitor" для начала.

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

Ответ 5

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

Это очень не-OO, чтобы иметь общедоступные свойства. Все должно быть инкапсулировано, просто потому, что (среди прочих причин) использование прямого манипулирования атрибутами не дает вам способов легко реорганизовать или выполнить (более) контрольные проверки, когда какой-то внешний источник изменяет это поле. Например, скажем, у вас есть класс со многими полями, который используется в проекте несколько раз, и этот проект содержит несколько тысяч файлов; это проект, который работает и расширяется уже несколько лет. Скажем, что компания меняет свою бизнес-модель или что проблема найдена с некоторым типом данных поля и теперь требуется наличие некоторой проверки; вы будете дублировать эту проверку во всех этих тысячах исходных кодов, которые напрямую обращаются к публичному пользователю? В PHP решение может быть простым, но не на большинстве языков программирования OO (например, Java). Дело в том, что OO на основе инкапсуляции. Короче говоря, инкапсуляция не только создает чистый код, но также может быть хорошо поддерживаема (не говоря уже экономичный и сплоченный).

Ваше предложение о том, что персональный член (массив) управляется __get/__set, хорош. Таким образом, если вам нужна дополнительная проверка по дороге, просто создайте сеттер и/или ваш геттер, и это будет конец. Некоторые могут спорить с тем, что это счетчик продуктивности, так как завершение кода не может срабатывать на __get/__set. ИМХО, полагаясь на завершение кода, - это просто ленивое кодирование. Но опять же, если у каждого члена есть собственный getter и/или setter, вы можете написать более полную документацию API. Лично я обычно использую эту технику для внутренних или очень общих классов. Если все ваши поля не требуют какой-либо проверки, или есть, как вы сказали, несколько десятков из них, то использование магических методов было бы приемлемым, на мой взгляд.

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

В заключение, в PHP, если у вас уже есть классы, которые используются, которые не инкапсулируют их поля, например, что-то вроде

class SomeObject {
   public $foo;
   public $bar;
   public $baz;
   //...
}

вы можете просто исправить этот класс без необходимости рефакторировать что-то вроде:

class SomeObject {
   private $_foo;   // having underscore as prefix helps to know what 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

И вы удовлетворяете как парадигму OO, так и прямой доступ к атрибутам экземпляра. Вы могли бы также иметь __call() проверить префикс 'get' или 'set' и вызвать __get() и __set() соответственно, но я бы не пошел так далеко, хотя это действительно позволило бы классам общего назначения обращаться к ним с частными членами через ->member и ->getMember()/->setMember()