Вызов конкретных функций класса через общий интерфейс

У меня есть следующие конкретные классы, которые реализуют общий интерфейс

EvaluatorA : IEvaluator
EvaluatorB : IEvaluator
EvaluatorC : IEvaluator

Интерфейс IEvaluator имеет только одну функцию - Evaluate, которая реализована во всех трех типах вычислителей. И у меня есть класс драйвера, который вызывает оценщик на основе конфигурации, однако он имеет доступ (по дизайну) только к IEvalutor, т. е. ему не нужно знать, какой конкретный оценщик вызывается в данный момент.

Проблема возникает, когда одному из оценщиков, скажем EvaluatorC, нужно реализовать новую функцию Predict, а мое требование касается только EvaluatorC, функцию Predict нужно вызывать после вызова Evaluate.

Временное решение 1. Одним из решений является проверка типа оценщика:

// evaluator is previously instanciated
evaluator.Evaluate();
if (evaluator is EvaluatorC)
    evaluator.Predict();

Как видите, это не аккуратно. Скажем, завтра мне нужно вызвать другую функцию Dance только для EvaluatorB и функцию Sing для обоих EvaluatorA и EvaluatorB, это становится беспорядочным.

Временное решение 2. Добавьте функцию Predict в интерфейс IEvaluator, а для других оценщиков просто реализуйте функцию с пустым телом. Это может работать для функций с типом возврата void, но требуется дополнительная проверка безопасности в программе драйвера, если тип возврата не void. Кроме того, я не уверен, что функция с пустым телом только в качестве заполнителей является хорошей идеей или нет.

Временное решение 3. Измените интерфейс на абстрактный класс. Это похоже на решение 2, но обеспечивает реализацию по умолчанию Predict. Однако этот подход мне тоже не понравился, так как он изменяет первоначальную структуру проекта и не дает много преимуществ по сравнению с решением 2.

В общем, у меня нет удовлетворительного решения этой проблемы. Я надеюсь, что decoration pattern может помочь (но я не уверен). Эта проблема не специфична для каких-либо языков программирования. Пожалуйста, прыгайте и делитесь своими идеями.

Правка 1: добавлены некоторые сведения об обязанностях оценщика. Предполагается, что оценщики оценивают данное решение и возвращают некоторые показатели. После получения оценочных показателей программа-драйвер выполнит некоторые служебные задачи, такие как составление отчетов, обратите внимание, что это необходимо для всех оценщиков. Затем одному из оценщиков (EvaluatorC) нужно вызвать Predict() на основе сгенерированных отчетов. Однако другим оценщикам этот шаг не требуется.


comment
Почему бы не вызвать Predict в методе Evaluate EvaluatorC?   -  person sdgfsdh    schedule 03.10.2017
comment
@sdgfsdh, хорошая мысль. Это первоначальный план, однако обнаруживается, что после вызова Evaluate() в программе-драйвере необходимо выполнить некоторую вспомогательную работу. Predict() можно вызывать только после этого.   -  person Lin    schedule 03.10.2017
comment
Тогда драйвер работает и в методе Evaluate? Похоже, вы еще не нашли подходящую абстракцию для своей задачи.   -  person sdgfsdh    schedule 03.10.2017
comment
Трудно помочь, не зная, что на самом деле делают Evaluate и Predict. Похоже, что Predict не является обычным явлением для Evaluator, поэтому его просто не должно быть внутри этого класса. Вы можете переместить его в другой класс Predictor, который вы будете использовать внутри своего класса driver после вызова evaluate. Predictor может получить ответ от Evaluator и сделать свое дело.   -  person Alex Buyny    schedule 03.10.2017


Ответы (1)


Поскольку мы говорим о шаблонах проектирования, я думаю, то, что вы описываете, напоминает шаблон метода шаблона< /а>.

Суть в том, что вы определяете скелет вашего алгоритма в базовом классе и позволяете подклассам выбирать, хотят ли они реализовать определенные шаги или нет. Вот пример.

abstract class EvaluatorPredictor {
  void evaluateAndPredict() {
    evaluate();
    predict();
  }

  protected void evaluate() {
    // no-op
  };

  protected void predict() {
    // no-op
  };
}

class AEvaluator extends EvaluatorPredictor {
  protected void evaluate() {
    System.out.println("I implement this");
  }
}

class BEvaluator extends EvaluatorPredictor {
  protected void evaluate() {
    System.out.println("I implement this");
  }


  protected void predict() {
    System.out.println("I implement this too");
  }
}

Обратите внимание, что вы можете получить больше мощности с композицией. Например, EvaluatorPredictor или любой другой класс в этом отношении может использовать два объекта, которые реализуют интерфейс Evaluator и Predictor соответственно.

class Whatever {
  constructor(e Evaluator, p Predictor) {
    this.e = e;
    this.p = p;
  }

  void evaluateAndPredict() {
    this.e.evaluate();
    this.p.predict();
  }
}

PS. Извините за мой плохой синтаксис, похожий на Java. Я уверен, что есть ошибки в объявлениях методов или модификаторах доступа. Надеюсь, вышеизложенное поможет.

person gkats    schedule 04.10.2017