【发布时间】:2015-11-13 11:04:59
【问题描述】:
同一个班级我需要不同的__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() 方法中。条件语句取决于存储在静态变量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()的模式。
【问题讨论】:
-
那么,您在哪里以及如何决定输出哪种格式?可以从一行更改为下一行吗?那么为此设置全局配置是没有意义的。
echo $person->name(Person::FORMAL)怎么样?即传递具有一堆预定义可能格式的参数? -
我不太确定你在这里期待什么;你的第一个缺点似乎是不可避免的,我不明白你所说的第二个是什么意思。如果有某种方法可以“动态注入
__toString”,那么它的代码就会存在于某个地方。switch语句似乎比任何其他实现都不太可能隐藏错误。 -
或者有一个专用的、可配置的renderer。这似乎是开销,但从长远来看,它使事情变得容易。