Смешивание черты и базового класса одним и тем же методом - PullRequest
0 голосов
/ 16 января 2019

Могу ли я безопасно наследовать как черту, так и базовый класс, которые используют один и тот же метод?

Как:

trait MyTrait {
   def getValue:String
}

class SomeClass {
   def getValue:String = [concrete implementation]
}

, то:

class MyClass extends SomeClass with MyTrait

Причина для этого заключается в том, что я хотел бы использовать его в другом (абстрактном) классе, который определяет требуемый API только через черту MyTrait, и я не могу SomeClass наследовать эту черту напрямую (потому что из другой не связанной библиотеки).

abstract class AbstractOtherClass {
   val mainObject:MyTrait
}

и затем конкретная реализация этого класса:

class OtherClass extends AbstractOtherClass {
   val mainObject:MyTrait = new MyClass

   def something = {
      println(mainObject.getValue)  // shall call SomeClass.getValue
   }
}

Решение
После предложения SergGr у меня получилось такое решение:

abstract class AbstractOtherClass {
   def getValue:String
}

class OtherClass extends AbstractOtherClass {
   val mainObject:SomeClass = ...

   def getValue:String = mainObject.getValue
}

1 Ответ

0 голосов
/ 16 января 2019

Похоже, здесь есть два разных вопроса

  1. Это работает просто случайно?
  2. Это хороший дизайн?

Пока ваш trait имеет только объявление метода, но не имеет реализации, он работает по проекту. Это один из необычных, но ожидаемых шаблонов использования. Если несколько trait s или trait и class имеют реализации одного и того же метода, все еще существует явно заданная логика линеаризации , которая определяет, как разрешать конфликты, но я бы сказал, что в большинстве случаев полагаться на это - плохой дизайн.

То, что вы пытаетесь сделать, для меня выглядит как шаблон адаптера , и я бы сказал, что с точки зрения проектирования лучше реализовать его с использованием композиции объектов, а не наследования:

trait MyTrait {
   def getValue:String
}

class SomeClassWrapper(delegate: SomeClass) extends MyTrait {
    override def getValue:String = delegate.getValue       
}

Делая это таким образом, вы не привязываете свой API к реализации библиотеки. Например, вы можете переименовать метод в значение getAbcValue, если это имеет больше смысла в вашем контексте. Или, если в какой-то момент вы найдете другую библиотеку, которая выполняет ту же работу лучше, но которая имеет другое имя для такого метода (например, calculateValue), вам нужно создать еще один класс-оболочку и не нужно изменять все getValue звонки на calculateValue звонки.

...