2016-09-25 6 views
1

Я пишу «родительский» класс, который должен быть расширен. Я хочу, чтобы расширители устанавливали несколько свойств один раз, когда они определяют класс, но я не хочу, чтобы эти значения менялись после того, как они были изначально установлены. Каков правильный способ сделать это?Как создать класс php со свойствами, которые могут быть установлены только один раз

Update:

Чтобы уточнить, я хочу свойство выступать как константа, что расширяющий класс задает один раз, но не может изменить впоследствии. По этой причине я не могу просто сделать его частной собственностью с помощью метода getter. Расширяющийся класс может просто добавить сеттер, не так ли?

+1

Престол [Видимость] (http://php.net/manual/en/language.oop5. visibility.php), особенно 'private', о том, как запретить членам кого-то менять. –

+0

Создайте 'protected' для этих свойств в родительском классе, а в child возьмите эти свойства в конструкторе и создайте для них только метод getter. Еще один способ - попробовать Factory Design Pattern – 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 Поскольку частная, Бар может получить к нему доступ только через обеспеченный добытчик Foo, но не может получить доступ к нему напрямую. Это препятствует тому, чтобы Bar менял его (пока это не объект, конечно, см. Примечание выше о клонировании).

Если вашему классу child нужен другой конструктор, чем родительский, вам необходимо убедиться, что он фактически вызывает родительский конструктор с неизменяемыми значениями, например. в баре

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 предотвращает любой подкласс от модификации способа.


Что касается ваших комментариев:

Что произойдет, если Bar добавлен метод, как 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) 
} 

Так нет, расширяющий класс может не только добавить сеттер. Частный частный.


Кроме того, есть ли способ сделать это без прохождения переменных через конструктор? Я хотел бы эти атрибуты, которые будут определены в унаследованной классе [...]

Вы могли бы использовать шаблон, показанный в другом месте на этой странице, где вы добавляете метод установки, что позволяет устанавливать только один раз, а затем вызывает исключение на последующие вызовы. Тем не менее, мне это не нравится, поскольку для этого нужен конструктор. И я не вижу проблемы с передачей переменных через конструктор. Рассмотрю это:

public function __construct($somethingElse) 
{ 
    parent::__construct(42); 
    $this->somethingElse = $somethingElse; 
} 

Таким образом, $value устанавливается из конструктора в унаследованном классе. Невозможно изменить его извне и изнутри. И никакой дополнительной логики в какой-то сеттер не требуется.

[...] поэтому они контролируются версией.

Я не уверен, что вы действительно имеете в виду контролируемую версию. Если вам нужен контроль версий, используйте для этого правильную систему, например. git или mercurial или любой другой широко доступный VCS.


На стороне записки: есть RFC, который стремится добавить непреложным классы и свойства в качестве признака родного языка. Тем не менее, он по-прежнему находится в проекте на сегодняшний день.

+0

Но что останавливает расширение формы класса, добавляя метод setter для изменения значения позже? – emersonthis

+0

@emerson закрывает видимость – Gordon

+0

Что произойдет, если 'Bar' добавит метод setMyProperty ($ value)? Будет ли $ this-> myProperty неопределенным, потому что он частный? – emersonthis

3

Объявите свойство как личное и установите его через защищенный сеттер. В этом инкубаторе вы можете сделать ваш контроль

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

Try думать другой подход: если вам нужен набор неизменных свойств, вы можете инкапсулировать их в объекте, затем используйте этот объект как компонент.

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(); 

EDIT:

Чтение правку, я думаю, что вы хотите:

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

Отличный ответ. Я рассматриваю метод хранения объектов. Я определенно могу видеть, как это будет действительно полезно в реальных проектах. Мой прецедент - это скорее игра/учение, которое я стараюсь держать как можно проще. Есть ли способ сделать метод private variable без передачи значений в конструктор? Я хочу, чтобы эти значения были частью подкласса. – emersonthis

+0

@emersonthis вы можете определить метод «configure» для вашего класса компонента. И/или использовать твердое свойство, используемое «MyClass». Использование 'new MyComponent (« Hello World »);' или $ myComponent-> configure (["property" => "Hello World"]); –

+0

@emersonthis see my edit now –

 Смежные вопросы

  • Нет связанных вопросов^_^