Я понимаю, что локальные переменные ограничены областью, в которой они были объявлены, и переменные экземпляра существуют, пока существует объект. Но предположим, у меня есть два класса:
PersonalInformation.php
<?php
class PersonalInformation {
private $name;
private $surname;
private $gender;
private $birthday;
function getName(){...}
function setName(){...}
//Other getter and setter
}
и PersonalInformationController.php, который принимает входные данные из формы, istantiate объект PersonalInformation и устанавливает его атрибуты:
class PersonalInformationController {
private $personalInformation;
function __construct() {
$this->personalInformation = new PersonalInformation();
}
function doPost() {
$this->setPersonalDetails();
$this->setResidence();
$this->setContact();
}
private function setPersonalDetails() {
$name = filter_input(INPUT_POST, "name");
$surname = filter_input(INPUT_POST, "surname");
$gender = filter_input(INPUT_POST, "gender");
$birthday = filter_input(INPUT_POST, "birthday");
$nationality = filter_input(INPUT_POST, "nationality");if (empty($name) || empty($surname)) {
throw new RequiredFieldException("Name and surname can't be empty");
} else if (!is_string($name) || !is_string($surname)) {
throw new InvalidFieldException('Input must be a string!');
} else {
$this->personalInformation->setName($name);
$this->personalInformation->setSurname($surname);
}
if (!empty($gender) && is_string($gender)) {
$this->personalInformation->setGender($gender);
} else {
throw new InvalidFieldException('Input must be a string!');
}
if (!empty($birthday) && is_string($birthday)) {
$this->personalInformation->setBirthday($birthday);
}
if (!empty($nationality) && is_string($nationality)) {
$this->personalInformation->setNationality($nationality);
}
}
private function setResidence() {
$address = filter_input(INPUT_POST, "address");
$zipCode = filter_input(INPUT_POST, "zipCode");
$city = filter_input(INPUT_POST, "city");
$nation = filter_input(INPUT_POST, "nation");
if (!empty($address) && is_string($address)) {
$this->personalInformation->setAddress($address);
}
//...//
}
private function setContact() { ... }
}
Этот класс имеет три основных метода (setPersonalDetails () — setResidence () — setContact ()), которые принимают входные данные из формы, на html-странице, помещают их в локальные переменные (то есть $ name, $ surname и т. Д.) и проверьте тип, чтобы установить их в объекте PersonalInformation.
Вот мой вопрос: «С точки зрения разработки кода (особенно расширяемости и читабельности), существуют некоторые различия между использованием этих локальных переменных или объявлением их как переменных экземпляра, чтобы оставить три метода только для проверки типа этих переменные (и не принимать входные данные)? » , И так сделайте что-то подобное:
class PersonalInformationController {
private $personalInformation;
private $name;
private $surname;
private $gender;
private $birthday;
private $nationality;
private $cellphone;
//Other instance variablesfunction __construct() {
$this->personalInformation = new PersonalInformation();
}
function doPost() {
$name = filter_input(INPUT_POST, "name");
$surname = filter_input(INPUT_POST, "surname");
$gender = filter_input(INPUT_POST, "gender");
$birthday = filter_input(INPUT_POST, "birthday");
$nationality = filter_input(INPUT_POST, "nationality");
//...
$address = filter_input(INPUT_POST, "address");
$zipCode = filter_input(INPUT_POST, "zipCode");
$city = filter_input(INPUT_POST, "city");
$nation = filter_input(INPUT_POST, "nation");
}
private function setPersonalDetails() {
// NOW THIS METHOD ONLY CHECKS THE TYPE OF THE INPUT
if (empty($this->name) || empty($this->surname)) {
throw new RequiredFieldException("Name and surname can't be empty");
} else if (!is_string($this->name) || !is_string($this->surname)) {
throw new InvalidFieldException('Input must be a string!');
} else {
$this->personalInformation->setName($this->name);
$this->personalInformation->setSurname($this->surname);
}
if (!empty($this->gender) && is_string($this->gender)) {
$this->personalInformation->setGender($this->gender);
} else {
throw new InvalidFieldException('Input must be a string!');
}
if (!empty($this->birthday) && is_string($this->birthday)) {
$this->personalInformation->setBirthday($this->birthday);
}
if (!empty($this->nationality) && is_string($this->nationality)) {
$this->personalInformation->setNationality($this->nationality);
}
}
//setResidence() and setContact() are like the previous method.
}
Этот вопрос, похоже, больше связан с дизайном приложения, чем с производительностью.
Вместо того, чтобы рассматривать производительность локальных переменных против переменных, обдумайте назначение каждой переменной и спросите себя Does it belong here?
, Классы могут стать довольно большими. В конце концов, вы хотите хранить столько информации, сколько необходимо.
Переменные экземпляра — это свойства объекта (имя, день рождения, ширина, высота и т. Д.). Они либо описывают объект, либо содержат информацию, о которой объект должен знать. Переменные, которые не соответствуют одному из этих требований, должны не быть переменной экземпляра. Вместо этого вы можете передать их в ваши методы в качестве параметров.
В объектно-ориентированных приложениях локальные переменные обычно ограничены областью действия функции (иначе метод), в которых они находятся. Эти переменные обычно уничтожаются или больше не используются после завершения выполнения функции.
Ваше приложение содержит объект домена с именем PersonalInformation
, Доменный объект имеет одну цель: хранить информацию о thing
или же abstract thing
(например, личная информация). Следовательно, он должен содержать только такие свойства, как name, surname, birthday, ...
и метод, который напрямую манипулирует этими свойствами, например, методы получения и установки.
Доменный объект также находится там, где вы должны разместить свою логику проверки. Если вы используете PersonalInformation
объект в нескольких контроллерах, вы будете переписывать одни и те же правила проверки ввода для многих файлов. Этого можно избежать, устанавливая значения в свойствах объекта, а затем проверяя их внутренне через validate()
метод.
Таким образом, ваша логика проверки может быть удалена из вашего контроллера.
Контроллеры предназначены для приема запросов, указания другим частям приложения обрабатывать данные POST / DELETE / PUT, а затем либо визуализировать представление (например, шаблон HTML или строку JSON), либо перенаправить в другую часть приложения.
В большинство случаев, ваши контроллеры не следует обрабатывать логику проверки для ваших доменных объектов.
Я реорганизовал ваш код, чтобы соответствовать этим принципам, и добавил комментарии, чтобы объяснить цель внесенных изменений.
<?php
class PersonalInformationController
{
/**
* Though this controller interacts with one or more PersonalInformation objects, those objects
* do not describe this controller. Nor do they provide any additional information that the
* controller MUST know about.
*
* This variable should be local, not a class property.
*/
//private $personalInformation;
/**
* Because $personalInformation is no longer a class property,
* it does not need to be set in the constructor.
*/
function __construct() {}
function doPost()
{
/**
* The try/catch block will catch any validation exceptions thrown by the validate()
* method withing the $personalInformation object.
*/
try {
/**
* This instance of the PersonalInformation object is only being used within the scope of
* this function, so it should be a local variable.
*
* @var PersonalInformation personalInformation
*/
$personalInformation = new PersonalInformation();
$personalInformation->setName(filter_input(INPUT_POST, "name"));
$personalInformation->setSurname(filter_input(INPUT_POST, "surname"));
$personalInformation->setGender(filter_input(INPUT_POST, "gender"));
$personalInformation->setBirthday(filter_input(INPUT_POST, "birthday"));
$personalInformation->setNationality(filter_input(INPUT_POST, "nationality"));
/** Set other properties of personalInformation ... */
/**
* This validate() method will check the integrity of the data you passed into the setter
* methods above. If any of the domain object's properties are invalid, an exception will be thrown.
*/
$personalInformation->validate();
/** save the object / show view / other controller logic */
} catch(RequiredFieldException $e) {
/** A field was empty. Error handling logic here. */
} catch(InvalidFieldException $e) {
/** A field value was incorrect. Error handling logic here. */
}
}
}
/**
* Class PersonalInformation
*
* This class is a domain object. Notice that it only contains properties that describe PersonalInformation.
* All of the logic contained in this class manipulates the properties, and is "unaware" of any outside entities.
*/
class PersonalInformation {
private $name;
private $surname;
private $gender;
private $birthday;
private $nationality;
/**
* Try to avoid duplicate code. If all the validation for your domain object is the same,
* use a method, such as this, to store all of the validation logic.
*
* @throws \InvalidFieldException
* @throws \RequiredFieldException
*/
public function validate() {
if(empty($this->name) || empty($this->surname)) {
throw new RequiredFieldException("Name and surname can't be empty");
} else if(!is_string($this->name) || !is_string($this->surname)) {
throw new InvalidFieldException('Name and surname must be a strings!');
}
if(empty($this->gender) || !is_string($this->gender)) {
throw new InvalidFieldException('Gender must be a string!');
}
if(empty($this->birthday) || !is_string($this->birthday)) {
throw new InvalidFieldException('Birthday must be a string!');
}
if(empty($this->nationality) || !is_string($this->nationality)) {
throw new InvalidFieldException('Nationality must be a string!');
}
/** other validation rules **/
}
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
/**
* @param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return mixed
*/
public function getSurname()
{
return $this->surname;
}
/**
* @param mixed $surname
*/
public function setSurname($surname)
{
$this->surname = $surname;
}
/**
* @return mixed
*/
public function getGender()
{
return $this->gender;
}
/**
* @param mixed $gender
*/
public function setGender($gender)
{
$this->gender = $gender;
}
/**
* @return mixed
*/
public function getBirthday()
{
return $this->birthday;
}
/**
* @param mixed $birthday
*/
public function setBirthday($birthday)
{
$this->birthday = $birthday;
}
/**
* @return mixed
*/
public function getNationality()
{
return $this->nationality;
}
/**
* @param mixed $nationality
*/
public function setNationality($nationality)
{
$this->nationality = $nationality;
}
}
Обратите внимание, что приложение теперь использует меньше переменных и функций. Он хранит только столько информации, сколько необходимо в каждом классе. Следовательно, ваше приложение будет работать лучше и использовать меньше памяти. Код чистый, простой и простой в обслуживании.
Надеюсь это поможет!
Для меня это пахнет преждевременная оптимизация. Оптимизация, подобная этой, сбьет не более нескольких микросекунд; в приложениях PHP, где выполнение каждого запроса может занять несколько сотен миллисекунд, это вряд ли стоит усилий.
Я создал короткий PhpBench тест, чтобы проверить это. Тестовый скрипт просто считает локальную переменную и переменную экземпляра класса 1 миллион раз:
/**
* @Revs(16)
* @Iterations(1)
*/
class VariableAccess
{
private $var = 0;
public function benchLocalVar()
{
$var = 0;
for ($i = 0; $i < 1000000; $i ++) {
$var ++;
}
}
public function benchInstanceVar()
{
$this->var = 0;
for ($i = 0; $i < 1000000; $i ++) {
$this->var ++;
}
}
}
Вот результаты:
$ ./vendor/bin/phpbench run test.php --report=default
PhpBench 0.11-dev (@git_sha@). Running benchmarks.
\VariableAccess
benchLocalVar I0 P0 [μ Mo]/r: 111,398.125 111,398.125 (μs) [μSD μRSD]/r: 0.000μs 0.00%
benchInstanceVar I0 P0 [μ Mo]/r: 96,092.250 96,092.250 (μs) [μSD μRSD]/r: 0.000μs 0.00%
2 subjects, 2 iterations, 32 revs, 0 rejects
(best [mean mode] worst) = 96,092.250 [103,745.188 103,745.188] 96,092.250 (μs)
⅀T: 207,490.375μs μSD/r 0.000μs μRSD/r: 0.000%
suite: 1339fa11bf3f46fe629a65f838db6a37419e7cfe, date: 2016-04-17, stime: 16:20:01
+----------------+------------------+--------+--------+------+------+-----+----------+---------------+---------+---------+
| benchmark | subject | groups | params | revs | iter | rej | mem | time | z-value | diff |
+----------------+------------------+--------+--------+------+------+-----+----------+---------------+---------+---------+
| VariableAccess | benchLocalVar | | [] | 16 | 0 | 0 | 273,240b | 111,398.125μs | 0.00σ | +13.74% |
| VariableAccess | benchInstanceVar | | [] | 16 | 0 | 0 | 273,296b | 96,092.250μs | 0.00σ | 0.00% |
+----------------+------------------+--------+--------+------+------+-----+----------+---------------+---------+---------+
В этом тесте использование переменных экземпляра на самом деле быстрее, чем использование локальных переменных. Тем не менее, мы говорим о 15 микросекунд за миллион итераций!
Не позволяйте преждевременной оптимизации влиять на дизайн вашей программы. Не жертвуйте читабельностью или расширяемым дизайном только для некоторой неясной оптимизации производительности, если только вы точно не знаете, что у вас есть узкое место в производительности в этом конкретном фрагменте кода.