OO भाषा में वस्तु स्थिति का कार्यान्वयन?


11

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

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

जहाँ तक मैं देख सकता हूँ, कार्स राज्य केवल अपनी कक्षा के भीतर उपयोग किया जाता है।

मेरा प्रश्न यह है कि क्या यह ऊपर वर्णित प्रकृति की राज्य मशीन के कार्यान्वयन से निपटने का सबसे अच्छा तरीका है? यह सबसे स्पष्ट समाधान की तरह ध्वनि करता है, लेकिन अतीत में मैंने हमेशा सुना है कि "स्विच स्टेटमेंट खराब हैं"।

मुख्य समस्या जो मैं यहां देख सकता हूं, वह यह है कि स्विच स्टेटमेंट संभवतः बहुत बड़ी हो सकती है क्योंकि हम अधिक स्टेट्स जोड़ते हैं (यदि आवश्यक समझा जाता है) और कोड अनपेक्षित और बनाए रखने में कठिन हो सकता है।

इस समस्या का एक बेहतर समाधान क्या होगा?


3
आपका वर्णन मेरे लिए राज्य मशीन की तरह नहीं है; यह केवल कार वस्तुओं का एक गुच्छा जैसा लगता है, प्रत्येक की अपनी आंतरिक स्थिति होती है। अपने वास्तविक, कामकाजी कोड को codereview.stackexchange.com पर पोस्ट करने पर विचार करें ; काम कोड पर प्रतिक्रिया देने में वे लोग बहुत अच्छे हैं।
रॉबर्ट हार्वे

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

1
मुझे अभी भी लगता है कि आपको अपने कोड को कोड करने के लिए विचार करना चाहिए।
रॉबर्ट हार्वे

1
मेरे लिए एक राज्य मशीन की तरह लगता है। object.state = object.function(object.state);
रॉबर्ट ब्रिस्टो-जॉनसन

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

जवाबों:


13
  • मैंने स्टेट पैटर्न का उपयोग करके कार को राज्य की मशीन में बदल दिया । राज्य चयन के लिए नोटिस नं switchया if-then-elseबयानों का उपयोग किया जाता है।

  • इस मामले में सभी राज्य आंतरिक वर्ग हैं लेकिन इसे अन्यथा लागू किया जा सकता है।

  • प्रत्येक राज्य में मान्य राज्य होते हैं जो इसे बदल सकते हैं।

  • उपयोगकर्ता को एक से अधिक संभव होने की स्थिति में अगले राज्य के लिए संकेत दिया जाता है, या केवल एक संभव होने की स्थिति में पुष्टि करने के लिए।

  • आप इसे संकलित कर सकते हैं और इसका परीक्षण करने के लिए इसे चला सकते हैं।

  • मैंने एक ग्राफिक संवाद बॉक्स का उपयोग किया क्योंकि यह आसान था कि इसे ग्रहण में अंतःक्रियात्मक रूप से चलाया जाए।

यहाँ छवि विवरण दर्ज करें

यूएमएल आरेख यहां से लिया गया है

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JOptionPane;

public class Car {

    private State state;
    public static final int ST_OFF=0;
    public static final int ST_IDDLE=1;
    public static final int ST_DRIVE=2;
    public static final int ST_REVERSE=3;

    Map<Integer,State> states=new HashMap<Integer,State>();

    public Car(){
        this.states.put(Car.ST_OFF, new Off());
        this.states.put(Car.ST_IDDLE, new Idle());
        this.states.put(Car.ST_DRIVE, new Drive());
        this.states.put(Car.ST_REVERSE, new Reverse()); 
        this.state=this.states.get(Car.ST_OFF);
    }

    private abstract class State{

        protected List<Integer> nextStates = new ArrayList<Integer>();

        public abstract void handle();
        public abstract void change();

        protected State promptForState(String prompt){
            State s = state;
            String word = JOptionPane.showInputDialog(prompt);
            int ch = -1;
            try {
                ch = Integer.parseInt(word);
            }catch (NumberFormatException e) {
            }   

            if (this.nextStates.contains(ch)){
                s=states.get(ch);
            } else {
                System.out.println("Invalid option");
            }
            return s;               
        }       

    }

    private class Off extends State{

        public Off(){ 
            super.nextStates.add(Car.ST_IDDLE);             
        }

        public void handle() { System.out.println("Stopped");}

        public void change() {
            state = this.promptForState("Stopped, iddle="+Car.ST_IDDLE+": ");
        }

    }

    private class Idle extends State{
        private List<Integer> nextStates = new ArrayList<Integer>();
        public Idle(){
            super.nextStates.add(Car.ST_DRIVE);
            super.nextStates.add(Car.ST_REVERSE);
            super.nextStates.add(Car.ST_OFF);       
        }

        public void handle() {  System.out.println("Idling");}

        public void change() { 
            state=this.promptForState("Idling, enter 0=off 2=drive 3=reverse: ");
        }

    }

    private class Drive extends State{

        private List<Integer> nextStates = new ArrayList<Integer>();
        public Drive(){
            super.nextStates.add(Car.ST_IDDLE);
        }       
        public void handle() {System.out.println("Driving");}

        public void change() {
            state=this.promptForState("Idling, enter 1=iddle: ");
        }       
    }

    private class Reverse extends State{
        private List<Integer> nextStates = new ArrayList<Integer>();
        public Reverse(){ 
            super.nextStates.add(Car.ST_IDDLE);
        }           
        public void handle() {System.out.println("Reversing");} 

        public void change() {
            state = this.promptForState("Reversing, enter 1=iddle: ");
        }       
    }

    public void request(){
        this.state.handle();
    }

    public void changeState(){
        this.state.change();
    }

    public static void main (String args[]){
        Car c = new Car();
        c.request(); //car is stopped
        c.changeState();
        c.request(); // car is iddling
        c.changeState(); // prompts for next state
        c.request(); 
        c.changeState();
        c.request();    
        c.changeState();
        c.request();        
    }

}

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

@PythonNewb क्या आपने इसे चलाया?
ट्यूलेंस कोरडोवा

हां, यह पूरी तरह से काम करता है। मेरे पास कोड के लिए कार्यान्वयन थोड़ा अलग होगा, लेकिन सामान्य विचार महान है। मुझे लगता है कि मैं राज्य कक्षाओं को एन्क्लोजिंग क्लास से बाहर जाने पर विचार कर सकता हूं।
PythonNewb

1
@PythonNewb मैंने कोड को इंटरफ़ेस के बजाय अमूर्त वर्ग का उपयोग करते हुए इनपुट लॉजिक के लिए परिवर्तन स्थिति / प्रॉम्प्ट को पुन: प्रस्तुत करने वाले एक छोटे संस्करण में बदल दिया। यह 20 लाइन छोटी है, लेकिन मैंने इसका परीक्षण किया और काम किया। आप हमेशा संपादित इतिहास को देखते हुए पुराने, लंबे संस्करण प्राप्त कर सकते हैं।
ट्यूलेंस कोर्डोवा

1
@ कैलेथ तथ्य के रूप में मैंने इसे इस तरह लिखा है क्योंकि मैं आमतौर पर ऐसा करता हूं कि वास्तविक जीवन में, अर्थात्, विनिमेय टुकड़ों को मानचित्रों में संग्रहीत करें और उन्हें एक पैरामीटर फ़ाइल से आईडी की लोड के आधार पर प्राप्त करें। आमतौर पर मैं नक्शे में जो चीजें संग्रहीत करता हूं, वे वस्तुएं स्वयं नहीं होती हैं लेकिन उनके निर्माता अगर वस्तुएं महंगी होती हैं या उनमें बहुत अधिक गैर-स्थिर स्थिति होती है।
ट्यूलेंस कोर्डोवा

16

स्विच स्टेटमेंट खराब हैं

यह इस तरह का सरलीकरण है जो ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग को एक बुरा नाम देता है। का प्रयोग ifकेवल एक स्विच कथन का उपयोग के रूप में "बुरा" के रूप में है। किसी भी तरह से आप बहुरूपिए नहीं भेज रहे हैं।

यदि आपके पास एक नियम होना चाहिए जो ध्वनि काटने में फिट बैठता है, तो यह प्रयास करें:

स्विच स्टेटमेंट बहुत खराब हो जाते हैं जब आपके पास उनकी दो प्रतियां होती हैं।

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

यदि आप अपने आप को स्विच स्टेटमेंट में अधिक से अधिक सामान चमकाने की कोशिश करते हुए पाते हैं, चारों ओर के मामलों का ज्ञान फैला रहे हैं, या यह चाहते हैं कि इसे कॉपी करने के लिए इतना बुरा नहीं था तो यह मामलों को अलग-अलग कक्षाओं में रिफैक्ट करने का समय है।

यदि आपके पास कुछ समय से अधिक ध्वनि काटने का समय है, तो स्विच स्टेटमेंट के बारे में c2 में स्विच स्टेटमेंट गंध के बारे में बहुत अच्छी तरह से संतुलित पृष्ठ है ।

OOP कोड में भी, हर स्विच खराब नहीं है। यह है कि आप इसका उपयोग कैसे कर रहे हैं, और क्यों।


2

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


2

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

आपका कार्यान्वयन ठीक लगता है। आप सही हैं कि अगर आप कई और राज्यों को जोड़ते हैं, तो आपको बनाए रखना मुश्किल होगा। लेकिन यह केवल कार्यान्वयन का मुद्दा नहीं है - विभिन्न व्यवहार वाले कई राज्यों के साथ एक वस्तु का होना अपने आप में एक समस्या है। आपकी कार की इमेजिंग में 25 राज्य हैं प्रत्येक में अलग-अलग व्यवहार और राज्य संक्रमण के लिए अलग-अलग नियम थे। बस इस व्यवहार को निर्दिष्ट और प्रलेखित करना एक बहुत बड़ा काम होगा। आपके पास हजारों राज्य-संक्रमण नियम होंगे! का आकार switchसिर्फ एक बड़ी समस्या का लक्षण होगा। इसलिए हो सके तो इस सड़क से नीचे जाने से बचें।

राज्य को स्वतंत्र सबस्टेशनों में तोड़ने का एक संभावित उपाय है। उदाहरण के लिए, क्या वास्तव में DRIVE से अलग राज्य है? शायद कार राज्यों को दो में विभाजित किया जा सकता है: इंजन राज्य (OFF, IDLE, DRIVE) और दिशा (FORWARD, REVERSE)। इंजन की स्थिति और दिशा संभवतः अधिक स्वतंत्र होगी, इसलिए आप तर्क दोहराव और राज्य संक्रमण नियमों को कम करते हैं। कम राज्यों वाली अधिक वस्तुएं एक ही वस्तु को कई राज्यों के साथ प्रबंधित करना बहुत आसान है।


1

आपके उदाहरण में, कारें केवल क्लासिक कंप्यूटर विज्ञान अर्थों में राज्य मशीनें हैं। उनके पास राज्यों का एक छोटा, अच्छी तरह से परिभाषित सेट और कुछ प्रकार के राज्य संक्रमण तर्क हैं।

मेरा पहला सुझाव संक्रमण तर्क को अपने स्वयं के कार्य (या वर्ग, यदि आपकी भाषा प्रथम श्रेणी के कार्यों का समर्थन नहीं करता है) को तोड़ने पर विचार करना है।

मेरा दूसरा सुझाव है कि राज्य में संक्रमण तर्क को तोड़ने पर विचार किया जाए, जिसका अपना कार्य होगा (या वर्ग, यदि आपकी भाषा प्रथम श्रेणी के कार्यों का समर्थन नहीं करती है)।

किसी भी योजना में, राज्य को बदलने की प्रक्रिया कुछ इस तरह दिखाई देगी:

mycar.transition()

या

mycar.state.transition()

दूसरा, निश्चित रूप से, तुच्छ रूप से पहले की तरह दिखने के लिए कार क्लास में लपेटा जा सकता है।

दोनों स्थितियों में, एक नया राज्य (कहते हैं, DRAFTING), केवल एक नए प्रकार के राज्य ऑब्जेक्ट को जोड़ने और उन वस्तुओं को बदलने में शामिल होगा जो विशेष रूप से नए राज्य में स्विच करते हैं।


0

यह निर्भर करता है कि कितना बड़ा switchहो सकता है।

आपके उदाहरण में, मुझे लगता है switchकि ठीक है क्योंकि वास्तव में कोई अन्य राज्य नहीं है जो मैं सोच सकता हूं कि आपके Carपास हो सकता है, इसलिए यह समय के साथ बड़ा नहीं होगा।

यदि एकमात्र समस्या में एक बड़ा स्विच हो रहा है, जहां प्रत्येक के caseपास बहुत सारे निर्देश हैं, तो बस प्रत्येक के लिए अलग-अलग निजी तरीके बनाएं।

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

कुछ परिदृश्यों में, आपके पास ऐसी विधियाँ हो सकती हैं जो केवल तभी कार्य करती हैं जब राज्य A या B हो, लेकिन C या D न हों, या कई विधियाँ ऐसी हों, जो राज्य पर निर्भर हों। फिर एक या कई switchबयान बेहतर होंगे।


0

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

जैसा कि दूसरों ने कहा है, स्विच स्टेटमेंट में कुछ भी गलत नहीं है। विकल्प अक्सर अधिक जटिल और समझने में कठिन होते हैं।

जब तक स्विच के मामलों की संख्या हास्यास्पद रूप से बड़ी नहीं हो जाती, तब तक बात काफी प्रबंधनीय रह सकती है। यह पठनीय रखने में पहला कदम राज्य के व्यवहार को लागू करने के लिए एक फ़ंक्शन कॉल के साथ प्रत्येक मामले में कोड को बदलना है।

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