我正在寫一個「父」類,這意味着要擴展。我希望擴展器在定義類時設置一些屬性,但我不希望這些值在初始設置後更改。什麼是正確的方法來做到這一點?如何設計屬性只能設置一次的PHP類
更新:
爲了澄清,我希望屬性的擴展類設置一次作爲一個恆定的,但不能事後修改。出於這個原因,我不能簡單地用一個getter方法將它變成一個私有財產。擴展類可以添加一個setter,對吧?
我正在寫一個「父」類,這意味着要擴展。我希望擴展器在定義類時設置一些屬性,但我不希望這些值在初始設置後更改。什麼是正確的方法來做到這一點?如何設計屬性只能設置一次的PHP類
更新:
爲了澄清,我希望屬性的擴展類設置一次作爲一個恆定的,但不能事後修改。出於這個原因,我不能簡單地用一個getter方法將它變成一個私有財產。擴展類可以添加一個setter,對吧?
您將您不想寫爲變量的變量設置爲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,旨在增加不可改變的類和屬性作爲母語的功能。不過,截至今天仍處於草案中。
但是,什麼能阻止擴展類形式添加setter方法來稍後修改該值? – emersonthis
@emerson私人知名度阻止它 – Gordon
如果'Bar'添加了像setMyProperty($ value)這樣的方法會發生什麼? $ this-> myProperty會是不確定的,因爲它是私人的嗎? – emersonthis
將該屬性聲明爲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
很好的回答。我正在考慮對象存儲方法。我可以肯定地看到,在真實世界的項目中這將如何真正有用。我的使用案例更多的是我試圖儘可能保持簡單的遊戲/教學練習。有沒有辦法做私人變量的方法,而不傳遞給構造函數的值?我希望這些值成爲子類的一部分。 – emersonthis
@emersonthis你可以爲你的組件類定義一個「配置」方法。和/或使用「MyClass」使用的硬編碼屬性。使用'new MyComponent(「Hello World」);'或$ myComponent-> configure([「property」=>「Hello World」]); –
@emersonthis看到我現在編輯 –
參見[隱現](http://php.net/manual/en/language.oop5。 visibility.php),特別是'private',關於如何防止其他人更改成員。 –
在父類中爲這些屬性設置'protected',並在child中將這些屬性放在構造函數中,並且只爲它們創建getter方法。另一種方法是嘗試工廠設計模式 – Bills