如何使用只能设置一次的属性设计php类
我正在写一个“家长”类,它是用来扩展的。我希望扩展器在定义类时只设置一次少数属性,但我不希望这些值在初始设置后发生更改。正确的方法是什么 更新:如何使用只能设置一次的属性设计php类,php,oop,inheritance,abstract-class,subclass,Php,Oop,Inheritance,Abstract Class,Subclass,我正在写一个“家长”类,它是用来扩展的。我希望扩展器在定义类时只设置一次少数属性,但我不希望这些值在初始设置后发生更改。正确的方法是什么 更新: 为了澄清这一点,我希望该属性充当一个常量,扩展类设置一次,但之后不能修改。出于这个原因,我不能简单地用getter方法将其作为私有属性。扩展类可以只添加一个setter,对吗?您可以将不希望写入的变量设置为private,然后不向子类提供任何变体,例如 abstract class Foo { private $value;
为了澄清这一点,我希望该属性充当一个常量,扩展类设置一次,但之后不能修改。出于这个原因,我不能简单地用getter方法将其作为私有属性。扩展类可以只添加一个setter,对吗?您可以将不希望写入的变量设置为
private
,然后不向子类提供任何变体,例如
abstract class Foo
{
private $value;
public function __construct($value)
{
$this->value = $value;
}
protected function getValue()
{
return $this->value;
}
}
根据$value
是什么,您可能需要在将其返回到子类之前对其进行克隆
在你的儿童课上,你会
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更改它(当然,只要它不是对象,请参见上面关于克隆的注释)
如果您的子类需要一个不同于父类的构造函数,则需要确保它实际调用了具有不可变值的父类构造函数,例如,在Bar中
public function __construct($value, $something)
{
parent::__construct($value);
$this->something = $something;
}
也看到
另一个(也是更可取的)选择是将不可变值封装在一个或多个类中,并在父类中聚合/组合它们,而不是继承它们:
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;
}
}
使getterfinal
防止任何子类修改方法
关于你的评论:
如果Bar添加了像setMyProperty($value)这样的方法,会发生什么?$this->myProperty会因为是私有的而不确定吗
您将在Bar实例中使用相同的名称创建一个新的公共属性集。但是父级的私有实例变量将保持不变
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
是从继承类中的构造函数设置的。没有办法从外部改变它,也没有办法从内部改变它。在某种setter中不需要额外的逻辑
[…]所以它们是版本控制的
我不确定你的意思是版本控制。如果您想要版本控制,请使用适当的系统,例如git或mercurial或任何其他广泛使用的VCS
另一方面:有一个,旨在添加不可变的类和属性作为本机语言特性。然而,到今天为止,它仍在起草中 将不希望写入的变量设置为private
,然后不向子类提供任何变体,例如
abstract class Foo
{
private $value;
public function __construct($value)
{
$this->value = $value;
}
protected function getValue()
{
return $this->value;
}
}
根据$value
是什么,您可能需要在将其返回到子类之前对其进行克隆
在你的儿童课上,你会
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更改它(当然,只要它不是对象,请参见上面关于克隆的注释)
如果您的子类需要一个不同于父类的构造函数,则需要确保它实际调用了具有不可变值的父类构造函数,例如,在Bar中
public function __construct($value, $something)
{
parent::__construct($value);
$this->something = $something;
}
也看到
另一个(也是更可取的)选择是将不可变值封装在一个或多个类中,并在父类中聚合/组合它们,而不是继承它们:
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;
}
}
使getterfinal
防止任何子类修改方法
关于你的评论:
如果Bar添加了像setMyProperty($value)这样的方法,会发生什么?$this->myProperty会因为是私有的而不确定吗
您将在Bar实例中使用相同的名称创建一个新的公共属性集。但是父级的私有实例变量将保持不变
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;
}
这样,