क्या प्रतिमाएं आंतरिक या बाहरी विधि में होनी चाहिए?


17

इनमें से कौन सा डिज़ाइन बेहतर है? प्रत्येक का भला - बुरा क्या है? आप किसका उपयोग करेंगे? इस तरह के तरीकों से निपटने के किसी भी अन्य सुझाव की सराहना की जाती है।

यह मान लेना उचित है कि ड्रा () एकमात्र स्थान है जिसे अन्य ड्रा विधियों से कहा जाता है। यह कई और अधिक ड्रा * विधियों और शो * गुणों का विस्तार करने की आवश्यकता है, न कि केवल तीन यहां दिखाए गए हैं।

public void Draw()
{
    if (ShowAxis)
    {
        DrawAxis();
    }

    if (ShowLegend)
    {
        DrawLegend();
    }

    if (ShowPoints && Points.Count > 0)
    {
        DrawPoints();
    }
}

private void DrawAxis()
{
    // Draw things.
}

private void DrawLegend()
{
    // Draw things.
}

private void DrawPoints()
{
    // Draw things.
}

या

public void Draw()
{
    DrawAxis();
    DrawLegend();
    DrawPoints();
}

private void DrawAxis()
{
    if (!ShowAxis)
    {
        return;
    }

    // Draw things.
}

private void DrawLegend()
{
    if (!ShowLegend)
    {
        return;
    }

    // Draw things.
}

private void DrawPoints()
{
    if (!ShowPoints ||  Points.Count <= 0))
    {
        return;
    }

    // Draw things.
}

अधिक संदर्भ के लिए, मैंने SO - stackoverflow.com/questions/2966216/…
Tesserex

1
जब भी मुझे एक वाक्यांश दिखाई देता है जैसे "यह कई और अधिक विस्तार करने की आवश्यकता है ...", मुझे तुरंत लगता है कि "यह एक लूप होना चाहिए"।
पॉल कसाई

जवाबों:


28

मुझे नहीं लगता कि इस तरह की चीज़ के लिए आपके पास एक कंबल नियम हो सकता है, यह स्थिति पर निर्भर करता है।

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

यदि आप पाते हैं कि आपको कई स्थानों पर विधियों को कॉल करने से पहले चेक बनाने हैं, तो आप तरीकों के अंदर चेक डालना चाहते हैं और यह स्पष्ट करने के लिए उनका नाम बदल सकते हैं कि यह हो रहा है


7

मैं दूसरा कहता हूं।

तरीकों में अमूर्तता का समान स्तर होना चाहिए।

दूसरे उदाहरण में, Draw()विधि की जिम्मेदारी नियंत्रक की तरह कार्य करना है, प्रत्येक व्यक्तिगत ड्राइंग विधियों को कॉल करना। इस Draw()पद्धति के सभी कोड अमूर्तता के समान स्तर पर हैं। यह दिशानिर्देश पुन: उपयोग परिदृश्यों में खेलने के लिए आता है। उदाहरण के लिए, यदि आपको DrawPoints()किसी अन्य सार्वजनिक विधि में विधि का पुन: उपयोग करने का प्रलोभन दिया गया था (चलो इसे कॉल करें Sketch()), तो आपको गार्ड क्लॉज (यदि कथन को आकर्षित करने के लिए निर्णय लेना है तो) को दोहराने की आवश्यकता नहीं होगी।

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

इस विचार पर रॉबर्ट सी। मार्टिन की पुस्तक "क्लीन कोड" में चर्चा की गई है, जिसकी मैं अत्यधिक अनुशंसा करता हूँ।


1
अच्छी चीज़। एक अन्य उत्तर में किए गए एक बिंदु को संबोधित करने के लिए, मैं सुझाव दूंगा कि DrawAxis()एट के नाम बदल दिए जाएं । अल। यह इंगित करने के लिए कि उनका निष्पादन सशर्त है, हो सकता है TryDrawAxis()। अन्यथा आपको Draw()अपने व्यवहार की पुष्टि करने के लिए विधि से प्रत्येक व्यक्ति के लिए प्रस्तुत करना होगा।
जोश अर्ल

6

विषय के बारे में मेरी राय काफी विवादास्पद है, लेकिन मेरे साथ है, जैसा कि मेरा मानना ​​है कि अंतिम परिणाम पर सभी बहुत सहमत हैं। मेरे पास वहां पहुंचने का एक अलग तरीका है।

मेरे लेख फंक्शन हेल में , मैं समझाता हूं कि छोटे तरीकों को बनाने के लिए मैं बंटवारे के तरीकों को क्यों पसंद नहीं करता। मैं उन्हें केवल तब विभाजित करता हूं जब मैं जानता हूं , वे पुन: उपयोग किए जाएंगे, या जब मैं उन्हें पुन: उपयोग कर सकता हूं।

ओपी ने कहा:

यह मान लेना उचित है कि ड्रा () एकमात्र स्थान है जिसे अन्य ड्रा विधियों से कहा जाता है।

यह मुझे एक (मध्यवर्ती) तीसरे विकल्प का उल्लेख नहीं करता है। मैं 'कोड ब्लॉक' या 'कोड पैराग्राफ' बनाता हूं, जो दूसरों के लिए फ़ंक्शंस बनाएंगे।

public void Draw()
{
    // Draw axis.
    if (ShowAxis)
    {
        // Drawing code ...
    }

    // Draw legend.
    if (ShowLegend)
    {
        // Drawing code ...
    }

    // Draw points.
    if (ShowPoints && Points.Count > 0)
    {
        // Drawing code ...
    }
}

ओपी ने यह भी कहा:

यह कई और अधिक ड्रा * विधियों और शो * गुणों का विस्तार करने की आवश्यकता है, न कि केवल तीन यहां दिखाए गए हैं।

... तो यह विधि बहुत जल्दी बड़ी हो जाएगी। लगभग सभी इस बात से सहमत हैं कि पठनीयता घट जाती है। मेरी राय में उचित समाधान कई तरीकों में विभाजित नहीं है, लेकिन कोड को पुन: प्रयोज्य कक्षाओं में विभाजित करना है। मेरा समाधान शायद कुछ इस तरह दिखेगा।

private void Initialize()
{               
    // Create axis.
    Axe xAxe = new Axe();
    Axe yAxe = new Axe();

    _drawObjects = new List<Drawable>
    {
        xAxe,
        yAxe,
        new Legend(),
        ...
    }
}

public void Draw()
{
    foreach ( Drawable d in _drawObjects )
    {
        d.Draw();
    }
}

संभोग तर्कों को अभी भी पारित करने की आवश्यकता होगी और ऐसे।


यद्यपि मैं फ़ंक्शन नरक के बारे में आपसे असहमत हूं, लेकिन आपका समाधान (IMHO) सही है, और वह जो क्लीन कोड और एसआरपी के उचित अनुप्रयोग से उत्पन्न होगा।
पॉल कसाई

4

उन मामलों के लिए जहां आप एक ऐसे क्षेत्र की जाँच कर रहे हैं जो नियंत्रित करता है कि ड्रॉ करना है या नहीं, यह समझ में आता है कि यह अंदर है Draw()। जब वह निर्णय अधिक जटिल हो जाता है, तो मैं उत्तरार्द्ध को प्राथमिकता देता हूं (या इसे विभाजित करता हूं)। यदि आपको अधिक लचीलेपन की आवश्यकता है, तो आप हमेशा इसका विस्तार कर सकते हैं।

private void DrawPoints()
{
    if (ShouldDrawPoints())
    {
        DoDrawPoints();
    }
}

protected void ShouldDrawPoints()
{
    return ShowPoints && Points.Count > 0;
}

protected void DoDrawPoints()
{
    // Draw things.
}

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


यह अच्छा है: सब कुछ जो दोहराया जाता है, अपने तरीके से होता है। अब आप एक DrawPointsIfItShould()विधि भी जोड़ सकते हैं जो शॉर्टकट if(should){do}:)
कोनरक

4

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

स्टीवन ज्यूरिस का जवाब सवाल में दो संस्करणों की तुलना में बेहतर है, हालांकि।


3

Show___प्रत्येक भाग के लिए अलग-अलग गुण होने के बजाय , मैं शायद थोड़ा क्षेत्र परिभाषित करूँगा। यह थोड़ा आसान होगा:

[Flags]
public enum DrawParts
{
    Axis = 1,
    Legend = 2,
    Points = 4,
    // More...
}

public class MyClass
{
    public void Draw() {
        if (VisibleParts.HasFlag(DrawPart.Axis))   DrawAxis();
        if (VisibleParts.HasFlag(DrawPart.Legend)) DrawLegend();
        if (VisibleParts.HasFlag(DrawPart.Points)) DrawPoints();
        // More...
    }

    public DrawParts VisibleParts { get; set; }
}

इसके अलावा, मैं बाहरी पद्धति में दृश्यता की जांच करने की दिशा में झुकूंगा, आंतरिक नहीं। यह सब के बाद है, DrawLegendऔर नहीं DrawLegendIfShown। लेकिन यह बाकी वर्ग पर निर्भर करता है। यदि अन्य स्थान हैं जो उस DrawLegendपद्धति को कहते हैं और उन्हें ShowLegendभी जांचने की आवश्यकता है, तो मैं शायद चेक को स्थानांतरित कर दूंगा DrawLegend


2

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


1

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

int do_something()
{
    sometype* x;
    if(!(x = sometype_new())) return -1;
    foo(x);
    bar(x);
    baz(x);
    return 0;
}

के बजाय:

int do_something()
{
    sometype x* = sometype_new();
    if(ERROR == foo(x)) return -1;
    if(ERROR == bar(x)) return -1;
    if(ERROR == baz(x)) return -1;
    return 0;
}

और निश्चित रूप से यह और भी नास्टियर हो जाता है यदि आप सिंगल-एग्जिट लागू करते हैं, तो आपके पास मुफ्त में मेमोरी की आवश्यकता है, आदि।


0

यह सब संदर्भ पर निर्भर करता है:

void someThing(var1, var2)
{
    // If input fails validation then return quickly.
    if (!isValid(var1) || !isValid(var2))
    {   return;
    }


    // Otherwise do what is logical and makes the code easy to read.
    if (doTaskConditionOK())
    {   doTask();
    }

    // Return early if it is logical
    // This is OK in C++ but not C like languages.
    // You have to be careful of cleanup in C like languages while RIAA will do
    // that auto-magically in C++. 
    if (allFinished)
    {   return;
    }

    doAlternativeIfNotFinished();

    // -- Alternative to the above for C
    if (!allFinished)
    {   
        doAlternativeIfNotFinished();
    }

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