#scala #design-patterns
#scala #шаблоны проектирования
Вопрос:
У меня есть абстрактный класс (библиотека Java), который принимает аргументы конструктора и имеет вызываемый метод execute
, который я хочу украсить:
public abstract class Task {
private final String name;
protected Task(String name) {
this.name = name;
}
public abstract void execute(String str) throws Exception
}
И у меня есть классы Scala, которые в настоящее время наследуют предыдущий:
class FooTask extends Task("fooTask") {
override def execute(str: String): Unit = println(str "foo")
}
class BarTask extends Task("barTask") {
override def execute(str: Strin): Unit = println(str "bar")
}
Возможно ли написать декоратор для класса задач, подобного этому:
trait TaskWithErrorLogging { self: Task =>
override def execute(str: String): Unit =
Try(self.execute(str)) match {
case Failure(exception) =>
println("LOGGED " exception)
throw exception;
case _ =>
}
}
А затем использовать его для регистрации ошибок?
class BarTask extends Task("barTask") with TaskWithErrorLogging {
override def execute(str: String): Unit = println(str "bar") // Error should be logged
}
Эти задачи создаются автоматически инжектором фреймворка, поэтому нет способа написать new FooTask with TaskWithErrorLogging
В настоящее время переопределенный метод декоратора игнорируется (он компилируется, но не выполняется). Добавление abstract
модификатора к методу в декораторе не компилируется. Как правильно реализовать это решение для ведения журнала? Может быть, есть другой вариант, кроме наращиваемого признака?
Комментарии:
1. Я не уверен, правильно ли я понимаю проблему, но если
tasks are being instantiated automatically by framework
тогда вы просто не можете использовать подход stackable trait decorator, потому что это функция времени компиляции. Созданиеstack
с помощью usingwith
происходит так же, как если бы вы объявили новый класс. Вы не можете объявить класс на основе уже созданного объекта.2. Я имею в виду, что я не могу объявить это
class BarTask extends Task("barTask")
, а затем написатьnew BarTask with TaskWithErrorLogging
, я могу только написатьclass BarTask extends Task("barTask") with TaskWithErrorLogging
3. Как я уже писал, этот подход не работает или не компилируется (зависит от того, добавляете ли вы
abstract
модификатор к признаку)4. кстати, ваш признак
TaskWithErrorLogging
не может быть скомпилирован в scala, потому что он имеет специфичное для Java объявление параметра.5. Я исправил описание кода, спасибо
Ответ №1:
В настоящее время переопределенный метод декоратора игнорируется (он компилируется, но не выполняется)
Он не выполняется, потому что он переопределен BarTask
. И если он выполнялся, он имеет бесконечную рекурсию: self.execute(str)
вызовет тот же метод.
Самый простой способ
trait TaskWithErrorLogging extends Task {
override def execute(str: String): Unit =
Try(doExecute(str)) match {
case Failure(exception) =>
println("LOGGED " exception)
throw exception;
case _ =>
}
def doExecute(str: String): Unit
}
class BarTask extends Task("barTask") with TaskWithErrorLogging {
override def doExecute(str: String): Unit = println(str "bar")
}
Или, если вы действительно хотите использовать наращиваемые декораторы, TaskWithErrorLogging
их все равно нужно смешивать после метода, который он украшает, например
trait TaskWithErrorLogging extends Task {
abstract override def execute(str: String): Unit =
Try(super.execute(str)) match { // note super, not self!
case Failure(exception) =>
println("LOGGED " exception)
throw exception;
case _ =>
}
}
class BarTask0 extends Task("barTask") {
override def execute(str: String): Unit = println(str "bar")
}
class BarTask extends BarTask0 with TaskWithErrorLogging
Комментарии:
1. Вы имеете в виду, что нет другого способа, кроме как добавить другой метод с другим именем вместо
execute
прямого вызова?