ऑब्जेक्ट ओरिएंटेड भाषाओं में, वस्तुओं को स्वयं कब काम करना चाहिए और वस्तुओं पर परिचालन कब करना चाहिए?


11

मान लीजिए कि एक Pageवर्ग है, जो पृष्ठ रेंडरर को निर्देशों के एक सेट का प्रतिनिधित्व करता है। और मान लीजिए Rendererकि एक वर्ग है जो जानता है कि स्क्रीन पर एक पृष्ठ कैसे प्रस्तुत किया जाए। कोड को दो अलग-अलग तरीकों से बनाना संभव है:

/*
 * 1) Page Uses Renderer internally,
 * or receives it explicitly
 */
$page->renderMe(); 
$page->renderMe($renderer); 

/*
 * 2) Page is passed to Renderer
 */
$renderer->renderPage($page);

प्रत्येक दृष्टिकोण के पेशेवरों और विपक्ष क्या हैं? कब बेहतर होगा? दूसरा कब बेहतर होगा?


पृष्ठभूमि

थोड़ी और पृष्ठभूमि जोड़ने के लिए - मैं खुद को एक ही कोड में दोनों दृष्टिकोणों का उपयोग कर पा रहा हूं। मैं 3rd पार्टी पीडीएफ लाइब्रेरी का उपयोग कर रहा हूं TCPDF। मेरी कोड में कहीं न कहीं मैं है काम करने के लिए पीडीएफ प्रतिपादन के लिए निम्नलिखित है:

$pdf = new TCPDF();
$html = "some text";
$pdf->writeHTML($html);

कहें कि मैं पृष्ठ का प्रतिनिधित्व बनाना चाहता हूं। मैं एक टेम्पलेट बना सकता हूं जो पीडीएफ पेज स्निपेट को प्रस्तुत करने के लिए निर्देश रखता है जैसे:

/*
 * A representation of the PDF page snippet:
 * a template directing how to render a specific PDF page snippet
 */
class PageSnippet
{    
    function runTemplate(TCPDF $pdf, array $data = null): void
    {
        $pdf->writeHTML($data['html']);
    }
}

/* To be used like so */
$pdf = new TCPDF();
$data['html'] = "some text";
$snippet = new PageSnippet();
$snippet->runTemplate($pdf, $data);

1) यहाँ सूचना है कि मेरे पहले कोड उदाहरण के रूप में, $snippet खुद को चलाता है । यह भी जानने के लिए $pdfऔर इसके साथ $dataकाम करने के लिए और किसी के साथ परिचित होना चाहिए ।

लेकिन, मैं एक PdfRendererक्लास बना सकता हूँ जैसे:

class PdfRenderer
{
    /**@var TCPDF */
    protected $pdf;

    function __construct(TCPDF $pdf)
    {
        $this->pdf = $pdf;
    }

    function runTemplate(PageSnippet $template, array $data = null): void
    {
        $template->runTemplate($this->pdf, $data);
    }
}

और फिर मेरा कोड इस पर जाता है:

$renderer = new PdfRenderer(new TCPDF());
$renderer->runTemplate(new PageSnippet(), array('html' => 'some text'));

2) यहां काम करने के लिए आवश्यक और $rendererप्राप्त करता है । यह मेरे दूसरे कोड उदाहरण के समान है।PageSnippet$data

इसलिए, भले ही रेंडरर पेज स्निपेट प्राप्त करता है, रेंडरर के अंदर, स्निपेट अभी भी स्वयं चलता है । यह कहना है कि दोनों दृष्टिकोण खेल में हैं। मुझे यकीन नहीं है कि आप अपने ओओ के उपयोग को केवल एक या केवल दूसरे तक ही सीमित कर सकते हैं। दोनों की आवश्यकता हो सकती है, भले ही आप एक-दूसरे को मुखौटा लगाते हों।


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

7
@DavidArno उन स्थानों का उपयोग करें जिन्हें आप गर्म करते हैं ! :)
कैंडिड_ऑरेंज

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

@ एरिक Eidt, क्या आप अपने उत्तर को रद्द कर सकते हैं, जैसा कि मुझे लगता है कि यह बहुत अच्छा "आगे विकल्प" उत्तर है।
डेविड अरनो

1
ठोस सिद्धांतों के अलावा, आप जीआरएएसपी पर एक नज़र डाल सकते हैं , विशेष रूप से विशेषज्ञ भाग पर। सवाल यह है कि जिम्मेदारी पूरी करने के लिए आपके पास कौन सी जानकारी है?
ओनेसिमुस्बाउंड

जवाबों:


13

यह पूरी तरह से इस पर निर्भर करता है कि आपको क्या लगता है कि OO है

ओओपी = एसओएलआईडी के लिए, ऑपरेशन को कक्षा का हिस्सा होना चाहिए अगर यह कक्षा की एकल जिम्मेदारी का हिस्सा है।

OO = आभासी प्रेषण / बहुरूपता के लिए, ऑपरेशन वस्तु का हिस्सा होना चाहिए अगर इसे गतिशील रूप से भेजा जाना चाहिए, अर्थात यदि इसे एक इंटरफ़ेस के माध्यम से कहा जाता है।

ओओ = एनकैप्सुलेशन के लिए, ऑपरेशन को कक्षा का हिस्सा होना चाहिए यदि यह आंतरिक स्थिति का उपयोग करता है जिसे आप उजागर नहीं करना चाहते हैं।

OO = "मुझे धाराप्रवाह इंटरफेस पसंद है", सवाल यह है कि कौन सा संस्करण अधिक स्वाभाविक रूप से पढ़ता है।

OO के लिए = वास्तविक दुनिया की संस्थाओं का मॉडलिंग करना, जो वास्तविक दुनिया की संस्था इस ऑपरेशन को करती है?


वे सभी दृष्टिकोण आमतौर पर अलगाव में गलत हैं। लेकिन कभी-कभी इनमें से एक या अधिक दृष्टिकोण एक डिजाइन निर्णय पर पहुंचने में सहायक होते हैं।

जैसे बहुरूपता के दृष्टिकोण का उपयोग करना: यदि आपके पास अलग-अलग प्रतिपादन रणनीतियाँ हैं (जैसे अलग-अलग आउटपुट प्रारूप, या अलग-अलग प्रतिपादन इंजन), तो $renderer->render($page)बहुत मायने रखता है। लेकिन अगर आपके पास अलग-अलग पृष्ठ प्रकार हैं जिन्हें अलग-अलग तरीके से प्रस्तुत किया जाना चाहिए, $page->render()तो बेहतर हो सकता है। यदि आउटपुट पृष्ठ प्रकार और रेंडरिंग रणनीति दोनों पर निर्भर करता है, तो आप विज़िटर पैटर्न के माध्यम से डबल डिस्पैच कर सकते हैं।

यह मत भूलो कि कई भाषाओं में, फ़ंक्शन के तरीके नहीं होते हैं। एक साधारण कार्य जैसे render($page)यदि अक्सर एक बिल्कुल ठीक (और आश्चर्यजनक सरल) समाधान।


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

@CandiedOrange यह एक अच्छा बिंदु है, लेकिन मैं SRP के तहत आपके तर्क को बुक करूंगा: यह पेज की कैपिटल-आर रिस्पांसिबिलिटी होगी यह तय करने के लिए कि यह कैसे प्रस्तुत की जाती है, शायद किसी प्रकार की बहुरूपिया रेंडरिंग रणनीति का उपयोग करते हुए।
आमोन

मुझे लगा कि $rendererयह तय करना है कि कैसे रेंडर करना है। जब $pageकरने के लिए बातचीत कर रही $rendererयह सब कहते हैं क्या रेंडर करने के लिए है। कैसे नहीं। $pageपता नहीं कैसे है। यह मुझे SRP मुसीबत में डाल देता है?
कैंडिड_ऑरेंज

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

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

2

एलन के के अनुसार , वस्तुएं आत्मनिर्भर, "वयस्क" और जिम्मेदार जीव हैं। वयस्क चीजें करते हैं, वे संचालित नहीं होते हैं। अर्थात्, वित्तीय लेन-देन स्वयं को बचाने के लिए जिम्मेदार है , पृष्ठ स्वयं को प्रदान करने के लिए जिम्मेदार है , आदि, और अधिक संक्षेप में, OOP में एनकैप्सुलेशन बड़ी बात है। विशेष रूप से, यह प्रसिद्ध टेल के माध्यम से प्रकट सिद्धांत मत पूछो और के सार्वजनिक नीचता (कि @CandiedOrange हर समय :) उल्लेख करने के लिए पसंद करती है) getters और setters

व्यवहार में इसका परिणाम सभी आवश्यक संसाधनों को रखने के लिए होता है जैसे कि अपना काम करना, जैसे डेटाबेस सुविधाएं, सुविधाएं प्रदान करना, आदि।

इसलिए आपके उदाहरण पर विचार करने पर, मेरा OOP- संस्करण निम्नलिखित जैसा दिखेगा:

class Page
{
    private $data;
    private $renderer;

    public function __construct(ICanRender $renderer, $data)
    {
        $this->renderer = $renderer;
        $this->data = $data;
    }

    public function render()
    {
        $this->renderer->render($this->data);
    }
}

यदि आप रुचि रखते हैं, तो डेविड वेस्ट अपनी पुस्तक ऑब्जेक्ट थिंकिंग में मूल ओओपी सिद्धांतों के बारे में बात करते हैं ।


1
इसे स्पष्ट रूप से कहने के लिए, जो ऐतिहासिक रुचि के कारण 15 साल पहले सॉफ़्टवेयर विकास के साथ कुछ करने के बारे में किसी ने क्या कहा, उसकी परवाह करता है?
डेविड अर्नो

1
" मुझे परवाह है कि एक आदमी जिसने ऑब्जेक्ट-ओरिएंटेड अवधारणा का आविष्कार किया है वह क्या वस्तु है के बारे में कहा। " क्यों? अपनी दलीलों में "अथॉरिटी की अपील" के आधार पर आपको ललकारने से परे, एक शब्द के आविष्कारक के विचारों पर 15 साल बाद क्या संभव हो सकता है?
डेविड अरनो

2
@Zapadlo: आप एक तर्क प्रस्तुत नहीं करते हैं कि संदेश पेज से रेंडरर तक क्यों है और आसपास दूसरा तरीका नहीं है। वे दोनों वस्तु हैं, और इसलिए दोनों वयस्क हैं, है ना?
जैक्सबी

1
" अथॉरिटी फॉरेसी के लिए अपील यहां लागू नहीं की जा सकती " ... " इसलिए अवधारणाओं का सेट जो आपकी राय में ओओपी का प्रतिनिधित्व करता है, वास्तव में गलत है [क्योंकि यह मूल परिभाषा का विरूपण है] "। मैं इसे लेता हूं आप यह नहीं जानते कि अधिकार क्षरण की अपील क्या है? सुराग: आप एक यहाँ इस्तेमाल किया। :)
डेविड अरनो

1
@ डेविड अरनो तो, क्या सभी अधिकार गलत करने की अपील कर रहे हैं? क्या आप "मेरी राय के लिए अपील करेंगे?" हर बार जब कोई चाचा बोबिज़्म का हवाला देता है, तो क्या आप अधिकार के लिए अपील के बारे में शिकायत करेंगे? ज़ापडिओ ने एक सम्मानित स्रोत प्रदान किया। आप परस्पर विरोधी स्रोतों का अपमान कर सकते हैं, या दोहरा सकते हैं, लेकिन बार-बार शिकायत करने पर कि किसी ने प्रशस्ति पत्र प्रदान नहीं किया है रचनात्मक है।
user949300

2

$page->renderMe();

यहाँ हम pageखुद को प्रस्तुत करने के लिए पूरी तरह से जिम्मेदार हैं। यह एक निर्माता के माध्यम से एक रेंडर के साथ आपूर्ति की गई हो सकती है, या इसमें उस कार्यक्षमता का निर्माण हो सकता है।

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

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

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

$page->renderMe($renderer);

यहां, हमें अभी भी एक पृष्ठ की आवश्यकता है जो खुद को प्रस्तुत करने में सक्षम हो, लेकिन हम इसे एक सहायक वस्तु के साथ आपूर्ति कर रहे हैं जो वास्तविक प्रतिपादन कर सकता है। यहां दो परिदृश्य उत्पन्न हो सकते हैं:

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

$renderer->renderPage($page);

यहां, हमने एसआरपी का पूरा सम्मान किया है। पृष्ठ ऑब्जेक्ट पृष्ठ पर जानकारी रखने के लिए ज़िम्मेदार है और रेंडरर उस पृष्ठ को प्रस्तुत करने के लिए ज़िम्मेदार है। हालाँकि, हमने अब पृष्ठ ऑब्जेक्ट के इनकैप्सुलेशन को पूरी तरह से कमजोर कर दिया है क्योंकि इसे अपने पूरे राज्य को सार्वजनिक करने की आवश्यकता है।

इसके अलावा, हमने एक नई समस्या खड़ी कर दी है: रेंडरर को अब कसकर पृष्ठ वर्ग में जोड़ दिया गया है। जब हम किसी पृष्ठ पर कुछ अलग करना चाहते हैं तो क्या होता है?

कौन सा सबसे अच्छा है? इनमें से कोई भी नहीं। वे सभी उनकी खामियां हैं।


असहमत हैं कि वी 3 एसआरपी का सम्मान करता है। रेंडरर को बदलने के लिए कम से कम 2 कारण हैं: यदि पेज बदलता है, या यदि आप इसे रेंडर करते हैं तो वह बदल जाता है। और एक तिहाई, जिसे आप कवर करते हैं, अगर रेंडरर को पृष्ठों के अलावा अन्य वस्तुओं को प्रस्तुत करना होगा। अन्यथा, अच्छा विश्लेषण।
user949300

2

इस प्रश्न का उत्तर अप्रतिम है। यह $renderer->renderPage($page);सही कार्यान्वयन है। यह समझने के लिए कि हम इस निष्कर्ष पर कैसे पहुंचे, हमें एनकैप्सुलेशन को समझने की आवश्यकता है।

पेज क्या है? यह एक प्रदर्शन का प्रतिनिधित्व है जिसे कोई उपभोग करेगा। वह "कोई" मानव या बॉट हो सकता है। ध्यान दें कि Pageएक प्रतिनिधित्व है, और प्रदर्शन ही नहीं है। क्या एक प्रतिनिधित्व का प्रतिनिधित्व किए बिना मौजूद है? क्या एक पेज रेंडरर के बिना कुछ है? उत्तर है हां, एक प्रतिनिधित्व का प्रतिनिधित्व किए बिना मौजूद हो सकता है। प्रतिनिधित्व करने के लिए एक बाद का चरण है।

पेज के बिना रेंडर क्या है? क्या एक रेंडरर बिना पेज के रेंडर कर सकता है? नहीं, तो एक रेंडरर इंटरफ़ेस को renderPage($page);विधि की आवश्यकता है ।

इसमें गलत क्या है $page->renderMe($renderer);?

यह तथ्य है कि renderMe($renderer)अभी भी आंतरिक रूप से कॉल करना होगा $renderer->renderPage($page);। यह कानून का उल्लंघन करता है जो राज्यों को बताता है

प्रत्येक इकाई को केवल अन्य इकाइयों के बारे में सीमित ज्ञान होना चाहिए

ब्रह्मांड में Pageकोई मौजूद है या नहीं, इस वर्ग को परवाह नहीं Rendererहै। यह केवल एक पृष्ठ का प्रतिनिधित्व होने के बारे में परवाह करता है। तो कक्षा या इंटरफ़ेस का Rendererउल्लेख कभी भी अंदर नहीं किया जाना चाहिए Page


अद्यतन किए गए उत्तर

यदि मुझे आपका प्रश्न सही लगा, तो PageSnippetकक्षा को केवल पृष्ठ स्निपेट होने से संबंधित होना चाहिए।

class PageSnippet
{    
    /** string */
    private $html;

    function __construct($data = ['html' => '']): void
    {
        $this->html = $data['html'];
    }

   public function getHtml()
   {
       return $this->html;
   }
}

PdfRenderer प्रतिपादन के साथ संबंध है।

class PdfRenderer
{
    /**@var TCPDF */
    protected $pdf;

    function __construct(TCPDF $pdf = new TCPDF())
    {
        $this->pdf = $pdf;
    }

    function runTemplate(string $html): void
    {
        $this->pdf->writeHTML($html);
    }
}

ग्राहक उपयोग

$renderer = new PdfRenderer();
$snippet = new PageSnippet(['html' => '<html />']);
$renderer->runTemplate($snippet->getHtml());

विचार करने के लिए कुछ बिंदु:

  • $dataएक सहयोगी सरणी के रूप में चारों ओर से गुजरने के लिए इसका बुरा अभ्यास । यह एक वर्ग का उदाहरण होना चाहिए।
  • तथ्य यह है कि पृष्ठ प्रारूप सरणी htmlकी संपत्ति के अंदर समाहित है $data, आपके डोमेन के लिए विशिष्ट विवरण है, और PageSnippetइस विवरण से अवगत है।

लेकिन, क्या होगा अगर, पेज के अलावा, आपके पास चित्र, लेख और ट्रिप्टिच हैं? आपकी योजना में, एक रेंडरर को उन सभी के बारे में जानना होगा। जो कि काफी लीकेज है। सिर्फ विचार के लिए भोजन।
user949300

@ user949300: यदि रेंडरर को चित्रों आदि को प्रस्तुत करने में सक्षम होने की आवश्यकता है, तो जाहिर है कि उनके बारे में जानना आवश्यक है।
जैक्सबी

1
केंट बेक द्वारा स्मॉलटाक बेस्ट प्रैक्टिस पैटर्न, रिवर्सलिंग पद्धति पैटर्न का परिचय देता है , जिसमें ये दोनों समर्थित हैं। लिंक किए गए लेख से पता चलता है कि एक ऑब्जेक्ट एक printOn:aStreamविधि का समर्थन करता है , लेकिन यह सब वह करता है जो ऑब्जेक्ट को प्रिंट करने के लिए स्ट्रीम बताता है। आपके उत्तर के साथ समानता यह है कि ऐसा कोई कारण नहीं है कि आपके पास एक पेज नहीं हो सकता है जो एक रेंडरर और एक रेंडरर को प्रदान किया जा सकता है जो एक पृष्ठ को प्रस्तुत कर सकता है, एक कार्यान्वयन और सुविधाजनक इंटरफेस का विकल्प।
ग्राहम ली

2
आपको किसी भी मामले में SRP को तोड़ना / ठगना होगा, लेकिन अगर रेंडरर को यह जानना होगा कि कई अलग-अलग चीजों को कैसे प्रस्तुत करना है, तो यह वास्तव में "बहुत सारी जिम्मेदारी" है, और, यदि संभव हो तो, इससे बचा जाना चाहिए।
user949300

1
मुझे आपका उत्तर पसंद है लेकिन मुझे यह सोचकर ललचा गया है कि Page$ रेंडर के बारे में पता होना असंभव नहीं है। मैंने अपने प्रश्न में कुछ कोड जोड़ा, देखें PageSnippetकक्षा। यह प्रभावी रूप से एक पृष्ठ है, लेकिन यह किसी प्रकार के संदर्भ के बिना मौजूद नहीं हो सकता है $pdf, जो वास्तव में इस मामले में एक तृतीय पक्ष पीडीएफ रेंडरर है। .. हालाँकि, मुझे लगता है कि मैं एक ऐसी PageSnippetकक्षा बना सकता हूँ, जो केवल पीडीएफ के लिए पाठ निर्देशों की एक सरणी रखती है, और कुछ अन्य वर्ग के निर्देशों की व्याख्या करती है। इस तरह मैं इंजेक्शन लगाने से बच सकते हैं $pdfमें PageSnippet, अतिरिक्त जटिलता की कीमत पर
डेनिस

1

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

आप Page"पेज रेंडर करने के लिए निर्देशों का एक सेट" शामिल हैं। मैं कुछ इस तरह की कल्पना करता हूं:

renderer.renderLine(x, y, w, h, Color.Black)
renderer.renderText(a, b, Font.Helvetica, Color.Black, "bla bla...")
etc...

तो यह होगा $page->renderMe($renderer), क्योंकि पृष्ठ को रेंडरर के संदर्भ की आवश्यकता है।

लेकिन वैकल्पिक रूप से रेंडरिंग निर्देश भी प्रत्यक्ष कॉल के बजाय डेटा संरचना के रूप में व्यक्त किए जा सकते हैं, जैसे।

[
  Line(x, y, w, h, Color.Black), 
  Text(a, b, Font.Helvetica, Color.Black, "bla bla...")
]

इस मामले में वास्तविक रेंडरर इस डेटा संरचना को पेज से प्राप्त करेगा और संबंधित रेंडरिंग निर्देशों को निष्पादित करके इसे संसाधित करेगा। इस तरह के दृष्टिकोण के साथ निर्भरताएं उलट जाएंगी - पृष्ठ को रेंडरर के बारे में जानने की आवश्यकता नहीं है, लेकिन रेंडरर को एक पृष्ठ प्रदान किया जाना चाहिए, जिसे वह फिर प्रस्तुत कर सकता है। तो विकल्प दो:$renderer->renderPage($page);

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

यदि आप तय नहीं कर सकते हैं, या आपको लगता है कि आप भविष्य में दृष्टिकोण बदल सकते हैं, तो आप अप्रत्यक्ष की एक परत के पीछे निर्णय छिपा सकते हैं, एक समारोह:

renderPage($page, $renderer)

मेरे द्वारा सुझाए गए एकमात्र दृष्टिकोण के $page->renderMe()बाद से यह सुझाव नहीं है कि एक पृष्ठ में केवल एक ही रेंडर हो सकता है। लेकिन क्या होगा अगर आपके पास ए ScreenRendererऔर ऐड है PrintRenderer? एक ही पृष्ठ दोनों द्वारा प्रदान किया जा सकता है।


EPUB या HTML के संदर्भ में, पृष्ठ की अवधारणा एक रेंडरर के बिना मौजूद नहीं है।
मौविविएल

1
@mouviciel: मुझे यकीन नहीं है कि मैं समझ सकता हूं कि आपका क्या मतलब है। निश्चित रूप से आपके पास एक HTML पेज हो सकता है बिना उसे रेंडर किए? उदाहरण के लिए, Google क्रॉलर उन्हें रेंडर किए बिना पृष्ठों को प्रोसेस करता है।
जैक्सबी

2
शब्द पृष्ठ की एक अलग धारणा है: पृष्ठांकन प्रक्रिया का परिणाम जब एक HTML पृष्ठ मुद्रित होता है, तो हो सकता है कि @mouviciel के मन में क्या हो। हालांकि, इस सवाल pageमें स्पष्ट रूप से रेंडरर के लिए एक इनपुट है, आउटपुट नहीं, यह धारणा स्पष्ट रूप से फिट नहीं है।
डॉक ब्राउन

1

SOLID का D भाग कहता है

"सार विवरण पर निर्भर नहीं होना चाहिए। विवरण सार पर निर्भर होना चाहिए।"

तो, पेज और रेंडरर के बीच, जो एक स्थिर अमूर्त होने की अधिक संभावना है, परिवर्तन की संभावना कम है, संभवतः एक इंटरफ़ेस का प्रतिनिधित्व कर रहा है? कॉन्ट्रैरिएव, जो "विस्तार" है?

मेरे अनुभव में, अमूर्तता आमतौर पर रेंडरर है। उदाहरण के लिए, यह एक साधारण स्ट्रीम या XML हो सकता है, बहुत सार और स्थिर। या कुछ काफी मानक लेआउट। आपका पृष्ठ कस्टम व्यवसाय ऑब्जेक्ट, "विवरण" होने की अधिक संभावना है। और आपके पास अन्य व्यावसायिक वस्तुओं को प्रदान किया जाना चाहिए, जैसे "चित्र", "रिपोर्ट", "चार्ट" आदि ... (शायद मेरी टिप्पणी में "tryptich" नहीं)

लेकिन यह स्पष्ट रूप से आपके डिजाइन पर निर्भर करता है। पृष्ठ सार हो सकता है, उदाहरण के लिए <article>मानक सबपार्ट्स के साथ HTML टैग के बराबर । और आपके पास विभिन्न कस्टम व्यवसाय रिपोर्टिंग "रेंडरर्स" हैं। उस स्थिति में, रेंडरर को पृष्ठ पर निर्भर होना चाहिए।


0

मुझे लगता है कि अधिकांश वर्गों को दो श्रेणियों में से एक में विभाजित किया जा सकता है:

  • डेटा युक्त कक्षाएं (परस्पर या अपरिवर्तनीय कोई फर्क नहीं पड़ता)

ये ऐसी कक्षाएं हैं जिनकी किसी और चीज पर निर्भरता नहीं है। वे आम तौर पर आपके डोमेन का हिस्सा हैं। उनके पास कोई तर्क या केवल तर्क नहीं होना चाहिए जो सीधे उसके राज्य से प्राप्त किया जा सकता है। एक कर्मचारी वर्ग के पास एक फ़ंक्शन isAdultहो सकता है, जो सीधे उससे प्राप्त किया जा सकता है, birthDateलेकिन ऐसा फ़ंक्शन नहीं है hasBirthDayजिसके लिए बाहरी जानकारी (वर्तमान तिथि) की आवश्यकता होती है।

  • सेवाएं प्रदान करने वाली कक्षाएं

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

आपका उदाहरण

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

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

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

उदाहरण के लिए, आपके पास वर्ग के दोनों, लेकिन अलग-अलग सेटिंग्स के साथ MobileRendererStandardRenderer, ए हो सकता है Renderer

इसलिए जैसा कि Pageडेटा में है और इसे गूंगा रखा जाना चाहिए, इस मामले में सबसे साफ समाधान Pageएक को पास करना होगा Renderer:

$renderer->renderPage($page)

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