Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 16

Discussione: [JavaFX] Generalizzare classe

  1. #1

    [JavaFX] Generalizzare classe

    ciao!

    ho un controller che potrebbe rappresentare o una lista di editori, o una lista di autori.
    la struttura di autori / editori è la stessa; ad esempio:
    codice:
    public class Author {
    
        private String id;
        private String name;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return name;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Author)) {
                return false;
            }
            Author a = (Author) obj;
            return this.name.equals(a.toString());
        }
    
        @Override
        public int hashCode() {
            int hash = 3;
            hash = 59 * hash + (this.name != null ? this.name.hashCode() : 0);
            return hash;
        }
    }
    questa la classe che rappresenta il controller:
    codice:
    public class ControllerAuthorEditor {
    
        private Service service;
        private ReadJson jsonRead;
        private Stage stage;
    
        @FXML
        private BorderPane aePane;
    
        @FXML
        private ComboBox<Author> comboAE;
    
        @FXML
        public void initialize() {
            jsonRead = new ReadJson();
            service = new Service();
            TypeToken<List<Author>> authorToken = new TypeToken<List<Author>>() {
            };
            try {
                List<Author> listAuthors = jsonRead.readJson(new File(UrlAndPath.JSON_AUTORI), authorToken);
                ObservableList<Author> authors = FXCollections.observableArrayList(listAuthors);
                comboAE.setItems(authors);
                comboAE.setCellFactory(new ComboListCell<Author>());
                new KeyComboListener(comboAE);
            } catch (IOException e) {
                GenericDialog.showDialog(e.getMessage(), Alert.AlertType.ERROR);
            }
        }
    
        private Stage getStage() {
            stage = (Stage) aePane.getScene().getWindow();
            return stage;
        }
    
        @FXML
        private void escPressed(KeyEvent event) {
            if (event.getCode() == KeyCode.ESCAPE) {
                getStage();
                stage.close();
            }
        }
    }
    e questo il layout (che devo ancora terminare):
    codice:
    <BorderPane fx:id="aePane" onKeyPressed="#escPressed" xmlns="http://javafx.com/javafx/8.0.121"
                xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.mp.book.ControllerAuthorEditor">
        <center>
            <SplitPane fx:id="splitPane" dividerPositions="0.5" prefHeight="160.0" prefWidth="200.0"
                       BorderPane.alignment="CENTER">
                <items>
                    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
                        <children>
                            <TextField/>
                        </children>
                        <children>
                            <ComboBox fx:id="comboAE"></ComboBox>
                        </children>
                    </AnchorPane>
                    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0"/>
                </items>
            </SplitPane>
        </center>
    </BorderPane>
    vorrei generalizzare il controller, e fare in modo da riempire la ComboBox o con gli autori o con gli editori (le liste sono prese da due file json differenti).
    mi dareste un input per iniziare???

  2. #2
    allora, ho provato a fare così:
    codice:
    public class ControllerAuthorEditor<T> {
    
        private Service service;
        private ReadJson jsonRead;
        private Stage stage;
    
        @FXML
        private BorderPane aePane;
    
        @FXML
        private ComboBox<T> comboAE;
    
        @FXML
        public void initialize() {
            jsonRead = new ReadJson();
            service = new Service();
            TypeToken<List<T>> token = new TypeToken<List<T>>() {
            };
            try {
                List<T> list = jsonRead.readJson(new File(UrlAndPath.JSON_AUTORI), token);
                ObservableList<T> oList = FXCollections.observableArrayList(list);
                comboAE.setItems(oList);
                comboAE.setCellFactory(new ComboListCell<T>());
                //new KeyComboListener(comboAE);
            } catch (IOException e) {
                GenericDialog.showDialog(e.getMessage(), Alert.AlertType.ERROR);
            }
        }
    
        private Stage getStage() {
            stage = (Stage) aePane.getScene().getWindow();
            return stage;
        }
    
        @FXML
        private void escPressed(KeyEvent event) {
            if (event.getCode() == KeyCode.ESCAPE) {
                getStage();
                stage.close();
            }
        }
    }
    solo che gli items della ComboBox sono in formato JSON.
    come se il JSON non venisse parsato.

    questo il metodo che legge il file:
    codice:
        public <T> T readJson(File file, TypeToken<T> typeToken) throws IOException {
            try (JsonReader reader = new JsonReader(new FileReader(file))) {
                T t = gson.fromJson(reader, typeToken.getType());
                return t;
            }
        }

  3. #3
    Il punto è che

    new TypeToken<List<Author>>() {}

    ovvero una estensione con anonymous inner class è il modo per avere a RUNTIME la rappresentazione completa della parametrizzazione, ovvero in quel caso List<Author>

    Se fai

    new TypeToken<List<T>>() {}

    quella rappresentazione completa viene a mancare, ovvero Gson non può certo "capire" che vuoi una lista di Author


    Le soluzioni ci sono, sicuramente anche più di una. Una che mi viene in mente:

    - classe astratta

    codice:
    public abstract class ControllerAuthorEditor<T> {
        // .....
    
        protected abstract TypeToken<List<T>> getToken();    // lo usi da initialize()
    }

    - sottoclasse concreta es.

    codice:
    public class ControllerAuthor extends ControllerAuthorEditor<Author> {
        protected TypeToken<List<Author>> getToken() {
            return new TypeToken<List<Author>>() {};
        }
    }

    (idem per Editor)

    Ovvero, detto in generale, "specializzare" solo la costruzione del TypeToken.
    Andrea, www.andbin.net – Senior Java developer – SCJP 5 (91%) – SCWCD 5 (94%)

  4. #4
    ciao andbin!

    intanto grazie per la risposta.

    c'è una cosa che non ho ben capito però.
    questa sarebbe la sottoclasse concreta:
    codice:
    public class ControllerAuthor extends ControllerAuthorEditor<Author> {
    
        private Service service;
        private ReadJson jsonRead;
        private Stage stage;
    
        @FXML
        private BorderPane aePane;
    
        @FXML
        private ComboBox<Author> comboAE;
    
        @FXML
        public void initialize() {
            jsonRead = new ReadJson();
            service = new Service();
            TypeToken<List<Author>> token = getToken();
            try {
                List<Author> list = jsonRead.readJson(new File(UrlAndPath.JSON_AUTORI), token);
                ObservableList<Author> oList = FXCollections.observableArrayList(list);
                comboAE.setItems(oList);
                comboAE.setCellFactory(new ComboListCell<Author>());
                new KeyComboListener(comboAE);
            } catch (IOException e) {
                GenericDialog.showDialog(e.getMessage(), Alert.AlertType.ERROR);
            }
        }
    
        private Stage getStage() {
            stage = (Stage) aePane.getScene().getWindow();
            return stage;
        }
    
        @FXML
        private void escPressed(KeyEvent event) {
            if (event.getCode() == KeyCode.ESCAPE) {
                getStage();
                stage.close();
            }
        }
    
        @Override
        protected TypeToken<List<Author>> getToken() {
            return new TypeToken<List<Author>>() {
            };
        }
    }
    suppongo che debba spostare anche altri metodi nella superclasse, sennò non cambia nulla da come stavo prima, o sbaglio??
    ad esempio il metodo getStage(), o l'evento escPressed.
    sennò ne avrei una copia identica per sottoclasse editor.

  5. #5
    No, se devono fare le stesse identiche cose, allora tutto (campi, initialize(), ecc...) va messo nella superclasse (ControllerAuthorEditor).

    La sottoclasse come ti ho mostrato io es. ControllerAuthor serve principalmente per specializzare la costruzione del TypeToken, perché è una cosa che NON puoi fare in una classe "generica" come la ControllerAuthorEditor<T> (dove tutto sarebbe <T> / List<T> ).

    Poi se tu avessi altro da "specializzare" ... beh, ben venga, lo puoi fare in una delle sottoclassi specifiche.
    Andrea, www.andbin.net – Senior Java developer – SCJP 5 (91%) – SCWCD 5 (94%)

  6. #6
    ok, allora ho solo due dubbi prima di mettermi a lavoro:
    - la classe non deve essere più astratta a questo punto, giusto?
    - il secondo dubbio riguarda il file FXML di layout: cosa deve richiamare nel parametro fx:controller??

    codice:
    <BorderPane fx:id="aePane" onKeyPressed="#escPressed" xmlns="http://javafx.com/javafx/8.0.121"
                xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.mp.book.ControllerAuthor">
        <center>
            <SplitPane fx:id="splitPane" dividerPositions="0.5" prefHeight="160.0" prefWidth="200.0"
                       BorderPane.alignment="CENTER">
                <items>
                    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
                        <children>
                            <TextField/>
                        </children>
                        <children>
                            <ComboBox fx:id="comboAE"></ComboBox>
                        </children>
                    </AnchorPane>
                    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0"/>
                </items>
            </SplitPane>
        </center>
    </BorderPane>

  7. #7
    Quote Originariamente inviata da fermat Visualizza il messaggio
    - la classe non deve essere più astratta a questo punto, giusto?
    Se metti il getToken astratto come ho mostrato io

    protected abstract TypeToken<List<T>> getToken();

    allora la classe DEVE essere abstract. E' una regola ...

    E l'ho messo abstract non "a caso". Nella superclasse non c'è una sua implementazione che abbia senso. Quindi conviene "forzare" le sottoclassi ad implementarlo appropriatamente per dare il "giusto" TypeToken.


    Quote Originariamente inviata da fermat Visualizza il messaggio
    - il secondo dubbio riguarda il file FXML di layout: cosa deve richiamare nel parametro fx:controller??

    codice:
    <BorderPane fx:id="aePane" onKeyPressed="#escPressed" xmlns="http://javafx.com/javafx/8.0.121"
                xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.mp.book.ControllerAuthor">
    La sottoclasse concreta (comunque quella specifica), naturalmente.



    Poi ripeto, se avessi bisogno di altre specializzazioni, le puoi fare mettendo un apposito metodo di "aggancio" nella superclasse in modo che una sottoclasse debba/possa ridefinirlo.
    Ultima modifica di andbin; 27-12-2017 a 15:41
    Andrea, www.andbin.net – Senior Java developer – SCJP 5 (91%) – SCWCD 5 (94%)

  8. #8
    specializzazioni non saprei ancora, però ad esempio questi due metodi:
    codice:
        private Stage getStage() {
            stage = (Stage) aePane.getScene().getWindow();
            return stage;
        }
    
        @FXML
        private void escPressed(KeyEvent event) {
            if (event.getCode() == KeyCode.ESCAPE) {
                getStage();
                stage.close();
            }
        }
    farli astratti e da implementare nella sottoclasse non ha molto senso, farebbero la stessa cosa.
    non so se riesco a farvi capire il mio dubbio!

  9. #9
    Quote Originariamente inviata da fermat Visualizza il messaggio
    farli astratti e da implementare nella sottoclasse non ha molto senso, farebbero la stessa cosa.
    non so se riesco a farvi capire il mio dubbio!
    Se i due controller devono comportarsi allo stesso modo, allora ripeto tutto (campi, initialize() ecc..) va messo nella superclasse!



    Ti faccio invece un esempio di "specializzazione" oltre al getToken. Immagina che appena dopo aver letto la lista da JSON con il readJson vuoi fare delle operazioni particolari sulla lista, che so ... rimuovere oggetti che non ritieni validi, impostare/inizializzare altro negli oggetti Author/Editor, ecc...

    Nella superclasse farai:

    codice:
    public class ControllerAuthorEditor<T> {
        // ....
    
        public void initialize() {
            // ....
            List<T> list = jsonRead.readJson( ...... );
            list = postRead(list);
            // ....
        }
    
        // ....
    
        protected List<T> postRead(List<T> input) {
            return input;   // NOP, no operation
        }
    }

    Con il postRead ti sei "aperto" la possibilità di far fare ad una sottoclasse qualcosa di particolare sulla lista appena dopo la lettura.
    In ControllerAuthorEditor non fa nulla di particolare, restituisce la stessa lista e basta.

    In ControllerAuthor potresti fare es.

    codice:
    public class ControllerAuthor extends ControllerAuthorEditor<Author> {
        // .... getToken()
    
        @Override
        protected List<Author> postRead(List<Author> input) {
            Iterator<Author> iter = input.iterator();
    
            while (iter.hasNext()) {
                Author author = iter.next();
    
                if (author.getName() == null) {
                    iter.remove();   // rimuove un Author senza name
                }
            }
    
            return input;
        }
    }

    Ora ti è chiara la "flessibilità" di questa cosa?
    Andrea, www.andbin.net – Senior Java developer – SCJP 5 (91%) – SCWCD 5 (94%)

  10. #10
    si a livello teorico si.

    devo capire come applicarlo al mio caso.

    intanto grazie!!

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2018 vBulletin Solutions, Inc. All rights reserved.