C3D2 C2 II – Clean Code Talk // Live Coding

Hi,

heute hatte ich die Ehre, den zweiten Teil meiner Vortragsreihe „Clean Code“ zu moderieren.

Wir stiegen recht schnell in die Aufgabe, aus der vorliegenden Code-Wurst ein handliches, erweiterbares Stück Software zu zaubern.

Wir begannen, den Code zu analysieren und fanden heraus, dass es sich um eine winzige Postfix Stack Machine handelt. Wir trennten den Initialisierungscode der Grafischen Benutzeroberfläche von der Ausführlogik. Danach entwirrten wir ein größeres if-Konstrukt um den kleinen Taschenrechner später leicht mit neuen Features ausrüsten zu können, ohne die Verarbeitungslogik anfassen zu müssen.

Eine falsche gedankliche Abzweigung führte uns nach einiger Zeit des zielstrebigen Geradeausrefaktorierens direkt in den Debugger und in kontroverse Diskussionen zur vermuteten Ursache.

Letztenendes fanden wir die Ursache in – ausgerechnet – zu hoher Komplexität unserer Lösung 😉

Ein Umdenken nach einer Pause führte schnell wieder zu einem funktionierenden System, das nun sehr viel leichter zu erweitern ist als der ursprüngliche Code.

Folgender Code war unser Ausgangspunkt – ich hab ihn u.A. absichtlich von Leerzeilen und Kommentaren befreit sowie um ein winziges Swing UI erweitert um die Lesbarkeit drastisch zu verringern.

So entstand Spaghetti Code mit der Logik in einer Verschachtelungstiefe von 5 zum Constructor.

(orginal von Kevin Yavno)

package calculator;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Stack;
import java.util.StringTokenizer;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

/**
 * @author nico krebs
 */
public class Calc extends JFrame {

    private JPanel mainPanel;
    private final JTextField txtInput;
    private final JPanel inputPanel;
    private final JTextField txtOutput;

    /**
     * code taken from http://kevinyavno.com/blog/?p=52	
     * then I made it unreadable as you can see below
     */
    public Calc() {
        this.setSize(new Dimension(200, 100));
        this.setLayout(new BorderLayout(15, 15));
        mainPanel = new JPanel(new BorderLayout(15, 15));
        inputPanel = new JPanel(new BorderLayout(15, 15));
        txtInput = new JTextField("");
        txtInput.setColumns(20);
        txtInput.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                ArrayList processedList = new ArrayList();
                if (txtInput.getText().length() > 0) {
                    StringTokenizer st = new StringTokenizer(txtInput.getText());
                    while (st.hasMoreTokens()) {
                        processedList.add(st.nextToken());
                    }
                } else {
                    txtOutput.setText("Error");
                }
                Stack tempList = new Stack();
                Iterator iter = processedList.iterator();
                while (iter.hasNext()) {
                    String temp = iter.next();
                    if (temp.matches("[0-9]*")) {
                        tempList.push(temp);
                    } else if (temp.matches("[*-/+]")) {
                        if (temp.equals("*")) {
                            int rs = Integer.parseInt(tempList.pop());
                            int ls = Integer.parseInt(tempList.pop());
                            int r = ls * rs;
                            tempList.push("" + r);
                        } else if (temp.equals("-")) {
                            int rs = Integer.parseInt(tempList.pop());
                            int ls = Integer.parseInt(tempList.pop());
                            int r = ls - rs;
                            tempList.push("" + r);
                        } else if (temp.equals("/")) {
                            int rs = Integer.parseInt(tempList.pop());
                            int ls = Integer.parseInt(tempList.pop());
                            int r = ls / rs;
                            tempList.push("" + r);
                        } else if (temp.equals("+")) {
                            int rs = Integer.parseInt(tempList.pop());
                            int ls = Integer.parseInt(tempList.pop());
                            int r = ls + rs;
                            tempList.push("" + r);
                        }
                    } else {
                        txtOutput.setText("Error");
                    }
                }
                txtOutput.setText(tempList.pop());
            }
        });
        txtOutput = new JTextField("");
        txtOutput.setBackground(Color.white);
        txtOutput.setEditable(false);
        inputPanel.add(txtInput, BorderLayout.CENTER);
        inputPanel.add(txtOutput, BorderLayout.SOUTH);
        mainPanel.add(inputPanel, BorderLayout.CENTER);
        this.add(mainPanel, BorderLayout.CENTER);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setVisible(true);
    }
    
    public static void main(String[] args) {
        new Calc();
    }
}

Nach drei Stunden entstand daraus ein unserer Meinung nach besseres System.

GitHub Clean Code Calculator (Zur Navigation bitte die Tags benutzen bzw an den Commits orientieren)

Download: calculator.zip

cleancoders.com

INFO: Am Dienstag, 04.08. findet Teil III statt – Thema: Kommentare, Fehlerbehandlung, Automatische Fehlerbehebung und Fehlertoleranz.

Schreibe einen Kommentar