कई तरीकों बनाम कई विधियों के साथ एकल विधि जिसे क्रम में बुलाया जाना चाहिए


16

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

क्या एक प्रोग्रामिंग प्रतिमान है जिसका मैं उपयोग कर सकता हूं जो बग को कम कर देगा और कोड को पढ़ना आसान बना देगा?


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

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

गैर-संक्रियात्मक संचालन के उदाहरण: छवि परिवर्तन (रोटेशन, स्केलिंग और क्रॉपिंग), मैट्रिक्स गुणन, आदि
rwong

शायद आप करीने का उपयोग कर सकते हैं: यह गलत क्रम में विधियों / कार्यों को लागू करना असंभव बना देगा।
जियोर्जियो

आप यहां किस पद्धति पर काम कर रहे हैं? मेरा मतलब है, मुझे लगता है कि मानक एक परिवर्तन वस्तु को पास करना है (जैसे कि 2 डी सामान के लिए जावा का Affine Transform ) जो आप इसे लागू करने वाले किसी विधि से पास करते हैं। ट्रांसफ़ॉर्म की सामग्री भिन्न है उस आदेश के आधार पर जिसे आप इस पर प्रारंभिक संचालन कहते हैं, डिज़ाइन द्वारा (इसलिए यह "आप इसे उसी क्रम में कहते हैं जिसकी आपको आवश्यकता है", न कि "उस क्रम में जिसे मैं चाहता हूँ")।
क्लॉकवर्क-म्यूजियम

जवाबों:


24

लौकिक युग्मन से सावधान रहें । हालांकि, यह हमेशा एक मुद्दा नहीं है।

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

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

इस तरह से जटिल कोड देखना चाहिए :

public List<Widget> process(File file) throws IOException {
  try (BufferedReader in = new BufferedReader(new FileReader(file))) {
    List<Widget> widgets = new LinkedList<>();
    String line;
    while ((line = in.readLine()) != null) {
      if (isApplicable(line)) { // Filter blank lines, comments, etc.
        Ore o = preprocess(line);
        Ingot i = smelt(o);
        Alloy a = combine(i, new Nonmetal('C'));
        Widget w = smith(a);
        widgets.add(w);
      }
    }
    return widgets;
  }
}

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

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

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


2
धन्यवाद, मुझे लगता है कि यह समस्या से निपटने का एक अच्छा तरीका है। भले ही यह वस्तुओं की संख्या को बढ़ाता है (और यह अनावश्यक महसूस हो सकता है), यह सुव्यवस्था को बनाए रखते हुए आदेश देता है।
tomsrobots

10

"आदेश में निष्पादित किया जाना चाहिए" तर्क बहुत बुरा है क्योंकि आपके सभी कोड को सही क्रम में निष्पादित किया जाना चाहिए। आखिरकार, आप एक फ़ाइल पर नहीं लिख सकते हैं, फिर इसे खोलें फिर इसे बंद कर सकते हैं, क्या आप कर सकते हैं?

आपको इस बात पर ध्यान केंद्रित करना चाहिए कि आपके कोड को किस तरह सबसे अधिक बनाए रखा जा सकता है। इसका मतलब आमतौर पर ऐसे लेखन कार्य होंगे जो छोटे होते हैं और आसानी से समझ में आते हैं। प्रत्येक फ़ंक्शन का एक ही उद्देश्य होना चाहिए और इसके साइड इफेक्ट्स नहीं होते हैं जो कि अप्रत्याशित होते हैं।


5

मैं एक " ImageProcesssor « (या जो भी नाम आपकी परियोजना के अनुरूप हो) और एक कॉन्फ़िगरेशन ऑब्जेक्ट ProcessConfiguration बनाऊंगा , जो सभी आवश्यक पैरामीटर रखता है।

 ImageProcessor p = new ImageProcessor();

 ProcessConfiguration config = new processConfiguration().setTranslateX(100)
                                                         .setTranslateY(100)
                                                         .setRotationAngle(45);
 p.process(image, config);

Imageprocessor के अंदर आप एक mehtod के पीछे पूरी प्रक्रिया को एनकैप कर देते हैं process()

public class ImageProcessor {

    public Image process(Image i, ProcessConfiguration c){
        Image processedImage=i.getCopy();
        shift(processedImage, c);
        rotate(processedImage, c);
        return processedImage;
    }

    private void rotate(Image i, ProcessConfiguration c) {
        //rotate
    }

    private void shift(Image i, ProcessConfiguration c) {
        //shift
    }
}

इस विधि को सही क्रम में परिवर्तनकारी तरीकों कॉल shift(), rotate()। प्रत्येक विधि उत्तीर्ण ProcessConfiguration से उपयुक्त पैरामीटर प्राप्त करता है

public class ProcessConfiguration {

    private int translateX;

    private int rotationAngle;

    public int getRotationAngle() {
        return rotationAngle;
    }

    public ProcessConfiguration setRotationAngle(int rotationAngle){
        this.rotationAngle=rotationAngle;
        return this;
    }

    public int getTranslateY() {
        return translateY;
    }

    public ProcessConfiguration setTranslateY(int translateY) {
        this.translateY = translateY;
        return this;
    }

    public int getTranslateX() {
        return translateX;
    }

    public ProcessConfiguration setTranslateX(int translateX) {
        this.translateX = translateX;
        return this;
    }

    private int translateY;

}

मैंनें इस्तेमाल किया फ्लुइड इंटरफेस का

public ProcessConfiguration setRotationAngle(int rotationAngle){
    this.rotationAngle=rotationAngle;
    return this;
}

जो निफ्टी इनिशियलाइज़ेशन की अनुमति देता है (जैसा कि ऊपर देखा गया है)।

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

private void shift(Image i, ProcessConfiguration c)

यह एक छवि को स्थानांतरित करने के बारे में है और विस्तृत पैरामीटर किसी तरह कॉन्फ़िगर किए गए हैं

वैकल्पिक रूप से, आप एक प्रोसेसिंगपिपलाइन बना सकते हैं :

public class ProcessingPipeLine {

    Image i;

    public ProcessingPipeLine(Image i){
        this.i=i;
    };

    public ProcessingPipeLine shift(Coordinates c){
        shiftImage(c);
        return this;
    }

    public ProcessingPipeLine rotate(int a){
        rotateImage(a);
        return this;
    }

    public Image getResultingImage(){
        return i;
    }

    private void rotateImage(int angle) {
        //shift
    }

    private void shiftImage(Coordinates c) {
        //shift
    }

}

एक विधि के लिए एक कॉल कॉल processImageइस तरह के एक पाइपलाइन को तुरंत रोक देगा और पारदर्शी बना देगा कि क्या और किस क्रम में किया जाता है: शिफ्ट , घुमाएं

public Image processImage(Image i, ProcessConfiguration c){
    Image processedImage=i.getCopy();
    processedImage=new ProcessingPipeLine(processedImage)
            .shift(c.getCoordinates())
            .rotate(c.getRotationAngle())
            .getResultingImage();
    return processedImage;
}

3

क्या आपने किसी प्रकार की करी का उपयोग करने पर विचार किया है ? कल्पना कीजिए कि आपके पास एक वर्ग Processeeऔर एक वर्ग है Processor:

class Processor
{
    private final Processee _processee;

    public Processor(Processee p)
    {
        _processee = p;
    }

    public void process(T1 a1, T2 a2)
    {
        // Process using a1
        // then process using a2
    }
}

अब आप कक्षा Processorको दो वर्गों से बदल सकते हैं Processor1और Processor2:

class Processor1
{
    private final Processee _processee;

    public Processor1(Processee p)
    {
        _processee = p;
    }

    public Processor2 process(T1 a1)
    {
        // Process using argument a1

        return new Processor2(_processee);
    }
}

class Processor2
{
    private final Processee _processee;

    public Processor(Processee p)
    {
        _processee = p;
    }

    public void process(T2 a2)
    {
        // Process using argument a2
    }
}

तब आप सही क्रम में संचालन का उपयोग कर कॉल कर सकते हैं:

new Processor1(processee).process(a1).process(a2);

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


हमारे पास लगभग एक ही विचार था;) एकमात्र अंतर यह है, कि आपकी पाइपलाइन एक सख्त प्रसंस्करण क्रम को लागू करती है।
थॉमस जंक

@ThomasJunk: जहां तक ​​मैंने समझा, यह एक आवश्यकता है: "इन परिणामों को सही परिणाम प्राप्त करने के लिए एक बहुत ही विशिष्ट क्रम में बुलाया जाना चाहिए"। एक सख्त निष्पादन आदेश होने से फ़ंक्शन रचना बहुत पसंद आती है।
जियोर्जियो

और इसलिए मैंने किया था। लेकिन, यदि प्रोसेसिंग ऑर्डर बदलता है, तो आपको बहुत अधिक रिफैक्टिंग करना होगा;)
थॉमस जंक

@ थोमसजंक: सच। यह वास्तव में आवेदन पर निर्भर करता है। यदि प्रसंस्करण चरणों को बहुत बार स्वैप किया जा सकता है, तो संभवतः आपका दृष्टिकोण बेहतर है।
जियोर्जियो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.