【问题标题】:PHP multiple __toString methods, switched in the runtimePHP多个__toString方法,在运行时切换
【发布时间】: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-&gt;name(Person::FORMAL) 怎么样?即传递具有一堆预定义可能格式的参数?
  • 我不太确定你在这里期待什么;你的第一个缺点似乎是不可避免的,我不明白你所说的第二个是什么意思。如果有某种方法可以“动态注入__toString”,那么它的代码就会存在于某个地方。 switch 语句似乎比任何其他实现都不太可能隐藏错误。
  • 或者有一个专用的、可配置的renderer。这似乎是开销,但从长远来看,它使事情变得容易。

标签: php tostring


【解决方案1】:

好的,我假设您问的原因是您希望保持清洁,但无法将 __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 为了保持代码干净,Person 实体类当然应该有私有 props firstname 和 surename 以及设置和获取方法

【讨论】:

    【解决方案2】:

    在我看来,最干净的方法是只为名字和姓氏提供 getter,并在需要的地方手动组装字符串。在您当前的解决方案中,您只是用不必要的类污染了您的工作区,并使类Person 比它应该的更复杂。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-10
      • 1970-01-01
      • 1970-01-01
      • 2011-01-17
      • 2020-12-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多