2015-11-13 46 views
1

對於同一個類,我需要不同的__toString()在運行時切換的PHP多個__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()的模式。

+2

那麼,你在哪裏以及如何決定輸出哪種格式?這可以從一行改變到另一行嗎?那麼爲此設置一個全局配置是沒有意義的。 'echo $ person-> name(Person :: FORMAL)'';即用一堆預定義的可能格式傳遞一個參數? – deceze

+1

我不確定你在這裏期待什麼;你的第一個缺點似乎是不可避免的,我不明白你的意思。如果有某種方式「動態地注入__toString」,那麼代碼就會生存在某個地方。 「switch」聲明似乎不像其他任何實現那樣可能隱藏錯誤。 – IMSoP

+1

或者有一個專用的,可配置的*渲染器*。這看起來很管用,但從長遠來看,它使事情變得簡單。 – Yoshi

回答

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 保持代碼乾淨,Person實體類當然應該有私人的道具名字和surename和設置方法,並得到

0

在我看來,最乾淨的方法是隻爲firstname和surname提供getter,並在需要它們的地方手動組裝字符串。在你目前的解決方案中,你只是用不必要的類來污染你的工作空間,並且使類Person比它應該更復雜。

相關問題