Code design

Allgemeine Themen rund um Java

Moderator: wegus

Antworten
mathiasj
Beiträge: 1
Registriert: 14.04.2014, 11:05

Code design

Beitrag von mathiasj » 04.08.2014, 02:24

Hallo,
ich habe jetzt in ein paar Tagen einen Notenrechner mit JavaFX geschrieben, der einfach nur Noten entgegennimmt und einen Graph zeichnet und so. Ich bin aber mit dem Code design noch nicht so ganz zufrieden, denn ich habe irgendwie sehr viel Code in einer Klasse, da ich die ganzen Handler als Lambda expression geschrieben habe. Hier ist der Code, bitte schreibt mir Verbesserungen vor allem am design, aber auch gerne am Programm selbst:

Code: Alles auswählen

package net.softwarepage.gradecalculator.code;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Objects;

/**
 *
 * @author Mathias
 */
public class Grade implements Serializable {
    
    private int points;
    private String subject;

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 89 * hash + this.points;
        hash = 89 * hash + Objects.hashCode(this.subject);
        hash = 89 * hash + Objects.hashCode(this.calendar);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Grade other = (Grade) obj;
        if (this.points != other.points) {
            return false;
        }
        if (!Objects.equals(this.subject, other.subject)) {
            return false;
        }
        if (!Objects.equals(this.calendar, other.calendar)) {
            return false;
        }
        return true;
    }
    private Calendar calendar;

    public Grade(String subject, int points, Calendar calendar) {
        this.subject = subject;
        this.points = points;
        this.calendar = calendar;
    }
    
    /**
     * @return the points
     */
    public int getPoints() {
        return points;
    }

    /**
     * @param points the points to set
     */
    public void setPoints(int points) {
        this.points = points;
    }

    /**
     * @return the subject
     */
    public String getSubject() {
        return subject;
    }

    /**
     * @param subject the subject to set
     */
    public void setSubject(String subject) {
        this.subject = subject;
    }

    /**
     * @return the date
     */
    public Calendar getCalendar() {
        return calendar;
    }

    /**
     * @param calendar the calendar to set
     */
    public void setCalendar(Calendar calendar) {
        this.calendar = calendar;
    }
    
}

package net.softwarepage.gradecalculator.code;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.stage.Modality;
import javafx.stage.Stage;

/**
 *
 * @author Mathias
 */
public class GradeCalculator extends Application {

    private final static File HOME = new File(System.getProperty("user.home") + "/GradeCalculator");

    private ArrayList<Grade> grades;
    private HashSet<String> subjects;

    @Override
    public void start(Stage primaryStage) {
        Locale.setDefault(Locale.ENGLISH);

        initGradesAndSubjects();

        VBox root = new VBox(20);
        Scene scene = new Scene(root, 520, 520);
        grades.forEach(e -> System.out.println(e.getPoints() + "/" + e.getSubject()));

        Text headlineText = new Text();
        headlineText.setCache(true);
        headlineText.setFont(new Font(50));
        headlineText.setText("Grade calculator");

        Text addGradeText = new Text("Add a grade:");

        ComboBox gradeCB = new ComboBox();
        ComboBox subjectCB = new ComboBox();
        gradeCB.getItems().addAll(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
        gradeCB.setPromptText("Amount of points");
        subjectCB.setEditable(true);
        subjectCB.setPromptText("Enter the subject");
        System.out.println("Subjects:");
        subjects.forEach(e -> System.out.println(e));
        subjectCB.getItems().addAll(subjects);
        gradeCB.setMinWidth(173);

        Button addGradeButton = new Button("Add grade");
        addGradeButton.setMinWidth(90);
        addGradeButton.setOnAction(e -> {
            try {
                System.out.println(subjectCB.getEditor().getText());
                if (((String) subjectCB.getEditor().getText()).equals("")) {
                    System.out.println("Invalid");
                    return;
                }
                Calendar cal = Calendar.getInstance();
                grades.add(new Grade((String) subjectCB.getValue(), (int) gradeCB.getSelectionModel().getSelectedItem(), cal));
                subjects.add((String) subjectCB.getValue());
                subjectCB.getItems().clear();
                subjects.forEach(element -> subjectCB.getItems().add(element));
                System.out.println((String) subjectCB.getValue() + (int) gradeCB.getSelectionModel().getSelectedItem());
                subjectCB.getSelectionModel().clearSelection();
                subjectCB.getEditor().clear();
                gradeCB.getSelectionModel().clearSelection();
            } catch (Exception ex) {
                System.out.println("Invalid");
            }
            saveGradesAndSubjects();
            System.out.println("saved");
        });

        VBox spaceVBox = new VBox();
        spaceVBox.setMinHeight(80);
        Button statisticsButton = new Button("View statistics");
        statisticsButton.setOnAction((ActionEvent e) -> {
            VBox vbox = new VBox(10);
            vbox.setStyle("-fx-padding: 10 0 10 0;");
            ComboBox ssubjectCB = new ComboBox();
            ssubjectCB.getItems().add("All subjects");
            ssubjectCB.getItems().addAll(subjects);
            LineChart<String, Number> chart = buildLineChart();

            final ToggleGroup group = new ToggleGroup();

            RadioButton graphRB = new RadioButton("Graph");
            graphRB.setToggleGroup(group);
            graphRB.setSelected(true);

            RadioButton tableRB = new RadioButton("Table");
            tableRB.setToggleGroup(group);

            HBox rbHBox = new HBox(10);
            rbHBox.getChildren().addAll(graphRB, tableRB);
            rbHBox.setAlignment(Pos.CENTER);

            final Text averageText = new Text();
            setAverage(true, "All grades", averageText);

            Button backButton = new Button("Back");
            backButton.setOnAction(ActionEvent -> {
                primaryStage.setScene(scene);
            });

            vbox.setAlignment(Pos.TOP_CENTER);
            vbox.getChildren().addAll(ssubjectCB, rbHBox, chart, averageText, backButton);
            Scene statisticsScene = new Scene(vbox, 520, 520);
            primaryStage.setScene(statisticsScene);

            TableView<Grade> table = buildStatisticsTable();

            ssubjectCB.getSelectionModel().selectFirst();

            group.selectedToggleProperty().addListener((ObservableValue<? extends Toggle> ov, Toggle old_toggle, Toggle new_toggle) -> {
                if (group.getSelectedToggle() != null) {
                    if (graphRB.isSelected()) {
                        updateChart(chart, ssubjectCB, averageText);
                        Platform.runLater(() -> {
                            vbox.getChildren().remove(table);
                            vbox.getChildren().add(2, chart);
                        });
                    } else {
                        updateStatisticsTable(table, ssubjectCB, averageText);
                        Platform.runLater(() -> {
                            vbox.getChildren().remove(chart);
                            vbox.getChildren().add(2, table);
                        });
                    }
                }
            });

            ssubjectCB.setOnAction(event -> {
                if (graphRB.isSelected()) {
                    updateChart(chart, ssubjectCB, averageText);
                } else {
                    updateStatisticsTable(table, ssubjectCB, averageText);
                }
            });
        });

        Button editButton = new Button("Edit grades");
        editButton.setMinWidth(90);
        editButton.setOnAction(ae -> {
            VBox vbox = new VBox(20);
            TableView<Grade> table = buildEditingTable();
            Button resetButton = new Button("Reset statistics");
            resetButton.setOnAction(e -> {
                Stage promptStage = new Stage();
                promptStage.setTitle("Info");
                promptStage.getIcons().add(new Image(GradeCalculator.class.getResourceAsStream("icon.png")));
                VBox v = new VBox(10);
                v.setAlignment(Pos.CENTER);
                Label promptLabel = new Label("Do you really want to reset all your statistics? You will lose all your saved grades and subjects. Continue?");
                promptLabel.setStyle("-fx-padding: 0 10 0 10");
                promptLabel.setWrapText(true);
                promptLabel.setTextAlignment(TextAlignment.CENTER);
                HBox h = new HBox(15);
                Button yesButton = new Button("Yes");
                yesButton.setOnAction(ybe -> {
                    grades.clear();
                    subjects.clear();
                    table.getItems().clear();
                    saveGradesAndSubjects();
                    promptStage.close();
                });
                Button noButton = new Button("No");
                h.setAlignment(Pos.CENTER);
                noButton.setOnAction(nbe -> {
                    promptStage.close();
                });
                h.getChildren().addAll(yesButton, noButton);

                v.getChildren().addAll(promptLabel, h);

                Scene promptScene = new Scene(v, 500, 100);
                promptStage.setScene(promptScene);
                promptStage.initModality(Modality.APPLICATION_MODAL);
                promptStage.setOnCloseRequest(we -> {
                    we.consume();
                });
                promptStage.show();
            });
            Button backButton = new Button("Back");
            backButton.setOnAction(e -> {
                primaryStage.setScene(scene);
                subjectCB.getItems().clear();
                subjectCB.getItems().addAll(subjects);
            });
            vbox.setStyle("-fx-padding: 0 0 10 0");
            vbox.setAlignment(Pos.TOP_CENTER);
            vbox.getChildren().addAll(table, resetButton, backButton);
            Scene editingScene = new Scene(vbox, 520, 520);
            primaryStage.setScene(editingScene);
        });

        root.setAlignment(Pos.BASELINE_CENTER);
        root.getChildren().addAll(headlineText, addGradeText, gradeCB, subjectCB, addGradeButton, spaceVBox, statisticsButton, editButton);

        primaryStage.setTitle("Grade calculator");
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.getIcons().add(new Image(GradeCalculator.class.getResourceAsStream("icon.png")));
        primaryStage.show();

        headlineText.requestFocus();
    }

    public void updateChart(LineChart<String, Number> chart, ComboBox ssubjectCB, Text averageText) {
        chart.getData().clear();
        XYChart.Series series = new XYChart.Series();
        String itemString = (String) ssubjectCB.getSelectionModel().getSelectedItem();
        if (itemString.equals("All subjects")) {
            grades.forEach(element -> {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM, HH:mm:ss");
                series.getData().add(new XYChart.Data(simpleDateFormat.format(element.getCalendar().getTime()), element.getPoints()));
            });

            setAverage(true, "All grades", averageText);
        } else {
            grades.forEach(element -> {
                if (element.getSubject().equals(itemString)) {
                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM, HH:mm:ss");
                    series.getData().add(new XYChart.Data(simpleDateFormat.format(element.getCalendar().getTime()), element.getPoints()));
                }
            });

            setAverage(false, itemString, averageText);
        }
        series.setName(itemString);
        chart.getData().add(series);
    }

    public void updateStatisticsTable(TableView table, ComboBox ssubjectCB, Text averageText) {
        table.getItems().clear();
        String itemString = (String) ssubjectCB.getSelectionModel().getSelectedItem();
        if (itemString.equals("All subjects")) {
            table.getItems().addAll(grades);
            setAverage(true, "All grades", averageText);
        } else {
            grades.forEach(i -> {
                if (i.getSubject().equals(itemString)) {
                    table.getItems().add(i);
                }
            });
            setAverage(false, itemString, averageText);
        }
    }

    public void setAverage(boolean all, String subject, Text averageText) {
        float localAverage = 0;
        int localNumberOfGrades = 0;
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(1);
        if (all) {
            localNumberOfGrades = grades.size();
        }
        for (Grade g : grades) {
            if (all) {
                localAverage += g.getPoints();
            } else if (!all && g.getSubject().equals(subject)) {
                localAverage += g.getPoints();
                localNumberOfGrades++;
            }
        }
        if (localNumberOfGrades == 0) {
            averageText.setText("Average points: 0");
            return;
        }
        localAverage /= localNumberOfGrades;
        averageText.setText("Average points: " + nf.format(localAverage));
    }

    public TableView<Grade> buildStatisticsTable() {
        TableView<Grade> table = new TableView();
        TableColumn<Grade, String> subjectColumn = new TableColumn<>("Subject");
        TableColumn<Grade, Integer> pointsColumn = new TableColumn<>("Points");
        subjectColumn.setMinWidth(260);
        pointsColumn.setMinWidth(260);
        subjectColumn.setResizable(false);
        pointsColumn.setResizable(false);
        table.setEditable(false);
        table.getColumns().addAll(subjectColumn, pointsColumn);
        subjectColumn.setCellValueFactory(new PropertyValueFactory<>("subject"));
        pointsColumn.setCellValueFactory(new PropertyValueFactory<>("points"));
        table.setItems(FXCollections.observableArrayList(grades));
        return table;
    }

    public TableView<Grade> buildEditingTable() {
        TableView<Grade> table = new TableView();
        TableColumn<Grade, String> subjectColumn = new TableColumn<>("Subject");
        TableColumn<Grade, Integer> pointsColumn = new TableColumn<>("Points");
        subjectColumn.setMinWidth(260);
        pointsColumn.setMinWidth(260);
        subjectColumn.setResizable(false);
        pointsColumn.setResizable(false);
        table.setEditable(true);
        table.getColumns().addAll(subjectColumn, pointsColumn);
        subjectColumn.setCellValueFactory(new PropertyValueFactory<>("subject"));
        pointsColumn.setCellValueFactory(new PropertyValueFactory<>("points"));
        table.setRowFactory(
                (TableView<Grade> tableView) -> {
                    final TableRow<Grade> row = new TableRow<>();
                    final ContextMenu rowMenu = new ContextMenu();
                    MenuItem removeItem = new MenuItem("Delete");
                    removeItem.setOnAction((ActionEvent e) -> {
                        if (table.getItems().size() == 1) {
                            return;
                        }
                        tableView.getItems().remove(row.getItem());
                        grades.clear();
                        grades.addAll(tableView.getItems());
                        saveGradesAndSubjects();
                    });
                    rowMenu.getItems().add(removeItem);

                    row.contextMenuProperty().bind(
                            Bindings.when(Bindings.isNotNull(row.itemProperty()))
                            .then(rowMenu)
                            .otherwise((ContextMenu) null));
                    return row;
                });
        table.setItems(FXCollections.observableArrayList(grades));

        subjectColumn.setCellFactory(cf -> {
            return new TableCell<Grade, String>() {
                TextField tf = new TextField();

                {
                    setAlignment(Pos.CENTER);
                    this.setOnMouseClicked(me -> {
                        if (me.getButton().equals(MouseButton.PRIMARY)) {
                            if (me.getClickCount() == 2) {
                                if (isEmpty()) {
                                    return;
                                }
                                setText(null);
                                setGraphic(tf);
                                tf.requestFocus();
                                Grade grade = getTableView().getItems().get(getIndex());
                                tf.setText(grade.getSubject());
                                tf.selectAll();
                            }
                        }
                    });

                    tf.setOnAction(ae -> {
                        setGraphic(null);
                        Grade grade = getTableView().getItems().get(getIndex());
                        grade.setSubject(tf.getText());
                        setText(grade.getSubject());
                        subjects.add(tf.getText());
                        saveGradesAndSubjects();
                    });

                    tf.focusedProperty().addListener(cl -> {
                        if (!tf.isFocused()) {
                            setGraphic(null);
                            Grade grade = getTableView().getItems().get(getIndex());
                            setText(grade.getSubject());
                        }
                    });
                }

                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);
                    if (!empty) {
                        setText(item);
                    } else {
                        setText(null);
                        setGraphic(null);
                    }
                }
            };
        });
        pointsColumn.setCellFactory(tc -> {
            return new TableCell<Grade, Integer>() {
                ComboBox cb = new ComboBox();
                boolean firstSelection = true;

                {
                    setAlignment(Pos.CENTER);
                    cb.getItems().addAll(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
                    cb.setMinWidth(258);
                    setOnMouseClicked((MouseEvent mouseEvent) -> {
                        if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
                            if (mouseEvent.getClickCount() == 2) {
                                if (isEmpty()) {
                                    System.out.println("empty");
                                    return;
                                }
                                System.out.println("sat graphic");
                                setText(null);
                                setGraphic(cb);
                                cb.requestFocus();
                                Grade grade = getTableView().getItems().get(getIndex());
                                cb.getItems().forEach(i -> {
                                    if ((int) i == grade.getPoints()) {
                                        cb.getSelectionModel().select(i);
                                    }
                                });
                            }
                        }
                    });

                    cb.setOnAction(e -> {
                        if (firstSelection) {
                            firstSelection = false;
                            return;
                        }
                        Grade grade = getTableView().getItems().get(getIndex());
                        grade.setPoints((int) cb.getSelectionModel().getSelectedItem());
                        setGraphic(null);
                        setText(String.valueOf(grade.getPoints()));
                        saveGradesAndSubjects();
                    });

                    cb.focusedProperty().addListener(c -> {
                        if (!cb.isFocused()) {
                            setGraphic(null);
                            Grade grade = getTableView().getItems().get(getIndex());
                            setText(String.valueOf(grade.getPoints()));
                        }
                    });
                }

                @Override
                protected void updateItem(Integer item, boolean empty) {
                    super.updateItem(item, empty);
                    if (!empty) {
                        setText(String.valueOf(item));
                    } else {
                        setText(null);
                        setGraphic(null);
                    }
                }
            };
        });

        return table;
    }

    public LineChart<String, Number> buildLineChart() {
        final CategoryAxis xAxis = new CategoryAxis();
        final NumberAxis yAxis = new NumberAxis(0, 15, 1);
        xAxis.setLabel("Day");
        final LineChart<String, Number> lineChart = new LineChart<>(xAxis, yAxis);

        XYChart.Series series = new XYChart.Series();
        series.setName("All subjects");

        //Add all grades
        grades.forEach(e -> {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM, HH:mm:ss");
            series.getData().add(new XYChart.Data(simpleDateFormat.format(e.getCalendar().getTime()), e.getPoints()));
        });

        lineChart.getData().add(series);
        return lineChart;
    }

    public void initGradesAndSubjects() {
        try {
            ObjectInputStream gradesOis = null;
            ObjectInputStream subjectOis = null;
            try {
                FileInputStream gradeReader = new FileInputStream(HOME + "/grades.gc");
                gradesOis = new ObjectInputStream(gradeReader);
                grades = (ArrayList<Grade>) gradesOis.readObject();

            } catch (FileNotFoundException ex) {
                Logger.getLogger(GradeCalculator.class
                        .getName()).log(Level.SEVERE, null, ex);
                grades = new ArrayList<>();
            }
            try {
                FileInputStream subjectsReader = new FileInputStream(HOME + "/subjects.gc");
                subjectOis = new ObjectInputStream(subjectsReader);
                subjects = (HashSet<String>) subjectOis.readObject();
            } catch (FileNotFoundException fnfe) {
                fnfe.printStackTrace();
                subjects = new HashSet<>();

            }
        } catch (IOException ex) {
            Logger.getLogger(GradeCalculator.class
                    .getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(GradeCalculator.class
                    .getName()).log(Level.SEVERE, null, ex);
        }
        if (grades == null) {
            grades = new ArrayList<>();
        }
        if (subjects == null) {
            subjects = new HashSet<>();
        }
    }

    public void saveGradesAndSubjects() {
        try {
            File gradeFile = new File(HOME + "/grades.gc");
            File subjectsFile = new File(HOME + "/subjects.gc");

            HOME.mkdirs();

            FileOutputStream gradeStream = new FileOutputStream(gradeFile);
            FileOutputStream subjectsStream = new FileOutputStream(subjectsFile);

            ObjectOutputStream gradesOis = new ObjectOutputStream(gradeStream);
            ObjectOutputStream subjectStream = new ObjectOutputStream(subjectsStream);

            gradesOis.writeObject(grades);
            subjectStream.writeObject(subjects);

        } catch (FileNotFoundException ex) {
            Logger.getLogger(GradeCalculator.class
                    .getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(GradeCalculator.class
                    .getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
}

-mathiasj

SamysSmile
Beiträge: 11
Registriert: 30.06.2009, 07:44

Re: Code design

Beitrag von SamysSmile » 24.09.2015, 00:13

Versuche dein Code aufzuteilen. Trenne den GUI Part von der Logik.

Zum vertiefen schau dir das MVC Muster an.

https://de.wikipedia.org/wiki/Model_View_Controller


Hier ein Tipp von mir: Wenn du es nicht schaffst in kürzester Zeit JavaFX durch Swing zu ersetzen dann hast du beim MVC was falsch gemacht.

Antworten