बेसिक MDAS जावा स्विंग कैलकुलेटर

Oct 28 2020

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

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

जैसे 2 * 3 + 4 - 5 / 5 = 9 के बजाय 1

मेरे कोड में एक एकल वर्ग शामिल है। बहुत कोड नहीं है इसलिए मुझे नहीं पता था कि क्या इसे कई वर्गों में विभाजित करने का एक अच्छा कारण था, हालांकि मैंने कभी ऐसा कुछ नहीं लिखा है, इसलिए कृपया मुझे सही करने के लिए स्वतंत्र महसूस करें।

उदाहरण gif और runnable जार के साथ रेपो


package calculator;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class GUI extends JFrame {
    
    private static final long serialVersionUID = 1L;
    
    private String title = "Basic MDAS Calculator";
    private int currentNumber;
    private JLabel displayLabel = new JLabel(String.valueOf(currentNumber), JLabel.RIGHT);
    private JPanel panel = new JPanel();
    
    private boolean isClear = true;
    
    final String[] ops = new String[] {"+", "-", "x", "/"};
    
    private ArrayList<Integer> numHistory = new ArrayList<Integer>();
    private ArrayList<String> opHistory = new ArrayList<String>();
    

    public GUI() {
        setPanel();
        setFrame();
    }
    
    private void setFrame() {
        this.setTitle(title);
        this.add(panel, BorderLayout.CENTER);
        this.setBounds(10,10,300,700); 
        this.setResizable(false);
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    private void setPanel() {
        panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        panel.setLayout(new GridLayout(0, 1));
        
        displayLabel.setFont(new Font("Verdana", Font.PLAIN, 42));
        panel.add(displayLabel);
        panel.add(Box.createRigidArea(new Dimension(0, 0)));
        
        createButtons();
    }
    
    private void createButtons() {
        
        // 0-9
        for (int i = 0; i < 10; i++) {
            final int num = i;
            
            JButton button = new JButton( new AbstractAction(String.valueOf(i)) { 
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    
                    // If somebody presses "=" and then types a number, start a new equation instead
                    // of adding that number to the end like usual
                    if (!isClear) {
                        currentNumber = 0;
                        isClear = true;
                    }
                    
                    if (currentNumber == 0) {
                        currentNumber = num;
                    } else {
                        currentNumber = currentNumber * 10 + num;
                    }
                    
                    displayLabel.setText(String.valueOf(currentNumber));
                }
            });
            
            panel.add(button);
        }
        
        // +, -, x, /
        for (String op : ops) {
            
            JButton button = new JButton( new AbstractAction(op) { 
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    numHistory.add(currentNumber);
                    currentNumber = 0;
                    
                    opHistory.add(op);
                    displayLabel.setText(op);
                }
            });
            
            panel.add(button);
        }
        
        // =
        JButton button = new JButton( new AbstractAction("=") { 
            private static final long serialVersionUID = 1L;
            private int i;

            @Override
            public void actionPerformed(ActionEvent e) {    
                // Display result
                numHistory.add(currentNumber);
                
                while (opHistory.size() > 0) {
                    
                    if (opHistory.contains("x")) {
                        i = opHistory.indexOf("x");
                        numHistory.set(i, numHistory.get(i) * numHistory.get(i+1));
                    } else if (opHistory.contains("/")) {
                        i = opHistory.indexOf("/");
                        numHistory.set(i, numHistory.get(i) / numHistory.get(i+1));
                    } else if (opHistory.contains("+")) {
                        i = opHistory.indexOf("+");
                        numHistory.set(i, numHistory.get(i) + numHistory.get(i+1));
                    } else if (opHistory.contains("-")) {
                        i = opHistory.indexOf("-");
                        numHistory.set(i, numHistory.get(i) - numHistory.get(i+1));
                    }
                    
                    opHistory.remove(i);
                    numHistory.remove(i+1);
                }
                
                displayLabel.setText(String.valueOf(numHistory.get(0)));
                
                currentNumber = numHistory.get(0);
                numHistory.clear();
                
                if (isClear) {
                    isClear = false;
                }
            }
        });
        
        panel.add(button);
    }

    public static void main(String[] args) {
        new GUI();
    }

}

मैं किसी भी सुझाव की सराहना करेंगे।

जवाब

5 Bobby Oct 28 2020 at 23:41
package calculator;

पैकेज के नाम सॉफ्टवेयर को लेखक के साथ जोड़ना चाहिए, जैसे com.github.razemoon.basicmdasjavacaluclator।


public class GUI extends JFrame {

Java nming सम्मेलनों के लिए, आप सामान्य रूप से UpperCamelCase का उपयोग करेंगे, और "Gui", "HtmlWidgetToolkit" या "HtmlCssParser" जैसे समरूपों के लिए भी लोअरकेस का उपयोग करेंगे।


private static final long serialVersionUID = 1L;

आपको केवल इस क्षेत्र की आवश्यकता है यदि यह अत्यधिक संभावना है कि वर्ग क्रमबद्ध होगा ... इस मामले में, सबसे अधिक संभावना नहीं है।


final String[] ops = new String[] {"+", "-", "x", "/"};

यह क्यों है package-private?

इसके अलावा, finalसरणियाँ उतनी नहीं हैं finalजितनी आप सोचते हैं, व्यक्तिगत मूल्य अभी भी बदले जा सकते हैं। आप सबसे अधिक संभावना एक Enum चाहते हैं .... वास्तव में, आप एक इंटरफ़ेस चाहते हैं, लेकिन इस उदाहरण में, एक Enum सबसे अधिक संभावना पर्याप्त रूप से ठीक करेगा।


    private ArrayList<Integer> numHistory = new ArrayList<Integer>();
    private ArrayList<String> opHistory = new ArrayList<String>();

हमेशा इस मामले में, घोषणाओं के लिए सबसे कम सामान्य इंटरफ़ेस का उपयोग करने का प्रयास करें List।


    private void setFrame() {
        this.setTitle(title);
        this.add(panel, BorderLayout.CENTER);
        this.setBounds(10,10,300,700); 
        this.setResizable(false);
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

आप thisयहाँ क्यों उपयोग कर रहे हैं लेकिन कहीं और नहीं?


        this.setResizable(false);

क्यों? जहाँ तक मैं देख सकता हूँ, आपका फ्रेम पूरी तरह से आकार देने योग्य है। इसे बदलने योग्य नहीं होने के कारण, आप केवल यह सुनिश्चित करते हैं कि आपका एप्लिकेशन विभिन्न LaFs और फ़ॉन्ट-आकारों के तहत अनुपयोगी हो जाए।


for (int i = 0; i < 10; i++) {

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

for (int number = 0; number <= 9; number++) {
// Or
for (int digit = 0; digit <= 9; digit++) {

final int num = i;

केवल इसलिए कि आप कर सकते हैं चर नामों को छोटा न करें, टाइपिंग की कमी हुई मात्रा कम पठनीयता के लायक नहीं है।


बटनों के निर्माण के बारे में, मुझे सहायक तरीके और कक्षाएं बनाना पसंद है जो कोड को पढ़ने में आसान बनाते हैं, इस मामले में मैं इस तरह से लंबोदर के लिए जाऊंगा:

private JButton createButton(String text, Runnable action) {
    return new JButton(new AbstractButton(text) {
        @Override
        public void actionPerformed(ActionEvent e) {
            action.run();
        }
    })
}

// In createButtons:

panel.add(createButton(Integer.toString(number), () -> {
    // Code for the number button goes here.
}));

एक अन्य विकल्प यह होगा कि private class NumberActionजो अपने कंस्ट्रक्टर में एक संख्या स्वीकार करता है और संबंधित कार्रवाई करता है। यह आपको अंतिम-पुनर्वितरण से छुटकारा पाने की अनुमति भी देगा।


private int i;

यह एक बहुत ही बुरा चर नाम है।


    public GUI() {
        setPanel();
        setFrame();
    }
    
    private void setFrame() {
        this.setTitle(title);
        this.add(panel, BorderLayout.CENTER);
        this.setBounds(10,10,300,700); 
        this.setResizable(false);
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

// ...

    public static void main(String[] args) {
        new GUI();
    }

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

    public GUI() {
        setPanel();
        setFrame();
    }
    
    private void setFrame() {
        this.setTitle(title);
        this.add(panel, BorderLayout.CENTER);
        this.setBounds(10,10,300,700); 
        this.setResizable(false);
    }

// ...

    public static void main(String[] args) {
        GUI gui = new GUI();
        gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        gui.setVisible(true);
    }

आपके तर्क में किसी भी प्रकार की त्रुटि से निपटने की संभावना नहीं है, मेरा मानना ​​है कि उत्तराधिकार में एक ऑपरेटर बटन को दो बार दबाने से त्रुटि पैदा होती है।


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


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


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

4 Doi9t Oct 29 2020 at 04:33

आप ऐरे डिक्लेरेशन का उपयोग कर सकते हैं।

जब आप पूर्व-परिभाषित मानों के साथ एक सरणी चाहते हैं जो निरंतर हो सकती है, तो आप सरणी को गुमनाम रूप से घोषित कर सकते हैं।

  final String[] ops = {"+", "-", "x", "/"};

संचालन के लिए Enums का उपयोग करें।

ऑपरेशन की एक सरणी होने के बजाय, मेरा सुझाव है कि आप इसके बजाय एक एनम बनाएं।

public enum Operators {
   PLUS("+"), MINUS("-"), MUL("x"), DIV("/");
   private final String operator;
   Operators(String operator) {
      this.operator = operator;
   }
   public String getOperator() {
      return operator;
   }
}

यह आपको सरणी से अधिक लाभ देगा, क्योंकि आप दोहराव को दूर करने में सक्षम होंगे।

//[...]
for (Operators op : Operators.values()) {
   JButton button = new JButton( new AbstractAction(op.getOperator()) {
      private static final long serialVersionUID = 1L;

      @Override
      public void actionPerformed(ActionEvent e) {
         numHistory.add(currentNumber);
         currentNumber = 0;

         opHistory.add(op);
         displayLabel.setText(String.valueOf(op.getOperator()));
      }
   });
   panel.add(button);
}
//[...]
//[...]
if (opHistory.contains(Operators.MUL)) {
   i = opHistory.indexOf(Operators.MUL);
   numHistory.set(i, numHistory.get(i) * numHistory.get(i + 1));
} else if (opHistory.contains(Operators.DIV)) {
   i = opHistory.indexOf(Operators.DIV);
   numHistory.set(i, numHistory.get(i) / numHistory.get(i + 1));
} else if (opHistory.contains(Operators.PLUS)) {
   i = opHistory.indexOf(Operators.PLUS);
   numHistory.set(i, numHistory.get(i) + numHistory.get(i + 1));
} else if (opHistory.contains(Operators.MINUS)) {
   i = opHistory.indexOf(Operators.MINUS);
   numHistory.set(i, numHistory.get(i) - numHistory.get(i + 1));
}
//[...]

इसके अलावा, मेरी राय में, इससे कोड को रिफ्लेक्टर के साथ काम करने में आसानी होगी।

विभाजित करते समय, विभाजन करने divisorसे पहले हमेशा जांचें ।

शून्य से विभाजित होने पर, java.lang.ArithmeticExceptionजावा द्वारा फेंक दिया जाता है; मेरा सुझाव है कि आप एक चेक जोड़ें :)

इतिहास रखने के लिए Queueइसके बजाय का उपयोग करें List।

उपयोग करके Listआपको एक सूचकांक का उपयोग करना होगा, Queueपहले आइटम को हटाने के लिए ( java.util.Queue#poll); एकमात्र दोष, आपको हटाने के लिए वास्तविक कोड को फिर से सक्रिय करना होगा indexOf।

private Queue<String> opHistory = new ArrayDeque<>();

ऐसा करने से, आप कोड को छोटा कर देंगे।

while (opHistory.size() > 0) {
   Operators currentOperator = opHistory.poll();

   switch (currentOperator) { //Java 14+ Switch, you can use if or the older version of the switch.
       case MUL -> numHistory.set(i, numHistory.get(i) * numHistory.get(i+1));
       case DIV -> numHistory.set(i, numHistory.get(i) / numHistory.get(i+1));
       case PLUS -> numHistory.set(i, numHistory.get(i) + numHistory.get(i+1));
       case MINUS -> numHistory.set(i, numHistory.get(i) - numHistory.get(i+1));
   }

   numHistory.remove(i + 1);
}

कई बार उपयोग किए जाने पर वेरिएबल्स के लिए एक्सप्रेशन निकालें।

अपने कोड में, आप समान अभिव्यक्तियों को चर में निकाल सकते हैं; यह कोड को छोटा और पढ़ने में आसान बना देगा।

final Integer first = numHistory.get(i);
final Integer second = numHistory.get(i + 1);
switch (currentOperator) {
    case MUL -> numHistory.set(i, first * second);
    case DIV -> numHistory.set(i, first / second);
    case PLUS -> numHistory.set(i, first + second);
    case MINUS -> numHistory.set(i, first - second);
}