2016-09-25 94 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看到我现在编辑 –