Мне нужен другой __toString()
для того же класса.
У меня есть класс Person, который содержит имя и фамилию. В соответствии с текущим контекстом я хочу отобразить его с другим порядком, форматированием и т.д. Представьте три сценария:
Thomas Müller
MÜLLER Thomas
Müller, Thomas
Я могу создавать общедоступные методы для каждого дисплея.
<meta charset='utf-8'/>
<?php
class Person
{
protected $firstname;
protected $surname;
public function __construct($firstname, $surname)
{
$this->firstname = $firstname;
$this->surname = $surname;
}
public function toStringWithFirstnameFirst()
{
return $this->firstname . " " . $this->surname;
}
public function toStringWithSurnameFirstUppercase()
{
$surnameConverted = mb_convert_case($this->surname, MB_CASE_UPPER, "UTF-8");
return $surnameConverted . " " . $this->firstname;
}
public function toStringWithSurnameFirstAndFirstnameAfterComma()
{
return $this->surname . ", " . $this->firstname;
}
}
$person = new Person("Thomas", "Müller");
echo $person->toStringWithFirstnameFirst() . "<br/>";
echo $person->toStringWithSurnameFirstUppercase() . "<br/>";
echo $person->toStringWithSurnameFirstAndFirstnameAfterComma() . "<br/>";
?>
Но я придерживался DescriptiveButVeryLongToStringMethodNames
. Я хочу просто echo $person;
в коде.
Мое первое решение - разместить switch-case
__toString()
внутри метода __toString()
. Условный оператор зависит от состояния класса, хранящегося в статической переменной self::$chosenToStringMethod
. Поэтому мне нужен статический метод для установки состояния класса, а также констант класса, которые служат в качестве перечислений.
<meta charset='utf-8'/>
<?php
class Person
{
protected $firstname;
protected $surname;
const PRINT_FIRSTNAME_FIRST = 1;
const PRINT_SURNAME_FIRST_UPPERCASE = 2;
const PRINT_SURNAME_FIRST_FIRSTNAME_AFTER_COMMA = 3;
static private $chosenToStringMethod;
public function __construct($firstname, $surname)
{
$this->firstname = $firstname;
$this->surname = $surname;
}
static public function setToStringMethod($choice)
{
self::$chosenToStringMethod = $choice;
}
private function toStringWithFirstnameFirst()
{
return $this->firstname . " " . $this->surname;
}
private function toStringWithSurnameFirstUppercase()
{
$surnameConverted = mb_convert_case($this->surname, MB_CASE_UPPER, "UTF-8");
return $surnameConverted . " " . $this->firstname;
}
private function toStringWithSurnameFirstAndFirstnameAfterComma()
{
return $this->surname . ", " . $this->firstname;
}
public function __toString()
{
switch (self::$chosenToStringMethod) {
case self::PRINT_FIRSTNAME_FIRST:
return $this->toStringWithFirstnameFirst();
break;
case self::PRINT_SURNAME_FIRST_UPPERCASE:
return $this->toStringWithSurnameFirstUppercase();
break;
case self::PRINT_SURNAME_FIRST_FIRSTNAME_AFTER_COMMA:
return $this->toStringWithSurnameFirstAndFirstnameAfterComma();
break;
default:
return "No __toString method set";
break;
}
}
}
$person = new Person("Thomas", "Müller");
echo $person . "<br/>";
Person::setToStringMethod(Person::PRINT_FIRSTNAME_FIRST);
echo $person . "<br/>";
Person::setToStringMethod(Person::PRINT_SURNAME_FIRST_UPPERCASE);
echo $person . "<br/>";
Person::setToStringMethod(Person::PRINT_SURNAME_FIRST_FIRSTNAME_AFTER_COMMA);
echo $person . "<br/>";
?>
Я вижу некоторые недостатки этого решения:
Person
становится тяжелымswitch-case
могут скрывать некоторые ошибки Я хочу, чтобы класс Person
содержал только свою собственную функциональность не всех видов toStrings. Я бы предпочел иметь некоторый шаблон, который может динамически вводить __toString()
.
хорошо, я предполагаю, почему вы спрашиваете, что вы хотите сохранить чистоту, но нет способа установить метод __toString() для существующего объекта, поэтому лучшим решением будет разделить функциональность
сначала создайте PersonRender:
class PersonRender
{
const PRINT_FIRSTNAME_FIRST = 1;
const PRINT_SURNAME_FIRST_UPPERCASE = 2;
const PRINT_SURNAME_FIRST_FIRSTNAME_AFTER_COMMA = 3;
static public $chosenToStringMethod;
private $person;
public function __construct($person)
{
$this->person = $person;
}
public function render()
{
switch (self::$chosenToStringMethod)
{
case self::PRINT_SURNAME_FIRST_UPPERCASE :
return $this->toStringWithSurnameFirstUppercase();
case self::PRINT_SURNAME_FIRST_FIRSTNAME_AFTER_COMMA :
return $this->toStringWithSurnameFirstAndFirstnameAfterComma();
default :
return $this->toStringWithFirstnameFirst();
}
}
private function toStringWithFirstnameFirst()
{
return "{$this->person->firstname} {$this->person->surname}";
}
private function toStringWithSurnameFirstUppercase()
{
$surnameConverted = mb_convert_case($this->person->surname, MB_CASE_UPPER, "UTF-8");
return "{$surnameConverted} {$this->person->firstname}";
}
private function toStringWithSurnameFirstAndFirstnameAfterComma()
{
return "{$this->person->surname}, {$this->person->firstname}";
}
}
а затем класс Person:
class Person
{
public $firstname, $surname;
public function __construct($firstname, $surname)
{
$this->firstname = $firstname;
$this->surname = $surname;
}
public function __toString() {
$render = new PersonRender($this);
return $render->render();
}
}
и небольшое испытание:
$person = new Person('Foo', 'Bar');
echo $person;
echo '<hr />';
PersonRender::$chosenToStringMethod = PersonRender::PRINT_SURNAME_FIRST_UPPERCASE;
echo $person;
РЕДАКТИРОВАТЬ 1, чтобы сохранить код чистым, класс лица должен, конечно, иметь собственное имя реквизита, а также переопределить и установить
На мой взгляд, самым чистым способом было бы просто предоставить getter для имени и фамилии и вручную собрать строки, в которых они вам нужны. В ваших текущих решениях вы просто загрязняете свое рабочее пространство ненужными классами и делаете класс Person
более сложным, чем он должен быть.
echo $person->name(Person::FORMAL)
; т.е. передать параметр с кучей заранее определенных возможных форматов?__toString
», код для этого где-то жил бы. И операторswitch
кажется не более вероятным, чтобы скрыть ошибки, чем любая другая реализация.