2013-10-11 12 views
7

Questa è una domanda su qualcosa che non sono sicuro su come risolvere in Java. Voglio fare affermazioni triple basate su tre tipi di dati, URI, String o Letterali, ogni tipo è codificato in modo diverso. Ho scritto metodi di codifica che accettano questi tipi.Accettare diversi tipi di argomenti in Java

public static String makeStatement(URI subject, URI predicate, String object) { 
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n"; 
} 

public static String makeStatement(String subject, URI predicate, String object) { 
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n"; 
} 

public static String makeStatement(URI subject, URI predicate, Literal object) { 
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n"; 
} 

private static String encode(String binding) { 
    return "?" + binding; 
} 

private static String encode(URI uri) { 
    return "<" + uri.stringValue() + ">"; 
} 

private static String encode(Literal literal) { 
    return "\"" + literal.stringValue() + "\"" + literal.getDatatype(); 
} 

Ma, come posso accettare qualsiasi combinazione di questi tipi di ciò richiederebbe 9 funzioni makeStatement, che sono fondamentalmente facendo la stessa cosa e che sembra una cattiva idea, soprattutto perché potrebbe essere possibile voglio aggiungere un altro digita più tardi.

Normalmente risponderei a una domanda del genere con il suggerimento di creare una super classe, ma non posso modificare String, URI e Literal. Un'altra opzione sarebbe quella di definire

public static String makeStatement(Object subject, Object predicate, Object object) { 
    String encodedSubject = "", encodedPredicate = "", encodedObject = ""; 
    if (subject.getClass().equals(URI.class)) { 
     encodedSubject = encode((URI) subject); 
} 
    return " " + encode(encodedSubject) + " " + encode(encodedPredicate) + " " + encode(encodedObject) + ".\n"; 
} 

e quindi controllare le classi per ogni argomento, ma questo non considerare molto elegante. Un altro suggerimento sarebbe quello di definire qualcosa come makeStatement (URI subjectURI, String subjectString, Literal subjectLiteral, URI predicateURI .. etc) e quindi controllare quali argomenti sono nulli e passare da lì, ma ciò significherebbe digitare un sacco di null quando chiamo la funzione. Una terza opzione è https://stackoverflow.com/a/12436592/1014666, ma anche in questo caso è necessaria una digitazione aggiuntiva quando si chiama la funzione makeStatement.

Qualche suggerimento?

+0

Un collega ha affrontato un problema simile qualche tempo fa e ha scritto uno script Python che scrive tutte le 9 combinazioni come testo nel file .java – mbatchkarov

+0

Bello, ma non molto elegante :) – Rhand

+0

scrivere un metodo generico per accettare tutto come un oggetto e dentro che fanno l'istanza di controllo ed eseguono l'operazione di cui hai bisogno. Potrebbe darti un'altra prospettiva. – Arung

risposta

3

È possibile utilizzare un builder:

public class StatementMaker { 
    private static String encode(String binding) { 
     return "?" + binding; 
    } 

    private static String encode(URI uri) { 
     return "<" + uri.stringValue() + ">"; 
    } 

    private static String encode(Literal literal) { 
     return "\"" + literal.stringValue() + "\"" + literal.getDatatype(); 
    } 

    public static Statement from(String b) { 
     return new Statement(encode(b)); 
    } 

    public static Statement from(URI b) { 
     return new Statement(encode(b)); 
    } 

    public static Statement from(Literal b) { 
     return new Statement(encode(b)); 
    } 

    public static class Statement { 

     private StringBuilder buf; 
     private Statement(String s) { 
      buf = new StringBuilder(" "); 
      buf.append(s); 
     } 

     public Statement with(String s) { 
      buf.append(" ").append(encode(b)); 
      return this; 
     } 

     public Statement with(URI s) { 
      buf.append(" ").append(encode(b)); 
      return this; 
     } 

     public Statement with(Literal s) { 
      buf.append(" ").append(encode(b)); 
      return this; 
     } 

     public String toString() { 
      return buf.toString() + ".\n"; 
     } 

    } 
} 

È ora possibile costruire affermazione come ad esempio:

StatementMaker.from(subject).with(predicate).with(object).toString()

Nel codice che deve il bilancio è possibile abbreviare il codice ulteriormente con l'importazione statica:

import static my.package.StatementMaker.from;

Poi la dichiarazione è ridotto a:

from(subject).with(predicate).with(object).toString()

È possibile aggiungere altri 3 metodi alla classe interna:

public static class Statement { 

    private StringBuilder buf; 
    private Statement(String s) { 
     buf = new StringBuilder(" "); 
     buf.append(s); 
    } 

    public Statement with(String s) { 
     buf.append(" ").append(encode(b)); 
     return this; 
    } 

    public Statement with(URI s) { 
     buf.append(" ").append(encode(b)); 
     return this; 
    } 

    public Statement with(Literal s) { 
     buf.append(" ").append(encode(b)); 
     return this; 
    } 

    public String and(String s) { 
     buf.append(" ").append(encode(b)); 
     return buf.toString() + ".\n"; 
    } 

    public String and(URI s) { 
     buf.append(" ").append(encode(b)); 
     return buf.toString() + ".\n"; 
    } 

    public String and(Literal s) { 
     buf.append(" ").append(encode(b)); 
     return buf.toString() + ".\n"; 
    } 


    public String toString() { 
     return buf.toString() + ".\n"; 
    } 

} 

Quindi è possibile utilizzare evitare una toString() chiamata in questo modo:

String statement = from(subject).with(predicate).and(object);

+0

Mi piace questa risposta come makeStatement diventa molto più chiara, tuttavia, il toString su la fine di ogni riga è meno piacevole. – Rhand

+0

Non hai nemmeno bisogno dei metodi makeStatement dopo questo. Se si desidera evitare di poter aggiungere 3 metodi alla classe interna Statement. Li aggiungerò per rispondere. –

+0

Aggiunto un modo per evitare una chiamata toString(). –

2

L'overloading dei metodi funziona alla grande se ci sono solo poche opzioni. Quello che hai qui è un po 'ossessivo. Non è necessario avere tutte le opzioni se c'è un modo semplice per convertire da uno all'altro.

Quindi dimenticarsi di avere ogni possibilità di scelta e rendere disponibili quelle più utilizzate.

+0

Sfortunatamente non esiste un modo semplice per convertire da uno all'altro. Per il software che sto attualmente lavorando mi trovo a scrivere funzioni di cui l'80% delle linee potrebbe consistere in chiamate makeStatement. – Rhand

+0

Non è un modo semplice per convertire da URI a String e viceversa? – Kayaman

+0

Certo, c'è un modo semplice per convertire da un URI a una stringa, ma nel mio contesto voglio avvolgere un URI con <> e inserire un? prima di una stringa. – Rhand

1

Normalmente risponderei a una simile domanda con il suggerimento di creare una super classe, ma non riesco a modificare Stringa, URI e Letterale. Un'altra opzione sarebbe quella di definire

Avrei optato per un approccio simile, ma invece di estrarre una superclasse, che come hai affermato, non puoi fare, potresti creare un wrapper.

public class LiteralWrapper { 
    private String string = null; 
    private URI uri = null; 
    private Literal literal = null; 

    public LiteralWrapper(String sting) { 
     this.string = string; 
    } 

    public LiteralWrapper(URI uri) { 
     this.uri = uri; 
    } 

    public LiteralWrapper(Literal literal) { 
     this.literal = literal; 
    } 

    // Note that this class is immutable, 
    // so you know you cannot have more than one non-null member. 
    // Probably not a bad idea to add some getters, though. 


    /* The encode functions from your original question */ 

    private static String encode(String binding) { 
     return "?" + binding; 
    } 

    private static String encode(URI uri) { 
     return "<" + uri.stringValue() + ">"; 
    } 

    private static String encode(Literal literal) { 
     return "\"" + literal.stringValue() + "\"" + literal.getDatatype(); 
    } 

    @Override 
    public String toString() { 
     if (literal != literal) { 
      return encode(literal); 
     } 
     if (uri != null) { 
      return encode(uri); 
     } 
     return encode(string); 
    } 
} 

Ora il codice makeStatement diventa banale:

public static String makeStatement(LiteralWrapper subject, LiteralWrapper predicate, LiteralWrapper object) { 
    return " " + subject + " " + predicate + " " + object + ".\n"; 
} 

EDIT:
Come per il commento qui sotto, questo rende chiamando makeStatement un po 'fastidioso. Invece di essere in grado di fare makeStatement(myString, myUri, myLiteral), sei costretto a chiamare makeStatement(new LiteralWrapper(myString), new LiteralWrapper(myUri), new LiteralWrapper(myLiteral)). Questo problema non può essere completamente evitato con la soluzione di data, ma può essere mitigato con l'introduzione di po 'di zucchero sintattico nella forma di metodi di fabbrica:

public static LiteralWrapper wrap(String string) { 
    return new LiteralWrapper(string); 
} 

public static LiteralWrapper wrap(URI uri) { 
    return new LiteralWrapper(uri); 
} 

public static LiteralWrapper wrap(Literal literal) { 
    return new LiteralWrapper(literal); 
} 

Ora, è possibile staticly importare questi involucri, ovunque è necessario utilizzare makeStatement:

import static org.some.package.LiteralWrapper.wrap; 
/* other imports*/ 

public class MyClass { 
    public void someFunction() { 
     /* some business logic */ 

     makeStatemet(wrap(myString), wrap(myURI), wrap(myLiteral)); 
    } 
} 
+0

Grazie per la risposta, questo è simile alla risposta a cui ho fatto riferimento nella mia domanda. Finora questa è l'opzione più elegante, tuttavia ciò trasformerebbe il mio makeStatement ("subject", "predicate", "object") in makeStatement (new StatementWrapper ("subject"), new StatementWrapper ("predicate"), StatementWrapper (" oggetto")); che in realtà non migliora la leggibilità. – Rhand

+0

@Rhand Concordo sul fatto che questo ostacoli la leggibilità. Vedere la mia modifica alla risposta sopra per un modo per mitigare il problema. IMHO, la soluzione risulta in un codice abbastanza leggibile, ma è tutto negli occhi di chi guarda. – Mureinik

+0

Mi piace la funzionalità di wrap (questa è ampliata da alcune delle altre risposte) – Rhand

1
public static String makeStatement(Object subject, Object predicate, Object object) { 
    return " " + encode(subject) + " " + encode(predicate) + " " + encode(object) + ".\n"; 
} 

private static String encode(Object obj) { 
    String encodedOj =""; 
    if (obj.getClass().equals(URI.class)) { 
     encodedOj = encode((URI) obj); 
    }else if(obj.getClass().equals(Literal.class)){ 
     encodedOj = encode((Literal) obj); 
    }else if(obj.getClass().equals(String.class)){ 
     encodedOj = encode((String) obj); 
    } 
    return encodedOj; 
} 

private static String encode(String binding) { 
    return "?" + binding; 
} 

private static String encode(URI uri) { 
    return "<" + uri.stringValue() + ">"; 
} 

private static String encode(Literal literal) { 
    return "\"" + literal.stringValue() + "\"" + literal.getDatatype(); 
} 
+0

Sai se c'è un riscontro di prestazioni per controllare la classe di ogni oggetto Object? Rispetto alla risposta di U Mad questa sembra la differenza principale (e la funzione makeStatement è molto diversa). – Rhand

+0

Grazie per la tua risposta, questo funzionerebbe, ma preferisco la risposta di U Mad – Rhand

+1

Il problema è che qualsiasi oggetto può essere passato. Questo è di per sé un odore di codice, ma ciò che è peggio, se 'makeStatement' viene chiamato con qualcosa che è non 'URI',' Literal' o 'String', il metodo' encode (Object) 'fallisce silenziosamente e restituisce una stringa vuota. Dovresti almeno aggiungere 'else throw new IllegalArgumentException' a quel if block. –

0

È possibile creare divertente e facile da usare involucro utilizzando metodi factory statici (vedi Effective Java, punto 1) e classi anonime (che si comportano come chiusure).

Ecco come:

public class Item { 

    private static interface Methods { 
     public String encode(); 
    } 

    private final Methods methods; 

    private Item(Methods methods) { 
     this.methods = methods; 
    } 

    public static Item of(final String binding) { 
     return new Item(new Methods() { 
      @Override 
      public String encode() { 
       return "?" + binding; 
      } 
     }); 
    } 

    public static Item of(final URI uri) { 
     return new Item(new Methods() { 
      @Override 
      public String encode() { 
       return "<" + uri.stringValue() + ">"; 
      } 
     }); 
    } 

    public static Item of(final Literal literal) { 
     return new Item(new Methods() { 
      @Override 
      public String encode() { 
       return "\"" + literal.stringValue() + "\"" + literal.getDatatype(); 
      } 
     }); 
    } 

    public String encode() { 
     return methods.encode(); 
    } 
} 

E 'davvero facile aggiungere nuovi tipi supportati (che era uno dei vostri requisiti) con questo approccio: basta creare un nuovo metodo factory statica accettare questo tipo: Item.of(NewType val).

Quindi si avrebbe un metodo:

public static String makeStatement(Item subject, Item predicate, Item object) { 
    return subject.encode() + " " + predicate.encode() + " " + object.encode(); 
} 

e chiamarlo così:

makeStatement(Item.of(subject), Item.of(predicate), Item.of(object)); 

L'aggiunta di nuovi metodi è anche abbastanza facile (ma sarebbe un po 'come la modifica, invece di estensione): basta aggiungerli all'interfaccia Methods e implementare le chiusure per tutti i tipi supportati. Il compilatore ti farà, comunque.

+0

Grazie per la tua risposta, questo funzionerebbe, ma preferisco la risposta di U Mad – Rhand

+1

In effetti, è molto meglio per il tuo particolare problema. Stavo cercando di risolvere un po 'più generale (accettando diversi tipi di argomenti). – siledh

Problemi correlati