Jawaban:
Tidak ada yang mendefinisikan properti.
Anda hanya dapat mendeklarasikan properti karena itu adalah wadah data yang disimpan dalam memori saat inisialisasi.
Fungsi di sisi lain dapat dideklarasikan (tipe, nama, parameter) tanpa didefinisikan (fungsi tubuh hilang) dan dengan demikian, dapat dibuat abstrak.
"Abstrak" hanya menunjukkan bahwa sesuatu dideklarasikan tetapi tidak didefinisikan dan karena itu sebelum menggunakannya, Anda perlu mendefinisikannya atau menjadi tidak berguna.
Tidak, tidak ada cara untuk menegakkan itu dengan kompiler, Anda harus menggunakan pemeriksaan run-time (katakanlah, dalam konstruktor) untuk $tablename
variabel, misalnya:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
Untuk menegakkan ini untuk semua kelas turunan Foo_Abstract Anda harus membuat konstruktor Foo_Abstract final
, mencegah pengesampingan.
Anda bisa mendeklarasikan pengambil abstrak:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
Bergantung pada konteks properti jika saya ingin memaksakan deklarasi properti objek abstrak di objek anak, saya suka menggunakan konstanta dengan static
kata kunci untuk properti di konstruktor objek abstrak atau metode setter / pengambil. Anda dapat menggunakan secara opsional final
untuk mencegah metode ditimpa di kelas-kelas yang diperluas.
Selain itu objek anak menimpa properti objek induk dan metode jika didefinisikan ulang. Misalnya jika properti dinyatakan sebagai protected
di induk dan didefinisikan ulang seperti public
di anak, properti yang dihasilkan adalah publik. Namun, jika properti tersebut dinyatakan private
dalam induk, properti itu akan tetap private
dan tidak tersedia untuk anak.
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;
Sebagaimana dinyatakan di atas, tidak ada definisi yang tepat. Namun, saya menggunakan solusi sederhana ini untuk memaksa kelas anak untuk mendefinisikan properti "abstrak":
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
properti.
the only "safe" methods to have in a constructor are private and/or final ones
, bukankah penyelesaian masalah saya seperti itu? Saya menggunakan
$name
. Anda dapat mengimplementasikan setName()
fungsi tanpa pengaturan yang sebenarnya $name
.
getName
bukan $name
bekerja lebih baik. abstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
Saya bertanya pada diri sendiri pertanyaan yang sama hari ini, dan saya ingin menambahkan dua sen saya.
Alasan kami ingin abstract
properti adalah untuk memastikan bahwa subclass mendefinisikannya dan membuang pengecualian ketika mereka tidak. Dalam kasus khusus saya, saya membutuhkan sesuatu yang bisa bekerja dengan static
sekutu.
Idealnya saya ingin sesuatu seperti ini:
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.
}
Saya berakhir dengan implementasi ini
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
{
}
Seperti yang Anda lihat, di A
saya tidak mendefinisikan $prop
, tapi saya menggunakannya dalam static
pengambil. Oleh karena itu, kode berikut berfungsi
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
Di C
, di sisi lain, saya tidak mendefinisikan $prop
, jadi saya mendapatkan pengecualian:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
Saya harus memanggil getProp()
metode untuk mendapatkan pengecualian dan saya tidak bisa mendapatkannya di pemuatan kelas, tetapi cukup dekat dengan perilaku yang diinginkan, setidaknya dalam kasus saya.
Saya mendefinisikan getProp()
sebagai final
untuk menghindari bahwa beberapa orang pintar (alias sendiri dalam 6 bulan) tergoda untuk melakukan
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
Seperti yang Anda ketahui dengan hanya menguji kode Anda:
Kesalahan fatal: Properti tidak dapat dinyatakan abstrak di ... pada baris 3
Tidak, tidak ada. Properti tidak dapat dinyatakan abstrak dalam PHP.
Namun Anda dapat menerapkan fungsi getter / setter abstrak, ini mungkin yang Anda cari.
Properti tidak diimplementasikan (terutama properti publik), mereka hanya ada (atau tidak):
$foo = new Foo;
$foo->publicProperty = 'Bar';
Kebutuhan akan properti abstrak dapat mengindikasikan masalah desain. Sementara banyak jawaban menerapkan jenis pola metode Templat dan itu berfungsi, itu selalu terlihat agak aneh.
Mari kita lihat contoh aslinya:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
Menandai sesuatu abstract
berarti menandainya sebagai sesuatu yang harus dimiliki. Nah, nilai yang harus dimiliki (dalam hal ini) adalah ketergantungan yang diperlukan, sehingga harus diteruskan ke konstruktor selama instantiasi :
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
Maka jika Anda benar-benar menginginkan kelas bernama yang lebih konkret, Anda dapat mewarisi seperti:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
Ini dapat berguna jika Anda menggunakan wadah DI dan harus melewati tabel berbeda untuk objek yang berbeda.
PHP 7 membuatnya sedikit lebih mudah untuk membuat "properti" abstrak. Sama seperti di atas, Anda akan membuatnya dengan membuat fungsi abstrak, tetapi dengan PHP 7 Anda dapat menentukan tipe pengembalian untuk fungsi itu, yang membuat segalanya jauh lebih mudah ketika Anda membangun kelas dasar yang dapat diperluas oleh siapa pun.
<?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;
jika nilai tablename tidak akan pernah berubah selama masa hidup objek, mengikuti akan menjadi implementasi yang sederhana namun aman.
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';
}
}
kuncinya di sini adalah bahwa nilai string 'pengguna' ditentukan dan dikembalikan langsung di getTablename () dalam implementasi kelas anak. Fungsi ini meniru properti "readonly".
Ini cukup mirip dengan solusi yang diposting sebelumnya yang menggunakan variabel tambahan. Saya juga suka solusi Marco meskipun bisa sedikit lebih rumit.