มีวิธีใดในการกำหนดคุณสมบัติคลาสนามธรรมใน PHP หรือไม่?
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
มีวิธีใดในการกำหนดคุณสมบัติคลาสนามธรรมใน PHP หรือไม่?
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
คำตอบ:
ไม่มีสิ่งที่เรียกว่าการกำหนดคุณสมบัติ
คุณสามารถประกาศคุณสมบัติได้เนื่องจากเป็นที่เก็บข้อมูลที่สงวนไว้ในหน่วยความจำเมื่อเริ่มต้น
ในทางกลับกันสามารถประกาศฟังก์ชัน (ประเภท, ชื่อ, พารามิเตอร์) ได้โดยไม่ต้องกำหนด (ส่วนของฟังก์ชันขาดหายไป) ดังนั้นจึงสามารถทำให้เป็นนามธรรมได้
"บทคัดย่อ" เพียงระบุว่ามีการประกาศบางสิ่งบางอย่าง แต่ไม่ได้กำหนดไว้ดังนั้นก่อนที่จะใช้งานคุณต้องกำหนดไม่เช่นนั้นจะไม่มีประโยชน์
ไม่ไม่มีวิธีบังคับใช้กับคอมไพลเลอร์คุณจะต้องใช้การตรวจสอบรันไทม์ (พูดในตัวสร้าง) สำหรับ$tablename
ตัวแปรเช่น:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
ในการบังคับใช้สิ่งนี้สำหรับคลาสที่ได้รับทั้งหมดของ Foo_Abstract คุณจะต้องสร้างตัวสร้างของ Foo_Abstract final
เพื่อป้องกันการลบล้าง
คุณสามารถประกาศรับนามธรรมแทน:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
ขึ้นอยู่กับบริบทของคุณสมบัติหากฉันต้องการบังคับให้ประกาศคุณสมบัติของวัตถุนามธรรมในวัตถุลูกฉันต้องการใช้ค่าคงที่กับstatic
คำสำคัญสำหรับคุณสมบัติในตัวสร้างวัตถุนามธรรมหรือวิธีการ setter / getter คุณสามารถเลือกใช้final
เพื่อป้องกันไม่ให้เมธอดถูกแทนที่ในคลาสเพิ่มเติมได้
นอกเหนือจากนั้นอ็อบเจ็กต์ลูกจะแทนที่คุณสมบัติอ็อบเจ็กต์พาเรนต์และเมธอดหากกำหนดใหม่ ตัวอย่างเช่นหากคุณสมบัติถูกประกาศเป็นprotected
ในพาเรนต์และกำหนดใหม่เหมือนpublic
ในลูกคุณสมบัติที่ได้จะเป็นแบบสาธารณะ อย่างไรก็ตามหากมีการประกาศคุณสมบัติในพาเรนต์ทรัพย์สินprivate
นั้นจะยังคงอยู่private
และไม่สามารถใช้ได้กับเด็ก
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;
ตามที่ระบุไว้ข้างต้นไม่มีคำจำกัดความที่แน่นอนดังกล่าว อย่างไรก็ตามฉันใช้วิธีแก้ปัญหาง่ายๆนี้เพื่อบังคับให้คลาสย่อยกำหนดคุณสมบัติ "นามธรรม":
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
คุณสมบัติ
the only "safe" methods to have in a constructor are private and/or final ones
ไม่ใช่วิธีแก้ปัญหาของฉันในกรณีเช่นนี้? ฉันกำลังใช้
$name
แต่ก็ไม่ได้บังคับให้ชั้นเด็กที่จะตั้งจริง คุณสามารถใช้ฟังก์ชั่นโดยไม่ได้ตั้งค่าจริงsetName()
$name
getName
แทนได้$name
ผลดีกว่า abstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
วันนี้ฉันถามตัวเองด้วยคำถามเดียวกันและฉันต้องการเพิ่มสองเซ็นต์ของฉัน
เหตุผลที่เราต้องการabstract
คุณสมบัติคือการตรวจสอบให้แน่ใจว่าคลาสย่อยกำหนดคุณสมบัติเหล่านั้นและทิ้งข้อยกเว้นเมื่อไม่มี ในกรณีเฉพาะของฉันฉันต้องการบางอย่างที่สามารถใช้งานได้static
พันธมิตรได้
ตามหลักการแล้วฉันต้องการสิ่งนี้:
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.
}
ฉันลงเอยด้วยการใช้งานนี้
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
{
}
อย่างที่คุณเห็นในA
ฉันไม่ได้กำหนด$prop
แต่ฉันใช้มันในเชิงstatic
รุก ดังนั้นรหัสต่อไปนี้ใช้งานได้
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
ในC
ทางกลับกันฉันไม่ได้กำหนด$prop
ดังนั้นฉันจึงได้รับข้อยกเว้น:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
ฉันต้องโทร getProp()
เมธอดเพื่อรับข้อยกเว้นและฉันไม่สามารถโหลดได้ในการโหลดคลาส แต่มันค่อนข้างใกล้เคียงกับพฤติกรรมที่ต้องการอย่างน้อยก็ในกรณีของฉัน
ฉันให้คำจำกัดความgetProp()
ว่าfinal
จะหลีกเลี่ยงไม่ให้ผู้ชายที่ฉลาดบางคน (หรือใน 6 เดือน) ถูกล่อลวงให้ทำ
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
ดังที่คุณทราบได้จากการทดสอบโค้ดของคุณ:
ข้อผิดพลาดร้ายแรง: ไม่สามารถประกาศคุณสมบัติเป็นนามธรรมใน ... ในบรรทัดที่ 3
ไม่มีไม่มี คุณสมบัติไม่สามารถประกาศนามธรรมใน PHP
อย่างไรก็ตามคุณสามารถใช้นามธรรมของฟังก์ชัน getter / setter ได้นี่อาจเป็นสิ่งที่คุณกำลังมองหา
คุณสมบัติไม่ได้ใช้งาน (โดยเฉพาะคุณสมบัติสาธารณะ) มีอยู่จริง (หรือไม่):
$foo = new Foo;
$foo->publicProperty = 'Bar';
ความต้องการคุณสมบัติที่เป็นนามธรรมสามารถบ่งบอกถึงปัญหาการออกแบบ ในขณะที่คำตอบจำนวนมากใช้รูปแบบวิธีการของเทมเพลตแต่มันก็ดูแปลก ๆ อยู่เสมอ
ลองดูตัวอย่างต้นฉบับ:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
การทำเครื่องหมายบางอย่างabstract
คือการระบุว่าเป็นสิ่งที่ต้องมี ดีต้องมีค่า (ในกรณีนี้) เป็นพึ่งพาจำเป็นดังนั้นจึงควรถูกส่งผ่านไปสร้างในช่วง instantiation :
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
จากนั้นถ้าคุณต้องการคลาสที่มีชื่อที่เป็นรูปธรรมมากขึ้นคุณสามารถสืบทอดได้ดังนี้:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
สิ่งนี้จะมีประโยชน์หากคุณใช้คอนเทนเนอร์ DI และต้องส่งตารางที่แตกต่างกันสำหรับวัตถุต่างๆ
PHP 7 ช่วยให้สร้าง "คุณสมบัติ" นามธรรมได้ง่ายขึ้น เช่นเดียวกับข้างต้นคุณจะสร้างมันได้โดยการสร้างฟังก์ชันนามธรรม แต่ด้วย PHP 7 คุณสามารถกำหนดประเภทการส่งคืนสำหรับฟังก์ชันนั้นได้ซึ่งจะทำให้สิ่งต่างๆง่ายขึ้นมากเมื่อคุณสร้างคลาสพื้นฐานที่ทุกคนสามารถขยายได้
<?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;
หากค่า tablename จะไม่เปลี่ยนแปลงตลอดอายุการใช้งานของออบเจ็กต์ต่อไปนี้จะเป็นการใช้งานที่เรียบง่าย แต่ปลอดภัย
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';
}
}
คีย์ในที่นี้คือค่าสตริง "ผู้ใช้" ถูกระบุและส่งคืนโดยตรงใน getTablename () ในการใช้คลาสย่อย ฟังก์ชันนี้เลียนแบบคุณสมบัติ "อ่านอย่างเดียว"
ค่อนข้างคล้ายกับโซลูชันที่โพสต์ไว้ก่อนหน้านี้ซึ่งใช้ตัวแปรเพิ่มเติม ฉันชอบวิธีแก้ปัญหาของ Marco แม้ว่าจะซับซ้อนกว่าเล็กน้อย