Odpowiedzi:
Nie ma czegoś takiego jak definiowanie właściwości.
Właściwości można deklarować tylko, ponieważ są to kontenery danych zarezerwowanych w pamięci podczas inicjalizacji.
Z drugiej strony funkcja może być zadeklarowana (typy, nazwa, parametry) bez zdefiniowania (brak treści funkcji), a zatem może być abstrakcyjna.
„Abstrakt” oznacza jedynie, że coś zostało zadeklarowane, ale nie zostało zdefiniowane, dlatego przed użyciem należy to zdefiniować lub stanie się bezużyteczne.
Nie, nie ma sposobu, aby wymusić to za pomocą kompilatora, musiałbyś użyć sprawdzeń w czasie wykonywania (powiedzmy w konstruktorze) dla $tablename
zmiennej, np .:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
Aby wymusić to dla wszystkich klas pochodnych Foo_Abstract, musiałbyś utworzyć konstruktor Foo_Abstract final
, zapobiegając nadpisywaniu.
Zamiast tego możesz zadeklarować abstrakcyjny getter:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
W zależności od kontekstu właściwości, jeśli chcę wymusić deklarację abstrakcyjnej właściwości obiektu w obiekcie podrzędnym, lubię używać stałej ze static
słowem kluczowym dla właściwości w konstruktorze obiektów abstrakcyjnych lub metodach ustawiających / pobierających. Opcjonalnie można użyć, final
aby zapobiec przesłonięciu metody w klasach rozszerzonych.
Poza tym obiekt potomny przesłania właściwość i metody obiektu nadrzędnego, jeśli zostanie ponownie zdefiniowany. Na przykład, jeśli właściwość jest zadeklarowana jako protected
nadrzędna i przedefiniowana tak, jak public
w przypadku elementu podrzędnego, wynikowa właściwość jest publiczna. Jeśli jednak nieruchomość zostanie zadeklarowana private
w rodzicu, pozostanie private
i nie będzie dostępna dla dziecka.
http://www.php.net//manual/en/language.oop5.static.php
abstract class AbstractFoo
{
public $bar;
final public function __construct()
{
$this->bar = static::BAR;
}
}
class Foo extends AbstractFoo
{
//const BAR = 'foobar';
}
$foo = new Foo; //Fatal Error: Undefined class constant 'BAR' (uncomment const BAR = 'foobar';)
echo $foo->bar;
Jak stwierdzono powyżej, nie ma takiej dokładnej definicji. Ja jednak używam tego prostego obejścia, aby zmusić klasę podrzędną do zdefiniowania właściwości „abstract”:
abstract class Father
{
public $name;
abstract protected function setName(); // now every child class must declare this
// function and thus declare the property
public function __construct()
{
$this->setName();
}
}
class Son extends Father
{
protected function setName()
{
$this->name = "son";
}
function __construct(){
parent::__construct();
}
}
static
nieruchomości.
the only "safe" methods to have in a constructor are private and/or final ones
, czy moje obejście nie jest w takim przypadku? używam w tym szeregowych
$name
. Możesz zaimplementować tę setName()
funkcję bez jej faktycznego ustawienia $name
.
getName
zamiast $name
działa lepiej. abstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
Zadałem sobie dzisiaj to samo pytanie i chciałbym dodać moje dwa centy.
Powodem, dla którego chcielibyśmy abstract
właściwości jest upewnienie się, że podklasy je definiują i zgłaszanie wyjątków, gdy tego nie robią. W moim przypadku potrzebowałem czegoś, co mogłoby współpracować z static
sojusznikiem.
Idealnie chciałbym coś takiego:
abstract class A {
abstract protected static $prop;
}
class B extends A {
protected static $prop = 'B prop'; // $prop defined, B loads successfully
}
class C extends A {
// throws an exception when loading C for the first time because $prop
// is not defined.
}
Skończyło się na tej implementacji
abstract class A
{
// no $prop definition in A!
public static final function getProp()
{
return static::$prop;
}
}
class B extends A
{
protected static $prop = 'B prop';
}
class C extends A
{
}
Jak widać, A
nie definiuję $prop
, ale używam go w static
getterze. Dlatego działa następujący kod
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
W C
natomiast nie definiuję $prop
, więc dostaję wyjątki:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
Muszę wywołać getProp()
metodę, aby uzyskać wyjątek i nie mogę jej uzyskać podczas ładowania klasy, ale jest dość blisko pożądanego zachowania, przynajmniej w moim przypadku.
I zdefiniować getProp()
jako final
celu uniknięcia że niektóre mądry facet (aka siebie w ciągu 6 miesięcy) chciałoby się zrobić
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
Jak mogłeś się dowiedzieć, po prostu testując swój kod:
Błąd krytyczny: właściwości nie można zadeklarować jako abstrakcyjne w ... w wierszu 3
Nie, nie ma. Właściwości nie mogą być deklarowane jako abstrakcyjne w PHP.
Możesz jednak zaimplementować abstrakcyjną funkcję pobierającą / ustawiającą, może to być to, czego szukasz.
Właściwości nie są zaimplementowane (zwłaszcza właściwości publiczne), po prostu istnieją (lub nie):
$foo = new Foo;
$foo->publicProperty = 'Bar';
Potrzeba abstrakcyjnych właściwości może wskazywać na problemy projektowe. Chociaż wiele odpowiedzi implementuje wzorzec metody Template i działa, zawsze wygląda to trochę dziwnie.
Spójrzmy na oryginalny przykład:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
Zaznaczenie czegoś abstract
oznacza wskazanie, że jest to rzecz obowiązkowa. Cóż, wartość obowiązkowa (w tym przypadku) jest wymaganą zależnością, więc powinna zostać przekazana do konstruktora podczas tworzenia instancji :
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
Następnie, jeśli faktycznie chcesz bardziej konkretnej nazwanej klasy, możesz dziedziczyć w następujący sposób:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
Może to być przydatne, jeśli używasz kontenera DI i musisz przekazywać różne tabele dla różnych obiektów.
PHP 7 znacznie ułatwia tworzenie abstrakcyjnych "właściwości". Tak jak powyżej, stworzysz je, tworząc abstrakcyjne funkcje, ale w PHP 7 możesz zdefiniować typ zwracania dla tej funkcji, co znacznie ułatwia budowanie klasy bazowej, którą każdy może rozszerzyć.
<?php
abstract class FooBase {
abstract public function FooProp(): string;
abstract public function BarProp(): BarClass;
public function foo() {
return $this->FooProp();
}
public function bar() {
return $this->BarProp()->name();
}
}
class BarClass {
public function name() {
return 'Bar!';
}
}
class FooClass extends FooBase {
public function FooProp(): string {
return 'Foo!';
}
public function BarProp(): BarClass {
// This would not work:
// return 'not working';
// But this will!
return new BarClass();
}
}
$test = new FooClass();
echo $test->foo() . PHP_EOL;
echo $test->bar() . PHP_EOL;
jeśli wartość tablename nigdy się nie zmieni w czasie życia obiektu, następująca będzie prosta, ale bezpieczna implementacja.
abstract class Foo_Abstract {
abstract protected function getTablename();
public function showTableName()
{
echo 'my table name is '.$this->getTablename();
}
}
class Foo extends Foo_Abstract {
//Foo must 'implement' getTablename()
protected function getTablename()
{
return 'users';
}
}
kluczem jest tutaj to, że wartość ciągu „users” jest określona i zwrócona bezpośrednio w funkcji getTablename () w implementacji klasy potomnej. Funkcja naśladuje właściwość „tylko do odczytu”.
Jest to dość podobne do rozwiązania opublikowanego wcześniej, w którym używana jest dodatkowa zmienna. Podoba mi się również rozwiązanie Marco, chociaż może być nieco bardziej skomplikowane.