2012-02-18 6 views
13

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

वैसे भी, आपको लगता है इसके बजाय इनमें proprties से प्रत्येक के लिए तरीकों बनाने के लिए बेहतर विचार है एक स्विच बयान में यह कर के हो?


संपादित करें: स्पीड परीक्षण

मैं, केवल प्रदर्शन को लेकर चिंतित हूं अन्य सामग्री पर @Gordon उल्लेख किया मुझे लगता है कि महत्वपूर्ण नहीं हैं:

  • अनावश्यक जटिलता - यह नहीं है वास्तव में मेरे एप्लिकेशन जटिलता
  • नाजुक गैर स्पष्ट एपीआई वृद्धि - मैं विशेष रूप से मेरी एपीआई "पृथक" होना चाहते हैं; तो यहाँ पी

परीक्षण है कि मैं बनाया है, जो मुझे लगता है कि प्रदर्शन हिट agument अनुचित है कर रहे हैं: प्रलेखन दूसरों है कि यह कैसे उपयोग करने के लिए बताना चाहिए 50.000 कॉल (पीएचपी 5.3 पर के लिए

परिणाम .9):

enter image description here

(t1 = स्विच, t2 = गेटर, t3 = आगे गेटर कॉल के साथ जादू)

नहीं

यकीन है कि क्या "सह" बात मतलब के साथ जादू टी 3 पर। यह नहीं कर सकते संचयी समय हो क्योंकि t2 2K तो होना चाहिए ...

कोड:

class B{} 



class A{ 
    protected 
    $props = array(
     'test_obj' => false, 
    ); 

    // magic 
    function __get($name){ 
    if(isset($this->props[$name])){ 
     switch($name){ 

     case 'test_obj': 
      if(!($this->props[$name] instanceof B)) 
      $this->props[$name] = new B; 

     break; 
     } 

     return $this->props[$name]; 
    } 

    trigger_error('property doesnt exist'); 
    } 

    // standard getter 
    public function getTestObj(){ 
    if(!($this->props['test_obj'] instanceof B)) 
     $this->props['test_obj'] = new B; 

    return $this->props['test_obj']; 
    } 
} 



class AA extends A{ 

    // magic 
    function __get($name){ 
    $getter = "get".str_replace('_', '', $name); // give me a break, its just a test :P 

    if(method_exists($this, $getter)) 
     return $this->$getter(); 


    trigger_error('property doesnt exist'); 
    } 


} 


function t1(){ 
    $obj = new A; 

    for($i=1;$i<50000;$i++){ 
    $a = $obj->test_obj; 

    } 
    echo 'done.'; 
} 

function t2(){ 
    $obj = new A; 

    for($i=1;$i<50000;$i++){ 
    $a = $obj->getTestObj(); 

    } 
    echo 'done.'; 
} 

function t3(){ 
    $obj = new AA; 

    for($i=1;$i<50000;$i++){ 
    $a = $obj->test_obj; 

    } 
    echo 'done.'; 
} 

t1(); 
t2(); 
t3(); 

पुनश्च: मैं क्यों __get() मानक गेटर तरीकों से अधिक उपयोग करने के लिए करना चाहते हैं? एपी सौंदर्य एकमात्र कारण है; क्योंकि मैं किसी भी असली नुकसान नहीं दिख रहा है, मुझे लगता है यह इसके लायक है: पी


संपादित करें: अधिक गति परीक्षण

इस बार मैं microtime इस्तेमाल किया कुछ औसत को मापने के लिए:

पीएचपी 5.2। 4 और 5.3.0 (इसी तरह के परिणाम):

t1 - 0.12s 
t2 - 0.08s 
t3 - 0.24s 

पीएचपी 5.3.9, xdebug सक्रिय के साथ इस कारण है कि यह इतनी धीमी गति से है:

t1 - 1.34s 
t2 - 1.26s 
t3- 5.06s 

पीएचपी xdebug साथ 5.3.9 अक्षम:

t1 - 0.30 
t2 - 0.25 
t3 - 0.86 


एक अन्य विधि:

// magic 
    function __get($name){ 
    $getter = "get".str_replace('_', '', $name); 

    if(method_exists($this, $getter)){ 
     $this->$name = $this->$getter(); // <-- create it 
     return $this->$name;     
    } 


    trigger_error('property doesnt exist'); 
    } 

का अनुरोध नाम के साथ एक सार्वजनिक संपत्ति पहले __get कॉल के बाद गतिशील बनाया जाएगा। यह गति मुद्दों को हल करती है - पीएचपी 5.3 में 0.1s हो रही है (यह 12 गुना तेजी से तो मानक गेटर है), और तानाना मुद्दा गॉर्डन द्वारा उठाए गए। आप बच्चे वर्ग में गेटटर को ओवरराइड कर सकते हैं।

नुकसान यह है कि संपत्ति बन जाता है है लिखने योग्य :(

+0

इन परीक्षणों पर, "सह" कार्य के समय की मात्रा है, जिसमें से किसी भी कार्य के समय शामिल है (मूल रूप से यह वास्तविक समय की मात्रा है जब फ़ंक्शन तब तक निष्पादित होने तक कार्य शुरू होता है)। यह "स्वयं" समय से अलग है, जो अन्य कार्यों को बुलाए जाने पर टाइमर को प्रभावी रूप से "रोक देता है"। इस मामले में, टी 3 के लिए "सह" माइनस "सेल्फ" इस काम में कितना समय व्यतीत किया गया है -> $ गेटर() कॉल, और "स्वयं" यह है कि इसमें अन्य सामान करने में कितना समय लगेगा __get() फ़ंक्शन। –

उत्तर

18

अपने कोड के परिणाम के रूप में मेरे Win7 मशीन पर PHP 5.3.6 के साथ Zend डिबगर द्वारा रिपोर्ट है (3-4 बार) नियमित कॉल से धीमे। हम कुल मिलाकर 50k कॉल के लिए 1 से भी कम समय से निपट रहे हैं, इसलिए छोटे पैमाने पर उपयोग किए जाने पर यह नगण्य है। हालांकि, यदि आपका इरादा जादू विधियों के चारों ओर अपना पूरा कोड बनाना है, तो आप अंतिम आवेदन को देखना चाहेंगे कि यह अभी भी नगण्य है या नहीं।

बल्कि अनौपचारिक प्रदर्शन पहलू के लिए बहुत कुछ। अब आइए देखें कि आप "महत्वपूर्ण नहीं" पर विचार करते हैं। मैं तनाव पर जा रहा हूं क्योंकि यह वास्तव में प्रदर्शन पहलू से कहीं अधिक महत्वपूर्ण है।

Uneeded जोड़ा जटिलता के बारे में आप लिखते हैं

यह वास्तव में मेरे ऐप जटिलता

बेशक यह करता है

वृद्धि नहीं करता है। आप आसानी से अपने कोड की घोंसले की गहराई को देखकर इसे खोज सकते हैं। अच्छा कोड बाईं ओर रहता है। आपका अगर/स्विच/केस/यदि चार स्तर गहरा है।इसका मतलब है कि अधिक संभावित निष्पादन पट्टियां हैं और इससे Cyclomatic Complexity अधिक हो जाएगा, जिसका अर्थ है बनाए रखना और समझना कठिन है।

यहाँ (। बाहर नियमित रूप से मनुष्य आउटपुट PHPLoc से छोटा है w \) अपने वर्ग एक के लिए संख्या है:

Lines of Code (LOC):         19 
    Cyclomatic Complexity/Lines of Code:   0.16 
    Average Method Length (NCLOC):      18 
    Cyclomatic Complexity/Number of Methods:  4.00 

4,00 का मान का मतलब है इस जटिलता से मध्यम दर्जे की छोर पर पहले से ही है। आपके द्वारा अपने स्विच में रखे गए प्रत्येक अतिरिक्त मामले के लिए यह संख्या 2 से बढ़ जाती है। इसके अतिरिक्त, यह आपके कोड को प्रक्रियात्मक गड़बड़ी में बदल देगा क्योंकि सभी तर्क स्विच इकाइयों में अलग-अलग इकाइयों में विभाजित करने के बजाय हैं, उदाहरण के लिए एकल गेटर्स

एक गेटटर, यहां तक ​​कि एक आलसी लोडिंग, को मामूली जटिल होने की आवश्यकता नहीं है। एक सादे पुराने पीएचपी मनुष्य के साथ एक ही कक्षा पर विचार करें:

class Foo 
{ 
    protected $bar; 
    public function getBar() 
    { 
     // Lazy Initialization 
     if ($this->bar === null) { 
      $this->bar = new Bar; 
     } 
     return $this->bar; 
    } 
} 

इस पर PHPLoc चल रहा है आप एक बेहतर cyclomatic जटिलता

Lines of Code (LOC):         11 
    Cyclomatic Complexity/Lines of Code:   0.09 
    Cyclomatic Complexity/Number of Methods:  2.00 

दे देंगे और इस वर्ष मनुष्य आप हर अतिरिक्त सादा के लिए 2 पर बनी रहेगी जोड़ें।

इसके अलावा, ध्यान दें कि जब आप अपने संस्करण के उपप्रकारों का उपयोग करना चाहते हैं, तो आपको __get अधिभारित करना होगा और परिवर्तन करने के लिए पूरे स्विच/केस ब्लॉक को कॉपी और पेस्ट करना होगा, जबकि एक सादे पुराने गेटर के साथ आप बस अधिभारित करेंगे गेटर्स जिन्हें आपको बदलने की जरूरत है।

हां, यह सभी गेटर्स जोड़ने के लिए और अधिक टाइपिंग काम है, लेकिन यह भी बहुत आसान है और अंततः अधिक रखरखाव कोड का नेतृत्व करेगा और आपको एक स्पष्ट एपीआई प्रदान करने का लाभ भी होगा, जो हमें आपके अन्य विवरण में ले जाता है

मैं विशेष रूप से चाहता हूं कि मेरा एपीआई "पृथक" हो; प्रलेखन दूसरों है कि यह कैसे उपयोग करने के लिए बताना चाहिए: पी

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

इसके साथ ही कहा, जब आप कहते हैं

एकमात्र कारण एपीआई सुंदरता है;

तो मैं कहता हूं: तो __get का उपयोग न करें क्योंकि इसका विपरीत प्रभाव होगा। यह एपीआई बदसूरत बना देगा।जादू जटिल और गैर स्पष्ट है और कहा कि वास्तव में क्या उन WTF क्षणों की ओर जाता है है:

मैं किसी भी असली नुकसान नहीं दिख रहा है, मुझे लगता है:

Code Quality: WTF per minute

अब समाप्त हो करने के लिए यह इसके लायक है

आप उम्मीद करते हैं कि अब उन्हें देखें। यह इसके लायक नहीं है।

लेज़ी लोड हो रहा है के लिए अतिरिक्त दृष्टिकोण के लिए, various Lazy Loading patterns from Martin Fowler's PoEAA देखें:

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

ये दृष्टिकोण कुछ हद तक भिन्न होते हैं और विभिन्न व्यापार-बंद होते हैं। आप संयोजन दृष्टिकोण का भी उपयोग कर सकते हैं। पुस्तक में पूर्ण चर्चा और उदाहरण शामिल हैं।

+0

मुझे यह स्वीकार करना होगा कि आप उन दो बिंदुओं के बारे में सही हैं, लेकिन गति के बारे में मुझे लगता है कि ज़ेंड डीबगर 1 झूठ बोल रहा है! – Alex

+0

पहली __get कॉल पर संपत्ति बनाने के बारे में आप क्या सोचते हैं? :) – Alex

+2

@Alex मुझे यह पसंद नहीं है। अगर मुझे यह जानने की ज़रूरत है कि कक्षा किस परिभाषा को परिभाषित करती है तो मुझे इसे कक्षा घोषणा के शीर्ष पर देखने के बजाय विधि बॉडी से एकत्र करना होगा। साथ ही, जब आप उन्हें फ्लाई पर घोषित करते हैं तो संपत्ति सार्वजनिक होगी। यदि आपको इस तरह की चीजें चाहिए तो आपको पाइथन पसंद हो सकता है :) – Gordon

2

वर्ग के नाम और $prop में महत्वपूर्ण नामों में से अपने पूंजीकरण का मिलान नहीं हुआ, तो आप ऐसा कर सकते हैं:

class Dummy { 
    private $props = array(
     'Someobject' => false, 
     //etc... 
    ); 

    function __get($name){ 
     if(isset($this->props[$name])){ 
      if(!($this->props[$name] instanceof $name)) { 
       $this->props[$name] = new $name(); 
      } 

      return $this->props[$name]; 
     } 

     //trigger_error('property doesnt exist'); 
     //Make exceptions, not war 
     throw new Exception('Property doesn\'t exist');  
    } 
} 

और अगर पूंजीकरण जब तक यह एक ही पैटर्न का पालन नहीं करता तब तक यह काम नहीं कर सका। अगर पहला पत्र हमेशा पूंजीकृत होता है तो आप कक्षा नाम प्राप्त करने के लिए ucfirst() का उपयोग कर सकते हैं।


संपादित

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

class Something { 
    private $props = array('Someobject' => false); 

    public getSomeobject() { 
     if(!($this->props['Someobject'] instanceof Someobject)) { 
      //Instantiate and do extra stuff 
     } 

     return $this->props['Someobject']; 
    } 

    public getSomeOtherObject() { 
     //etc.. 
    } 
} 
+0

यह एक ही पैटर्न का पालन नहीं करता है। कुछ बयानों में मुझे कक्षा को तुरंत चालू करने की आवश्यकता है ... – Alex

+0

@ एलेक्स यह शायद बेहतर है कि केवल सादा तरीकों का उपयोग करें। –

1

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

public function someobject() { 
    if(!($this->props[$name] instanceof Someobject)) 
     $this->props[$name] = new Someobject; 
     // do stuff to initialize someobject 
    } 
    if (count($argv = func_get_args())) { 
     // do stuff to SET someobject from $a[0] 
    } 
    return $this->props['someobject']; 
} 

जादू तरीकों बचने के लिए आप जिस तरह से आप इस

$bar = $foo->someobject; // this won't work without __get() 
$bar = $foo->someobject(); // use this instead 

$foo->someobject($bar); // this is how you would set without __set() 

संपादित

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

Benchmark results

आप देख सकते हैं, अपने __get तरीकों के लिए कॉल एक अच्छा सौदा कर रहे हैं:

+0

यह बात है .. मुझे लगता है कि प्रदर्शन हिट नहीं है। Xdebug और wincache grind का उपयोग करना और __get कॉल के लिए मैंने देखा अधिकतम समय 2ms था, उनमें से अधिकतर 0.1ms – Alex

+0

हाँ हैं, वैसे भी वेब-ऐप्स के बारे में यह बात है, अधिकांश प्रदर्शन हिट नगण्य हैं, और इसलिए ** वास्तव में ** नहीं चिंता जब तक आप फेसबुक नहीं हैं। उस ने कहा, मैं अपनी परियोजनाओं में वैसे भी __get का उपयोग करता हूं। – Umbrella

+0

वीयर क्या है कि मैंने GetSomeObject() की तरह एक सामान्य विधि बनाई है, और इसे कॉल करते समय इसे __get संस्करण - 0.2ms – Alex

2

मैं अपने गुण के कुछ "डायनामिक" बनाने के लिए __get() का उपयोग कर रहा है (उन्हें प्रारंभ केवल जब अनुरोध किया गया)। ये "नकली" गुण एक निजी सरणी संपत्ति के अंदर संग्रहीत हैं, जिन्हें मैं __get के अंदर देख रहा हूं।

वैसे भी, आपको लगता है इसके बजाय इनमें proprties से प्रत्येक के लिए तरीकों बनाने के लिए बेहतर विचार है एक स्विच बयान में यह कर के हो?

जिस तरह से आप अपने प्रश्न पूछते हैं, मुझे नहीं लगता कि यह वास्तव में किसी के बारे में सोचता है। विचारों के बारे में बात करने के लिए, सबसे पहले यह स्पष्ट होना चाहिए कि आप किस समस्या को हल करना चाहते हैं।

दोनों जादू _get के साथ-साथ सामान्य getter methods मूल्य प्रदान करने में सहायता करते हैं। हालांकि, आप PHP में क्या नहीं कर सकते हैं केवल पढ़ने-योग्य संपत्ति बनाना है।

यदि आपको केवल पढ़ने योग्य संपत्ति की आवश्यकता है, तो आप केवल PHP में _get फ़ंक्शन के साथ ऐसा कर सकते हैं (विकल्प आरएफसी में है)।

आप एक्सेसर तरीकों के साथ ठीक हैं, और आप तरीकों 'कोड लिखकर बारे में चिंतित हैं, कि है कि आप के लिए आपको लगता है कि लेखन पहलू के बारे में वास्तव में चिंतित हैं एक बेहतर आईडीई का उपयोग करें।

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

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

और गति कुछ ऐसा है जो आपको अलग परीक्षण नहीं करना चाहिए, यह आपको अच्छे सुझाव नहीं देता है। आपके प्रश्न में गति एक दवा की तरह लगता है;) लेकिन उस दवा को लेने से आपको बुद्धिमानी से निर्णय लेने की शक्ति नहीं मिलेगी।

संबंधित मुद्दे