Вы устанавливаете переменные, которые вы не хотите записывать, как 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, который стремится добавить непреложным классы и свойства в качестве признака родного языка. Тем не менее, он по-прежнему находится в проекте на сегодняшний день.
Престол [Видимость] (http://php.net/manual/en/language.oop5. visibility.php), особенно 'private', о том, как запретить членам кого-то менять. –
Создайте 'protected' для этих свойств в родительском классе, а в child возьмите эти свойства в конструкторе и создайте для них только метод getter. Еще один способ - попробовать Factory Design Pattern – Bills