Come rappresenti un oggetto quando hai bisogno di sapere con quale sottotipo stai lavorando?

1

Ho alcuni dati che devo rappresentare in un modulo. Ad esempio, potrebbe apparire come questo (in psuedo-json):

[
    {
        "value1" : "int"
        "value2" : "{1, 2, 3, 4, 5}"
        "value3" : "int"
    }
    {
        "value1" : "{1, 3, 5}"
    }
]

Quindi quando visualizzo il primo modulo, l'utente dovrebbe avere un campo di testo intero, una casella combinata e un altro campo di testo da compilare, ma il secondo dovrebbe avere solo una casella combinata. Nel mio modello, sto visualizzando questo dato che ogni Form ha un List<FormElement> in modo da poter eseguire correttamente iterazioni su ogni elemento del modulo per generare la GUI. Ma ora ho il problema di aver bisogno del tipo specifico di FormElement ogni volta che uso la lista.

Ho provato a modellarlo in questo modo (psuedo-code):

class Form {
    List<FormElement> integerField
}

class FormElement {
    String message
    Integer value
    FormType type
}

enum FormType {
    ENUMERATED,
    INTEGER
}

E quindi potevo semplicemente eseguire i controlli nel codice non-model, ma avrei comunque bisogno di un modo per collegare i possibili valori per i tipi enumerati agli oggetti enumerati. Quindi ho pensato di fare qualcosa del genere:

class Form {
    FormElement integerField
}

abstract class FormElement {
    String message
    Integer value
    boolean validate(Integer value)
}

class EnumElement extends FormElement {
    List<String> values
    boolean validate(Integer value) { return value in values }
}

class IntegerElement extends FormElement {
    boolean validate(Integer value) { return true }
}

Ora funziona. Tuttavia, nel mio codice, devo fare il casting di tipo all over, qualcosa del genere (o potrei "evitare" il casting di tipo con un enum come prima):

void generateField(FormElement element) {
    makeLabel(element.message)
    if (element instanceof EnumElement) {
        makeCombobox(((EnumElement) element).values);
    } else if (element instanceof IntegerElement) {
        makeNumericTextField()
    }
}

Ho la sensazione che ci debba essere un modo migliore per farlo.

In un esempio più complicato, potrebbe essere che il campo debba restringere a numeri interi, oa float, o essere stringhe arbitrarie, o essere una combobox enumerata, o essere una casella di controllo per un booleano, un "foglio di calcolo" per una matrice , xml per un oggetto arbitrario, ecc. Ci deve essere un modo migliore di ripetere il "passaggio" ovunque io abbia bisogno di usare il tipo.

    
posta Justin 22.06.2016 - 23:22
fonte

2 risposte

1

il modo classico è utilizzare una classe Factory con il tuo switch

Factory.Create(data)
{
    switch data.class
    case : combo
       return new Combo(data)
    ....

}

questo può essere reso più generico usando il reflection fino a quando non si finisce con un contenitore per l'iniezione delle dipendenze

container.Register<FormElement,Combo>().Named("combo")

container.Resolve<FormElement>().WithName(data.type).WithConstructionParameter(data)
    
risposta data 22.06.2016 - 23:47
fonte
0

Invece di controllare e richiamare il makeComboBox rispetto a makeNumericTextField , prendi in considerazione un generico constructUI , che sarebbe un metodo virtuale sulla classe base FormElement che viene sovrascritto dalle varie sottoclassi, ognuna delle quali fa l'equivalente.

L'idea alla base di OOP è di istruire l'oggetto a fare qualcosa di utile da solo e di ritornare a te (base o astratto) in modo da non dover affrontare switch ing (o if...else if ing) . Mettiamo il metodo "fai qualcosa di utile" nella classe base, e prendiamo parametri e valori di ritorno ugualmente astratti. Il trucco per fare ciò quando si ha l'odore del codice "controlla quale sottoclasse vedere come procedere" è trovare l'astrazione giusta da inserire nella classe base. In questo caso, suggerirei qualcosa che crei l'elemento dell'interfaccia utente.

    
risposta data 23.06.2016 - 00:40
fonte