Вызывать методы интерфейса в другом порядке

#java #design-patterns

#java #дизайн-шаблоны

Вопрос:

У меня есть один интерфейс с несколькими методами:

 interface IExample {
 methodA();
 methodB();
 methodC();
 ...
 methodN();
}
 

У меня также есть несколько реализаций этого интерфейса (например, класс A, класс B …). У меня также есть HashMap, куда я помещаю эти реализации на основе определенного ключа:

 commands = new HashMap<String, IExample>();
commands.put("key1", new A());
.
.
commands.put("keyN", new N());
 

Я использую шаблон разработки стратегии для извлечения каждой реализации при возникновении какого-либо события:

 Event event = ...
IExample example = UtilClass.commands.get(event.getKey());
 

и теперь я могу вызывать методы для конкретной реализации:

 example.methodA();
...
 

Проблема в том, что в зависимости от события порядок вызова метода разный. Итак, для ключа 1 порядок вызова равен:

 example.methodC();
example.methodA();
...
 

но для другого ключа, скажем, key2, порядок вызова метода:

 example.methodA()
example.methodC()
...
 

Какой шаблон проектирования или подход я могу использовать, чтобы решить эту проблему простым и понятным способом? Не использовать что-то подобное:

  if (example instance of A) {    call order for A... }

 if (example instance of B) {    call order for B... }
 

Комментарии:

1. Вы могли бы написать метод, который вызывает другие методы в некотором порядке, и переопределить его там, где это необходимо, чтобы вызывать их в другом порядке. Это обычная альтернатива if ( instanceof ) коду.

Ответ №1:

Вы просто добавляете другой метод в свой IExample интерфейс:

 interface IExample {
    methodA();
    methodB();
    methodC();
    ...
    methodN();

    execute();
}

class A implements IExample {
    //...
    execute() {
        methodA();
        methodC();
    }
}

...

commands.get("key1").execute();
 

В случае, если IExample интерфейс не удалось изменить или дублируется много порядков, вы можете переместить execute() метод в другой интерфейс IExecutor :

 interface IExecutor {
    execute();
}

class Executor1 implements IExecutor {
    private final IExample example;
    public Executor1(IExample example) { this.example = example; }
    execute() {
        example.methodA();
        example.methodC();
    }
}
 

и управлять хэш-картой IExecutor вместо IExample :

 commands = new HashMap<String, IExecutor>();
commands.put("key1", new Executor1(new ExampleA()));
commands.put("key2", new Executor1(new ExampleB()));
...
commands.get("key1").execute();
 

Ответ №2:

Я бы сделал что-то вроде этого: создайте интерфейс IExecuteOrder с помощью метода, который говорит: executeOrder(IExample example);

Затем реализуйте этот executeOrder метод в разных классах в соответствии с вашим порядком выполнения. Например.

 class ExecuteOrder1 implements IExecuteOrder{
  public executeOrder(IExample example){
  example.methodA();
  example.methodC();
  example.methodB();
 }
}
 

Затем поместите эту реализацию в подобную карту.

 Map<String, IExecuteOrder> map = new HashMap<>();
map.put("key1", new ExecuteOrder1());
map.put("key2", new ExecuteOrder2());
...
map.put("keyn", new ExecuteOrderN());
 

Наконец, вы вызовете свой метод:

 map.get("key1").executeOrder(example);
 

Ответ №3:

Я бы просто объявил другой метод в IExample интерфейсе:

 interface IExample {

    methodA();
    methodB();
    methodC();
    // ...
    methodN();

    runInOrder();
}
 

Каждый класс должен был бы реализовать этот runInOrder метод, который просто вызывал methodX бы методы в требуемом порядке для этого внедрения.

Т.е. класс A (который соответствует "key1" ) будет реализован runInOrder следующим образом:

 @Override
public void runInOrder() {

    this.methodC();
    this.methodA();
}
 

И т.д. Тогда вы могли бы сделать:

 Event event = ...
IExample example = UtilClass.commands.get(event.getKey());

example.runInOrder();