您将不想写的变量设置为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只能通过Foo提供的Getter来访问,不能直接访问。这可以防止 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;
}
}
使getter final 防止任何子类修改方法。
关于你的 cmets:
如果 Bar 添加 setMyProperty($value) 之类的方法会怎样? $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 内部更改。并且在某种 setter 中不需要额外的逻辑。
[...] 所以它们是受版本控制的。
我不确定您是否真的是指版本控制。如果您想要版本控制,请为此使用适当的系统,例如git 或 mercurial 或任何其他广泛可用的 VCS。
附带说明:有一个RFC,旨在将不可变类和属性添加为本地语言功能。但是,截至今天,它仍处于草稿中。