"Это почти" отношения в Скале - PullRequest
1 голос
/ 11 ноября 2011

Я пытаюсь разработать иерархию классов с кучей похожих классов, которые не совсем разделяют отношения "есть". Давайте назовем эти Model классы. Эти классы предназначены для сопряжения с набором похожих алгоритмов, которые используют классы Model, но не имеют одинаковых требований. Давайте назовем эти Strategy классы. Хитрость в том, что классы Strategy требуют многих из классов Model, но не все классы Model могут реализовать эти необходимые методы. Мне бы не хотелось иметь пустые методы-заглушки, которые будут просто генерировать UnsupportedOperationException s, а вместо этого использовать безопасный для типов способ решения этой проблемы - есть ли шаблон проектирования, который я могу применить?

Например,

object Main extends App {
    trait A {
        def f(one: Int): Int
        def g(two: Int): Int
        def h(three: Int): Int
    }

    class A1 extends A {
        override def f(one: Int): Int = {one + 1}
        override def g(two: Int): Int = {two + 2}
        override def h(three: Int): Int = {assert(false); 0}
    }

    class A2 extends A {
        override def f(one: Int): Int = {assert(false); 0}
        override def g(two: Int): Int = {two - 2}
        override def h(three: Int): Int = {three - 3}
    }

    trait B {
        def combine(i: Int): Int
    }

    trait B1 extends B {
        this: A =>
        override def combine(i: Int) = {f(i) + g(i)}
    }

    trait B2 extends B {
        this: A =>
        override def combine(i: Int) = {g(i) + h(i)}
    }

    override def main(args: Array[String]): Unit = {
        val a11 = new A1 with B1
        val a22 = new A2 with B2

        println(a11.combine(3))
        println(a22.combine(3))

        val a12 = new A1 with B2
        println(a12.combine(3))
    }
}

Здесь A - это класс Model, а B - это класс Strategy. Обратите внимание, что A1 может быть не в состоянии реализовать h() и что A2 может быть не в состоянии реализовать f(), и в зависимости от класса стратегии это может быть или не быть проблемой. Мне бы хотелось узнать, какая реализация A может работать с какой реализацией B во время компиляции.

Я использовал self-types для выражения отношения «больше», чем «это», которое обычно идет с расширением.

Ответы [ 2 ]

5 голосов
/ 11 ноября 2011

Вот мое решение:

trait F { def f(one: Int): Int }
trait G { def g(two: Int): Int }
trait H { def h(three: Int): Int }

trait A
trait A1 extends A with F with G {
  def f(one: Int): Int = { one + 1 }
  def g(two: Int): Int = { two + 2 }
}
trait A2 extends A with G with H {
  def g(two: Int): Int = { two - 2 }
  def h(three: Int): Int = { three - 3 }
}

trait B {
  def combine(i: Int): Int
}
trait B1 extends B {
  this: A with F with G =>
  def combine(i: Int) = { f(i) + g(i) }
}
trait B2 extends B {
  this: A with G with H =>
  def combine(i: Int) = { g(i) + h(i) }
}

val a11 = new A1 with B1
val a22 = new A2 with B2

println(a11.combine(3))
println(a22.combine(3))

val a12 = new A1 with B2 // won't compile as you wanted
2 голосов
/ 11 ноября 2011

Вы также можете использовать структурные типы, классы даже не должны быть связаны в их иерархии:

class A1 {
  def f(i: Int) = {i + 1}
  def g(i: Int) = {i + 2}
}

class A2 {
  def g(i: Int) = {i * 2}
  def h(i: Int) = {i * i}
}

type FnG = { def f(i: Int): Int; def g(i: Int): Int}

class B {
  def combine1(a: FnG, i: Int) = a.f(i) + a.g(i)
  def combine2(a: { def g(i: Int): Int; def h(i: Int): Int}, i: Int) = 
      a.g(i) + a.h(i)
}

val a1 = new A1
val a2 = new A2
val b = new B
println(b combine1(a1, 3))
println(b combine2(a2, 3))

trait C {
  def combine(i: Int): Int
}
trait C1 extends C {
  this: FnG =>
  def combine(i: Int) = f(i) + g(i)
}
trait C2 extends C {
  this: { def g(i: Int): Int; def h(i: Int): Int} =>
  def combine(i: Int) = g(i) + h(i)
}

val newA1 = new A1 with C1
val newA2 = new A2 with C2

println(newA1 combine(3))
println(newA2 combine(3))

Таким образом, вам нужно только указать (для признаков), что базовые типы поддерживают определенные методы, а для классов, что переданный в классе поддерживает конкретные методы. Вам не нужно принимать какую-либо иерархию.

...