我正在写一个“父”类,这意味着要扩展。我希望扩展器在定义类时设置一些属性,但我不希望这些值在初始设置后更改。什么是正确的方法来做到这一点?如何设计属性只能设置一次的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