Trattare con diversi getter personalizzati

0

Ho una struttura dati con unità di dati contenenti diversi tipi di dati. Ho spostato i dati in oggetti "Field" in modo che ogni campo sia in grado di analizzare in modo indipendente l'input dell'utente in un modo desiderato.

public abstract class<T> AField {

    protected T value = null;

    public T getValue() {
        return this.value;
    }

    // This is the main reason I'm using a custom Field class
    public abstract void parse(String data) throws ParseException;

}

public class IntegerField extends AField<Integer>{

    public void parse(String data) {
        super.value = data.trim();
    }

}

public class StringField extends AField<String> {

    ...

}

// Note this has the same type as StringField but different parse implementation
public class PhoneNumberField extends AField<String> {

    ....

}

public class DataUnit {

    private AField[] fields;
    private IntegerField number = new IntegerField();
    private StringField name = new StringField();

    public DataUnit() {
        this.fields = new Field {
            this.number,
            this.name
        }
    }

    public void parseAll(String... data) throws ParseException {
        for (int i = 0; i < this.fields.length; i++) {
            fields[i].parse(data[i]);
        }
    }

    // Now the troublesome methods

    public Integer getNumber() {
        return this.number.getValue(),
    }

    public void parseNumber(String data) throws ParseException {
        this.number.parse(data);
    }

    public String getName() {
        ...
    }

    public void parseName(String data) throws ParseException {
        ...
    }

}

Puoi vedere come il numero di metodi salirà rapidamente quando aggiungo altri tipi di campi alla classe DataUnit o più metodi alla classe AField. DataUnit deve passare tutti i metodi di tutte le classi Field in avanti perché preferirei non esporre gli oggetti Field direttamente ad altre classi. Qual è il modo migliore per risolvere questo problema? Esistono modelli migliori per raggiungere questo obiettivo?

    
posta Wiener Boat 14.12.2014 - 22:34
fonte

2 risposte

1

Ho progettato e implementato una gerarchia come questa molti anni fa (prima dei generici) quindi ecco alcuni commenti. Possono aiutarti a impedirti di ripetere i miei errori:)

La proliferazione di metodi come parseNumber (), parseName () suggerisce che la gerarchia delle classi ha bisogno di un aggiustamento, tuttavia la tua gerarchia sembra ben progettata.

Il mio approccio sarebbe una combinazione di tecniche suggerite da Robert Harvey e mdolanci.

  • Crea una classe astratta con un metodo parse(String data) throws ParseException . La tua classe AField cerca di farlo ora, anche se dovrei riconsiderare il nome della classe (suggerisco BaseField).
  • Crea sovraccarichi per IntegerField () StringField () ecc., con logica di analisi appropriata per il tipo. Ancora una volta questo è esattamente quello che stai facendo.
  • La classe DataUnit sembra essere un contenitore per un elenco arbitrario di campi, ma ha un paio di campi dedicati (numero e nome) che non capisco. Se ogni DataUnit deve avere un numero e un nome, allora sono corretti. Ma non verranno duplicati se si aggiungono altri tipi di campi concreti come PhoneNumberField o ZipCodeField. Ad esempio, chiamare asZipCode () in un elenco di campi non è corretto, in quanto l'elenco potrebbe contenere più istanze di ZipCodeField. Ovviamente puoi aggiungere un metodo findFirstZipCodeField() che scorre l'elenco dei campi usando instanceof per trovare il primo campo di quel tipo.

Suggerisco inoltre di spostare la convalida effettiva in metodi statici in una classe separata focalizzata sulla convalida della stringa (come suggerito da @mdolanci), preferibilmente racchiusa in una libreria di convalida separata che non conosce nulla sui campi. Verrà il giorno in cui desideri riutilizzare questa logica in un'interfaccia REST, un'app per Android o qualcosa che non hai ancora pensato. Non fare questa separazione è stato uno dei miei più grandi errori di progettazione e ha portato a inutili duplicazioni di codice nelle nostre applicazioni.

    
risposta data 15.05.2015 - 10:00
fonte
0

Crea semplicemente una classe helper con metodi di supporto statici.

Esempio:

public class DataHelper {

    public static String asString(final Object value) {
        return asString(value, null);
    }

    public static String asString(final Object value, final String defaultValue) {
        if (value instanceof String)
            return (String) value;
        return defaultValue;
    }

    public static Number asNumber(final Object value) {
        return asNumber(value, null);
    }

    public static Number asNumber(final Object value, final Number defaultValue) {
        if (value instanceof Number)
            return (Number) value;

        // If necessary you can parse string values
        if (value instanceof String && ((String) value).matches("^[0-9]\.?[0-9]*$"))
            return Double.valueOf((String) value);

        return defaultValue;
    }

    public static Integer asInteger(final Object value) {
        return asInteger(value, null);
    }

    public static Integer asInteger(final Object value, final Integer defaultValue) {
        Number rv = asNumber(value);
        if (rv != null)
            return rv.intValue();
        return defaultValue;
    }

    public static Double asDouble(final Object value) {
        return asDouble(value, null);
    }

    public static Double asDouble(final Object value, final Double defaultValue) {
        Number rv = asNumber(value);
        if (rv != null)
            return rv.doubleValue();
        return defaultValue;
    }

    public static String asPhoneNumber(final Object value) {
        return asPhoneNumber(value, null);
    }

    // Here you could return custom object instead of string (if needed)
    public static String asPhoneNumber(final Object value, final String defaultValue) {
        String rv = asString(value);
        if (rv != null) {
            // Do your transformations
            // ...
            return rv;
        }
        return defaultValue;
    }

    public static Boolean asBoolean(final Object value) {
        return asBoolean(value, null);
    }

    public static Boolean asBoolean(final Object value, final Boolean defaultValue) {
        if (value instanceof Boolean)
            return (Boolean) value;

        if (value instanceof Number)
            return ((Number) value).intValue() > 0;

        if (value instanceof String && ((String) value).toLowerCase().matches("true|false|1|0|yes|no"))
            return ((String) value).toLowerCase().matches("true|1|yes");

        return defaultValue;
    }

    // ... etc.
}

Utilizzo:

public class Usage {

    public static void main(String[] args) {

        final Map<String, Object> sampleData = ImmutableMap.of(
                "name", "Sample name",
                "active", "1",
                "age", 37,
                "weight", "75.7"
        );

        final String name = DataHelper.asString(sampleData.get("name"));
        final Boolean active = DataHelper.asBoolean(sampleData.get("active"), false);
        final Integer age = DataHelper.asInteger(sampleData.get("age"), 0);
        final Double weight = DataHelper.asDouble(sampleData.get("weight"));

        // ... etc.

    }

}
    
risposta data 15.01.2015 - 01:29
fonte