PHP несколько методов __toString, переключаемых во время выполнения

1

Мне нужен другой __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().

  • 2
    Ну, где и как вы решаете, какой формат выводить? Может ли это измениться с одной строки на другую? Тогда нет смысла иметь глобальный конфиг для этого. Как насчет echo $person->name(Person::FORMAL) ; т.е. передать параметр с кучей заранее определенных возможных форматов?
  • 1
    Я не совсем уверен, что вы ожидаете здесь; Ваш первый недостаток кажется неизбежным, и я не понимаю, что вы подразумеваете под вторым. Если бы был какой-то способ «динамически внедрить __toString », код для этого где-то жил бы. И оператор switch кажется не более вероятным, чтобы скрыть ошибки, чем любая другая реализация.
Показать ещё 1 комментарий
Теги:
tostring

2 ответа

2
Лучший ответ

хорошо, я предполагаю, почему вы спрашиваете, что вы хотите сохранить чистоту, но нет способа установить метод __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, чтобы сохранить код чистым, класс лица должен, конечно, иметь собственное имя реквизита, а также переопределить и установить

0

На мой взгляд, самым чистым способом было бы просто предоставить getter для имени и фамилии и вручную собрать строки, в которых они вам нужны. В ваших текущих решениях вы просто загрязняете свое рабочее пространство ненужными классами и делаете класс Person более сложным, чем он должен быть.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню