Gli implementamenti si estendono, ma per quanto riguarda le variabili?

1

È preferibile scrivere programmi che dipendono dalle interfacce piuttosto che dalle superclassi, ma cosa succede se si desidera che una classe abbia determinate variabili? A volte vuoi che una classe implementa una certa variabile. (Ad esempio, immagina di avere un'interfaccia dell'origine dati e desideri che tutte le classi implementino un "utente String").

In questo caso, posso vedere la creazione di un'origine dati di classe astratta, che le altre classi potrebbero quindi estendere. Ma questo significa che non possono più estendere un'altra classe, a causa dell'ereditarietà multipla in Java.

Quindi, per concludere, se avessi lo scenario di cui sopra, dovrei ripensare il mio design, o c'è un pattern che non conosco a riguardo per poter realizzare il design con interfaccia con variabili?

    
posta Dylan Meeus 22.10.2014 - 21:54
fonte

4 risposte

7

Se è necessario condividere l'implementazione, utilizzare l'ereditarietà. Ecco a cosa serve Ma usa anche un'interfaccia e considera il fatto che l'ereditarietà viene utilizzata dalle implementazioni come un dettaglio di implementazione: il codice client non dovrebbe essere necessario sapere.

Ad esempio, vedi le classi della libreria List , AbstractList , ArrayList e LinkedList . Esistono dettagli di implementazione comuni delle due classi concrete condivise da entrambi che ereditano da AbstractList . Ma come client di loro, questo è un dettaglio non necessario che non usiamo mai ... ci interessa solo che entrambi implementino l'interfaccia, List .

    
risposta data 23.10.2014 - 07:09
fonte
6

La premessa di questa domanda è un po 'confusa. Non è possibile "implementare una variabile". Esiste o no. Inoltre, "volere" una classe per contenere una variabile significa:

  • Sei il proprietario del codice, quindi inserisci la variabile in ciascuna classe di implementazione (tramite la classe di base se lo desideri).
  • Non possiedi il codice (future implementazioni di questa interfaccia). Non puoi dettare la loro implementazione, solo le specifiche, quindi non preoccuparti di cosa potrebbero assegnare la loro variabile user .

Oltre a ciò, le tue opzioni sono limitate dalla lingua. In C #, la tua interfaccia potrebbe contenere una proprietà public String User { get; set; } . In Java, si è limitati a utilizzare una classe base, o possibilmente considerando i metodi di default di Java 8 nelle interfacce.

    
risposta data 23.10.2014 - 08:43
fonte
2

Puoi aggiungere quanto segue all'interfaccia:

public void setUser(String s);
public String getUser();

In questo modo gli implementatori sono forzati ad avere una variabile username nel loro stato. Sebbene gli implementatori possano nominarlo come preferiscono.

In questo modo continui a simulare l'ereditarietà multipla attraverso le interfacce.

Qualsiasi classe che ne estende un'altra che implementa l'interfaccia può accedere alla variabile con super.getUser(); oppure puoi anche proteggere la variabile di stato, ma questo è un dettaglio di implementazione.

    
risposta data 07.01.2016 - 14:40
fonte
0

Il fatto stesso di chiedere se hai bisogno di ripensare il tuo design indica che hai bisogno di ripensarlo.

Il risultato del ripensamento dovrebbe essere quello di (1) ridisegnare il codice in modo che segua la pratica generale per gli oggetti a Esporre comportamento, non dati o (2) identificarlo come caso eccezionale in cui la deviazione della norma generale è chiaramente giustificata (ad esempio Dati Trasferisci oggetto ).

Modo tecnico per rendere l'utilizzo del campo imposto dall'interfaccia è spiegato in questa risposta alla domanda precedente :

...Here you find a detailed example how to replace inheritance by delegation. This approach is so common, there is even an Eclipse refactoring exactly for this purpose

Applicato al tuo caso, l'approccio sopra potrebbe apparire come segue.

Per prima cosa, crei una classe (classe semplice, niente di speciale) con i campi desiderati:

public class Data {
    private String user;
    public void setUser(String user) { this.user = user; }
    public String getUser() { return user; }
}

Successivamente, definisci l'interfaccia che consente di delegare alla classe precedente:

public interface DataSource {
    public Data delegate();
    //... other methods here
}

Di conseguenza, qualsiasi oggetto che implementa l'interfaccia DataSource sarà anche costretto a fornire la funzionalità Data per lavorare con user String.

Vale la pena ricordare ancora una volta che è meglio farlo solo se questo è chiaramente giustificato - oltre al già citato problema di esporre i dati anziché il comportamento, questo va anche contro Law Of Demetra.

    
risposta data 23.10.2014 - 16:40
fonte