PHP सार गुण


126

क्या PHP में अमूर्त वर्ग गुणों को परिभाषित करने का कोई तरीका है?

abstract class Foo_Abstract {
    abstract public $tablename;
}

class Foo extends Foo_Abstract {
    //Foo must 'implement' $property
    public $tablename = 'users';   
}

जवाबों:


154

किसी संपत्ति को परिभाषित करने जैसी कोई बात नहीं है।

आप केवल संपत्तियों की घोषणा कर सकते हैं क्योंकि वे आरंभीकरण पर स्मृति में आरक्षित डेटा के कंटेनर हैं।

दूसरी ओर एक फ़ंक्शन को परिभाषित किया जा सकता है (प्रकार, नाम, पैरामीटर) परिभाषित किए बिना (फ़ंक्शन बॉडी गायब) और इस प्रकार, इसे सार बनाया जा सकता है।

"सार" केवल इंगित करता है कि कुछ घोषित किया गया था लेकिन परिभाषित नहीं किया गया था और इसलिए इसका उपयोग करने से पहले, आपको इसे परिभाषित करने की आवश्यकता है या यह बेकार हो जाता है।


59
कोई स्पष्ट कारण नहीं है कि 'अमूर्त' शब्द का इस्तेमाल स्थैतिक गुणों पर नहीं किया जा सकता है - लेकिन थोड़ा अलग अर्थ के साथ। उदाहरण के लिए यह इंगित कर सकता है कि एक उपवर्ग को संपत्ति के लिए एक मूल्य प्रदान करना है।
फ्रोडेबोरली

2
टाइपस्क्रिप्ट में अमूर्त गुण और एक्सेसर्स हैं । यह दुखद है कि php में यह असंभव है।
--еленько

52

नहीं, यह लागू करने का कोई तरीका नहीं है कि संकलक के साथ, आपको $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;
  }
}

अच्छी सुविधा, मुझे पसंद है कि आप अमूर्त गुणों को कैसे लागू करते हैं।
मैथ्यू डुमोलिन

4
इसके लिए आपको एब्सट्रैक्ट बेस क्लास में कंस्ट्रक्टर को फाइनल करना होगा।
हक्रे

3
कुछ स्पष्टीकरण: यदि आप कंस्ट्रक्टर के अंदर चेक करते हैं और यदि यह अनिवार्य होना चाहिए, तो आपको यह सुनिश्चित करने की आवश्यकता है कि यह हर इंस्टेंटेशन पर निष्पादित हो। इसलिए आपको रोकने की आवश्यकता है कि यह हटा दिया जाता है, उदाहरण के लिए वर्ग का विस्तार करके और निर्माणकर्ता को प्रतिस्थापित करके। अंतिम कीवर्ड आपको ऐसा करने की अनुमति होगी।
हेक्रे

1
मुझे "एब्सट्रैक्ट गेट्टर" समाधान पसंद है। जब आप क्लास एब्सट्रैक्ट में कोई फंक्शन घोषित करते हैं, तो आपको क्लास को एब्सट्रैक्ट घोषित करना चाहिए। इसका मतलब यह है कि वर्ग अनुपयोगी है जब तक कि इसे विस्तारित और पूरी तरह से लागू नहीं किया जाता है। उस वर्ग का विस्तार करते समय, आपको "गेट्टर" फ़ंक्शन के लिए एक कार्यान्वयन प्रदान करना होगा। इसका मतलब है कि आपको फैली हुई क्लास के अंदर एक संबंधित संपत्ति भी बनानी होगी, क्योंकि फ़ंक्शन के पास लौटने के लिए कुछ होना चाहिए। इस पैटर्न के बाद आपको एक ही परिणाम मिलता है जैसे कि आप एक अमूर्त संपत्ति घोषित करके करेंगे, यह भी एक साफ और स्पष्ट दृष्टिकोण है। कि वास्तव में यह कैसे किया जाता है।
सालिवन

1
एब्सट्रैक्ट गेट्टर का उपयोग करने से आप इसे एक वैल्यू जेनरेट करके इसे लागू कर सकते हैं , जैसा कि एक स्थिर मूल्य को वापस करने के लिए, जब ऐसा करने का कोई मतलब होता है। एक अमूर्त संपत्ति आपको ऐसा करने की अनुमति नहीं देगी, विशेष रूप से एक स्थिर संपत्ति।
टोबिया

27

संपत्ति के संदर्भ के आधार पर अगर मैं किसी चाइल्ड ऑब्जेक्ट में एब्सट्रैक्ट ऑब्जेक्ट प्रॉपर्टी की घोषणा को बाध्य करना चाहता हूं, तो मैं staticएब्सट्रैक्ट ऑब्जेक्ट कंस्ट्रक्टर या सेटर / गेट्टर विधियों में संपत्ति के लिए कीवर्ड के साथ एक निरंतर का उपयोग करना पसंद करता हूं । आप वैकल्पिक रूप से उपयोग कर सकते हैं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;

4
सबसे सुरुचिपूर्ण समाधान यहां
जेनी थिसिसन

24

जैसा कि ऊपर कहा गया है, ऐसी कोई सटीक परिभाषा नहीं है। हालाँकि, मैं इस साधारण वर्कअराउंड का उपयोग करके "अमूर्त" संपत्ति को परिभाषित करने के लिए बाल वर्ग को मजबूर करता हूं:

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गुणों के लिए समस्या को हल नहीं करता है ।
रॉबर्ट

1
मुझे नहीं लगता कि आप सार विधियों के लिए निजी हो सकते हैं।
जोर्जी

@ Phate01 जैसा कि मैंने इसे समझा, टिप्पणी में यह कहा गया है the only "safe" methods to have in a constructor are private and/or final ones, क्या मेरा मामला इस तरह का नहीं है? im इसमें निजीकरण का उपयोग कर रहा है
ulkas

4
यह अच्छा लग रहा है, लेकिन यह वास्तव में सेट करने के लिए एक बाल वर्ग को मजबूर नहीं करता है $name। आप setName()वास्तव में सेटिंग के बिना फ़ंक्शन को लागू कर सकते हैं $name
जॉन 20

3
मुझे लगता है कि बेहतर काम getNameकरने के बजाय इसका उपयोग करना $nameabstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
हामिद

7

मैंने आज खुद से वही सवाल पूछा है, और मैं अपने दो सेंट जोड़ना चाहूंगा।

जिन 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() अपवाद प्राप्त करने के लिए विधि को कॉल करना चाहिए और मैं इसे क्लास लोडिंग पर प्राप्त नहीं कर सकता, लेकिन यह वांछित व्यवहार के काफी करीब है, कम से कम मेरे मामले में।

मैं कुछ स्मार्ट आदमी (6 महीने में खुद को उर्फ) से बचने के लिए परिभाषित getProp()करता हूंfinal

class D extends A {
    public static function getProp() {
        // really smart
    }
}

D::getProp();
// => no exception...

यह बहुत सरल हैक है। उम्मीद है कि भविष्य में ऐसा करने की जरूरत नहीं है।
CMCDragonkai

6

जैसा कि आप अपने कोड का परीक्षण करके पता लगा सकते हैं:

घातक त्रुटि: 3 लाइन पर ... में गुणों को सार घोषित नहीं किया जा सकता है

नहीं वहाँ नहीं है। PHP में संपत्तियों को सार घोषित नहीं किया जा सकता है।

हालाँकि, आप एक गेट्टर / सेटर फ़ंक्शन सार को लागू कर सकते हैं, यह वही हो सकता है जिसे आप खोज रहे हैं।

गुण लागू नहीं किए गए हैं (विशेष रूप से सार्वजनिक गुण), वे बस मौजूद हैं (या नहीं):

$foo = new Foo;
$foo->publicProperty = 'Bar';

6

अमूर्त गुणों की आवश्यकता डिजाइन समस्याओं को इंगित कर सकती है। जबकि कई उत्तर टेम्पलेट विधि पैटर्न को लागू करते हैं और यह काम करता है, यह हमेशा अजीब लगता है।

आइए मूल उदाहरण पर एक नज़र डालें:

abstract class Foo_Abstract {
    abstract public $tablename;
}

class Foo extends Foo_Abstract {
    //Foo must 'implement' $property
    public $tablename = 'users';   
}

किसी चीज़ को चिन्हित abstractकरना यह इंगित करना होगा कि यह एक जरूरी चीज है। खैर, एक होना चाहिए मूल्य (इस मामले में) एक आवश्यक निर्भरता है, इसलिए इसे तत्काल के दौरान निर्माणकर्ता को पारित किया जाना चाहिए :

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');
    }
}

यह उपयोगी हो सकता है यदि आप डि कंटेनर का उपयोग करते हैं और विभिन्न वस्तुओं के लिए अलग-अलग तालिकाओं को पास करना है।


3

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;

1

यदि ऑब्जेक्ट के जीवनकाल के दौरान टैबलेन का मान कभी नहीं बदलेगा, तो निम्नलिखित एक सरल लेकिन सुरक्षित कार्यान्वयन होगा।

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 () में लौटाया गया है। फ़ंक्शन एक "आसानी से" संपत्ति की नकल करता है।

यह पहले से पोस्ट किए गए समाधान के समान है, जिस पर एक अतिरिक्त चर का उपयोग किया जाता है। मुझे मार्को का समाधान भी पसंद है, हालांकि यह थोड़ा अधिक जटिल हो सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.