【问题标题】:Design Issue: Trying to avoid code duplication using scala type bounds设计问题:尝试使用 Scala 类型边界避免代码重复
【发布时间】:2012-10-04 19:36:12
【问题描述】:

我是 Scala 新手,我正在尝试使用类型边界来避免以下代码中的代码重复(从不相关的东西中清除):

trait StandardStep1[-I1] {
  def next_step(i:I1):StandardStep2
}

trait StandardStep2

trait UniqueStep1[-I1] extends StandardStep1[I1] {
  def next_step(i:I1):UniqueStep2
}

trait UniqueStep2 extends StandardStep2

class DoubleStandardStep1[-IL,-IR](left:StandardStep1[IL], right:StandardStep1[IR]) extends StandardStep1[(IL,IR)] {
  def next_step(i:(IL,IR)) = {
    val (i_left, i_right) = i
    val new_left = left.next_step(i_left)
    val new_right = right.next_step(i_right)
    new DoubleStandardStep2(new_left, new_right)
  }
}

class DoubleStandardStep2(left:StandardStep2, right:StandardStep2) extends StandardStep2

class DoubleUniqueStep1[-IL,-IR](left:UniqueStep1[IL], right:UniqueStep1[IR]) extends UniqueStep1[(IL,IR)] {
  def next_step(i:(IL,IR)) = {
    val (i_left, i_right) = i
    val new_left = left.next_step(i_left)
    val new_right = right.next_step(i_right)
    new DoubleUniqueStep2(new_left, new_right)
  }
}

class DoubleUniqueStep2(left:UniqueStep2, right:UniqueStep2) extends UniqueStep2

解释:

StandardStep1 表示某个状态机中的一个步骤,并且有一个next_step 操作,它为给定的输入提供一个StandardStep2

UniqueStep1StandardStep1 的一种特殊类型,它在调用 next_step 时给出 UniqueStep2 - 显然 UniqueStep2 必须从 StandardStep2 继承。

现在我必须编写一个 DoubleStep 包装器:DoubleStandardStep1 也包装 StandardStep1s,并在调用其 next_step 时返回一个 DoubleStandardStep2DoubleUniqueStep1 做同样的事情,但它返回一个 DoubleUniqueStep2

*DoubleStep*s'next_step 的实现有一个明显的代码重复:它们都将输入拆分为i_lefti_right,并以相同的方式调用它们封装的步骤'next_step

我想知道如何通过创建一个通用抽象 DoubleStep1 来完成这部分代码,从而消除这种代码重复:

object DoubleStepHelper {
  def next_step_args[IL,IR,SL <: StandardStep1[IL],SR <: StandardStep1[IR]](left:SL,right:SR)(i:(IL,IR)) = { 
    val (i_left, i_right) = i
    val new_left = left.next_step(i_left)
    val new_right = right.next_step(i_right)
    (new_left, new_right)
  }
}

class DoubleStandardStep1[-IL,-IR](left:StandardStep1[IL], right:StandardStep1[IR]) extends StandardStep1[(IL,IR)] {
  def next_step(i:(IL,IR)) = {
    ((l,r) => new DoubleStandardStep2(l,r)).tupled(DoubleStepHelper.next_step_args(left,right)(i))
  }
}

class DoubleStandardStep2(left:StandardStep2, right:StandardStep2) extends StandardStep2


class DoubleUniqueStep1[-IL,-IR](left:UniqueStep1[IL], right:UniqueStep1[IR]) extends UniqueStep1[(IL,IR)] {
  def next_step(i:(IL,IR)) = {
    ((l,r) => new DoubleUniqueStep2(l,r)).tupled(DoubleStepHelper.next_step_args(left,right)(i))
  }
}

class DoubleUniqueStep2(left:UniqueStep2, right:UniqueStep2) extends UniqueStep2

尝试编译此代码(scala 2.9.2)失败并显示以下消息:

类型不匹配; 找到:(this.StandardStep2,this.StandardStep2) 必需:(this.UniqueStep2,this.UniqueStep2) ((l,r) => new DoubleUniqueStep2(l,r)).tupled(DoubleStepHelper.next_step_args(left,right)(i)) ^

我假设发生这种情况是因为假设 DoubleStepHelper.next_step_args 返回一个 (StandardStep2, StandardStep2) 值,当我们想用 DoubleUniqueStep2 包装它时这是不好的。

你能想出解决这个问题的方法吗?如果SLSR继承自UniqueStep1,如何告诉编译器DoubleStepHelper的next_step_args可能返回(UniqueStep2, UniqueStep2)

一个不错的功能是能够将 next_step_args 的结果类型定义为类似 (SL.next_step, SR.next_step) 的东西,这意味着它的返回类型是从返回构建的SLSR 中的特定函数类型(next_step)。

“视图”边界能以任何方式解决它吗?

我可以使用 asInstanceOf 强制转换,但这看起来很难看。

谢谢

【问题讨论】:

    标签: scala design-patterns code-duplication


    【解决方案1】:

    我找到了一个不错的解决方案。我创建了AbstractStep1AbstractDoubleStep1,其中后续步骤的类型作为类型参数给出。

    然后我可以从这个类继承下一步是StandardStep2UniqueStep2的情况。

    代码如下:

    trait AbstractStep1[-I,+S2] {
      def next_step(i:I):S2
    }
    
    trait StandardStep1[-I] extends AbstractStep1[I,StandardStep2]
    
    trait StandardStep2
    
    trait UniqueStep1[-I] extends AbstractStep1[I,UniqueStep2] with StandardStep1[I]
    
    trait UniqueStep2 extends StandardStep2
    
    abstract class AbstractDoubleStep1[-IL,-IR,-S2L <: StandardStep2,-S2R <: StandardStep2,+S2](left:AbstractStep1[IL,S2L],right:AbstractStep1[IR,S2R]) extends AbstractStep1[(IL,IR),S2] {
      def wrapper(l:S2L,r:S2R):S2
    
      def next_step(i:(IL,IR)):S2 = {
        val (i_left, i_right) = i
        val new_left = left.next_step(i_left)
        val new_right = right.next_step(i_right)
        wrapper(new_left, new_right)
      }
    }
    
    class DoubleStandardStep1[-IL,-IR](left:StandardStep1[IL], right:StandardStep1[IR]) extends AbstractDoubleStep1[IL,IR,StandardStep2,StandardStep2,DoubleStandardStep2](left,right) {
      def wrapper(l:StandardStep2,r:StandardStep2) = {
        new DoubleStandardStep2(l,r)
      }
    }
    
    class DoubleStandardStep2(left:StandardStep2, right:StandardStep2) extends StandardStep2
    
    
    class DoubleUniqueStep1[-IL,-IR](left:UniqueStep1[IL], right:UniqueStep1[IR]) extends AbstractDoubleStep1[IL,IR,UniqueStep2,UniqueStep2,DoubleUniqueStep2](left,right) {
      def wrapper(l:UniqueStep2,r:UniqueStep2) = {
        new DoubleUniqueStep2(l,r)
      }
    }
    
    class DoubleUniqueStep2(left:UniqueStep2, right:UniqueStep2) extends UniqueStep2
    

    我认为也可以将AbstractDoubleStep1 转换为triat 而不是抽象类,这将使其在mixin 继承中更有用,但这是目前对我来说并不重要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多