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 $tablenamebiế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 statictừ 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ư protectedtrong 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 privatetrong cha mẹ, nó sẽ vẫn còn privatevà 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();
}
}
staticcá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.
getNamethay vì $namelà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 abstractcá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 Atôi không định nghĩa $prop, nhưng tôi sử dụng nó trong một staticgetter. Do đó, đoạn mã sau hoạt động
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
Trong CMặ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ì đó abstractlà 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.