1

Lets say I have a Widget class. I also have TextWidget, ComboWidget, ChoiceWidget classes that inherited from Widget class.

I create this widgets based on the situation. After creating the widget, I just show some data on widget and get one from the user. After that the widget gets destroyed. My current code is something like this: (The code is totally arbitrary)

Widget createWidget(DataType type, Data data) {
    Widget widget;
    if (type == DataType.Text) {
        widget = new TextWidget();
        widget.setText(data.toStrig());
    } else if (type == DataType.Choice) {
        widget = new ChoiceWidget();
        widget.setChoices(data.toChoices())
    } else if (type == DataType.MultiText)
        widget = new ComboWidget();
        widget.setItems(data.toItems())
    }
    return widget;
}

After creating, I get some data from user using that widget. Then I just destroy it.

void widgetReturnPressedEvent(Widget widget) {
    UserData data;
    if (widget.type() == TextWidget)
        data.setText(((TextWidget)widget).getText())
    // ...
    // You get the idea.
}

I'm using this at different parts of my program. So doing if-else every time is not a good way to handle this situation. So what should I do here? Can something like this works, or is there a good pattern for this situation?

class DataWidget {
    Widget widget;
    Widget createWidget(DataType type) {
        Widget w;
        //...
        this.widget = w;
    }

    void setData(Data data) {
        if (this.widget == TextWidget)
            this.widget.setText(data.toString());
        //...
    }

    Data getData() {
        Data data;
        // set data
        return data;
    }

    Widget widget() {
        return this.widget;
    }
}
Spotted
  • 1,680
  • 10
  • 18
isamert
  • 113
  • 3
  • Possible duplicate of [Should conditional logic be always coded via type system where possible?](http://programmers.stackexchange.com/questions/303956/should-conditional-logic-be-always-coded-via-type-system-where-possible) – gnat Aug 18 '16 at 07:58
  • I'd put getValue/setValue on the Widget base class, and have each specific widget type override those methods. – paj28 Aug 18 '16 at 11:07

1 Answers1

0
  • Been there, done that. Factory Method Pattern.

  • Factory has a static HashMap<String,WidgetCreator> so creating a widget is a matter of looking up it's creator in the Map and calling the create method. That's why no if or switchis needed.

  • You can put all creators and the Factory in a separate package for a cleaner workspace.

  • Also, as each type of Widget knows what method to call on Data, why not pass data instead?

  • I assumed choises and items are List<String>, you can choose otherwise.

  • UML first (this software paints interfaces purple instead of putting <<name>> in the title)

  • Code bellow.

enter image description here

==> Factory.java <==

import java.util.HashMap;
import java.util.Map;

public class Factory {
    private static Map<String,WidgetCreator> widgetCreators = new HashMap<String,WidgetCreator>();

    static {
        widgetCreators.put("TEXT",new TextWidgetCreator());
        widgetCreators.put("CHOICE",new ChoiceWidgetCreator());
        widgetCreators.put("COMBO",new ComboWidgetCreator());
        // add as many creators as types od widgets you have
        // tags must be unique, feel free to use CONSTANSTS      
        // instead of magic literals for the tags
    }

    public static Widget getWidget(String tag) throws UnknownWidgetTagException{
        WidgetCreator c = widgetCreators.get(tag);
        if (c == null) {
            throw new UnknownWidgetTagException(tag);
        }
        return c;
    }
}

==> WidgetCreator.java <==

public interface WidgetCreator {
    public Widget createWidget(Data data);
}

==> Widget.java <==

public interface Widget {
    public void setData(Data data);
    public Data getData();
}

==> Data.java <==

import java.util.List;

public interface Data {
    public String toString();
    public List<String> toChoises();
    public List<String> toItems();
}

==> UnknownWidgetTagException.java <==

public class UnknownWidgetTagException extends Exception {

    public UnknownWidgetTagException(String tag) {
        System.out.println("No Widget registered with tag: "+tag);
    }

}

==> TextWidgetCreator.java <==

public class TextWidgetCreator implements WidgetCreator {

    @Override
    public Widget createWidget(Data data) {
        Widget w = new TextWidget();
        w.setData(data);
        return w;
    }

}

==> ChoiceWidgetCreator.java <==

public class ChoiceWidgetCreator implements WidgetCreator {

    @Override
    public Widget createWidget(Data data) {
        Widget w = new ChoiceWidget();
        w.setData(data);
        return w;
    }    
}

==> ComboWidgetCreator.java <==

public class ComboWidgetCreator implements WidgetCreator {

    @Override
    public Widget createWidget(Data data) {
        Widget w = new ComboWidget();
        w.setData(data);
        return w;       
    }
}

==> TextWidget.java <==

public class TextWidget implements Widget {

    private String text;
    private Data data;

    @Override
    public void setData(Data data) {
        this.data = data;
        this.text = data.toString();        
    }

    @Override
    public Data getData() {
        return data;
    }
}

==> ChoiceWidget.java <==

import java.util.List;

public class ChoiceWidget implements Widget {

    private List<String> choices;
    private Data data;

    @Override
    public void setData(Data data) {
        this.data = data;
        this.choices = data.toChoises();        
    }

    @Override
    public Data getData() {
        return data;
    }
}

==> ComboWidget.java <==

import java.util.List;

public class ComboWidget implements Widget {

    private List<String> items;
    private Data data;

    @Override
    public void setData(Data data) {
        this.data = data;
        this.items = data.toItems();        
    }

    @Override
    public Data getData() {
        return data;
    }

}

============

void widgetReturnPressedEvent(Widget widget) {
    UserData data;
    widget.setData(data); // widget already knows how to handle the data
}

===============

Tulains Córdova
  • 39,201
  • 12
  • 97
  • 154
  • Isn't creating a `WidgetCreator` interface and implementing it for every widget a bit overkill? Why didn't you prefer just one Factory class? Thanks for the great answer btw. – isamert Aug 18 '16 at 13:50
  • @isamert Not really, creators are very short. They have just one method and your IDE even creates the class for you with the stubs for you to fill in (once you tell it it will implement the interface. You can put all those creators on a separate package and forget about them. I've done it in several projects and I got rid of all that pesky type-checking. If you were going to code with just NotePad I wouldn't recommend using this pattern. – Tulains Córdova Aug 18 '16 at 13:54
  • A less elegant alternative is the abstract factory pattern which has a method for every type of widget... getTextWidget(), getChoiceWidget() etc. But I think that would lead to conditionals again. – Tulains Córdova Aug 18 '16 at 14:01
  • Yet another alternative is a combination of abstract factory and method factory, that would be having the creators as inner classes of the factory. Also: I think the latest version of Java allows lambdas so you could just have creator methods in the HashMap as if methods were objects. But I' not yet familiarised with functional programming and or lambdas. – Tulains Córdova Aug 18 '16 at 14:04
  • Having the interface `WidgetCreator` helps the IDE create the boilerplate for you when you need a new `Creator`. Besides, that level of abstraction is needed for the pattern to work. Most design patterns rely on interfaces. – Tulains Córdova Aug 18 '16 at 14:11
  • It's easier to implement this level of abstraction in Java, but for example, in C++, it would make so much code with headers and stuff. But the lambda-way seems interesting, I assume I can use it in C++14. For Java, I'll stick this way. Thanks again for the further clarification. – isamert Aug 18 '16 at 14:21