PHP में कक्षाएं लागू करने के लिए विधि हस्ताक्षर बदलना


9

क्या PHP की जेनेरिक की कमी के आसपास कोई भी सभ्य काम है जो स्थैतिक कोड निरीक्षण को प्रकार की स्थिरता का पता लगाने की अनुमति देता है?

मेरे पास एक अमूर्त वर्ग है, जिसे मैं उप-वर्ग करना चाहता हूं और यह भी लागू करता हूं कि किसी एक प्रकार के पैरामीटर को लेने से, एक पैरामीटर को लेने के लिए जो कि उस पैरामीटर का एक उप-वर्ग है, उनमें से एक विधि बदल जाती है।

abstract class AbstractProcessor {
    abstract function processItem(Item $item);
}

class WoodProcessor extends AbstractProcessor {
    function processItem(WoodItem $item){}
}

यह PHP में अनुमति नहीं है क्योंकि यह उन तरीकों के हस्ताक्षर को बदल रहा है जिन्हें अनुमति नहीं है। जावा स्टाइल जेनेरिक से आप कुछ ऐसा कर सकते हैं:

abstract class AbstractProcessor<T> {
    abstract function processItem(T $item);
}

class WoodProcessor extends AbstractProcessor<WoodItem> {
    function processItem(WoodItem $item);
}

लेकिन जाहिर है PHP उन लोगों का समर्थन नहीं करता है।

इस समस्या के लिए, लोग सुझाव देते instanceofहैं कि रन-टाइम पर त्रुटियों की जाँच करें

class WoodProcessor extends AbstractProcessor {
    function processItem(Item $item){
        if (!($item instanceof WoodItem)) {
            throw new \InvalidArgumentException(
                "item of class ".get_class($item)." is not a WoodItem");
        } 
    }
}

लेकिन यह केवल रनटाइम पर काम करता है, यह आपको स्थैतिक विश्लेषण का उपयोग करके त्रुटियों के लिए अपने कोड का निरीक्षण करने की अनुमति नहीं देता है - तो क्या PHP में इसे संभालने का कोई समझदार तरीका है?

समस्या का एक और पूर्ण उदाहरण है:

class StoneItem extends Item{}
class WoodItem extends Item{}

class WoodProcessedItem extends ProcessedItem {
    function __construct(WoodItem $woodItem){}
}

class StoneProcessedItem extends ProcessedItem{
    function __construct(StoneItem $stoneItem){}
}

abstract class AbstractProcessor {
    abstract function processItem(Item $item);

    function processAndBoxItem(Box $box, Item $item) {
       $processedItem = $this->processItem($item);
       $box->insertItem($item);
    }

    //Lots of other functions that can call processItem
}

class WoodProcessor extends AbstractProcessor {
    function processItem(Item $item) {
        return new ProcessedWoodItem($item); //This has an inspection error
    }
}

class StoneProcessor extends AbstractProcessor {
    function processItem(Item $item) {
        return new ProcessedStoneItem($item);//This has an inspection error
    }
}

क्योंकि मैं सिर्फ एक में गुजर रहा हूँ Itemकरने के लिए new ProcessedWoodItem($item)और यह पैरामीटर के रूप में एक WoodItem उम्मीद कोड निरीक्षण से पता चलता है कि कोई त्रुटि है।


1
आप इंटरफेसेस का उपयोग क्यों नहीं कर रहे हैं? आप इंटरफ़ेस विरासत का उपयोग कर सकते हैं कि आप क्या चाहते हैं, मुझे विश्वास है।
रिबॉल्डएडिएडी

क्योंकि दो वर्ग अपने कोड का 80% हिस्सा साझा करते हैं, जो कि सार वर्ग में है। इंटरफेस का उपयोग करने का अर्थ होगा कि या तो कोड को डुप्लिकेट करना, या साझा कोड को दूसरे वर्ग में स्थानांतरित करने के लिए एक बड़ा रिफ्लेक्टर है जिसे दो वर्गों के साथ जोड़ा जा सकता है।
14

मुझे पूरा यकीन है कि आप दोनों का एक साथ उपयोग कर सकते हैं।
रिबेल्डएडीडी

हाँ - लेकिन यह समस्या को हल नहीं करता है कि i) साझा तरीकों को 'आइटम' ii के आधार वर्ग का उपयोग करने की आवश्यकता है। प्रकार के विशिष्ट तरीके उप-वर्ग "वुडइम" का उपयोग करना चाहते हैं, लेकिन यह "घोषणा" जैसी त्रुटि देता है of WoodProcessor :: bar () AbstractProcessor :: bar (आइटम $ आइटम) के साथ संगत होना चाहिए "
Danack

मेरे पास वास्तव में व्यवहार का परीक्षण करने का समय नहीं है लेकिन क्या होगा यदि आपने इंटरफ़ेस का संकेत दिया है (IItem और IWoodItem बनाएं और IIWem से IWoodItem विरासत में मिला है)? फिर बेस क्लास और बच्चे में IWoodItem के लिए फ़ंक्शन हस्ताक्षर में संकेत IItem। टाट काम कर सकता है। शायद नहीं।
RibaldEddie

जवाबों:


3

आप बिना किसी तर्क के विधियों का उपयोग कर सकते हैं, इसके बजाय डॉक्टर-ब्लॉक के मापदंडों का दस्तावेजीकरण करें:

<?php

class Foo
{
    /**
     * @param string $world
     */
    public function hello()
    {
        list($world) = func_get_args();

        echo "Hello, {$world}\n";
    }
}

class Bar extends Foo
{
    /**
     * @param string $greeting
     * @param string $world
     */
    public function hello()
    {
        list($greeting, $world) = func_get_args();

        echo "{$greeting}, {$world}\n";
    }
}

$foo = new Foo();
$foo->hello('World');

$bar = new Bar();
$bar->hello('Bonjour', 'World');

मैं यह कहने वाला नहीं हूं कि मुझे लगता है कि यह एक अच्छा विचार है।

आपकी समस्या यह है, आपके पास सदस्यों की एक परिवर्तनीय संख्या के साथ एक संदर्भ है - उन लोगों को तर्क के माध्यम से मजबूर करने की कोशिश करने के बजाय, एक बेहतर और अधिक भविष्य के सबूत वाला विचार सभी संभावित तर्कों को ले जाने के लिए एक संदर्भ-प्रकार पेश करना है, ताकि तर्क सूची को कभी भी बदलने की जरूरत नहीं है।

इस तरह:

<?php

class HelloContext
{
    /** @var string */
    public $greeting;

    /** @var string */
    public $world;

    public static function create($world)
    {
        $context = new self;

        $context->world = $world;

        return $context;
    }

    public static function createWithGreeting($greeting, $world)
    {
        $context = new self;

        $context->greeting = $greeting;
        $context->world = $world;

        return $context;
    }
}

class Foo
{
    public function hello(HelloContext $context)
    {
        echo "Hello, {$context->world}\n";
    }
}

class Bar extends Foo
{
    public function hello(HelloContext $context)
    {
        echo "{$context->greeting}, {$context->world}\n";
    }
}

$foo = new Foo();
$foo->hello(HelloContext::create('World'));

$bar = new Bar();
$bar->hello(HelloContext::createWithGreeting('Bonjour', 'World'));

स्थैतिक कारखाने के तरीके निश्चित रूप से वैकल्पिक हैं - लेकिन उपयोगी हो सकते हैं, अगर सदस्यों के केवल कुछ विशिष्ट संयोजन एक सार्थक संदर्भ उत्पन्न करते हैं। यदि हां, तो आप __construct()संरक्षित / निजी घोषित करना चाह सकते हैं ।


PHP में OOP का अनुकरण करने के लिए हुप्स को कूदना पड़ता है।
ट्यूलेंस कोर्डोवा

4
@ user61852 मैं इसे PHP (मेरा विश्वास करें) का बचाव करने के लिए नहीं कह रहा हूं, लेकिन, लेकिन अधिकांश भाषाएँ आपको विरासत में मिली विधि-हस्ताक्षर को बदलने नहीं देंगी - ऐतिहासिक रूप से, यह PHP में संभव था, लेकिन विभिन्न कारणों से (कृत्रिम रूप से) प्रोग्रामर को प्रतिबंधित करने का निर्णय लिया गया ऐसा करने से, क्योंकि यह भाषा के साथ मूलभूत समस्याओं का कारण बनता है; यह परिवर्तन भाषा को अन्य भाषाओं के अनुरूप लाने के लिए किया गया था, इसलिए मुझे यकीन नहीं है कि आप किस भाषा की तुलना कर रहे हैं। मेरे उत्तर के दूसरे भाग में प्रदर्शित पैटर्न अन्य भाषाओं में भी लागू है, जैसे C # या Java।
mindplay.dk 14
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.