1

I'm trying to write a basic sample of interface implementation in java. The idea is to select an instance depending on a keyword. Here is an example:

I have a basic Action interface which declares a method invoke
I wrote two different implementations of the interface: ActionOpen and ActionClose which implements the method invoke and have a static method getKeyword() which returns the keyword associated with object
Then I wrote a factory that returns me the appropriate action instance depending on a keyword.

// Action interface

public interface Action {
  void invoke();
}

// Action implementations

public class ActionOpen implements Action {
  public static String getKeyword() { 
    return "open";
  }
  @Override
  public void invoke() {
    // ...
  }
}

public class ActionClose implements Action {
  public static String getKeyword() { 
    return "close";
  }
  @Override
  public void invoke() {
    // ...
  }
}

// Action factory

public class ActionFactory {
  public static Action getInstance(String action) {
    if (ActionOpen.getKeyword().equals(action)) return new ActionOpen();
    if (ActionClose.getKeyword().equals(action)) return new ActionClose();
    return null;
  }
}

I'm not happy with the serie of "if" in ActionFactory because it's not dynamic and I'll have to add new "if" in that factory everytime I create new action classes (for instance if I want to create a "save" action)

It would be better if action classes could register themselves in the factory automatically. I tried this using a static{ } block in order to do this, but unfortunately static block is not evaluated until I instantiate the action class (or if the class is abstract).

I couldn't find a design pattern that resolves such situation. Any idea ?

morbac
  • 301
  • 4
  • 16

1 Answers1

1

You could add the getKeyword() method to the interface:

public interface Action {
    void invoke();

    String getKeyword();
}

thanks to that you could register all the implementations in a static map:

private static final Map<String, Action> ACTION_BY_KEYWORD = Stream.of(
        new ActionOpen(),
        new ActionClose()
).collect(toMap(Action::getKeyword,
                identity()));

(toMap imported statically from Collectors and identity from Function)

and the getInstance() method would simplify to:

public static Action getInstance(String action) {
    return ACTION_BY_KEYWORD.get(action);
}

Additional note: if you're using a dependency injection framework (e.g. Spring), it could be done more automatically - the framework could find all the implementations of the interface and inject them into the configuration class as a collection, where you could map it to a Map the same as in the example above. Thanks to that when new implementations are added, they would be automatically loaded after being scanned by the framework.

Jonasz
  • 1,617
  • 1
  • 13
  • 19
  • Thanks you a lot. This avoid the "if" but an instanciation of every action class is still required. I'm not using injection framework but I guess that framework performs a kind of class enumeration (like `org.reflections`). I already tried such way but it made my web application loading fail and I was looking for a more basic solution. – morbac Jul 06 '22 at 09:57
  • You can find examples of such a reflection scanning [here](https://stackoverflow.com/questions/347248/how-can-i-get-a-list-of-all-the-implementations-of-an-interface-programmatically). There's nothing bad in creating objects by `new` if you do not use a DI framework - they need to be initialized somehow and somewhere - in this case in a single factory class. Dependency injection frameworks are so popular because they lift off the burden of doing that. – Jonasz Jul 06 '22 at 10:11