Câu trả lời:
Không có những thứ như xác định một tài sản.
Bạn chỉ có thể khai báo các thuộc tính vì chúng là các thùng chứa dữ liệu được lưu trữ trong bộ nhớ khi khởi tạo.
Mặt khác, một hàm có thể được khai báo (loại, tên, tham số) mà không được xác định (thân hàm bị thiếu) và do đó, có thể được làm cho trừu tượng.
"Trừu tượng" chỉ cho biết rằng một cái gì đó đã được khai báo nhưng không được xác định và do đó trước khi sử dụng nó, bạn cần xác định nó hoặc nó trở nên vô dụng.
Không, không có cách nào để thực thi điều đó với trình biên dịch, bạn phải sử dụng kiểm tra thời gian chạy (giả sử, trong hàm tạo) cho $tablename
biến, ví dụ:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
Để thực thi điều này cho tất cả các lớp dẫn xuất của Foo_Ab bát, bạn sẽ phải tạo hàm tạo của Foo_Ab khu vực final
, ngăn chặn ghi đè.
Bạn có thể khai báo một getter trừu tượng thay thế:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
Tùy thuộc vào ngữ cảnh của thuộc tính nếu tôi muốn buộc khai báo thuộc tính đối tượng trừu tượng trong đối tượng con, tôi muốn sử dụng hằng số với static
từ khóa cho thuộc tính trong phương thức xây dựng đối tượng trừu tượng hoặc phương thức setter / getter. Bạn có thể tùy ý sử dụng final
để ngăn chặn phương thức bị ghi đè trong các lớp mở rộng.
Ngoài ra, đối tượng con sẽ ghi đè các thuộc tính và phương thức của đối tượng cha nếu được định nghĩa lại. Ví dụ: nếu một thuộc tính được khai báo như protected
trong cha mẹ và được định nghĩa lại như public
ở con, thì kết quả là thuộc tính công khai. Tuy nhiên, nếu tài sản được khai báo private
trong cha mẹ, nó sẽ vẫn còn private
và không có sẵn cho đứa trẻ.
http://www.php.net//manual/en/lingu.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;
Như đã nêu ở trên, không có định nghĩa chính xác như vậy. Tuy nhiên, tôi sử dụng cách giải quyết đơn giản này để buộc lớp con xác định thuộc tính "trừu tượng":
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
các tài sản.
the only "safe" methods to have in a constructor are private and/or final ones
, không phải là cách giải quyết của tôi như vậy sao? tôi đang sử dụng tư nhân trong đó
$name
. Bạn có thể thực hiện setName()
chức năng mà không cần thiết lập $name
.
getName
thay vì $name
làm việc tốt hơn. abstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
Tôi đã tự hỏi mình câu hỏi tương tự ngày hôm nay và tôi muốn thêm hai xu của mình.
Lý do chúng tôi muốn abstract
các thuộc tính là để đảm bảo rằng các lớp con định nghĩa chúng và đưa ra các ngoại lệ khi chúng không có. Trong trường hợp cụ thể của tôi, tôi cần một cái gì đó có thể làm việc với static
đồng minh.
Lý tưởng nhất là tôi muốn một cái gì đó như thế này:
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.
}
Tôi đã kết thúc với việc thực hiện này
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
{
}
Như bạn có thể thấy, trong A
tôi không định nghĩa $prop
, nhưng tôi sử dụng nó trong một static
getter. Do đó, đoạn mã sau hoạt động
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
Trong C
Mặt khác, tôi không xác định $prop
, vì vậy tôi có được ngoại lệ:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
Tôi phải gọi getProp()
phương thức để có ngoại lệ và tôi không thể tải nó khi tải lớp, nhưng nó khá gần với hành vi mong muốn, ít nhất là trong trường hợp của tôi.
Tôi xác định getProp()
là final
để tránh việc một người thông minh nào đó (còn gọi là bản thân tôi trong 6 tháng) bị cám dỗ để làm
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
Như bạn có thể đã tìm ra bằng cách chỉ kiểm tra mã của bạn:
Lỗi nghiêm trọng: Thuộc tính không thể được khai báo trừu tượng trong ... trên dòng 3
Không có. Các thuộc tính không thể được khai báo trừu tượng trong PHP.
Tuy nhiên, bạn có thể thực hiện một tóm tắt chức năng getter / setter, đây có thể là những gì bạn đang tìm kiếm.
Các thuộc tính không được triển khai (đặc biệt là các thuộc tính công cộng), chúng chỉ tồn tại (hoặc không):
$foo = new Foo;
$foo->publicProperty = 'Bar';
Sự cần thiết cho các thuộc tính trừu tượng có thể chỉ ra các vấn đề thiết kế. Trong khi nhiều câu trả lời triển khai loại mẫu phương thức Mẫu và nó hoạt động, nó luôn trông có vẻ lạ.
Hãy xem ví dụ ban đầu:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
Để đánh dấu một cái gì đó abstract
là chỉ ra nó là một thứ bắt buộc phải có. Chà, một giá trị bắt buộc (trong trường hợp này) là một phụ thuộc bắt buộc, vì vậy nó phải được chuyển đến hàm tạo trong quá trình khởi tạo :
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
Sau đó, nếu bạn thực sự muốn một lớp có tên cụ thể hơn, bạn có thể kế thừa như vậy:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
Điều này có thể hữu ích nếu bạn sử dụng DI container và phải chuyển các bảng khác nhau cho các đối tượng khác nhau.
PHP 7 làm cho nó dễ dàng hơn một chút để tạo các "thuộc tính" trừu tượng. Cũng như trên, bạn sẽ tạo chúng bằng cách tạo các hàm trừu tượng, nhưng với PHP 7, bạn có thể định nghĩa kiểu trả về cho hàm đó, điều này giúp mọi thứ dễ dàng hơn rất nhiều khi bạn xây dựng một lớp cơ sở mà bất kỳ ai cũng có thể mở rộng.
<?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;
nếu giá trị tablename sẽ không bao giờ thay đổi trong suốt vòng đời của đối tượng, sau đây sẽ là một triển khai đơn giản nhưng an toàn.
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';
}
}
chìa khóa ở đây là giá trị chuỗi 'người dùng' được chỉ định và được trả về trực tiếp trong getTablename () khi triển khai lớp con. Hàm bắt chước một thuộc tính "chỉ đọc".
Điều này khá giống với một giải pháp được đăng trước đó sử dụng một biến bổ sung. Tôi cũng thích giải pháp của Marco mặc dù nó có thể phức tạp hơn một chút.