Friday, February 7, 2014

Nice Move With ValueSetter

I've been working on software architecture refactoring for last couple weeks. During a workshop one of architects showed me an interesting approach.

We were applying MVP-like style and then faced with the question: How a Presenter should tell a View to change its state?

Brute force approach looks like following:
public class Presenter {

 private View view;

 public void doSomething() {

//Here, services are processing something really important

 view.setTitle( "Updated" );
 view.setName( "New name" );
 }
}

public class Form extends JFrame implements View {

 private JTextfield nameTf;

 @Override
 public void setTitle( String title ) {
  setTitle( title );
 }

 @Override
 public void setName( String name ) {
  nameTf.setText( name );
 }
}
Well, this is actually ok, but the more components you have on the form, more verbose View interface becomes.

The move I am talking above is encapsulating a setter into separate object. Look here:
public abstract class ValueSetter {

private static Map<Class<?>, ValueSetter> SETTERS;
 
 static {
  SETTERS = new HashMap<Class<?>, ValueSetter>();
  SETTERS.put(JTextField.class, new JTextFieldSetter());
 }
 
 public static ValueSetter get( Class<?> type ) {
  if( SETTERS.containsKey(type) ) {
   return SETTERS.get(type);
  }
  
  throw new IllegalArgumentException( "Missing ValueSetter for " + type );
 }
 
 public abstract void set( Object target, Object value );

 private static class JTextFieldSetter extends ValueSetter {

  @Override
  public void set(Object target, Object value) {
   ((JTextField)target).setText( (String) value );
  }
 }

 private static class JFrameSetter extends ValueSetter {

  @Override
  public void set(Object target, Object value) {
   ((JFrame)target).setTitle( (String) value );
  }
 }
}
Now it is possible to write more elegant code:
public class Form extends JForm implements View {

 private Map<String, JComponent> components;

 @Override
 public void updateComponent(String id, Object value) {
  JComponent component = components.get(id);
  ValueSetter.get(component.getClass()).set(component, value);  
 }
}
So thanks Marcin for this nice tip and fresh idea for me ;)