1

Im implementing some generic components and I just wonder if my design patters makes sense and if there are any improvements that can be made. E.g., here is a generic panel that can be used to filter stuff:

/**
 * Abstract class for textfields used for filtering. When overriding abstract method onUpdateFilter, the first thing
 * that must be done is to set the paramsobject, or else filtering wont work.
 * @author fred
 *
 */

public abstract class FilterFormPanel extends Panel {

    private static final long serialVersionUID = 1L;
    private FilterForm filterForm;
    private Object paramsObject; //this is object because paramsobjects differ depending on entity type

    public FilterFormPanel(String id) {
        super(id);
        filterForm = new FilterForm("filterForm");
        add(filterForm);
    }

        public String getFilterString(){
        return filterForm.getFilterString();
    }

    public void setParamsObject(Object paramsObject){
        this.paramsObject = paramsObject;
    }

    /**
     *For developers to implement in class that contains the correct references to params and facade objects, dataviews etc.
     *e.g. they could do params.setFilter(<reference to an instance of this class>.getFilterString() and ajax stuff too) 
     */
    public abstract void onUpdateFilter(AjaxRequestTarget target, Object paramsObject);

    private class FilterForm extends Form<Void> {

        private static final long serialVersionUID = 1L;
        private transient String filterString;

        public FilterForm(String id) {
            super(id);
            final TextField<String> filterTextField = new TextField<String>("filterTextField", new PropertyModel<String>(this, "filterString")); //textField for user to enter filter string
            add(filterTextField);

            add(new AjaxButton("filterButton") { //button to click for performing overriden method
                private static final long serialVersionUID = 1L;
                @Override
                protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
                    onUpdateFilter(target, paramsObject);
                }
            }); 
        }

        public String getFilterString(){
            return filterString;
        }   
    }
}

Used as follows in another class:

    filterFormPanel = new FilterFormPanel("filterFormPanel"){

        private static final long serialVersionUID = 1L;

        @Override
        public void onUpdateFilter(AjaxRequestTarget target, Object paramsObject) {
            filterFormPanel.setParamsObject(params);
            params.setFilterString(filterFormPanel.getFilterString());
            //ajax stuff
            target.addComponent(dataViewContainer);
            nav.setVisible(dataProvider.size()!=0);
            target.addComponent(nav);
            emptyLabel.setVisible(dataProvider.size()==0);
            target.addComponent(emptyLabel);
        }

    };

    settingsContainer.add(filterFormPanel);

Its kind of annoying that one is forced to use the setParamsObject method first thing when one overrides the method. Is there a nicer way of achieving a reference to that object? And is this is general a sane way of implementing reusable and relatively generic components in wicket? Any feedback would be greatly appreciated, Im sure theres room for improvement here.

EDIT I: Just for some context, what Im doing is Im implementing pages like these

enter image description here

where I present the user with a dataview and options for filtering it. There are lots of pages for lots of different entities, but the GUI components can and should be made as generic as possible as to not violate DRY. The example code is obviously the filter textfield and button part of the page.

EDIT II: I want this component to be even more loosely coupled if possible, e.g. make it able to do completely different things, not just modifying a params object (say, e.g. have another case where I need to update TWO params objects, then I wont be able to use this panel). The onSubmit method in the form as it is now requires a reference to the objects to be used in the overriden method are known beforehand. Is there any way to not have it that way or set the presence and/or types of those objects dynamically?

EDIT III: The point is that this panels core function really is only to allow the user to

  • enter a string
  • notify and give access to that string to some other part of the system when the user clicks the button.

What that "other part of the system" does with the string should not really have to concern this panel, but as it is now, it is coupled to the params object upon which "the other part of the system" must perform some operation. It is this coupling I would like to get rid of if possible. I might as well want to use the string from this panel for just printing to console or use it for some other arbitrary task.

RRUZ
  • 134,889
  • 20
  • 356
  • 483
fred
  • 1,812
  • 3
  • 37
  • 57
  • 2
    Is this wicket 1.5? If so, you could use the new messaging system to send out notifications when the filter has changed. – bert Dec 14 '11 at 07:07
  • Wow, so wicket 1.5 offers that functionality? Sadly this is wicket 1.4, haha! Any pointers? – fred Dec 14 '11 at 07:15
  • Nevermind I figured out a way =) thanks though! – fred Dec 14 '11 at 07:32

3 Answers3

3

You can use the constructor of the class to set the object.

A more generic approach would be if you let paramsObject make use of the Java Generics (hence the name :)). You could superclass the entities or let them implement an interface.

rotsch
  • 1,909
  • 16
  • 36
  • Thanks. I thought about using the constructor but then one might as well throw in references to the dataview and all other components that need uṕdating as well rather than updating them when overriding the onUpdateFilter method. This is sort of what I wanted to get away from, I want this component to be even more loosely coupled if possible, e.g. make it able to do completely different things, not just modifying a params object, but the onSubmit method as it is now requires that at least the objects to be used in the overriden method are known beforehand. – fred Dec 14 '11 at 06:43
2

I work for more than 3 years on a web application using Wicket (started using 1.3.x now under 1.4.x and planning to upgrade to 1.5.x in a few weeks). The approach you are using is the one we use internally. We often use abstract classes to represents common panels. The only thing we do is what rotsch said in his answer, we use a lot of generics to infer the type arguments as much as possible.

Cedric Gatay
  • 1,553
  • 3
  • 12
  • 17
  • Cheers for you input mate, I appreciate it. Any ideas on how to achieve what Im asking about in the edits? – fred Dec 14 '11 at 07:02
0

Actually, even though I didnt realize it at first, this could be achieved quite easily with by simply doing this:

/**
 * Abstract class for textfields used for filtering.
 * @author fred
 *
 */

public abstract class FilterStringPanel extends Panel {

private static final long serialVersionUID = 1L;
private FilterForm filterForm;

public FilterStringPanel(String id) {
    super(id);
    filterForm = new FilterForm("filterForm");
    add(filterForm);
}

public String getFilterString(){
    return filterForm.getFilterString();
}

/**
 *For developers to implement in class that contains the correct references to params and facade objects, dataviews etc.
 *e.g. they could do params.setFilter(<reference to an instance of this class>.getFilterString() and ajax stuff too) 
 */
public abstract void onUpdateFilter(AjaxRequestTarget target);

private class FilterForm extends Form<Void> {

    private static final long serialVersionUID = 1L;
    private transient String filterString;

    public FilterForm(String id) {
        super(id);
        final TextField<String> filterTextField = new TextField<String>("filterTextField", new PropertyModel<String>(this, "filterString")); //textField for user to enter filter string
        add(filterTextField);

        add(new AjaxButton("filterButton") { //button to click for performing overriden method
            private static final long serialVersionUID = 1L;
            @Override
            protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
                onUpdateFilter(target);
            }
        }); 
    }

    public String getFilterString(){
        return filterString;
    }   
}
}

And then implementing it this way:

    settingsContainer.add(new FilterStringPanel("filterStringPanel"){
        private static final long serialVersionUID = 1L;
        @Override
        public void onUpdateFilter(AjaxRequestTarget target) {
            params.setFilterString(getFilterString());
            target.addComponent(dataViewContainer);
            nav.setVisible(dataProvider.size()!=0);
            target.addComponent(nav);
            emptyLabel.setVisible(dataProvider.size()==0);
            target.addComponent(emptyLabel);
        }
    });

This way we dont need to send any references to any objects (e.g. params objects or wicket components that needs to be targeted for updating with AJAX) and we can re-use this panel for whatever we want!

fred
  • 1,812
  • 3
  • 37
  • 57