Java: il metodo accetta vararg di tipi non correlati

2

Ho un metodo factory Java con una matrice varargs di Object s alla fine. La matrice può contenere qualsiasi combinazione di String s e ScaledJpeg s. La teoria è che una cella di tabella HTML può contenere un numero qualsiasi di nodi di testo o <image> nodi e verrà allineata a linee come se fossero solo parole a forma di divertente.

// constructor
public Cell(TextStyle ts, float w, CellStyle cs, Object... r) {
    if (w < 0) {
        throw new IllegalArgumentException("A cell cannot have a negative width");
    }
    if (r != null) {
        for (Object o : r) {
            if ( (o != null) &&
                 !(o instanceof String) &&
                 !(o instanceof ScaledJpeg) ) {
                throw new IllegalArgumentException(INVALID_ROW_TYPE_STR);
            }
        }
    }
    textStyle = ts; width = w; cellStyle = cs; rows = r;
    avgCharsForWidth = (int) ((width * 1220) / textStyle.avgCharWidth());
}

In Scala, potrei usare un elenco di Either[String,ScaledJpeg] . Ma un giorno probabilmente farò in modo che questo metodo consenta qualcos'altro, diciamo un altro% co_de annidato, ma così facendo si romperebbe il codice esistente che si aspettava solo due possibili tipi. Almeno in questo modo, le modifiche future non infrangeranno il codice esistente.

La soluzione corrente funziona, consente l'espansione ed è relativamente facile da usare, tranne che sconfigge la sicurezza del tipo e può solo generare un'eccezione in fase di esecuzione se qualcuno passa qualcosa di imprevisto come vararg. Ad esempio, ho appena dimenticato di aggiungere Cell a toString() e si è verificato durante il runtime. Quindi già questa è una terza classe da considerare (Poiché StringBuilder è definito su ogni oggetto, non voglio chiamarlo su ciò che viene passato perché farlo per la maggior parte dei tipi di oggetti sarebbe un errore molto più in basso nel codice che viola il comportamento a prova di errore di questo esempio).

toString() non può implementare nuove interfacce e probabilmente è una buona cosa. Anche se potessi fare una classe wrapper, tutto ciò che riguarda il modo in cui il testo e le immagini sono trattati da questa classe è completamente diverso tranne che sono allineati in linea come sarebbero in HTML (o in una Word doc).

Mentre scrivo questo, mi viene in mente che dovrei probabilmente usare un pattern Builder, creare una classe java.lang.String che ha metodi CellBuilder e addText() che eliminano il costruttore varargs. Ciò incapsulerebbe l'Elenco sottostante in modo che l'utente dell'API ottenga la sicurezza del tipo al 100% in fase di compilazione. Ma per curiosità, lo sto ancora postando nel caso ci siano altre soluzioni creative e forse migliori là fuori.

    
posta GlenPeterson 11.08.2014 - 17:01
fonte

2 risposte

2

Quello che vuoi è un'interfaccia Row con due implementazioni: una per le immagini e una per le stringhe. Quindi il tuo costruttore Cell prende una Row... r . Ciò consente al compilatore di verificare i tuoi tipi e ti impedisce di avere if image... else if string... in tutto il luogo. Aggiungere un nuovo tipo è semplice, basta creare un'altra implementazione. Se hai bisogno di un nuovo metodo nell'interfaccia, il tuo compilatore garantisce che tutte le implementazioni ce l'hanno.

    
risposta data 11.08.2014 - 17:55
fonte
2

Poiché assegni rows = r e r: Array[Object] , utilizzerei questo approccio:

sealed trait HasRow {
  def row: Object
}

class StringRow(s: String) extends HasRow {
  override def row = s // or some logic
}

class JpegRow(j: ScaledJpeg) extends HasRow {
  override def row = j // or some logic
}

class Cell(ts: TextStyle, w: Float, cs: CellStyle, r: HasRow*) {
 // ...
}

Come accennato, un approccio basato su builder è anche possibile e potrebbe essere migliore.

    
risposta data 11.08.2014 - 17:16
fonte

Leggi altre domande sui tag