Come progettare un'interfaccia per due oggetti simili ma distinti

1

Sto progettando un'API per un controllo JavaFX da inviare alla libreria ControlsFX. Questo controllo è chiamato ViewManager , e il suo scopo è quello di contenere una raccolta di View istanze, tutte alimentate dallo stesso insieme di dati (elementi da visualizzare, stato di selezione, impostazioni di visualizzazione, ecc ...)

Il mio tentativo originale era di avere View una classe astratta che definisca tutte le cose di cui ha bisogno - cose come i modelli di selezione e messa a fuoco, una collezione di elementi da visualizzare e alcune altre cose (alcune delle quali Non ho ancora concettualizzato).

Le viste possono esistere isolate da ViewManager , ma voglio progettare ViewManager per avere molte delle stesse funzioni di View in modo che tutte le viste nel gestore possano alimentare i dati del gestore e rimanere sincronizzati l'uno con l'altro. Il problema che sto incontrando, tuttavia, è che un'implementazione corretta di View (come GridView o ListView ) ha caratteristiche aggiuntive che non hanno senso per un ViewManager (come un riferimento alla sua ViewManager e la sua fabbrica di celle). Per questo motivo, non è corretto rendere ViewManager estendere View .

Un altro pensiero che ho avuto è stato quello di astrarre View in un'interfaccia e poi cambiare la mia classe in AbstractView . Questo potrebbe aver alleviato il problema precedente, ma poi ho scoperto che avevo bisogno di questa interfaccia per definire le firme per le cose che sono specifiche dell'implementazione - cose come itemsProperty() usate dalla mia classe del modello di selezione per collegare un listener.

L'unica soluzione che posso ricavare è che ViewManager sovrascriva le funzioni che non usa, e queste lanciano qualcosa come UnsupportedOperationException . C'è anche il fatto che un ViewManager non deve contenere un altro ViewManager , quindi dovrei anche verificare quel caso speciale quando aggiungo una vista al gestore e rispondo di conseguenza.

Ciò che mi sembrava un'idea abbastanza semplice ora si sta trasformando in un pasticcio di cattivo design. C'è un modo migliore per aggirare questo?

Per riferimento, ecco le mie implementazioni che ho finora:

Visualizza

/**
 * Abstract base class for all Views that will be usable by the {@link ViewManager}
 *
 * @param <T> The base type of the object that this view will display information about
 * @param <C> The base type of the {@link IndexedCell} that this view uses to display individual records
 */
public abstract class View<T, C extends AbstractViewCell<T>> extends ControlsFXControl {

    /***************************************************************************
     *
     * Properties
     *
     **************************************************************************/

    // --- Cell Factory
    private ObjectProperty<Callback<View<T, C>, C>> cellFactory = new SimpleObjectProperty<>(this, "cellFactory"); //$NON-NLS-1$
    public ObjectProperty<Callback<View<T, C>, C>> cellFactoryProperty() { return cellFactory; }
    public Callback<View<T, C>, C> getCellFactory() { return cellFactory.get(); }
    public void setCellFactory(Callback<View<T, C>, C> cellFactory) { this.cellFactory.set(cellFactory); }

    // --- Checkbox
    private final BooleanProperty showCheckbox = new SimpleBooleanProperty(this, "showCheckbox", Boolean.FALSE); //$NON-NLS-1$
    public BooleanProperty showCheckboxProperty() { return showCheckbox; }
    public boolean getShowCheckbox() { return showCheckbox.get(); }
    public void setShowCheckbox(boolean showCheckbox) { this.showCheckbox.set(showCheckbox); }

    // --- Items
    private ListProperty<T> items = new SimpleListProperty<>(this, "items", FXCollections.observableArrayList()); //$NON-NLS-1$
    public ListProperty<T> itemsProperty() { return items; }
    public ObservableList<T> getItems() { return items.get(); }
    public void setItems(ObservableList<T> items) { this.items.set(items); }

    // --- Focus Model
    private ObjectProperty<FocusModel<T>> focusModel = new SimpleObjectProperty<>(this, "focusModel"); //$NON-NLS-1$
    public ObjectProperty<FocusModel<T>> focusModelProperty() { return focusModel; }
    public FocusModel<T> getFocusModel() { return focusModel.get(); }
    public void setFocusModel(FocusModel<T> focusModel) { this.focusModel.set(focusModel); }

    // --- Selection Model
    private ObjectProperty<MultipleSelectionModel<T>> selectionModel = new SimpleObjectProperty<>(this, "selectionModel"); //$NON-NLS-1$
    public ObjectProperty<MultipleSelectionModel<T>> selectionModelProperty() { return selectionModel; }
    public MultipleSelectionModel<T> getSelectionModel() { return selectionModel.get(); }
    public void setSelectionModel(MultipleSelectionModel<T> selectionModel) { this.selectionModel.set(selectionModel); }

    // --- View Manager
    private ObjectProperty<ViewManager<T>> viewManager = new SimpleObjectProperty<>(this, "viewManager");
    public ObjectProperty<ViewManager<T>> viewManagerProperty() { return viewManager; }
    public ViewManager<T> getViewManager() { return viewManager.get(); }
    public void setViewManager(ViewManager<T> viewManager) { this.viewManager.set(viewManager); }



    /***************************************************************************
     *
     * Listeners
     *
     **************************************************************************/

    /**
     * Here we make sure that if, for whatever reason, the ViewManager changes, we un-bind and re-bind
     * the appropriate properties.
     */
    ChangeListener<ViewManager<T>> VIEW_MANAGER_PROPERTY_LISTENER = (o, oldManager, currentManager) -> {
        if (oldManager != null) {
            focusModel.unbindBidirectional(oldManager.focusModelProperty());
            selectionModel.unbindBidirectional(oldManager.selectionModelProperty());
            items.unbindBidirectional(oldManager.itemsProperty());
            showCheckbox.unbindBidirectional(oldManager.showCheckboxProperty());
        }

        if (currentManager != null) {
            focusModel.bindBidirectional(currentManager.focusModelProperty());
            selectionModel.bindBidirectional(currentManager.selectionModelProperty());
            items.bindBidirectional(currentManager.itemsProperty());
            showCheckbox.bindBidirectional(currentManager.showCheckboxProperty());
        }
    };

    WeakChangeListener<ViewManager<T>> WEAK_VIEW_MANAGER_PROPERTY_LISTENER = new WeakChangeListener<>(VIEW_MANAGER_PROPERTY_LISTENER);



    /***************************************************************************
     *
     * Constructors
     *
     **************************************************************************/

    public View() {
        viewManager.addListener(WEAK_VIEW_MANAGER_PROPERTY_LISTENER);
    }
}

ViewManager

public class ViewManager<T> extends ControlsFXControl {

    /***************************************************************************
     *
     * Listeners
     *
     **************************************************************************/



    /***************************************************************************
     *
     * Properties
     *
     **************************************************************************/

    // --- Checkbox
    private final BooleanProperty showCheckbox = new SimpleBooleanProperty(this, "showCheckbox", Boolean.FALSE); //$NON-NLS-1$
    public BooleanProperty showCheckboxProperty() { return showCheckbox; }
    public boolean getShowCheckbox() { return showCheckbox.get(); }
    public void setShowCheckbox(boolean showCheckbox) { this.showCheckbox.set(showCheckbox); }

    // --- Items
    private final ListProperty<T> items = new SimpleListProperty<>(this, "items", FXCollections.observableArrayList()); //$NON-NLS-1$
    public ListProperty<T> itemsProperty() { return items; }
    public ObservableList<T> getItems() { return items.get(); }

    // --- Focus Model
    private ObjectProperty<FocusModel<T>> focusModel = new SimpleObjectProperty<>(this, "focusModel"); //$NON-NLS-1$
    public ObjectProperty<FocusModel<T>> focusModelProperty() { return focusModel; }
    public FocusModel<T> getFocusModel() { return focusModel.get(); }
    public void setFocusModel(FocusModel<T> focusModel) { this.focusModel.set(focusModel); }


    // --- Selection Model
    private ObjectProperty<MultipleSelectionModel<T>> selectionModel = new SimpleObjectProperty<>(this, "selectionModel"); //$NON-NLS-1$
    public ObjectProperty<MultipleSelectionModel<T>> selectionModelProperty() { return selectionModel; }
    public MultipleSelectionModel<T> getSelectionModel() { return selectionModel.get(); }
    public void setSelectionModel(MultipleSelectionModel<T> selectionModel) { this.selectionModel.set(selectionModel); }

    // -- Views
    private final ListProperty<View<T, ? extends IndexedCell<T>>> views
            = new SimpleListProperty<View<T, ? extends IndexedCell<T>>>(FXCollections.observableArrayList()) {

        @Override
        protected void invalidated() {
            get().forEach(view -> view.setViewManager(ViewManager.this));
        }

        @Override
        public Object getBean() {
            return ViewManager.this;
        }

        @Override
        public String getName() {
            return "views";
        }
    };
    public ListProperty<View<T, ? extends IndexedCell<T>>> viewsProperty() { return views; }
    public ObservableList<View<T, ? extends IndexedCell<T>>> getViews() { return views.get(); }
    public void setViews(ObservableList<View<T, ? extends IndexedCell<T>>> views) { this.views.set(views); }



    /***************************************************************************
     *
     * Private Implementation
     *
     **************************************************************************/

    private void updateAllSelectionModels(ListChangeListener.Change<? extends Integer> change) {
        while (change.next()) {
            if (change.wasAdded()) {
                for (Integer added : change.getAddedSubList()) {
                    views.forEach(view -> view.getSelectionModel().select(added));
                }
            }

            if (change.wasRemoved()) {
                for (Integer removed : change.getRemoved()) {
                    views.forEach(view -> view.getSelectionModel().clearSelection(removed));
                }
            }
        }
    }
}

Ci sono molti altri pezzi coinvolti, ma non sono rilevanti per il problema discusso qui.

    
posta agent154 25.07.2015 - 20:49
fonte

0 risposte

Leggi altre domande sui tag