Search code examples
javaeclipseeclipse-plugineclipse-rcpe4

Separating UI event listening from a Part in Eclipse RCP: how to do it properly?


I have a huge Part source code I have to touch at 1 place. It is violating a lot of principles so I would like to extract at least the function I had to modify which is a @UIEventTopic handler. There are no tests and I would like to add them here so I know I do not break existing functionality.

I would like to move away from this:

public class MyPart {
  ...

  @Inject
  @Optional
  public void event(@UIEventTopic(EVENT) EventParam p) {
    ...
  }
}

To something like this:

public class MyPart {
  ...
}

public class MyEventHandler { 
  @Inject
  @Optional
  public void event(@UIEventTopic(EVENT) EventParam p, MyPart part) {
    ...
  }
}

With the Eclipse DI I see no easy way of creating an instance of the handler class. It cannot be a @Singleton because it is a Part which can have multiple instances, and adding the handler to the IEclipseContext in the @PostConstruct is ugly because it adds a circular dependency between the part and the handler. Is there a magic how I can enforce the instantiation through the e4xmi files, or some alternative way?

My current solution is to extract purely the functionality to a utility bean and return the data and set it on the part, but this is also something not too nice (requires a lot of additional null-checks, ifs, etc.).


Solution

  • I am not entirely sure that I understand your question, however, this is how I would proceed:

    Extract Delegate

    Move the code in event() to the MyEventHandler so that MyClass fully delegates the event handling

    public class MyPart {
      @Inject
      @Optional
      public void event( @UIEventTopic(EVENT) EventParam param ) {
        new MyEventHandler().handleEvent( this, param );
      }
    }
    
    class MyEventHandler { 
      void handleEvent(MyPart part, EventParam param) {
        // all code from event() goes here
      }
    }
    

    This should be a safe-enough refactoring to do without having tests - and in the end, you don't have a choice as there are no tests.

    Ensure the Status Quo

    Now I would write tests for handleEvent(), mocking the required methods of MyPart and thus make sure that I won't break existing behavior.

    Implement new Feature

    After that I would be able to make the desired changes to MyEventHandler::handleEvent in a test driven manner.

    Clean Up

    Then I would extract an interface from MyPart that has only those methods required for MyEventHandler to do its work. If said interface gets too big, it would indicate that there is more refactoring left to do.