I "troppi metodi" sono un valido motivo per violare la regola "composizione sull'ereditarietà"?

7

Di solito preferisco seguire la regola "composition over inheritance", fino a quando non comincio ad entrare nel mondo della GUI Java.

Nella mia esperienza, per aggiungere requisiti personalizzati in un componente della GUI, ad esempio, aggiungere una funzione personalizzata in un JButton, di solito creare una classe che si estende da JButton in questo modo:

public class CustomButton extends JButton{
    public void customMethod(){
    }
}

Penso che stia ovviamente violando la regola "composizione sull'ereditarietà", quindi cerco di ottenerla per composizione invece che per ereditarietà, ma il problema si verifica: JButton ha muri di metodi, se uso la composizione, sono necessari muri di mappatura dei metodi in la mia classe CustomButton:

public class CustomButton{
    protected JButton button;
    public void customMethod(){
    }

    public void add(PopupMenu popup){
        button.add(popup);
    }

    public void addActionListener(ActionListener l){
        button.addActionListener(l);
    }

    .
    .
    .
}

che potrebbe richiedere un orribile elenco di metodi da associare a JButton.

C'è anche un altro problema: non so come gestire i metodi built-in che richiedono JButton come parametri se il mio CustomButton non è la sottoclasse di JButton.

Quindi la mia domanda è, se la classe originale ha troppi metodi, è ragionevole usare l'ereditarietà invece della composizione?

    
posta ggrr 22.07.2016 - 04:38
fonte

5 risposte

6

Quale composizione sull'ereditarietà deve davvero dire qui è che sarebbe stato bello se JButton fosse stato progettato con il polimorfismo della composizione in modo che il tuo metodo CutomButton avrebbe potuto essere passato in esso. Non lo era.

Se il cliente ha bisogno di accedere a tutto ciò che JButton fornisce (dubbioso), ora devi creare un mucchio di metodi di delega di tipo boilerplate se vuoi fare la composizione al contrario. Ci sono dei refactoring automatici che possono fare un lavoro stupido come quello per te, ma è ancora una schifezza per la manutenzione. Chi vuole guardarlo? Bleh.

Quindi fai ereditarietà. Che cosa fa male? Beh, il il tuo problema non è divertente. Si evita ciò non creando più livelli di ereditarietà. Il problema ora è che pensi "ok, solo uno non farà male". E anche il prossimo ragazzo. E il prossimo.

Puoi fermarlo con una final sulla classe, ma è un tipo F.U. per il prossimo. Potresti fare per il prossimo ragazzo ciò che desideri che il JButton ragazzo abbia fatto per te. Lascia che ti passino qualcosa che implementa un CustomInterface con un customMethod() che chiamerai. Il nome di fantasia per questo è modello di adattatore dell'oggetto .

Ora stai facendo sia l'eredità che la composizione, ma almeno la follia può finire.

    
risposta data 22.07.2016 - 06:17
fonte
16

Se hai veramente bisogno di un componente generale come JButton , allora dovresti semplicemente usare l'ereditarietà. "Favorire la composizione sull'ereditarietà" non significa "non usare mai l'ereditarietà". Tuttavia, le persone hanno fatto la frase della parola d'ordine perché il 95% delle volte in cui i principianti pensano di aver bisogno di ereditarietà, non lo fanno.

Il 95% delle volte, in realtà non è necessario un componente generale come JButton . Hai bisogno di qualcosa di molto più specifico. Ad esempio, ecco l'intera interfaccia pubblica per un pulsante personalizzato su un'app calcolatrice che ho scritto in Java qualche tempo fa:

public interface Button
{
    public void setOperation(Operation operation);
    public Operation getOperation();
    public void setEnabled(boolean enabled);
}

Tutta la configurazione degli oggetti composti è stata eseguita nei costruttori, inclusi i gestori di eventi che chiamano la funzione primaria dell'operazione attualmente impostata quando si fa clic. Guardando questa interfaccia, non avresti idea che sia stata implementata da una GUI Swing, che è una buona cosa, perché voglio riutilizzare la maggior parte del mio codice base per fare una versione Android. La mia interfaccia è semantica. È accoppiato alle esigenze della mia applicazione, non alle esigenze di una libreria GUI casuale.

Impara a scrivere componenti GUI personalizzati con interfacce che assomigliano più alla mia anziché alle tue versioni di copia quasi esatte e inizierai a vedere anche i vantaggi della composizione sull'eredità.

    
risposta data 22.07.2016 - 06:35
fonte
2

La premessa della domanda sembra essere sbagliata: utilizzando la composizione, crei qualcosa che ha un pulsante, ma non è un pulsante stesso. La composizione non è una sostituzione diretta dell'ereditarietà.

JButton è una vista. La tua classe è un'altra vista che ha il pulsante come parte di esso o è un controller, osservando il pulsante. Ad ogni modo: non è un pulsante. E nessuno si aspetterebbe di poter richiamare i metodi dei pulsanti su di esso.

La delega di metodi a una proprietà è necessaria solo se devi adempiere a un contratto, ad esempio nel modello di decoratore. Se è necessario accedere al pulsante, esporlo tramite un metodo getter, non tramite la delega parziale dei metodi.

    
risposta data 23.07.2016 - 14:15
fonte
0

Potresti implementare metodi delegati che userete più spesso, per tutti gli altri assicurati che ci sia un modo per un cliente di ottenere JButton :

public class CustomButton {
    protected JButton button;

    public void customMethod() {
    }

    public void add(PopupMenu popup) {
        button.add(popup);
    }

    public void addActionListener(ActionListener l) {
        button.addActionListener(l);
    }

    public JButton getButton() {
        return button;
    }
}

Quindi, ad esempio per disattivare il pulsante:

customButton.getButton().setEnabled(false);
    
risposta data 22.07.2016 - 13:56
fonte
0

Nel tuo caso non stai sovrascrivendo nessuno dei metodi JButton che stai tentando di impostare alcune proprietà su JButton utilizzando il tuo metodo personalizzato, quindi non è necessario utilizzare l'ereditarietà. Puoi usare Builder Design Patter.
Ad esempio

public class CustomButtonBuilder {

      private JButton button;

      public CustomButtonBuilder(JButton button) {
             this.button = button;
      }

      // the build method is your same customMethod()
      public void build() {
      // here goes the custom setting for your button.
      }
}

Nell'esempio precedente viene creato JButton e il listener richiesto ecc. viene impostato utilizzando i metodi setter. Quindi viene passato a CustomButtonBuilder dove la tua funzionalità personalizzata verrà aggiunta all'oggetto JButton .

In questo modo puoi scegliere Composition over Eredità senza scrivere così tanti metodi.

    
risposta data 22.07.2016 - 15:09
fonte

Leggi altre domande sui tag