लक्षण बनाम इंटरफेस


344

मैं पीएचपी पर हाल ही में अध्ययन करने की कोशिश कर रहा हूं, और मुझे लगता है कि मैं खुद को लक्षणों पर लटका हुआ हूं। मैं क्षैतिज कोड पुन: उपयोग की अवधारणा को समझता हूं और जरूरी नहीं कि एक सार वर्ग से विरासत में मिले। मुझे समझ में नहीं आ रहा है: लक्षण बनाम इंटरफेस का उपयोग करने के बीच महत्वपूर्ण अंतर क्या है?

मैंने एक अच्छे ब्लॉग पोस्ट या लेख की खोज करने की कोशिश की है जिसमें बताया गया है कि एक या दूसरे का उपयोग कब करना है, लेकिन मैंने अब तक जो उदाहरण पाए हैं, वे समान प्रतीत होते हैं।


6
फ़ंक्शन बॉडी में इंटरफ़ेस का कोई कोड नहीं है। उनके पास वास्तव में कोई कार्य निकाय नहीं है।
हकर्रे

2
मेरे बहुत उत्थान के उत्तर के बावजूद, मैं चाहूंगा कि यह रिकॉर्ड के लिए कहा जाए कि मैं आम तौर पर एंटी-ट्रिट / मिक्सिन हूं । यह पढ़ने के लिए इस चैट प्रतिलेख को देखें कि कैसे लक्षण अक्सर ठोस OOP प्रथाओं को कमजोर करते हैं
rdlowrey

2
मैं इसके विपरीत बहस करूंगा। लक्षणों के आगमन से पहले और बाद के वर्षों के लिए PHP के साथ काम करने के बाद, मुझे लगता है कि यह उनके लायक साबित करना आसान है। बस इस व्यावहारिक उदाहरण के माध्यम से पढ़ें जो 'छवि मॉडल' को Imagickवस्तुओं की तरह चलने और बात करने में सक्षम बनाता है, पुराने दिनों में आवश्यक सभी कम लक्षण जैसे लक्षण।
क्विकशिफ्टिन

लक्षण और इंटरफ़ेस समान हैं। मुख्य अंतर यह है कि लक्षण आपको विधियों को लागू करने की अनुमति देते हैं, इंटरफ़ेस नहीं।
जॉन

जवाबों:


239

एक इंटरफ़ेस विधियों के एक सेट को परिभाषित करता है जिसे कार्यान्वयन वर्ग को लागू करना होगा

जब कोई विशेषता है use, तो विधियों का क्रियान्वयन भी साथ आता है - जो किसी में नहीं होता है Interface

यही सबसे बड़ा अंतर है।

PHP RFC के लिए क्षैतिज पुन : उपयोग से :

लक्षण PHP जैसे एकल विरासत भाषाओं में कोड पुन: उपयोग के लिए एक तंत्र है। एक विशेषता विभिन्न वर्ग पदानुक्रमों में रहने वाले कई स्वतंत्र वर्गों में स्वतंत्र रूप से तरीकों के सेट का पुन: उपयोग करने के लिए एक डेवलपर को सक्षम करके एकल वंशानुक्रम की कुछ सीमाओं को कम करने का इरादा है।


2
@JREAM व्यवहार में, कुछ भी नहीं। हकीकत में, बहुत कुछ।
एलेक गॉर्ज

79
सिवाय इसके कि लक्षण बिल्कुल भी इंटरफेस नहीं हैं। इंटरफेस स्पेसिफिकेशन हैं जिनके खिलाफ जांच की जा सकती है। लक्षणों के खिलाफ जाँच नहीं की जा सकती है, इसलिए वे केवल कार्यान्वयन कर रहे हैं। वे इंटरफेस के बिल्कुल विपरीत हैं। RFC में वह लाइन गलत है ...
ircmaxell

195
लक्षण अनिवार्य रूप से भाषा की सहायता से कॉपी और पेस्ट होते हैं
शाहिद

10
वह रूपक नहीं है। यह एक शब्द का अर्थ है। यह एक बॉक्स को वॉल्यूम के साथ सतह के रूप में वर्णन करने जैसा है।

6
Ircmaxell's और Shadi की टिप्पणियों पर विस्तार करने के लिए: आप जाँच सकते हैं कि कोई ऑब्जेक्ट एक इंटरफ़ेस (उदाहरण के माध्यम से) को लागू करता है या नहीं, और आप यह सुनिश्चित कर सकते हैं कि एक विधि तर्क पद्धति के हस्ताक्षर में एक प्रकार के संकेत के माध्यम से एक इंटरफ़ेस को लागू करता है। आप लक्षण के लिए संबंधित जांच नहीं कर सकते।
ब्रायन डी'एस्टस

530

सार्वजनिक सेवा घोषणा:

मैं रिकॉर्ड के लिए कहना चाहता हूं कि मेरा मानना ​​है कि लक्षण लगभग हमेशा एक कोड गंध होते हैं और रचना के पक्ष में बचना चाहिए। यह मेरी राय है कि एकल वंशानुक्रम को अक्सर विरोधी पैटर्न होने की बात के लिए दुरुपयोग किया जाता है और एकाधिक विरासत केवल इस समस्या को समेटती है। आप ज्यादातर मामलों में विरासत के आधार पर रचना के पक्ष में बेहतर कार्य करेंगे (यह एकल या एकाधिक हो)। यदि आप अभी भी लक्षण और उनके संबंधों में रुचि रखते हैं, तो पढ़ें ...


आइए इसकी शुरुआत करते हैं:

ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग (OOP) को समझना एक कठिन प्रतिमान हो सकता है। सिर्फ इसलिए कि आप कक्षाओं का उपयोग कर रहे हैं इसका मतलब यह नहीं है कि आपका कोड ऑब्जेक्ट-ओरिएंटेड (OO) है।

OO कोड लिखने के लिए आपको यह समझना होगा कि OOP वास्तव में आपकी वस्तुओं की क्षमताओं के बारे में है। आपको कक्षाओं के बारे में सोचना है कि वे वास्तव में क्या करते हैं, इसके बजाय वे क्या कर सकते हैं । यह पारंपरिक प्रक्रियात्मक प्रोग्रामिंग के विपरीत है जहां ध्यान कुछ कोड बनाने पर है "कुछ करो।"

यदि OOP कोड योजना और डिजाइन के बारे में है, तो एक इंटरफ़ेस ब्लूप्रिंट है और एक ऑब्जेक्ट पूरी तरह से निर्मित घर है। इस बीच, लक्षण बस एक ऐसा तरीका है जो घर को ब्लूप्रिंट (इंटरफ़ेस) द्वारा निर्धारित करने में मदद करता है।

इंटरफेस

तो, हमें इंटरफेस का उपयोग क्यों करना चाहिए? काफी बस, इंटरफेस हमारे कोड को कम भंगुर बनाते हैं। यदि आप इस कथन पर संदेह करते हैं, तो किसी से भी पूछें जो विरासत कोड को बनाए रखने के लिए मजबूर हो गया है जो इंटरफेस के खिलाफ नहीं लिखा गया था।

इंटरफ़ेस प्रोग्रामर और उसके कोड के बीच एक अनुबंध है। इंटरफ़ेस कहता है, "जब तक आप मेरे नियमों से खेलते हैं, तब तक आप मुझे पसंद कर सकते हैं, लेकिन मैं वादा करता हूं कि मैं आपका दूसरा कोड नहीं तोड़ूंगा।"

एक उदाहरण के रूप में, वास्तविक दुनिया के परिदृश्य पर विचार करें (कोई कार या विजेट नहीं):

आप सर्वर लोड को कम करने के लिए वेब एप्लिकेशन के लिए कैशिंग सिस्टम लागू करना चाहते हैं

आप APC का उपयोग करके प्रतिक्रियाओं का अनुरोध करने के लिए एक वर्ग लिखकर शुरू करते हैं:

class ApcCacher
{
  public function fetch($key) {
    return apc_fetch($key);
  }
  public function store($key, $data) {
    return apc_store($key, $data);
  }
  public function delete($key) {
    return apc_delete($key);
  }
}

फिर, आपकी HTTP प्रतिसाद वस्तु में, आप वास्तविक प्रतिक्रिया उत्पन्न करने के लिए सभी कार्य करने से पहले कैश हिट के लिए जांच करते हैं:

class Controller
{
  protected $req;
  protected $resp;
  protected $cacher;

  public function __construct(Request $req, Response $resp, ApcCacher $cacher=NULL) {
    $this->req    = $req;
    $this->resp   = $resp;
    $this->cacher = $cacher;

    $this->buildResponse();
  }

  public function buildResponse() {
    if (NULL !== $this->cacher && $response = $this->cacher->fetch($this->req->uri()) {
      $this->resp = $response;
    } else {
      // Build the response manually
    }
  }

  public function getResponse() {
    return $this->resp;
  }
}

यह दृष्टिकोण महान काम करता है। लेकिन हो सकता है कि कुछ हफ्ते बाद आप तय करें कि आप एपीसी के बजाय फाइल-आधारित कैश सिस्टम का उपयोग करना चाहते हैं। अब आपको अपना कंट्रोलर कोड बदलना होगा क्योंकि आपने अपने कंट्रोलर को ApcCacherक्लास की कार्यक्षमता के साथ काम करने के लिए प्रोग्राम किया है, बजाय इसके कि क्लास की क्षमताओं को व्यक्त करता है ApcCacher। मान लीजिए कि ऊपर के बजाय आपने कंक्रीट के बजाय Controllerकक्षा को भरोसेमंद बना दिया है:CacherInterfaceApcCacher

// Your controller's constructor using the interface as a dependency
public function __construct(Request $req, Response $resp, CacherInterface $cacher=NULL)

इसके साथ जाने के लिए आप अपने इंटरफ़ेस को इस तरह परिभाषित करते हैं:

interface CacherInterface
{
  public function fetch($key);
  public function store($key, $data);
  public function delete($key);
}

बदले में आपके पास आपकी ApcCacherऔर आपकी दोनों नई FileCacherकक्षाएं होती हैं CacherInterfaceऔर आप Controllerइंटरफ़ेस द्वारा आवश्यक क्षमताओं का उपयोग करने के लिए अपनी कक्षा को लागू करते हैं ।

यह उदाहरण (उम्मीद है) प्रदर्शित करता है कि एक इंटरफ़ेस के लिए प्रोग्रामिंग आपको बिना किसी चिंता के अपनी कक्षाओं के आंतरिक कार्यान्वयन को बदलने की अनुमति देता है यदि परिवर्तन आपके अन्य कोड को तोड़ देगा।

लक्षण

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

आपको केवल तब गुण का उपयोग करना चाहिए जब कई वर्ग समान कार्यक्षमता साझा करते हैं (संभवतः एक ही इंटरफ़ेस द्वारा निर्धारित)। किसी एकल वर्ग के लिए कार्यक्षमता प्रदान करने के लिए एक विशेषता का उपयोग करने में कोई समझदारी नहीं है: जो केवल कक्षा को करता है और एक बेहतर डिज़ाइन उस विशेषता को संबंधित कक्षा में स्थानांतरित करेगा।

निम्नलिखित विशेषता कार्यान्वयन पर विचार करें:

interface Person
{
    public function greet();
    public function eat($food);
}

trait EatingTrait
{
    public function eat($food)
    {
        $this->putInMouth($food);
    }

    private function putInMouth($food)
    {
        // Digest delicious food
    }
}

class NicePerson implements Person
{
    use EatingTrait;

    public function greet()
    {
        echo 'Good day, good sir!';
    }
}

class MeanPerson implements Person
{
    use EatingTrait;

    public function greet()
    {
        echo 'Your mother was a hamster!';
    }
}

एक अधिक ठोस उदाहरण: इंटरफ़ेस चर्चा से आपके FileCacherऔर आपके दोनों ApcCacher, एक ही विधि का उपयोग करके यह निर्धारित करते हैं कि कैश प्रविष्टि बासी है और इसे हटा दिया जाना चाहिए (जाहिर है कि वास्तविक जीवन में ऐसा नहीं है, लेकिन इसके साथ जाएं)। आप एक विशेषता लिख ​​सकते हैं और दोनों वर्गों को सामान्य इंटरफ़ेस आवश्यकता के लिए इसका उपयोग करने की अनुमति दे सकते हैं।

सावधानी का एक अंतिम शब्द: सावधान रहें कि लक्षण के साथ ओवरबोर्ड न जाएं। अक्सर लक्षण खराब डिजाइन के लिए बैसाखी के रूप में उपयोग किए जाते हैं जब अद्वितीय वर्ग कार्यान्वयन पर्याप्त होगा। आपको सर्वोत्तम कोड डिज़ाइन के लिए इंटरफ़ेस आवश्यकताओं को पूरा करने के लिए लक्षणों को सीमित करना चाहिए।


69
मैं वास्तव में उस त्वरित सरल उत्तर की तलाश में था जो ऊपर प्रदान किया गया था, लेकिन मुझे यह कहना है कि आपने एक उत्कृष्ट उत्तर दिया है जो दूसरों के लिए भेद स्पष्ट करने में मदद करेगा, यश।
डेटग्यूहॉवंडर्स

35
"[सी] रैटिंग लक्षण जो किसी दिए गए वर्ग में एक इंटरफ़ेस द्वारा आवश्यक क्षमताओं को पूरा करते हैं, एक आदर्श उपयोग मामला है"। बिल्कुल: +1
एलेक कण्ठ

5
क्या यह कहना उचित होगा कि PHP में लक्षण अन्य भाषाओं के मिश्रण के समान हैं?
Eno

5
@igorpan सभी इरादों और उद्देश्यों के लिए मैं कहूंगा कि PHP के लक्षण कार्यान्वयन है एकाधिक वंशानुक्रम के रूप में ही। यह ध्यान देने योग्य है कि यदि PHP में कोई लक्षण स्थैतिक गुणों को निर्दिष्ट करता है, तो गुण का उपयोग करने वाले प्रत्येक वर्ग की स्थैतिक संपत्ति की अपनी प्रति होगी। इससे भी महत्वपूर्ण ... यह देखते हुए कि कैसे यह पोस्ट अब SERPs पर बहुत अधिक है जब लक्षण के लिए क्वेरी मैं पृष्ठ के शीर्ष पर एक सार्वजनिक सेवा घोषणा जोड़ने जा रहा हूँ। आपको इसे पढ़ना चाहिए।
rdlowrey

3
गहराई से स्पष्टीकरण के लिए +1। मैं एक रूबी पृष्ठभूमि से आता हूं, जहां मिक्सिन एक बहुत उपयोग किया जाता है; सिर्फ अपने दो सेंट जोड़ने के लिए, अंगूठे का एक अच्छा नियम जो हम उपयोग करते हैं, उसे php में अनुवाद किया जा सकता है क्योंकि "इस तरीके को लागू नहीं करते हैं जो लक्षणों में $ इसे म्यूट करते हैं"। यह पागल डिबगिंग सत्रों की एक पूरी गुच्छा को रोकता है ... एक मिक्सिन को उस वर्ग पर कोई भी धारणा नहीं बनानी चाहिए जो इसमें मिलाया जाएगा (या आपको इसे बहुत स्पष्ट करना चाहिए और निर्भरता को कम से कम करना चाहिए)। इस संबंध में, मुझे लगता है कि इंटरफेस लागू करने वाले लक्षणों का आपका विचार अच्छा है।
m_x

67

traitअनिवार्य रूप से PHP का कार्यान्वयन है mixin, और प्रभावी रूप से विस्तार विधियों का एक सेट है, जिसे किसी भी वर्ग के अलावा के माध्यम से जोड़ा जा सकता है trait। विधियाँ तब उस वर्ग के कार्यान्वयन का हिस्सा बन जाती हैं, लेकिन विरासत के उपयोग के बिना

से पीएचपी मैनुअल (जोर मेरा):

लक्षण एकल विरासत भाषाओं जैसे PHP में कोड पुन: उपयोग के लिए एक तंत्र हैं । ... यह पारंपरिक विरासत के लिए एक अतिरिक्त है और व्यवहार की क्षैतिज संरचना को सक्षम करता है; वह है, विरासत की आवश्यकता के बिना वर्ग के सदस्यों के आवेदन।

एक उदाहरण:

trait myTrait {
    function foo() { return "Foo!"; }
    function bar() { return "Bar!"; }
}

उपरोक्त विशेषता के साथ, मैं अब निम्नलिखित कर सकता हूं:

class MyClass extends SomeBaseClass {
    use myTrait; // Inclusion of the trait myTrait
}

इस बिंदु पर, जब मैं कक्षा का एक उदाहरण बनाता हूं MyClass, तो इसके दो तरीके होते हैं, जिन्हें बुलाया जाता है foo()और bar()- जो आते हैं myTrait। और - ध्यान दें कि trait-dfined विधियों में पहले से ही एक विधि निकाय है - जो कि एInterface -dfined विधि नहीं कर सकता है।

इसके अतिरिक्त - PHP, कई अन्य भाषाओं की तरह, एकल वंशानुक्रम मॉडल का उपयोग करता है - जिसका अर्थ है कि एक वर्ग कई इंटरफेस से प्राप्त कर सकता है, लेकिन कई वर्ग। हालाँकि, एक PHP वर्ग कई हो सकता हैtrait समावेश - जो प्रोग्रामर को पुन: प्रयोज्य टुकड़ों को शामिल करने की अनुमति देता है - जैसे कि यदि वे कई आधार वर्गों को शामिल कर सकते हैं।

ध्यान देने योग्य कुछ बातें:

                      -----------------------------------------------
                      |   Interface   |  Base Class   |    Trait    |
                      ===============================================
> 1 per class         |      Yes      |       No      |     Yes     |
---------------------------------------------------------------------
Define Method Body    |      No       |       Yes     |     Yes     |
---------------------------------------------------------------------
Polymorphism          |      Yes      |       Yes     |     No      |
---------------------------------------------------------------------

बहुरूपता:

पहले के उदाहरण में, जहां MyClass विस्तार होता है SomeBaseClass , MyClass का एक उदाहरण है SomeBaseClass। दूसरे शब्दों में, एक सरणी जैसे के SomeBaseClass[] basesउदाहरण हो सकते हैं MyClass। इसी तरह, अगर MyClassबढ़ाया जाता है IBaseInterface, तो एक सरणी IBaseInterface[] basesमें उदाहरण हो सकते हैं MyClass। ऐसा कोई पॉलिमॉर्फिक निर्माण उपलब्ध नहीं है trait- क्योंकि traitयह अनिवार्य रूप से सिर्फ एक कोड है जो प्रोग्रामर की सुविधा के लिए प्रत्येक वर्ग में कॉपी किया जाता है जो इसका उपयोग करता है।

पूर्वता:

जैसा कि मैनुअल में वर्णित है:

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

तो - निम्नलिखित परिदृश्य पर विचार करें:

class BaseClass {
    function SomeMethod() { /* Do stuff here */ }
}

interface IBase {
    function SomeMethod();
}

trait myTrait {
    function SomeMethod() { /* Do different stuff here */ }
}

class MyClass extends BaseClass implements IBase {
    use myTrait;

    function SomeMethod() { /* Do a third thing */ }
}

MyClass का एक उदाहरण बनाते समय, ऊपर, निम्न होता है:

  1. Interface IBaseएक पैरामीटर रहित फ़ंक्शन की आवश्यकता होती है जिसे SomeMethod()प्रदान किया जाना है।
  2. आधार वर्ग BaseClassइस पद्धति का कार्यान्वयन प्रदान करता है - आवश्यकता को संतुष्ट करता है।
  3. के रूप में अच्छी तरह से trait myTraitबुलाया एक पैरामीटर रहित फ़ंक्शन प्रदान करता है SomeMethod(), जोBaseClass -version पर पूर्वता लेता है
  4. के class MyClassअपने संस्करण प्रदान करता है SomeMethod()- जो पूर्ववर्ती पर traitव्यापकता लेता है

निष्कर्ष

  1. एक Interfaceविधि निकाय के एक डिफ़ॉल्ट कार्यान्वयन प्रदान नहीं कर सकता है, जबकि एक traitकर सकते हैं।
  2. एक Interfaceएक है बहुरूपी , विरासत में मिला निर्माण - जबकि एक traitनहीं है।
  3. एकाधिक Interfaces का उपयोग एक ही कक्षा में किया जा सकता है, और इसलिए कई traits का उपयोग किया जा सकता है ।

4
"एक लक्षण एक सार वर्ग के C # अवधारणा के समान है" नहीं, एक सार वर्ग एक सार वर्ग है; यह अवधारणा PHP और C # दोनों में मौजूद है। मैं PHP में एक विशेषता की तुलना सी # में विस्तार के तरीकों से बने एक स्थिर वर्ग से करूँगा, इसके बजाय, एक प्रकार के रूप में हटाए गए प्रकार-आधारित प्रतिबंध को एक्सटेंशन विधि के विपरीत बहुत अधिक किसी भी प्रकार से उपयोग किया जा सकता है, जो केवल एक प्रकार का विस्तार करता है।
BoltClock

1
बहुत अच्छी टिप्पणी - और मैं आपसे सहमत हूँ। री-रीडिंग में, यह एक बेहतर सादृश्य है। मेरा मानना ​​है कि यह अभी भी बेहतर है, हालांकि, इसके बारे में सोचने के लिए mixin- और जैसा कि मैंने अपने उत्तर के उद्घाटन पर दोबारा गौर किया है, मैंने इसे प्रतिबिंबित करने के लिए अपडेट किया है। टिप्पणी करने के लिए धन्यवाद, @BoltClock!
ट्रॉय अल्फोर्ड

1
मुझे नहीं लगता कि c # एक्सटेंशन के तरीकों का कोई संबंध है। विस्तार विधियों को एकल वर्ग प्रकार (पाठ्यक्रम की श्रेणी पदानुक्रम का सम्मान) में जोड़ा जाता है, उनका उद्देश्य अतिरिक्त कार्यक्षमता के साथ एक प्रकार को बढ़ाना है, न कि कई वर्गों पर "कोड साझा करना" और एक गड़बड़ करना है। यह तुलनीय नहीं है! यदि किसी चीज का पुन: उपयोग करने की आवश्यकता है, तो इसका आमतौर पर मतलब होता है कि उसके पास खुद का स्थान होना चाहिए, जैसे अलग वर्ग जो सामान्य कार्यक्षमता वाले वर्गों से संबंधित होगा। कार्यान्वयन डिजाइन के आधार पर भिन्न हो सकते हैं, लेकिन मोटे तौर पर यह है कि। खराब कोड बनाने के लिए लक्षण सिर्फ एक और तरीका है।
सोफीजा

एक वर्ग में कई इंटरफ़ेस हो सकते हैं? मुझे यकीन नहीं है कि अगर मैं आपका ग्राफ गलत कर रहा हूं, लेकिन दसवीं कक्षा के लिए वाई, जेड मान्य है।
यान चाबोट

26

मुझे लगता है traitsकि ऐसी कक्षाएं बनाने के लिए उपयोगी हैं जिनमें विधियाँ शामिल हैं जिनका उपयोग कई अलग-अलग वर्गों के तरीकों के रूप में किया जा सकता है।

उदाहरण के लिए:

trait ToolKit
{
    public $errors = array();

    public function error($msg)
    {
        $this->errors[] = $msg;
        return false;
    }
}

आप इस विशेषता का उपयोग करने वाले किसी भी वर्ग में "त्रुटि" पद्धति का उपयोग कर सकते हैं ।

class Something
{
    use Toolkit;

    public function do_something($zipcode)
    {
        if (preg_match('/^[0-9]{5}$/', $zipcode) !== 1)
            return $this->error('Invalid zipcode.');

        // do something here
    }
}

जबकि interfacesआप केवल विधि हस्ताक्षर की घोषणा कर सकते हैं, लेकिन इसके कार्यों का कोड नहीं। इसके अलावा, एक इंटरफ़ेस का उपयोग करने के लिए आपको एक पदानुक्रम का पालन करने की आवश्यकता है, का उपयोग करके implements। लक्षण के साथ ऐसा नहीं है।

यह पूरी तरह से अलग है!


मुझे लगता है कि यह एक विशेषता का एक बुरा उदाहरण है। to_integerअधिक संभावना होगी कि एक IntegerCastइंटरफ़ेस में शामिल किया जाए क्योंकि पूर्णांक के लिए मूल रूप से (बुद्धिमानी से) कलाकारों के समान तरीका नहीं है।
मैथ्यू

5
"To_integer" को भूल जाओ - यह सिर्फ एक दृष्टांत है। एक उदाहरण। एक "हैलो, वर्ल्ड"। एक "example.com"।
जे। ब्रूनी

2
इस टूलकिट विशेषता से क्या लाभ होता है जो एक स्टैंडअलोन उपयोगिता वर्ग नहीं कर सकता है? इसके बजाय use Toolkitआप कर सकते हैं $this->toolkit = new Toolkit();या मैं खुद ही लाभ का कुछ लाभ याद कर रहा हूँ?
एंथनी

@ कहीं आपके Somethingकंटेनर में आप करते हैंif(!$something->do_something('foo')) var_dump($something->errors);
TheRealChx101

20

उत्तर से ऊपर के शुरुआती लोगों के लिए मुश्किल हो सकता है, यह समझने का सबसे आसान तरीका है:

लक्षण

trait SayWorld {
    public function sayHello() {
        echo 'World!';
    }
}

इसलिए यदि आप sayHelloअन्य वर्गों में कार्य करना चाहते हैं, तो पूरे कार्य को फिर से बनाए बिना आप लक्षण का उपयोग कर सकते हैं,

class MyClass{
  use SayWorld;

}

$o = new MyClass();
$o->sayHello();

बिल्कुल सटीक!

न केवल फ़ंक्शन आप विशेषता (फ़ंक्शन, चर, कॉन्स्ट ..) में कुछ भी उपयोग कर सकते हैं। भी आप कई लक्षणों का उपयोग कर सकते हैं:use SayWorld,AnotherTraits;

इंटरफेस

  interface SayWorld {
     public function sayHello();
  }

  class MyClass implements SayWorld { 
     public function sayHello() {
        echo 'World!';
     }
}

इसलिए यह इस प्रकार है कि इंटरफ़ेस लक्षणों से अलग है: आपको कार्यान्वित वर्ग में इंटरफ़ेस में सब कुछ फिर से बनाना होगा। इंटरफ़ेस कार्यान्वयन नहीं है। और इंटरफ़ेस में केवल फ़ंक्शन और कॉस्ट हो सकते हैं, इसमें चर नहीं हो सकते।

आशा है कि ये आपकी मदद करेगा!


5

ट्रिट्स का वर्णन करने के लिए अक्सर इस्तेमाल किया जाने वाला रूपक है ट्रेट्स कार्यान्वयन के साथ इंटरफेस हैं।

यह ज्यादातर परिस्थितियों में इसके बारे में सोचने का एक अच्छा तरीका है, लेकिन दोनों के बीच कई सूक्ष्म अंतर हैं।

एक शुरुआत के लिए, instanceof ऑपरेटर लक्षण के साथ काम नहीं करेगा (यानी, एक विशेषता एक वास्तविक वस्तु नहीं है) तो आप हमें यह देखने के लिए नहीं कर सकते हैं कि क्या एक वर्ग में एक निश्चित लक्षण है (या यह देखने के लिए कि क्या दो अन्यथा असंबंधित वर्ग एक विशेषता साझा करते हैं )। इसका मतलब है कि वे क्षैतिज कोड को फिर से उपयोग करने के लिए एक निर्माण है।

वहाँ रहे हैं अब PHP में काम करता है आप सभी की एक सूची लक्षण एक वर्ग का उपयोग करता है प्राप्त करने देगा, लेकिन विशेषता-विरासत साधन आप मज़बूती से करने के लिए पुनरावर्ती जांच कर जाँच लें कि कुछ बिंदु पर एक वर्ग के एक विशिष्ट विशेषता है की आवश्यकता होगी (वहाँ उदाहरण है PHP डोको पेज पर कोड)। लेकिन हाँ, यह निश्चित रूप से उतना सरल और साफ नहीं है जितना कि उदाहरण के लिए, और IMHO यह एक ऐसी विशेषता है जो PHP को बेहतर बनाएगी।

इसके अलावा, अमूर्त कक्षाएं अभी भी कक्षाएं हैं, इसलिए वे कई-विरासत से संबंधित कोड को फिर से उपयोग की समस्याओं को हल नहीं करते हैं। याद रखें कि आप केवल एक वर्ग (वास्तविक या सार) का विस्तार कर सकते हैं, लेकिन कई इंटरफेस को लागू कर सकते हैं।

मैंने पाया है कि छद्म कई उत्तराधिकार बनाने के लिए हाथ में हाथ का उपयोग करना वास्तव में अच्छा है। उदाहरण के लिए:

class SlidingDoor extends Door implements IKeyed  
{  
    use KeyedTrait;  
    [...] // Generally not a lot else goes here since it's all in the trait  
}

ऐसा करने का अर्थ है कि आप यह निर्धारित करने के लिए इंस्टॉफ़ का उपयोग कर सकते हैं कि क्या विशेष डोर ऑब्जेक्ट कीड है या नहीं, आपको पता है कि आपको विधियों आदि का एक सुसंगत सेट मिलेगा, और सभी कोड सभी वर्गों में एक ही स्थान पर होते हैं जो कि कीडरट्रेट का उपयोग करते हैं।


उस उत्तर का अंतिम भाग निश्चित रूप से है जो @rdlowrey अपने पोस्ट में "लक्षण" के तहत अंतिम तीन पैराग्राफ में अधिक विस्तार से कह रहा है; मुझे लगा कि वास्तव में एक साधारण कंकाल कोड स्निपेट इसे चित्रित करने में मदद करेगा।
जॉन क्लॉस

मुझे लगता है कि लक्षणों का उपयोग करने के लिए सबसे अच्छा ओओ तरीका इंटरफेस का उपयोग कर रहा है जहां आप कर सकते हैं। और अगर ऐसा कोई मामला है जब कई उपवर्ग उस इंटरफ़ेस के लिए एक ही तरह का कोड लागू करते हैं और आप उस कोड को उनके (सार) सुपरक्लास में नहीं ले जा सकते हैं -> इसे गुण के साथ लागू करें
खिलाड़ी-एक

4

लक्षण के लिए बस कर रहे हैं कोड पुन: उपयोग

इंटरफ़ेस केवल उन कार्यों के हस्ताक्षर प्रदान करता है जिन्हें उस वर्ग में परिभाषित किया जाना है जहां इसका उपयोग प्रोग्रामर के विवेक के आधार पर किया जा सकता है । इस प्रकार हमें कक्षाओं के एक समूह के लिए एक प्रोटोटाइप दे रहा है

संदर्भ के लिए- http://www.php.net/manual/en/language.oop5.traits.php


3

आप मूल रूप से कोड के एक स्वचालित "कॉपी-पेस्ट" के रूप में एक विशेषता पर विचार कर सकते हैं।

लक्षणों का उपयोग करना खतरनाक है क्योंकि यह जानने का कोई मतलब नहीं है कि यह निष्पादन से पहले क्या करता है।

हालाँकि, इनहेरिटेंस जैसे सीमाओं की कमी के कारण लक्षण अधिक लचीले होते हैं।

लक्षण एक विधि को इंजेक्ट करने के लिए उपयोगी हो सकते हैं जो एक वर्ग में कुछ जांचता है, उदाहरण के लिए, किसी अन्य विधि या विशेषता का अस्तित्व। उस पर एक अच्छा लेख (लेकिन फ्रेंच में, क्षमा करें)

फ्रांसीसी-पढ़ने वाले लोगों के लिए जो इसे प्राप्त कर सकते हैं, जीएनयू / लिनक्स पत्रिका एचएस 54 में इस विषय पर एक लेख है।


अभी भी नहीं मिलता है कि कैसे डिफ़ॉल्ट डिफ़ॉल्ट कार्यान्वयन के साथ इंटरफेस से भिन्न हैं
denis631

@ denis631 आप कोड के स्निपेट्स के रूप में लक्षण देख सकते हैं, और हस्ताक्षर अनुबंध के रूप में इंटरफेस कर सकते हैं। यदि यह मदद कर सकता है, तो आप इसे एक वर्ग के अनौपचारिक बिट के रूप में देख सकते हैं जिसमें कुछ भी हो सकता है। मुझे बताएं कि क्या इससे लाभ होता है।
बेंज

मैं देख रहा हूं कि PHP लक्षण को मैक्रोज़ के रूप में देखा जा सकता है जो कि संकलन समय पर विस्तारित होते हैं / बस उस कुंजी को उस कोड स्निपेट को अलियासिंग करते हैं। जंग के लक्षण, हालांकि, अलग दिखाई देते हैं (या मैं गलत हूं)। लेकिन चूँकि उन दोनों में शब्द विशेषता है, इसलिए मैं उन्हें एक ही मानूंगा, जिसका अर्थ एक ही अवधारणा है। जंग लक्षण लिंक: doc.rust-lang.org/rust-by-example/trait.html
denis631

2

यदि आप अंग्रेजी जानते हैं और जानते हैं कि traitइसका क्या मतलब है, तो यह वही है जो नाम कहता है। यह तरीकों और गुणों का एक श्रेणी-कम पैक है जिसे आप मौजूदा कक्षाओं में टाइप करके संलग्न करते हैं use

मूल रूप से, आप इसकी तुलना एक एकल चर से कर सकते हैं। क्लोजर फ़ंक्शंस useइन वेरिएबल्स को दायरे से बाहर कर सकते हैं और इस तरह से उनके अंदर वैल्यू होती है। वे शक्तिशाली हैं और हर चीज में इस्तेमाल किए जा सकते हैं। यदि उनका उपयोग किया जा रहा है, तो लक्षण भी समान होते हैं।


2

अन्य उत्तरों ने इंटरफेस और लक्षणों के बीच अंतर को समझाने का एक बड़ा काम किया। मैं एक उपयोगी वास्तविक दुनिया उदाहरण पर ध्यान केंद्रित करूंगा, विशेष रूप से एक जो प्रदर्शित करता है कि लक्षण उदाहरण चर का उपयोग कर सकते हैं - आपको न्यूनतम बॉयलरप्लेट कोड के साथ एक वर्ग में व्यवहार जोड़ने की अनुमति देता है।

फिर से, जैसा कि दूसरों द्वारा उल्लेख किया गया है, इंटरफेस के साथ जोड़ी को अच्छी तरह से विशेषता देता है, जिससे इंटरफ़ेस को व्यवहार अनुबंध और कार्यान्वयन को पूरा करने के लिए विशेषता की अनुमति मिलती है।

किसी वर्ग में ईवेंट प्रकाशित / सदस्यता क्षमता जोड़ना कुछ कोड आधारों में एक सामान्य परिदृश्य हो सकता है। 3 सामान्य उपाय हैं:

  1. इवेंट पब / सब कोड के साथ एक बेस क्लास को परिभाषित करें, और फिर जो क्लासेस ईवेंट की पेशकश करना चाहते हैं, वे क्षमताओं को प्राप्त करने के लिए इसे बढ़ा सकते हैं।
  2. इवेंट पब / सब कोड के साथ एक वर्ग को परिभाषित करें, और फिर अन्य वर्ग जो घटनाओं की पेशकश करना चाहते हैं, रचना के माध्यम से इसका उपयोग कर सकते हैं, तैयार की गई वस्तु को लपेटने के लिए अपने स्वयं के तरीकों को परिभाषित करते हुए, विधि कॉल को प्रॉक्सी करते हैं।
  3. इवेंट पब / सब कोड के साथ एक विशेषता को परिभाषित करें, और फिर अन्य वर्ग जो घटनाओं की पेशकश करना चाहते हैं use, वह गुण प्राप्त कर सकते हैं , उर्फ़ इसे आयात करते हैं, क्षमताओं को प्राप्त करने के लिए।

प्रत्येक कार्य कितनी अच्छी तरह से होता है?

# 1 अच्छा काम नहीं करता है। यह तब तक होता है, जब तक आपको एहसास नहीं होता कि आप आधार वर्ग का विस्तार नहीं कर सकते क्योंकि आप पहले से ही कुछ और कर रहे हैं। मैं इसका एक उदाहरण नहीं दिखाऊंगा क्योंकि यह स्पष्ट होना चाहिए कि इस तरह विरासत का उपयोग करना कितना सीमित है।

# 2 & # 3 दोनों अच्छा काम करते हैं। मैं एक उदाहरण दिखाऊंगा जो कुछ मतभेदों को उजागर करता है।

सबसे पहले, कुछ कोड जो दोनों उदाहरणों के बीच समान होंगे:

एक इंटरफ़ेस

interface Observable {
    function addEventListener($eventName, callable $listener);
    function removeEventListener($eventName, callable $listener);
    function removeAllEventListeners($eventName);
}

और उपयोग प्रदर्शित करने के लिए कुछ कोड:

$auction = new Auction();

// Add a listener, so we know when we get a bid.
$auction->addEventListener('bid', function($bidderName, $bidAmount){
    echo "Got a bid of $bidAmount from $bidderName\n";
});

// Mock some bids.
foreach (['Moe', 'Curly', 'Larry'] as $name) {
    $auction->addBid($name, rand());
}

ठीक है, अब दिखाते हैं कि किस तरह का कार्यान्वयन Auction लक्षण का उपयोग करते समय कक्षा भिन्न होगा।

सबसे पहले, यहां बताया गया है कि # 2 (रचना का उपयोग करके) कैसा दिखेगा:

class EventEmitter {
    private $eventListenersByName = [];

    function addEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName][] = $listener;
    }

    function removeEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName] = array_filter($this->eventListenersByName[$eventName], function($existingListener) use ($listener) {
            return $existingListener === $listener;
        });
    }

    function removeAllEventListeners($eventName) {
        $this->eventListenersByName[$eventName] = [];
    }

    function triggerEvent($eventName, array $eventArgs) {
        foreach ($this->eventListenersByName[$eventName] as $listener) {
            call_user_func_array($listener, $eventArgs);
        }
    }
}

class Auction implements Observable {
    private $eventEmitter;

    public function __construct() {
        $this->eventEmitter = new EventEmitter();
    }

    function addBid($bidderName, $bidAmount) {
        $this->eventEmitter->triggerEvent('bid', [$bidderName, $bidAmount]);
    }

    function addEventListener($eventName, callable $listener) {
        $this->eventEmitter->addEventListener($eventName, $listener);
    }

    function removeEventListener($eventName, callable $listener) {
        $this->eventEmitter->removeEventListener($eventName, $listener);
    }

    function removeAllEventListeners($eventName) {
        $this->eventEmitter->removeAllEventListeners($eventName);
    }
}

यहां बताया गया है कि # 3 (लक्षण) कैसा दिखेगा:

trait EventEmitterTrait {
    private $eventListenersByName = [];

    function addEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName][] = $listener;
    }

    function removeEventListener($eventName, callable $listener) {
        $this->eventListenersByName[$eventName] = array_filter($this->eventListenersByName[$eventName], function($existingListener) use ($listener) {
            return $existingListener === $listener;
        });
    }

    function removeAllEventListeners($eventName) {
        $this->eventListenersByName[$eventName] = [];
    }

    protected function triggerEvent($eventName, array $eventArgs) {
        foreach ($this->eventListenersByName[$eventName] as $listener) {
            call_user_func_array($listener, $eventArgs);
        }
    }
}

class Auction implements Observable {
    use EventEmitterTrait;

    function addBid($bidderName, $bidAmount) {
        $this->triggerEvent('bid', [$bidderName, $bidAmount]);
    }
}

ध्यान दें कि अंदर का कोड EventEmitterTraitवैसा ही है जैसा कि EventEmitterवर्ग के अंदर क्या है सिवाय विशेषता के कि triggerEvent()विधि संरक्षित बताए। इसलिए, आपको केवल एक फर्क देखने की जरूरत है वह है Auctionकक्षा का कार्यान्वयन

और अंतर बड़ा है। रचना का उपयोग करते समय, हम एक महान समाधान प्राप्त करते हैं, जिससे हमें अपनी पसंद के EventEmitterअनुसार कई वर्गों द्वारा पुन: उपयोग करने की अनुमति मिलती है । लेकिन, मुख्य दोष यह है कि हमारे पास बहुत सारे बॉयलरप्लेट कोड हैं जिन्हें हमें लिखने और बनाए रखने की आवश्यकता है क्योंकि Observableइंटरफ़ेस में परिभाषित प्रत्येक विधि के लिए , हमें इसे लागू करने और उबाऊ बॉयलरप्लेट कोड लिखने की आवश्यकता है जो कि केवल इसी पद्धति पर तर्कों के लिए आगे हमारी रचना की EventEmitterवस्तु। इस उदाहरण में विशेषता का उपयोग करने से हमें बॉयलरप्लेट कोड को कम करने और रखरखाव में सुधार करने में मदद करने से बचने में मदद मिलती है

हालाँकि, कई बार ऐसा भी हो सकता है कि आप अपनी Auctionकक्षा को पूर्ण रूप से लागू नहीं करना चाहते हैंObservable इंटरफ़ेस - शायद आप केवल 1 या 2 विधियों को उजागर करना चाहते हैं, या शायद बिल्कुल भी नहीं, ताकि आप अपनी स्वयं की विधि हस्ताक्षर परिभाषित कर सकें। ऐसे मामले में, आप अभी भी रचना विधि पसंद कर सकते हैं।

लेकिन, अधिकांश परिदृश्यों में विशेषता बहुत सम्मोहक है, खासकर यदि इंटरफ़ेस में बहुत सारी विधियाँ हैं, जो आपको बहुत सारे बॉयलरप्लेट लिखने का कारण बनता है।

* आप वास्तव में दोनों तरह से कर सकते हैं - EventEmitterयदि आप कभी भी इसका उपयोग करना चाहते हैं तो वर्ग को परिभाषित करें , और EventEmitterTraitगुण को भी परिभाषित करें, गुण के EventEmitterअंदर वर्ग कार्यान्वयन का उपयोग करते हुए :)


1

विशेषता एक वर्ग के समान है जिसे हम कई उत्तराधिकार के उद्देश्यों के लिए उपयोग कर सकते हैं और पुन: प्रयोज्य कोड भी कर सकते हैं।

हम वर्ग के अंदर गुण का उपयोग कर सकते हैं और साथ ही हम एक ही कक्षा में 'उपयोग कीवर्ड' के साथ कई लक्षणों का उपयोग कर सकते हैं।

इंटरफ़ेस कोड पुन: प्रयोज्य के लिए एक विशेषता के रूप में उपयोग कर रहा है

इंटरफ़ेस कई इंटरफेस का विस्तार करता है इसलिए हम कई विरासत की समस्याओं को हल कर सकते हैं लेकिन जब हम इंटरफ़ेस को लागू करते हैं तो हमें कक्षा के अंदर सभी तरीकों का निर्माण करना चाहिए। अधिक जानकारी के लिए नीचे दिए गए लिंक पर क्लिक करें:

http://php.net/manual/en/language.oop5.traits.php http://php.net/manual/en/language.oop5.interfaces.php


1

एक इंटरफ़ेस एक अनुबंध है जो कहता है "यह ऑब्जेक्ट इस कार्य को करने में सक्षम है", जबकि एक विशेषता वस्तु को कार्य करने की क्षमता दे रही है।

एक विशेषता अनिवार्य रूप से कक्षाओं के बीच "कॉपी और पेस्ट" कोड का एक तरीका है।

इस लेख को पढ़ने की कोशिश करें, PHP लक्षण क्या हैं?


0

मुख्य अंतर यह है कि इंटरफेस के साथ, आपको प्रत्येक वर्ग के भीतर प्रत्येक विधि के वास्तविक कार्यान्वयन को परिभाषित करना होगा जो कि इंटरफ़ेस ने कहा था, इसलिए आपके पास कई वर्ग एक ही इंटरफ़ेस को लागू कर सकते हैं, लेकिन अलग-अलग व्यवहार के साथ, जबकि लक्षण सिर्फ कोड में इंजेक्ट किए गए हैं। एक कक्षा; एक अन्य महत्वपूर्ण अंतर यह है कि विशेषता विधियाँ केवल क्लास-विधियाँ या स्थैतिक-विधियाँ हो सकती हैं, इंटरफ़ेस विधियों के विपरीत जो (और आमतौर पर) उदाहरण के तरीके हैं।

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