2016-09-25 93 views
1

我正在寫一個「父」類,這意味着要擴展。我希望擴展器在定義類時設置一些屬性,但我不希望這些值在初始設置後更改。什麼是正確的方法來做到這一點?如何設計屬性只能設置一次的PHP類

更新:

爲了澄清,我希望屬性的擴展類設置一次作爲一個恆定的,但不能事後修改。出於這個原因,我不能簡單地用一個getter方法將它變成一個私有財產。擴展類可以添加一個setter,對吧?

+1

參見[隱現](http://php.net/manual/en/language.oop5。 visibility.php),特別是'private',關於如何防止其他人更改成員。 –

+0

在父類中爲這些屬性設置'protected',並在child中將這些屬性放在構造函數中,並且只爲它們創建getter方法。另一種方法是嘗試工廠設計模式 – Bills

回答

4

您將您不想寫爲變量的變量設置爲private,然後不要爲它們爲子類提供任何變體,例如,

abstract class Foo 
{  
    private $value;  

    public function __construct($value) 
    { 
     $this->value = $value; 
    } 

    protected function getValue() 
    { 
     return $this->value; 
    } 
} 

根據什麼$value是,你可能需要clone它返回給孩子上課前。

在你的子類,你會做

class Bar extends Foo 
{ 
    public function someMethodUsingValue() 
    { 
     $value = $this->getValue(); 
     // do something with $value 
    } 
} 

$bar = new Bar(42); 
$bar->someMethodUsingValue(); 

因爲$value是私有的,酒吧只能通過富的提供吸氣訪問它,但不能直接訪問它。這可以防止Bar對其進行更改(只要它不是一個對象,請參閱上面關於克隆的注意事項)。

如果您的子類需要與父類不同的構造函數,則需要確保它實際調用具有不可變值的父構造函數,例如,在酒吧

public function __construct($value, $something) 
{ 
    parent::__construct($value); 
    $this->something = $something; 
} 

另見http://php.net/manual/en/language.oop5.visibility.php


又一(和優選的)的選擇是在一個或幾個類和骨料以封裝不可變值(一個或多個)/父類中構成它們,而不是繼承它們:

class Bar 
{ 
    private $data; 

    public function __construct() 
    { 
     $this->data = new ImmutableValue(42); 
    } 

    public function getValue() 
    { 
     return $this->data->getValue(); 
    } 
} 

class ImmutableValue 
{ 
    private $value; 

    public function __construct($value) 
    { 
     $this->value = $value; 
    } 

    public function getValue() 
    { 
     return $this->value; 
    } 
} 

正如你所看到的,這個不使用繼承。這不是必需的。請參考以下兩篇文章瞭解詳情:


既然你提到你的問題的常量,你可以明顯地實現了整個事情,而不使用繼承或成分並設置一個真正的常數,例如

class Bar 
{ 
    const VALUE = 42; 
} 

echo Bar::VALUE; // 42 

或使用恆定方法:

class Bar 
{ 
    public final function getValue() 
    { 
     return 42; 
    } 
} 

使吸氣final防止任何亞類從修改方法。


關於你的評論:

如果酒吧添加像setMyProperty($值)的方法會發生什麼? $ this-> myProperty會是不確定的,因爲它是私人的嗎?

您將最終在Bar實例中設置一個新的公共屬性,並且具有相同的名稱。但父項的私有實例var將保持不變。

class Foo 
{ 
    private $value = 1; 
} 

class Bar extends Foo 
{ 
    public function setValue($value) 
    { 
     $this->value = $value; 
    } 
} 

$bar = new Bar; 
$bar->setValue(42); 
var_dump($bar); 

會給你

object(Bar)#1 (2) { 
    ["value:private"]=> 
    int(1) 
    ["value"]=> 
    int(42) 
} 

所以,不,延伸類不能只是添加setter。私人是私人的。


此外,有沒有辦法做到這一點不通過構造函數傳遞變量?我想在繼承的類中定義的屬性[...]

,你可以利用這個頁面,在其中添加一個setter方法,允許設置只有一次在其他地方顯示的圖案,然後拋出一個異常隨後的調用。但是,我不喜歡這個,因爲它確實是一個構造函數。我沒有看到通過構造函數傳遞變量的問題。考慮一下:

public function __construct($somethingElse) 
{ 
    parent::__construct(42); 
    $this->somethingElse = $somethingElse; 
} 

這樣,$value從繼承類的構造函數中設置。沒有辦法從外部或Bar內部改變它。在某種設置器中不需要額外的邏輯。

[...]所以他們的版本控制。

我不確定你的意思是版本控制。如果你想要版本控制,爲此使用適當的系統,例如git或mercurial或任何其他廣泛可用的VCS。


在一個側面說明:有一個RFC,旨在增加不可改變的類和屬性作爲母語的功能。不過,截至今天仍處於草案中。

+0

但是,什麼能阻止擴展類形式添加setter方法來稍後修改該值? – emersonthis

+0

@emerson私人知名度阻止它 – Gordon

+0

如果'Bar'添加了像setMyProperty($ value)這樣的方法會發生什麼? $ this-> myProperty會是不確定的,因爲它是私人的嗎? – emersonthis

3

將該屬性聲明爲private,並通過受保護的setter對其進行設置。在這個二傳手,你可以做你的控制

abstract class YourClass { 
    private $myProperty; 

    protected function setMyProperty($value) { 
     if(is_null($this->myProperty)) { 
      $this->myProperty = $value; 
     }else { 
      throw new Exception("You can set this property only once"); 
     } 
    } 

    protected function getMyProperty() { 
      return $this->myProperty; 
    } 
} 

爲了獲取財產,你還需要一個受保護的吸氣劑(或市民如果你需要有來自外部的訪問)。

另一種觀點認爲:

有一個在面向對象的共同規則:prefer composition over inheritance

儘量想不同的方法:如果你需要一組不變的屬性,你可以在一個對象封裝它們,然後使用此對象作爲組件。

class MyClass { 
    /** 
    * @var MyComponent 
    */ 
    protected $component; 

    public function __construct($property) { 
     $this->setComponent(new MyComponent($property)); 
    } 

    protected function setComponent(MyComponent $myComponentInstance) { 
     $this->component = $myComponentInstance; 
    } 

    public function doSomething() { 
     echo $this->component->getMyProperty(); 
    } 
} 

class MyComponent { 
    private $myProperty; 

    public function __construct($property) { 
     $this->myProperty = $property; 
    } 
    public function getMyProperty() { 
     return $this->myProperty; 
    } 

} 

$myClass = new MyClass("Hello World"); 

$myClass->doSomething(); 

編輯:

閱讀你的編輯,我想你想要這樣的:

abstract class MyAbstractClass { 
    const FOO = 1; 

    public function getFoo() { 
     // hint: use "static" instead of "self" 
     return static::FOO; 
    } 
} 

class MyClass extends MyAbstractClass{ 
    // override 
    const FOO = 2; 
} 

$myClass = new MyClass(); 

echo $myClass->getFoo(); // 2 
+0

很好的回答。我正在考慮對象存儲方法。我可以肯定地看到,在真實世界的項目中這將如何真正有用。我的使用案例更多的是我試圖儘可能保持簡單的遊戲/教學練習。有沒有辦法做私人變量的方法,而不傳遞給構造函數的值?我希望這些值成爲子類的一部分。 – emersonthis

+0

@emersonthis你可以爲你的組件類定義一個「配置」方法。和/或使用「MyClass」使用的硬編碼屬性。使用'new MyComponent(「Hello World」);'或$ myComponent-> configure([「property」=>「Hello World」]); –

+0

@emersonthis看到我現在編輯 –