PHP में लक्षण - किसी भी वास्तविक दुनिया उदाहरण / सर्वोत्तम प्रथाओं? [बन्द है]


148

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

हालाँकि, मुझे अभी भी नहीं पता है कि मैं अपनी परियोजनाओं में लक्षणों का उपयोग कैसे करूँगा।

क्या कोई खुला स्रोत परियोजनाएं हैं जो पहले से ही लक्षण का उपयोग करती हैं? किसी भी अच्छे लेख / पठन सामग्री का उपयोग करके संरचना का उपयोग कैसे करें?


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

1
स्कैला मानक पुस्तकालय पर एक नज़र डालें और आपको लक्षणों के कई उपयोगी उदाहरण मिलेंगे।
दिमित्री

जवाबों:


89

मेरी निजी राय है कि क्लीन कोड लिखते समय लक्षणों के लिए वास्तव में बहुत कम एप्लिकेशन है।

एक वर्ग में हैक कोड के लक्षणों का उपयोग करने के बजाय निर्माणकर्ता या बसने वालों के माध्यम से निर्भरता में पारित करना बेहतर है:

class ClassName {
    protected $logger;

    public function __construct(LoggerInterface $logger) {
        $this->logger = $logger;
    }
    // or
    public function setLogger(LoggerInterface $logger) {
        $this->logger = $logger;
    }
}

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


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

14
@rickchristie ज़रूर, आप ऐसा कर सकते हैं। लेकिन आपको विशेषता के स्रोत कोड को संपादित करने की आवश्यकता होगी। तो आप इसका उपयोग करके हर वर्ग के लिए इसे बदल देंगे, न कि केवल उस विशेष के लिए जिसे आप एक अलग लकड़हारा चाहते हैं। और क्या होगा यदि आप एक ही वर्ग का उपयोग करना चाहते हैं लेकिन दो अलग-अलग लॉगर के साथ? या यदि आप परीक्षण करते समय मॉक-लॉगर पास करना चाहते हैं? आप निर्भरता इंजेक्शन का उपयोग करते हैं, तो आप कर सकते हैं, यदि आप लक्षण का उपयोग करते हैं, तो आप कर सकते हैं।
निकी सी

2
मैं आपकी बात देख सकता हूं, मैं भी विचार कर रहा हूं कि लक्षण इसके लायक हैं या नहीं। मेरा मतलब है, सिम्फनी 2 जैसी आधुनिक रूपरेखा में आपको सभी जगह निर्भरता इंजेक्शन है जो अधिकांश मामलों में लक्षणों पर सुपरियर लगता है। फिलहाल मुझे लक्षण अधिक दिखाई नहीं दे रहे हैं "कंपाइलर असिस्टेड कॉपी एंड पेस्ट"। ;)
मैक्स

11
फिलहाल मुझे लक्षण अधिक दिखाई नहीं दे रहे हैं "कंपाइलर असिस्टेड कॉपी एंड पेस्ट"। ;) : @ मोम: यह बिल्कुल वैसा ही है जैसा कि डिजाइन किया गया था, इसलिए यह पूरी तरह सही है। यह इसे और अधिक "बनाए रखने योग्य" बनाता है, क्योंकि इसकी केवल एक परिभाषा है, लेकिन यह मूल रूप से सिर्फ c & p है ...
ircmaxell

29
NikiC की बात याद आ रही है: एक विशेषता का उपयोग करना निर्भरता इंजेक्शन का उपयोग करने से नहीं रोकता है। इस स्थिति में, एक विशेषता हर वर्ग को यह बताएगी कि लॉगिंग को लागू करने के लिए सेट लॉगर () विधि और $ लकड़हारा संपत्ति के निर्माण की नकल नहीं करनी है। विशेषता उन्हें प्रदान करेगी। setLogger () LoggerInterface पर संकेत टाइप करता है जैसा कि उदाहरण देता है, ताकि किसी भी प्रकार के लकड़हारे को पारित किया जा सके। यह विचार नीचे गॉर्डन के जवाब के समान है (केवल ऐसा लगता है कि वह एक लकड़हारा इंटरफ़ेस के बजाय एक लकड़हारा सुपर क्लास पर इशारा कर रहा है। )।
एथन

205

मुझे लगता है कि स्वीकार किए गए अच्छे / सर्वोत्तम अभ्यासों को सीखने के लिए कुछ समय के लिए उन भाषाओं पर गौर करना होगा जिनमें कुछ समय के लिए लक्षण हैं। Trait पर मेरी वर्तमान राय यह है कि आपको उन्हें केवल उन कोड के लिए उपयोग करना चाहिए जिन्हें आपको अन्य वर्गों में डुप्लिकेट करना होगा जो समान कार्यक्षमता साझा करते हैं।

एक लकड़हारा विशेषता के लिए उदाहरण:

interface Logger
{
    public function log($message, $level);    
}

class DemoLogger implements Logger
{
    public function log($message, $level)
    {
        echo "Logged message: $message with level $level", PHP_EOL; 
    }
}

trait Loggable // implements Logger
{
    protected $logger;
    public function setLogger(Logger $logger)
    {
        $this->logger = $logger;
    }
    public function log($message, $level)
    {
        $this->logger->log($message, $level);
    }
}

class Foo implements Logger
{
    use Loggable;
}

और फिर आप ( डेमो )

$foo = new Foo;
$foo->setLogger(new DemoLogger);
$foo->log('It works', 1);

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

trait T {
    protected function foo() {}
}
class A { 
    public function foo() {}
}
class B extends A
{
    use T;
}

उपरोक्त एक त्रुटि ( डेमो ) में परिणाम देगा । इसी तरह, पहले से ही उपयोग की गई कक्षा में भी घोषित किए गए किसी भी तरीके को क्लास में कॉपी नहीं किया जाएगा, जैसे

trait T {
    public function foo() {
    return 1;
}
}
class A { 
    use T;
    public function foo() {
    return 2;
}
}

$a = new A;
echo $a->foo();

2 ( डेमो ) प्रिंट करेगा । ये ऐसी चीजें हैं जिनसे आप बचना चाहेंगे क्योंकि वे त्रुटियों को खोजने के लिए कठिन बनाते हैं। आप चीजों को उन गुणों में डालने से भी बचना चाहेंगे, जो उस वर्ग के गुणों या विधियों पर काम करते हैं जो इसका उपयोग करते हैं, जैसे

class A
{
    use T;
    protected $prop = 1;
    protected function getProp() {
        return $this->prop;
    }
}

trait T
{
    public function foo()
    {
        return $this->getProp();
    }
}

$a = new A;
echo $a->foo();

काम करता है ( डेमो ), लेकिन अब विशेषता ए के लिए गहन रूप से युग्मित है और क्षैतिज पुन: उपयोग का पूरा विचार खो गया है।

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

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

नोट: यह देखते हुए कि लक्षण PHP में एक नई अवधारणा है, ऊपर व्यक्त की गई सभी राय परिवर्तन के अधीन है। मेरे पास अभी तक अवधारणा का मूल्यांकन करने के लिए अधिक समय नहीं है। लेकिन मुझे आशा है कि यह आपको सोचने के लिए कुछ देने के लिए पर्याप्त है।


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

13
मुझे इस तरह के सच्चे प्रोग्रामर पसंद हैं, जो प्रत्येक के लिए लघु डीएससी के साथ एक वास्तविक कामकाजी उदाहरण का प्रस्ताव करते हैं। Thx
आर्थर कुशमैन

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

12
@sumanchalki सार वर्ग वंशानुक्रम के नियमों का पालन करता है। क्या होगा यदि आपको एक वर्ग की आवश्यकता है जो Loggable और Cacheable को लागू करता है? आपको AbstractLogger का विस्तार करने के लिए वर्ग की आवश्यकता होगी, जो तब AbstractCache का विस्तार करने की आवश्यकता है। लेकिन इसका मतलब है कि सभी Loggables Caches हैं। यह एक युग्मन है जो आप नहीं चाहते हैं। यह पुन: उपयोग को सीमित करता है और आपके वंशानुक्रम ग्राफ को गड़बड़ करता है।
गॉर्डन

1
मुझे लगता है कि डेमो लिंक मर चुके हैं
Pmpr

19

:) मुझे यह पसंद नहीं है कि किसी चीज़ के साथ क्या किया जाए, इस बारे में बहस और बहस करना। इस मामले में लक्षण। मैं आपको दिखाता हूं कि मुझे क्या लक्षण मिलते हैं जो आपके लिए उपयोगी हैं और आप इसे सीख सकते हैं या इसे अनदेखा कर सकते हैं।

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

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

कोशिश करो:

<?php
trait SortStrategy {
    private $sort_field = null;
    private function string_asc($item1, $item2) {
        return strnatcmp($item1[$this->sort_field], $item2[$this->sort_field]);
    }
    private function string_desc($item1, $item2) {
        return strnatcmp($item2[$this->sort_field], $item1[$this->sort_field]);
    }
    private function num_asc($item1, $item2) {
        if ($item1[$this->sort_field] == $item2[$this->sort_field]) return 0;
        return ($item1[$this->sort_field] < $item2[$this->sort_field] ? -1 : 1 );
    }
    private function num_desc($item1, $item2) {
        if ($item1[$this->sort_field] == $item2[$this->sort_field]) return 0;
        return ($item1[$this->sort_field] > $item2[$this->sort_field] ? -1 : 1 );
    }
    private function date_asc($item1, $item2) {
        $date1 = intval(str_replace('-', '', $item1[$this->sort_field]));
        $date2 = intval(str_replace('-', '', $item2[$this->sort_field]));
        if ($date1 == $date2) return 0;
        return ($date1 < $date2 ? -1 : 1 );
    }
    private function date_desc($item1, $item2) {
        $date1 = intval(str_replace('-', '', $item1[$this->sort_field]));
        $date2 = intval(str_replace('-', '', $item2[$this->sort_field]));
        if ($date1 == $date2) return 0;
        return ($date1 > $date2 ? -1 : 1 );
    }
}

class Product {
    public $data = array();

    use SortStrategy;

    public function get() {
        // do something to get the data, for this ex. I just included an array
        $this->data = array(
            101222 => array('label' => 'Awesome product', 'price' => 10.50, 'date_added' => '2012-02-01'),
            101232 => array('label' => 'Not so awesome product', 'price' => 5.20, 'date_added' => '2012-03-20'),
            101241 => array('label' => 'Pretty neat product', 'price' => 9.65, 'date_added' => '2012-04-15'),
            101256 => array('label' => 'Freakishly cool product', 'price' => 12.55, 'date_added' => '2012-01-11'),
            101219 => array('label' => 'Meh product', 'price' => 3.69, 'date_added' => '2012-06-11'),
        );
    }

    public function sort_by($by = 'price', $type = 'asc') {
        if (!preg_match('/^(asc|desc)$/', $type)) $type = 'asc';
        switch ($by) {
            case 'name':
                $this->sort_field = 'label';
                uasort($this->data, array('Product', 'string_'.$type));
            break;
            case 'date':
                $this->sort_field = 'date_added';
                uasort($this->data, array('Product', 'date_'.$type));
            break;
            default:
                $this->sort_field = 'price';
                uasort($this->data, array('Product', 'num_'.$type));
        }
    }
}

$product = new Product();
$product->get();
$product->sort_by('name');
echo '<pre>'.print_r($product->data, true).'</pre>';
?>

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


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

मुझे यह शब्द पसंद है strategies
रानी ओलीट

4

मैं ट्रिट्स के लिए उत्साहित हूं क्योंकि वे Magento के ईकॉमर्स प्लेटफॉर्म के लिए एक्सटेंशन विकसित करते समय एक सामान्य मुद्दे को हल करते हैं। यह समस्या तब होती है जब एक्सटेंशन कोर क्लास में कार्यक्षमता जोड़ते हैं (जैसे यूजर मॉडल कहते हैं) इसे बढ़ाकर। यह एक्सटेंशन से उपयोगकर्ता मॉडल का उपयोग करने के लिए Zend ऑटोलैडर (एक XML कॉन्फिग फ़ाइल के माध्यम से) को इंगित करके किया जाता है, और यह है कि नया मॉडल कोर मॉडल का विस्तार करता है। ( उदाहरण ) लेकिन क्या होगा यदि दो एक्सटेंशन एक ही मॉडल को ओवरराइड करते हैं? आपको एक "रेस कंडीशन" मिलती है और केवल एक लोड होता है।

समाधान अभी एक्सटेंशन को संपादित करने के लिए है, ताकि एक श्रृंखला में दूसरे के मॉडल ओवरराइड वर्ग का विस्तार हो, और फिर उन्हें सही क्रम में लोड करने के लिए एक्सटेंशन कॉन्फ़िगरेशन सेट करें ताकि विरासत श्रृंखला काम करे।

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

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

टीएल; डीआर मुझे लगता है कि मैगेंटो जैसे बड़े PHP सॉफ्टवेयर पैकेज के लिए एक्सटेंशन / मॉड्यूल / प्लगइन्स बनाने के लिए ट्रेट्स उपयोगी हो सकते हैं।


0

आप इस तरह से केवल पढ़ने के लिए एक विशेषता हो सकता है:

  trait ReadOnly{  
      protected $readonly = false;

      public function setReadonly($value){ $this->readonly = (bool)$value; }
      public function getReadonly($value){ return $this->readonly; }
  }

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


तो जिस वर्ग में useयह गुण होगा if($this -> getReadonly($value)), वह कॉल करेगा ; लेकिन यह एक त्रुटि उत्पन्न करेगा यदि आपने useयह विशेषता नहीं बनाई है । इस उदाहरण के लिए त्रुटिपूर्ण है।
लुसियो

ठीक है, आपको यह जांचने की आवश्यकता है कि क्या विशेषता पहले उपयोग में है। यदि ReadOnly विशेषता को किसी ऑब्जेक्ट पर परिभाषित किया गया है, तो आप पढ़ सकते हैं कि यह आसानी से है या नहीं।
निको


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