0

I need to extend an algorithm which uses extensively the double dispatch pattern.

This algorithm manipulate some different data classes (a lot of classes) and each classes require the same extension.

I don't want to modify the existing data classes (for limiting serialization problems) nor the algorithm class (for limiting regressions). So I designed a solution using new classes.

I want to use something like the following example but java refuses to recognize my types.

import Main.Data1;
import Main.ExtendedData;

public class Main
{
  static interface OperatorDispatcher
  {
    void dispatchDoSomething( OperatorDispatch operator );
  }

  static class Data1 implements OperatorDispatcher
  {
    @Override
    public void dispatchDoSomething( OperatorDispatch operator )
    {
      operator.doSomething( this );
    }    
  }

  static class Data2 implements OperatorDispatcher
  {
    @Override
    public void dispatchDoSomething( OperatorDispatch operator )
    {  
      operator.doSomething( this );
    }    
  }

  static interface OperatorDispatch
  {
    void doSomething( Data1 data );
    void doSomething( Data2 data );
  }

  static class MyOperator implements OperatorDispatch
  {
    public void doSomething( Data1 data1 )
    {
      System.out.println( "doSomething with Data1 : " + data1 );
    }

    public void doSomething( Data2 data2 )
    {
      System.out.println( "doSomething with Data2 : " + data2 );
    }
  }

  static interface ExtendedOperatorDispatch
  {
    void doSomething( Data1 data, Object extension );
    void doSomething( Data2 data, Object extension );
  }

  static class MyExtendedOperator implements ExtendedOperatorDispatch
  {
    public void doSomething( Data1 data1, Object extension )
    {
      System.out.println( "doSomething with Data1 : " + data1 + " and " + extension );
    }

    public void doSomething( Data2 data2, Object extension )
    {
      System.out.println( "doSomething with Data2 : " + data2 + " and " + extension );
    }
  }

  static interface ExtendedOperatorDispatcher extends OperatorDispatcher
  {        
    void dispatchDoSomething( ExtendedOperatorDispatch operator );
  }

  /*
   * I don't want to specialize this class for each data type.
   */
  static class ExtendedData< T > implements ExtendedOperatorDispatcher
  {
    T _data;
    Object _extension;

    public ExtendedData( T data, Object extension )
    {
      _data = data;
      _extension = extension;
    }

    @Override
    public void dispatchDoSomething( OperatorDispatch operator )
    {  
      /*
       * ERROR : The method doSomething(Main.Data1) in the type Main.OperatorDispatch is not applicable for the arguments (T)
       */
      operator.doSomething( _data );
    }

    @Override
    public void dispatchDoSomething( ExtendedOperatorDispatch operator )
    {  
      /*
       * ERROR : The method doSomething(Main.Data1, Object) in the type Main.ExtendedOperatorDispatch is not applicable for the arguments (T, Object)
       */
      operator.doSomething( _data, _extension );
    }    
  }

  public static void main( String[] args )
  {
    MyOperator operator = new MyOperator();

    Data1 data10 = new Data1();
    data10.dispatchDoSomething( operator );

    Data1 data11 = new Data1();
    data11.dispatchDoSomething( operator );

    Data2 data20 = new Data2();
    data20.dispatchDoSomething( operator );

    MyExtendedOperator extendedOperator = new MyExtendedOperator();

    ExtendedData< Data1 > extendedData10 = new ExtendedData< Data1 >( data10, "EXTENSION" );
    extendedData10.dispatchDoSomething( operator );
    extendedData10.dispatchDoSomething( extendedOperator );

    ExtendedData< Data2 > extendedData20 = new ExtendedData< Data2 >( data20, "EXTENSION" );
    extendedData20.dispatchDoSomething( operator );
    extendedData20.dispatchDoSomething( extendedOperator );    
  }  
}

You know how to fix it or you think about another solution, feel free to answer.

Thank's

1 Answers1

0

This will do the trick but there may be a simpler solution. For the dispatch, reflection can be used if performance are not crucial. Please note that there must be only one compatible method to work.

class ExtendedData<T> implements ExtendedOperatorDispatcher {
    T _data;
    Object _extension;
    private Class<? extends Object> _dataClass;
    private Class<? extends Object> _extensionClass;

    public ExtendedData(T data, Object extension) {
        _data = data;
        _extension = extension;
        _dataClass = (_data == null ? Object.class : _data.getClass());
        _extensionClass = (_extension == null ? Object.class : _extension
                .getClass());
    }

    @Override
    public void dispatchDoSomething(OperatorDispatch operator) {
        try {
            Method foundMethod = null;
            for (Method method : operator.getClass().getMethods()) {
                Class<?>[] params = method.getParameterTypes();
                if ("doSomething".equals(method.getName())
                        && params.length == 1)
                    if (params[0].isAssignableFrom(_dataClass)) {
                        if (foundMethod == null)
                            foundMethod = method;
                        else
                            throw new IllegalArgumentException(
                                    "Multiple method can be called");
                    }
            }
            foundMethod.invoke(operator, _data);
        } catch (SecurityException | IllegalAccessException
                | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void dispatchDoSomething(ExtendedOperatorDispatch operator) {
        try {
            Method foundMethod = null;
            for (Method method : operator.getClass().getMethods()) {
                Class<?>[] params = method.getParameterTypes();
                if ("doSomething".equals(method.getName())
                        && params.length == 2)
                    if (params[0].isAssignableFrom(_dataClass) && params[1].isAssignableFrom(_extensionClass)) {
                        if (foundMethod == null)
                            foundMethod = method;
                        else
                            throw new IllegalArgumentException(
                                    "Multiple method can be called");
                    }
            }
            foundMethod.invoke(operator, _data, _extension);
        } catch (SecurityException | IllegalAccessException
                | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
sebtic
  • 198
  • 1
  • 6
  • Interesting, thank you. Anyways the more I search for a solution the more I think I will write a lot of class instead of a generic one. – user2970188 Nov 08 '13 at 21:16
  • Writing lot of classes to do little work is useless. This sample code provide a solution to the question but I think that a better modeling can probably solve your problem without lot of code neither reflection. – sebtic Nov 09 '13 at 16:58
  • I feel trapped by java limitations. – user2970188 Nov 10 '13 at 16:30