Che cosa fare, se ho bisogno di una classe generica di uso generico all'interno, ma non voglio passare argomenti tipo

2

Supponiamo di avere qualcosa di simile:

class Page{
    Header header;
    Body body;
    Footer<TFooterModel> footer;  //TFooterModel is a type of content (subcontrol in some sense) that loaded inside footer.

    public Page(Header h, Body b, Footer<TFooterModel> f){...} //Let's assume that we setting part in constructor or using Builder pattern. 

}

Quindi, Footer è generico. Tuttavia, sarà logicamente sbagliato, per rendere Page anche generico e usare Page<TFooterModel> , solo perché in realtà non utilizzo TFooterModel da nessuna parte all'interno, tranne che specificando il tipo Footer's . Anche la conversione di Footer in catena ereditaria sembra errata, poiché è solo un contenitore per alcuni contenuti e non ha senso nell'ereditarietà, poiché i bambini saranno identici. Questo mi sembra un problema di progettazione, ma non sono sicuro di come risolverlo.

Quindi, qual è il modo corretto di gestire questi scenari, che usano davvero grandi sviluppatori?

Il piè di pagina è solo un contenitore, che carica alcuni contenuti utilizzando loadContent(BaseContent content) , ma ha un evento. onContentLoaded(OnContentLoadedHandler<ContentImpl> handler) , quindi una sola differenza tra i bambini sarà un tipo di OnContentLoadedHandler utilizzato, ecco perché penso che l'ereditarietà sia qualcosa di molto.

C'erano alcune opzioni proposte.

  1. Crea Footer non generico, eredita il piè di pagina generico da non generico e utilizzare non generici all'interno della pagina
  2. Passa a OnContentLoadedHandler in Footer's costruttore ed evita generico (comunque la mia opinione personale, potrebbe avere qualche insidia, dovuta alla risoluzione del tipo quando si chiama handler (se è lambda per esempio, o se è dynamic , ma non ne sono completamente sicuro))
  3. Aggiungi interfaccia e usalo invece (probabilmente logicamente simile a 1).
posta Ph0en1x 08.08.2016 - 22:06
fonte

2 risposte

2
class Page {
    Header header;
    Body body;
    Footer<TFooterModel> footer;
}

Se TFooterModel è un tipo concreto (classe o interfaccia nella tua base di codice, e tutti i suoi parametri istanziati per tipi concreti) questo va bene; non è necessario definire una classe separata che ne erediti.

Se TFooterModel è invece una tipo variabile (non un tipo concreto), allora questo codice è mal tipato; Page avrebbe bisogno di avere anche quella variabile di tipo come parametro ( Page<TFooterModel> ). Non c'è modo di aggirare questo, perché tutti gli oggetti istanziati reali di un tipo generico devono essere istanziati con una scelta di tipo concreto per tutti i loro parametri di tipo.

Quindi qualcosa deve istanziare il parametro di tipo Footer su un tipo concreto. Se Page non sta per "passare" un parametro di tipo per questo, allora potrebbe anche essere Page stesso che istanzia il parametro Footer . In questo modo:

  • Se TFooterModel è un tipo concreto, l'esempio di codice è già la soluzione migliore.
  • Se TFooterModel è una variabile di tipo, allora tutto ciò che Page deve fare è cambiarlo in un tipo concreto.

L'altra idea che è stata presentata è che, invece di Page che istanzia il parametro Footer in un tipo concreto, ha una terza classe che si estende da Footer solo per fare questa istanza. Questo è semplicemente inutile, e vanifica lo scopo di usare i generici in primo luogo - che possiamo riutilizzare Footer con varie istanze dei suoi parametri senza dover scrivere nuove classi o eseguire cast di runtime non sicuri. L'unica ragione per sottoclasse da Footer sarebbe quella di fornire alcune funzionalità aggiuntive che non sono già presenti genericamente nella superclasse.

    
risposta data 09.08.2016 - 00:10
fonte
1

Crea una classe denominata Footer (non generica). Cambia il Page.footer in questo tipo:

class Page {
  Header header;
  Body body;
  Footer footer; // note we changed the type of this field
}

Ricava un'altra classe denominata Footer<TFooterModel> dalla tua classe base Footer :

public class Footer<TFooterModel> : Footer {
  ...
}

Crea un'istanza del tuo piè di pagina come fai normalmente e assegnalo a Page.footer .

Per chiarire la confusione su questa risposta:

  1. Perché non rendere Page generico (cioè Page<TFooterModel> )? In breve: perché l'OP ha richiesto una soluzione che non richiedesse un argomento generico sulla classe Page .
  2. Perché questa risposta è migliore dell'opzione 3 nell'elenco OP? Non è necessariamente migliore. È solo una mia preferenza usare una classe qui poiché body e header sono classi e sono fondamentalmente la stessa cosa di footer (contenitori per dati di pagina).
  3. Perché questa risposta è migliore dell'opzione 2 nell'elenco OP? Non è necessariamente migliore. L'opzione 2 probabilmente funzionerebbe, ma sembrava meno semplice di questa opzione (dopotutto, qualcosa deve ospitare i richiami generici in cui passeresti, quindi probabilmente finirai con un Something<TFooterModel> .
risposta data 08.08.2016 - 22:10
fonte

Leggi altre domande sui tag