1

I have some data that I need to represent in a form. As an example, it might look like this (in psuedo-json):

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

So when I display the first form, the user should have an integer text field, a combobox, and another text field to fill out, but the second should only have a combobox. In my model, I'm viewing this as each Form has a List<FormElement> so that I can properly iterate over each form element to generate the GUI. But now I have the problem of needing the specific type of FormElement each time I use the list.

I've tried to model this like so (psuedo-code):

class Form {
    List<FormElement> integerField
}

class FormElement {
    String message
    Integer value
    FormType type
}

enum FormType {
    ENUMERATED,
    INTEGER
}

And then I could simply perform the checks in non-model code, but I'd still need a way to attach the possible values for enumerated types to the enumerated objects. So I thought of doing something like this:

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 }
}

Now this does work. However, in my code, I have to do type casting all over, something like so (or I could "avoid" the type casting with an enum like before):

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

I get the feeling that there has to be a better way to do this.


In a more complicated example, it could be that the field should restrict to integers, or to floats, or be arbitrary strings, or be an enumerated combobox, or be a checkbox for a boolean, a "spreadsheet" for a matrix, xml for an arbitrary object, etc. There has to be a better way than repeating the "switch" everywhere I need to use the type.

  • I guess the benefit of what I've come up with is that it's not duplicated everywhere, because I can still get the value of the field by calling getValue, but there are still problems when the data is a String instead, or worse some complex data structure such as an array or a matrix, etc. I could just return an Object, but then all the type casting comes yet again... – Justin Jun 22 '16 at 21:58
1

the classic way is to use a Factory class with your switch

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

}

this can be made more generic using reflection until you end up with a dependency injection container

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

container.Resolve<FormElement>().WithName(data.type).WithConstructionParameter(data)
  • I'm not sure I understand the second piece of code. But the first piece kind of makes sense; it's like an adapter, pass it in the object we're looking at and get the desired GUI element out. – Justin Jun 23 '16 at 4:41
0

Instead of checking and invoking specific makeComboBox vs. makeNumericTextField, consider invoking a generic constructUI, which would be a virtual method on the base class FormElement that is overridden by the various subclasses, each of which does the equivalent.

The idea behind OOP is to instruct the object to do something useful on its own and return to you (base or abstract) types so you don't have to deal with the switching (or if...else ifing). We put the "do something useful" method in the base class, and have it take parameters and return values that are also equally abstract. The trick to doing this when you have the "check what subclass to see how to proceed" code smell is to find the right abstraction to put in the base class. In this case, I'd suggest something that creates the user interface element.

  • I think this is probably a bad idea; it ties the specific View (of MVC) to the Model. If I change from one GUI library to another, the model would have to change as well. Better would be to have a specialized "UI" model element which can be converted into the appropriate GUI element via an adapter, but then this is tricky because there is more information needed than just "this needs a combo box" or "this needs a text field"; somehow I need to pass the values to the combo box – Justin Jun 23 '16 at 4:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.