Diskuze: Obecná rada jak se neztratit v kódu
V předchozím kvízu, Online test znalostí Java, jsme si ověřili nabyté zkušenosti z kurzu.


Atrament:17.3.2018 14:53
Tím jak se neztratit v kódu se zabývá (mimo jiné) Softwareové inženýrství, a vysvětlovat Softwareové inženýrství je absolutně mimo možný rozsah komentářů zde na fóru. Prostě si najdi nějaké knihy o navrhováni software, o objektově orientovaném přístupu, a design patterns a pusť se do studia:)
To se velmi těžko radí, když jsi sice pojmenoval problém, ale neřekls,
v čem konkrétně se ztrácíš. Musíš znát logiku programu a pokud možno
všechno programovat velmi obecně.
Viz třeba tento případ, kdy něco odebíráš a nebo přidáváš. Pokud ti
to spadne na takové věci, tak neprogramuješ objektově. Už ve spoustě
komentářích radím především začátečníkům, aby vždy, když
naprogramují nějakou třídu, tak ji zkusili vyndat z projektu a spustit v
podobném programu.
Dám ti příklad z praxe, kdy jsem teď sám toto pravidlo porušil a pak bych
na to doplatil.
V práci máme projekt, který potřebuje mimo jiné vypisovat specifický log. Jak do mailu, tak na výstup. Navíc jsme si zmysleli přepínač, kterým log zapínáme na několika úrovních (třeba když potřebujeme podrobnější výpis exceptiony atd.) Všechno bylo fajn, až na to, že jsem si nevšiml, že jsem omylem do těchto logů implementoval načítání ze souboru. Všechno pochopitelně funguje, ale když jsem tu samou třídu chtěl potom vzít a použít na jiném místě, tak jsem si právě všiml, že jsem závislý na souboru, který vůbec v danou chvíli nepotřebuju použít. No a tak jsem to vyřešil přetížením parametru, ale musel jsem chvilku popřemýšlet nad strukturou celého kódu - jestli tím zase nic nenaruším atd.
Z toho textu jsem pochopil, že jsi asi přecházel ze swingu na FX.
Přiznám se, že je to skvělý případ, kdy se ukáže, jak uvažuješ a jak
počítáš do budoucna s upgradem. FX je založený naprosto na odlišném
pohledu vykreslování, nežli swing. Nicméně je mnohem příjemnější,
když opravuješ jednu třídu, která má na starost pouze a jenom
vykreslování a nic jiného, než když máš ve třídě vykreslování a k
tomu řadu dalších funkcí.
A nechci ani rozvádět porušování zapouzdření. Nováčci díky tomuto
můžou mít spoustu problémů. Už jsem se setkal, že borec používal
spoustu globálních proměnných a pak se taky v tom ztrácel, protože
nevěděl, co se kde zrovna nastavuje. Potom musíš hodně dlouho pátrat v
debuggeru a projíždět si několikrát vlákno dokola, abys třeba našel
případnou chybu.
Přiznám se, že nechápu celkem tento ticket. K tomu, aby ses neztratil v kódu napomáhají komentáře, unitové testy (pokud si zvyklý je dělat a umíš je dobře napsat, tak pak se jen směješ), a hlavně dobrá implementace OOP. Pokud máš nějaké třídy, které jsou na sobě naprosto závislé, je to chyba. Samozřejmě, že je to pohodlnější, když tam přímo nastavíš vazby, ale pokud není třída soukromá a tudíž bys věděl, že bude vždy součástí nějaké jiné třídy a nikdy jinak, tak by každá třída měla stát sama o sobě a měla by mít i využití v jiném projektu. Takže moje rada, kterou dávám začátečníkům vždy - když naprogramuješ třídu a chceš mít aspoň malou jistotu, že programuješ objektově, vytvoř nový projekt, nasimuluj prostředí stávajícího projektu a spusť / použij tu třídu v jiném projektu. Doteď jsem nenašel žádný případ, kdy by měla být třída vázaná pouze na jeden projekt. To už se skoro 30 let nedělá.
Dobrý den,
jak jsem si pročítal Vaší odpověď, tak jsem si všiml, že jsem to
nenapsal úplně srozumitelně.., sorry. Na JavaFX jsem nepřešel ze Swingu,
ale postupně jsem jen doplňoval prvky. Prvky jako seznamy, formuláře,
tlačítka apod. Nejprve jsem měl k FX jen jediný holý panel, ve kterém se
vykresloval graf. Vše fungovalo při ruční změně vzorových dat.
A pak jsem se začal ztrácet při implementaci těch seznamů a hlavně formulářů, tlačítek, pomocí kteých jsem, přidával, upravoval nebo odebíral záznamy. Ať už záznamy do seznamu zaměstnanců nebo do seznamu vazeb.
Například právě formulář pro přidání zaměstnance přidal jméno a příjmení do seznamu, ale dále pak ještě musel tohoto zaměstnace přidat do výběru možností ve dvou ComboBoxech, které sloužily pro přidání záznamu vazby. Jeden ComboBox pro výběr nadřízeného, druhý pro výběr podřízeného. To vše kvůli tomu, aby uživatel jen vybral přidaného zaměstnace a nemusel například zadávat jeho unikátní klíč, který by se mu automaticky přidělil a někde u seznamu zaměstnanců zobrazoval. No a právě ten kód (a podobné ovládání aplikace), co tohle dělá, mi hrozně motal hlavu..
Nepochybuji o tom, že v kódu byla spousta chyb.. ale ta třída pro vykreslení grafu byla podlě mě i celkem přenositelná. Vlastně vše co jako vstup potřebovala bylo seznam zaměstnanců a vazeb. Dále se jen z nadřazené třídy zavolalo několik metod.
Unitové testy jsem nedělal, ale mají se dělat i když má třída jen jediný účel? Na ITnetwork je třeba k unitovým testům nasáno tohle: "Nemá úplně smysl pomocí unit-testů testovat, zda metoda obsahující databázový dotaz, která je použita v jednom kontroleru (nebo nějaké jiné řídící vrstvě) vrací co má. Naopak dává velmi dobrý smysl testovat např. validátor telefonních čísel, který používáme na 20 místech nebo dokonce v několika aplikacích." To je právě můj případ ne? Když metoda vezme vazby a poskládá je do matice.
Jestli jejich absence byla tou podstatnou chybou jak přispět k lepší čitelnosti, tak bych na tom určitě zapracoval.
Děkuji
Unitové testy má cenu dělat kdykoliv. Pouze, pokud voláš void metody,
které jen volají jiné metody, tak takové testy není třeba dělat. Ale má
to smysl. Je to sice sračka, ale právě v pozdějších updatech budeš rád,
že si s tím dáš práci.
Jelikož nevidíme tvůj kód a jestli je pracovní, tak ani vidět nemůžeme
(přeci jen už je to tvoje know how), tak se špatně soudí. Nicméně pokud
máš v kódu jen jednu metodu, je to chyba.
Třeba jak jsi to teď popisoval.
Možná se pletu, ale nemáš to náhodou tak, že když implementuješ nějaký
seznam ve formuláři, tak pak jak jsi popisoval:
Například právě formulář pro přidání zaměstnance přidal jméno a příjmení do seznamu, ale dále pak ještě musel tohoto zaměstnace přidat do výběru možností ve dvou ComboBoxech
, tak bych se docela divil, kdybys nedal všechno do eventů. Přitom by bylo krásné, kdybys právě vytvořil metody, které by něco někam přidávali a odebírali. A potom už jen zavoláš s příslušnými parametry (třeba jeden parametr, který by obsahoval hodnotu pro přidání a druhý parametr kam se má něco přidat) a všechno řešíš v těchto metodách. No a pak bys tyto metody otestoval lehce. Mohly by být prázdné, ale ty bys otestoval výsledek. Prostě když je použiješ, že se doopravdy v seznamu promítla nějaká změna.
Hlavně potom i ty komentáře a principy OOP. Unit testy jsou takové berličky pro budoucí update. V podstatě smysl unitových testů je v tom, že když je dobře napíšeš, tak potom můžeš kód upravovat jak chceš, ale když je spustíš, tak si ověříš, že jsou všechny stávající funkce zachovány. Tím máš i pak jistotu, žes nic neposral:)
Pro lepší čitelnost slouží hlavně komentáře. Když aktuálně něco programuješ, tak je ti všechno jasné a i víš, proč to či ono děláš. Ale otevřeš-li ten projekt za půl roku, Tak pak i přemýšlíš, proč jsi vlastně tuto metodu dělal. A to je třeba jeden z hlavních bodů, co bys měl komentovat. Ne to, jak ta metoda funguje (to vyčteš z jejího těla). Ale proč ta metoda vlastně je, proč se využívá, kdo a za jakých podmínek ji může použít atd. atd. atd. Prostě dokumentovat myšlenku, abys věděl, k čemu ta metoda je. Když uvidíš, že má metoda třeba návratový typ int, tak je blbost do dokumentace psát: Metoda vrací to a to číslo. To přece vidíš v její hlavičce. Ale nevíš, k čemu to číslo je. A to je důležité komentovat.
No a fakt ta nezbytná věc - dobrá struktura kódu. To se týká i metod.
Prostě je nesmysl, když třeba nějaká metoda zobrazuje formulář a pracuje
s nějakou událostí (třeba jen v tom smyslu, že vrací nějakou položku),
tak do této metody rvát i přidávání a odebírání ze seznamu.
Ano, ušetříš tím nějaký ten řádek a kód je na oko přehlednější.
Ale pak se v tom začneš takto ztrácet.
Je to to samé, jako třeba zkracování v proměnných.
Ono jde v Javě, jako v jiných jazycích deklarovat proměnné třeba
takto:
public class ExampleClass {
private int a = 0, b = 1, c = 3, d, e;
}
Ale když toto ukážeš zkušenému programátorovi, tak tě minimálně přerazí (jako třeba já:) toto je ukázka skutečného kódu, jaký já teď musím předělávat po bývalém kolegovi.... nejraději bych ho zabil!!!!!)
CO DO PRDELE ZNAMENÁ TO a, b, c????
Daleko lepší je napsat:
public class ExampleClass {
private int hodnotaZadanaUzivatelem;
private int vychoziHodnotaProNacteniZDatabaze = 1;
private int celkovyPocetClenuVeSkupine;
//atd...
}
Já doporučuju vyhýbat se takovémuto zkracování kódu. Ono to sice jde,
ale pak je to děs, když to po tobě někdo přebírá nebo to chceš po
nějaké době upravovat.
Toto je citát Martina Fowlera, který třeba ani začátečníci moc neznají,
ale je výstižný a není k němu potřeba žádný komentář:
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
Zkracovat kód znamená v podstatě využívat principy OOP (dědičnost,
rozhraní, abstraktní třídy, atd.) Ne to, že když to jde narvat do jedné
metody, tak to tam šoupnu:)
Možná to neděláš, ale třeba jo a třeba toto ti to potom znepřehledňuje.
Ptej se sám sebe - jestli ta dotyčná metoda vykonává pouze to, na co je
stavěná a nebo jestli ještě k tomu dělá řadu dalších blbin, které bys
od jejího názvu nečekal
Zdravím,
já jsem jen samouk a celý projekt dělám kvůli tomu, abych se postupně
učil, takže to sem nahrát můžu. Do těch testů se určitě pustím,
děkuji za informace.
Hodně věcí je v těch Eventech, protože přes metody mi to nějak nereagovalo.. Myslím, že kvůli tomu nahrávání dat do seznamu přes RowFactory. To jsem celé dělal podle nějakého tutoriálu.
Zde je kód hlavní metody
package main;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.ObservableList;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Callback;
import javax.imageio.ImageIO;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* Created by uzivatel on 5.6.17.
*/
public class Main extends Application {
//rozměry okna aplikace
public static final double width = 600;
public static final double height = 500;
//šířka všech tlačítek
private double buttonWidth = (width / 5.8) - (10 * 2);
//výška tlačítek
private double buttonHeight = 50;
//grafické prvky aplikace
private BorderPane root = new BorderPane();
//levý box s menu
private VBox right = new VBox(5.0);
//pravý box pro prostory editace položek a vazeb
private VBox left = new VBox();
//pravý box pro prostory editace položek a vazeb
private VBox menu = new VBox(9.0);
//název aplikace
private VBox head = new VBox();
//private Image headings = new Image(getClass().getResourceAsStream("map.png"), 35, 35, false, false);
//private Label headings = new Label("Organization");
//centrální prostřední část pro graf
private VBox central = new VBox();
//tlačítka hlavního menu
//private Button employeeButton = new Button("Zaměstnanci");
private Image employeeImage = new Image(getClass().getResourceAsStream("users.png"), 20, 20, false, false);
private Button employeeButton = new Button("Osoby", new ImageView(employeeImage));
//private Button edgesButton = new Button("Vazby");
private Image edgesImage = new Image(getClass().getResourceAsStream("hierarchy.png"), 20, 20, false, false);
private Button edgesButton = new Button("Vazby", new ImageView(edgesImage));
//prvky pro přidání nového záznamu
private HBox addEmployeeBox = new HBox();
private TextField jobField = new TextField();
private TextField nameField = new TextField();
private TextArea descriptionField = new TextArea();
private Button employeeAdd = new Button("Přidat");
//tabulka s daty pro zobrazení a editaci zaměstnanců
private TableView employeeList = new TableView();
private TableColumn firstColumnEmployeeList = new TableColumn("Název");
private TableColumn secondColumnEmployeeList = new TableColumn("Jméno");
//tabulka pro zobrazení a editaci vazeb mezi zaměstnanci
private TableView edgesList = new TableView();
private TableColumn<Edge, String> firstColumnEdgesList = new TableColumn("Počátek");
private TableColumn<Edge, String> secondColumnEdgesList = new TableColumn("Konec");
private HBox addEdgeBox = new HBox();
private Button edgeAdd = new Button("Přidat");
private ComboBox sourceComboBox = new ComboBox();
private ComboBox targetComboBox = new ComboBox();
ComboBox<Item> com = new ComboBox<>();
MenuBar menuBar = new MenuBar();
//menu File
Menu menuFile = new Menu("Soubor");
//new file
MenuItem newFile = new MenuItem();
Label lblNew = new Label("Nový");
//uložit soubor
MenuItem fileSave = new MenuItem();
Label lblSave = new Label("Uložit");
//menu pro otevřít soubor
MenuItem fileOpen = new MenuItem();
Label lblOpen = new Label("Otevřít");
//menu pro otevřít soubor
MenuItem saveImage = new MenuItem();
Label saveImagelabel = new Label("Export do PNG");
//menu Edit
Menu menuEdit = new Menu("Nápověda");
//aplikace
@Override
public void start(Stage primaryStage) throws Exception {
//nová instance dat
Data data = new Data();
data.setStage(primaryStage);
data.newFile();
Controler controler = new Controler();
//nová instance grafického modelu
Model model = new Model(data.getEmployee(), data.getEdges(), true);
//podokno pro kreslení
Pane canvas = new Pane();
canvas.setPrefSize(width, height);
canvas.getChildren().addAll(model.getData(), model.getLines());
/**
* Prvky levé části aplikace; respektive menu
*/
left.setPrefWidth(width / 5.8);
left.setStyle("-fx-background-color: #2B2A2F;");
menu.setStyle("-fx-background-color: #2B2A2F;");
menu.setPadding(new Insets(12, 10, 10, 10));
//prvky s názvem aplikace
head.setPrefWidth(width / 5.8);
head.setMinHeight(110);
head.setAlignment(Pos.CENTER);
final ImageView selectedImage = new ImageView();
Image image1 = new Image(Main.class.getResourceAsStream("map.png"));
selectedImage.setImage(image1);
head.getChildren().add(selectedImage);
head.setStyle("-fx-background-color: #DC414B;");
left.getChildren().add(head);
jobField.setId("nazev");
jobField.setPromptText("Pozice");
jobField.setMaxWidth(firstColumnEmployeeList.getPrefWidth());
nameField.setId("jmeno");
nameField.setPromptText("Jméno");
nameField.setMaxWidth(secondColumnEmployeeList.getPrefWidth());
descriptionField.setMaxWidth(380);
descriptionField.setMaxHeight(50);
descriptionField.setId("popis");
//in this event is added a new employee to data list
employeeAdd.setOnAction(e -> {
Item employee = null;
String keyOfCell = "";
//actual size of data list with employee, plus one means number
//for new item
int size = data.getDataListSize() + 1;
employee = controler.addNewEmployee(size, jobField, nameField);
keyOfCell = controler.addNewItemToTemp(employee);
data.getEmployee().add(employee);
data.getTemp().add(keyOfCell);
jobField.clear();
nameField.clear();
});
employeeButton.setPrefWidth(buttonWidth);
employeeButton.setPrefHeight(buttonHeight);
employeeButton.setPadding(new Insets(0, 0, 0, 0));
employeeButton.setId("menuButton");
employeeButton.setContentDisplay(ContentDisplay.TOP);
menu.getChildren().add(employeeButton);
addEmployeeBox.getChildren().addAll(
jobField,
nameField,
employeeAdd
);
addEmployeeBox.setSpacing(3);
employeeButton.setOnAction(e -> {
right.getChildren().clear();
right.getChildren().addAll(
employeeList,
addEmployeeBox
);
});
sourceComboBox.setItems(data.getTemp());
targetComboBox.setItems(data.getTemp());
sourceComboBox.setPrefWidth(firstColumnEmployeeList.getPrefWidth());
targetComboBox.setPrefWidth(firstColumnEmployeeList.getPrefWidth());
//this method makes a new edge when the add button is pressed
edgeAdd.setOnAction(e -> {
//new edge, now is null
Edge edge = null;
//now the new edge is inicialized
edge = controler.addNewEdge(data.getEmployee(), sourceComboBox, targetComboBox);
data.getEdges().add(edge);
canvas.getChildren().clear();
model.update(data.getEmployee(), data.getEdges());
canvas.getChildren().addAll(model.getData(), model.getLines());
String i = "";
i = com.getSelectionModel().getSelectedItem().getKey();
System.out.println(i);
i = com.getSelectionModel().getSelectedItem().getTitle();
System.out.println(i);
});
addEdgeBox.getChildren().addAll(sourceComboBox, targetComboBox, edgeAdd);
addEdgeBox.setSpacing(3);
edgesButton.setPrefWidth(buttonWidth);
edgesButton.setPrefHeight(buttonHeight);
edgesButton.setId("menuButton");
edgesButton.setContentDisplay(ContentDisplay.TOP);
edgesButton.setOnAction(e -> {
right.getChildren().clear();
right.getChildren().addAll(edgesList, addEdgeBox, com);
});
menu.getChildren().add(edgesButton);
/*
Prvky centrální části aplikace
*/
central.setPrefSize(width, height);
central.setStyle("-fx-background-color: darkgray;");
/*
Pravá strana
*/
right.setPrefWidth(width * 2 / 4);
right.setStyle("-fx-background-color: gray;");
right.setPadding(new Insets(10));
employeeList.getColumns().addAll(firstColumnEmployeeList, secondColumnEmployeeList);
employeeList.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
employeeList.setRowFactory(
tableView -> {
final TableRow<Item> row = new TableRow<>();
final ContextMenu rowMenu = new ContextMenu();
MenuItem removeItem = new MenuItem("Smazat");
removeItem.setOnAction(event -> {
int option = 0;
Confirm confirm = new Confirm(new String[]{"Ano", "Ne"},
"Potvrzení", "Odebrání položky znamená nevratnou změnu.\n Přejete si pokračovat?",
400, 120);
confirm.showAndWait();
option = confirm.getOption();
if (option == 1) {
ObservableList<Item> sel = employeeList.getSelectionModel().getSelectedItems();
for (Item m : sel) {
List<String> dependencies = new ArrayList<>(model.getDependencies(m.getKey()));
data.removeItemFromEmployee(dependencies);
data.removeItemFromTemporaryList(dependencies);
data.removeItemFromEdgeSource(dependencies);
data.removeItemFromEdgeTarget(dependencies);
}
}
canvas.getChildren().clear();
model.update(data.getEmployee(), data.getEdges());
canvas.getChildren().addAll(model.getData(), model.getLines());
});
rowMenu.getItems().addAll(removeItem);
// only display context menu for non-null items:
row.contextMenuProperty().bind(
Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu) null));
return row;
});
employeeList.setEditable(true);
firstColumnEmployeeList.setCellValueFactory(
new PropertyValueFactory<Item, String>("title")
);
firstColumnEmployeeList.setCellFactory(TextFieldTableCell.forTableColumn());
firstColumnEmployeeList.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<Item, String>>() {
@Override
public void handle(TableColumn.CellEditEvent<Item, String> t) {
t.getTableView().getItems().get(
t.getTablePosition().getRow()).setTitle(t.getNewValue());
}
}
);
secondColumnEmployeeList.setCellValueFactory(
new PropertyValueFactory<Item, String>("name")
);
secondColumnEmployeeList.setCellFactory(TextFieldTableCell.forTableColumn());
secondColumnEmployeeList.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<Item, String>>() {
@Override
public void handle(TableColumn.CellEditEvent<Item, String> t) {
t.getTableView().getItems().get(
t.getTablePosition().getRow()).setName(t.getNewValue());
}
}
);
employeeList.setItems(data.getEmployee());
edgesList.getColumns().addAll(firstColumnEdgesList, secondColumnEdgesList);
edgesList.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
edgesList.setRowFactory(
tableView -> {
final TableRow<Edge> row = new TableRow<>();
final ContextMenu rowMenu = new ContextMenu();
MenuItem removeItem = new MenuItem("Smazat");
removeItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
int option = 0;
Confirm confirm = new Confirm(new String[]{"Ano", "Ne"},
"Potvrzení", "Odebrání položky znamená nevratnou změnu.\n Přejete si pokračovat?",
400, 120);
confirm.showAndWait();
option = confirm.getOption();
if (option == 1) {
ObservableList<Edge> sel = edgesList.getSelectionModel().getSelectedItems();
for (Edge m : sel) {
data.getEdges().remove(m);
}
canvas.getChildren().clear();
model.update(data.getEmployee(), data.getEdges());
canvas.getChildren().addAll(model.getData(), model.getLines());
}
}
});
rowMenu.getItems().addAll(removeItem);
// only display context menu for non-null items:
row.contextMenuProperty().bind(
Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu) null));
return row;
});
edgesList.setEditable(true);
sourceComboBox.getSelectionModel().selectFirst();
targetComboBox.getSelectionModel().selectFirst();
com.setItems(data.getEmployee());
Callback<ListView<Item>, ListCell<Item>> factory = lv -> new ListCell<Item>() {
@Override
protected void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? "" : item.getName());
}
};
com.setCellFactory(factory);
com.setButtonCell(factory.call(null));
firstColumnEdgesList.setCellValueFactory(new PropertyValueFactory<>("source"));
firstColumnEdgesList.setCellFactory(ComboBoxTableCell.forTableColumn(data.getTemp()));
firstColumnEdgesList.setOnEditCommit(t -> {
t.getTableView().getItems().get(
t.getTablePosition().getRow()).setSource(t.getNewValue());
canvas.getChildren().clear();
model.update(data.getEmployee(), data.getEdges());
canvas.getChildren().addAll(model.getData(), model.getLines());
});
secondColumnEdgesList.setCellValueFactory(new PropertyValueFactory<>("target"));
secondColumnEdgesList.setCellFactory(ComboBoxTableCell.forTableColumn(data.getTemp()));
secondColumnEdgesList.setOnEditCommit(t -> {
t.getTableView().getItems().get(
t.getTablePosition().getRow()).setTarget(t.getNewValue());
canvas.getChildren().clear();
model.update(data.getEmployee(), data.getEdges());
canvas.getChildren().addAll(model.getData(), model.getLines());
});
edgesList.setItems(data.getEdges());
menuFile.setId("barline");
menuEdit.setId("barline");
menuFile.getItems().addAll(newFile, fileSave, fileOpen, saveImage);
menuBar.getMenus().addAll(menuFile, menuEdit);
newFile.setId("bar");
lblNew.setPrefWidth(150);
lblNew.setWrapText(true);
newFile.setGraphic(lblNew);
newFile.setOnAction(e -> {
int option = 0;
Confirm confirm = new Confirm(new String[]{"Ano", "Ne"},
"Potvrzení", "Bez uložení dojde k nevratým změnám.\n Přejete si pokračovat?",
400, 120);
confirm.showAndWait();
option = confirm.getOption();
if (option == 1) {
canvas.getChildren().clear();
data.getEmployee().clear();
data.getEdges().clear();
Read empty = new Read(primaryStage);
}
});
fileSave.setId("bar");
lblSave.setPrefWidth(150);
lblSave.setWrapText(true);
fileSave.setGraphic(lblSave);
fileSave.setOnAction(e -> {
Read save = new Read(primaryStage, data.getEmployee(), data.getEdges());
save.writeToFile();
});
fileOpen.setId("bar");
lblOpen.setPrefWidth(150);
lblOpen.setWrapText(true);
fileOpen.setGraphic(lblOpen);
fileOpen.setOnAction(e -> {
int option = 0;
Confirm confirm = new Confirm(new String[]{"Ano", "Ne"},
"Potvrzení", "Bez uložení dojde k nevratým změnám.\n Přejete si pokračovat?",
400, 120);
confirm.showAndWait();
option = confirm.getOption();
if (option == 1) {
data.printData();
Read open = new Read(primaryStage);
open.readFromFile();
if (open.checkOpenedFile() != null) {
data.getEmployee().clear();
data.getEdges().clear();
data.getEmployee().addAll(open.getEmployees());
data.getEdges().addAll(open.getEdges());
data.getTemp().clear();
data.fillTemp();
if (data.getTemp().size() >= 1) {
for (int i = 0; i < data.getTemp().size(); i++) {
if (i == 0) {
sourceComboBox.getSelectionModel().select(i);
targetComboBox.getSelectionModel().select(i + 1);
}
}
}
canvas.getChildren().clear();
model.update(data.getEmployee(), data.getEdges());
canvas.getChildren().addAll(model.getData(), model.getLines());
}
}
});
saveImage.setId("bar");
lblOpen.setPrefWidth(150);
lblOpen.setWrapText(true);
saveImage.setGraphic(saveImagelabel);
saveImage.setOnAction(e -> {
FileChooser fileChooser = new FileChooser();
//Set extension filter
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("png files (*.png)", "*.png"));
//Prompt user to select a file
File file = fileChooser.showSaveDialog(null);
if(file != null){
try {
//Pad the capture area
WritableImage writableImage = new WritableImage((int)canvas.getWidth(), (int)canvas.getHeight());
canvas.snapshot(null, writableImage);
RenderedImage renderedImage = SwingFXUtils.fromFXImage(writableImage, null);
//Write the snapshot to the chosen file
ImageIO.write(renderedImage, "png", file);
} catch (IOException ex) { ex.printStackTrace(); }
}
});
central.getChildren().add(canvas);
right.getChildren().addAll(
employeeList,
addEmployeeBox
);
left.getChildren().add(menu);
root.setTop(menuBar);
root.setLeft(left);
root.setCenter(central);
root.setRight(right);
Scene scene = new Scene(root);
scene.getStylesheets().add(Main.class.getResource("style.css").toExternalForm());
primaryStage.setMinHeight(height + 44);
primaryStage.setMinWidth(width + (width * 2 / 4) + (width / 5.8));
primaryStage.setTitle("Aplikace");
primaryStage.setScene(scene);
primaryStage.show();
//metoda sloužící pro upozornění na zavření aplikace
//dále upozorňuje uživatele na uložení jeho souborů
primaryStage.setOnCloseRequest((WindowEvent we) -> {
int option = 0;
String[] buttons = {"Ano", "Ne"};
Confirm window = new Confirm(buttons, "Ukončení aplikace",
"Opravdu si přejete ukončit aplikaci?\n Nezapoměňte si soubory uložit.",
400, 120);
window.showAndWait();
option = window.getOption();
if (option == 1) {
System.exit(-1);
}
});
}
//hlavní vlákno aplikace
public static void main(String[] args) {
launch(args);
}
}
no tě bůh:)
projíždím to jen baj očko, ale snad se mi to jen zdá....:)
Ty máš fakt všechno narvané v jedné metodě?
Jako ty atributy máš i krásně okomentované, a i hezky pojmenované, to
je fajn;)
ale už když jsem jich projížděl, tak se mi zdálo, že některé věci by
mohly být v jiné třídě atd.
Takže ty máš problém v té struktuře.
Zrovna teď jsem to vysvětloval jedné kočce, kterou učím programovat. Když
používáš OOP (a Java je celá v OOP i když zrovna teď jsi dokázal
programovat struktorovaně:) ), tak můžeš využít tu největší výhodu,
díky které se programování stalo nejen prací, ale i příjemným
koníčkem:)
Dám ti to jako příklad nějaké stavebnice.
Vem si třeba Lečo (jmenuje se to lego, ale nemůžu jim dělat reklamu, tak
používám substituci:) ). Chceš z leča postavit krásný dům a v něm
zařízení.
Tak přece taky nezačneš tak, že nejdřív postavíš z leča základ jedné
místnosti, do toho uděláš lečo nábytek, ten rovnou umístíš a až to
bude sedět, tak uděláš z leča další místnosti a tak to budeš
navalovat.
Asi bys nejdřív udělal z leča zdi a prostory.
Pak si uděláš plochu leča, na kterou to všechno umístíš (a je jedno kam
- prostě někam)
Pak si uděláš z leča skříň, židle, stůl, postel, bazén, sprchu a kdo
ví co ještě.
A potom budeš dávat jednotlivé kousky k sobě. A musí ti pochopitelně
sedět. Když vyhradíš, že do jistého místa leča se vleze kostička leča,
která má 4 puntíky, tak nebudeš dělat postel, která má pět
puntíků.
No a to samé je programování.
Prostě si projekt rozvrhneš do co nejmenších logických oddílů.
Základní třída bude dávat tyto oddíly dohromady (klidně to může být ta
populární třída Main)
A ostatní třídy budou řešit ty dotyčné vlastnosti.
Ono se to nezdá, ale třeba i na tak triviální projekt, jako je ten tvůj,
bys mohl použít nějaký obecný balíček tools, do kterého si dáš
pomocné třídy (já třeba mám v této třídě:
Delay - který mi obecně řeší pauzu vlákna, abych furt nemusel vypisovat
celý try catch, ale zavolal si jen jeden řádek a vyjímku zachycuju v této
třídě.
SwingColors a FXColors - tady logicky řeším barvičky. Takže když třeba
potřebuju vygenerovat náhodnou barvu, tak mám na to už tool.
TFile - (jako tool file, protože třída File už existuje, tak aby se mi to
nepletlo). Tady řeším obecně načítání, vytváření a přepisování
souborů
TImage - práce s obrázky
Database - obecné connectiony, takže nemusím řešit nějaký statement,
nastavování connectiony atd.atd.
Regex - abych rychle pracoval s regulárními výrazy
Log - který jsem si sám nastyloval
Mathic - tady si využívám různé matematické operace (od nalezení
nejmenšího společného jmenovatele, až po pythagorovu větu)
Prostě toto jsou třeba pomocné třídy, které můžu v podstatě kdekoliv
využít.
Ty bys mohl mít třeba třídy: Frame (pro vykreslení FXka a jeho komponent),
Image (kde bys řešil ty obrázky, co tam máš), Employee (která by řešila
všechno ohledně zaměstnanců) atd. atd. (přiznám se, že to projíždím
jen tak, rychlo sem, rychlo tam. Takže to je fakt jen příklad.
Nicméně rvát všechno do jedné metody a hlavně do jedné třídy není
to nejideálnější:) to se pak nediv, že se ztratíš:)
Jsi na ITnetworku a já tě velmi rád, jako každého odkazuju na OOP sekci. Je
tu moc krásně udělaná a naučí tě to dívat se na programování i z
tohoto pohledu (OOP). Nejen sekat příkazy jeden po druhém.
Jediné, co tady všem vyčítám, je porušování zapouzdření:( to je moje
jediná výtka vůči těmto stránkám. Ale jinak jsou naprosto dokonalé a ty
tutoriály tě hezky procvičí;) Neboj se za tyto stránky platit v podobě
článků. Není to rozhodně drahé a věř, že se ti to vyplatí;)
Zobrazeno 10 zpráv z 10.