design-patterns - 具有相同参数化类型的 Scala mixin

标签 design-patterns scala

我对 scala 非常陌生,并且正在寻求适应 scala 特定模式。
目标是将消息处理和消息生成分开。存在表示消息处理的基本协变参数化类型。具体实现可以通过常规的mixin组合,也可以通过底层协议(protocol)的混合实现。

要求是:

  • 尽可能简单地扩展
  • 类型安全以防止愚蠢的错误

  • 我提供了干净的示例代码(包含定义和使用):
    trait Protocol
    
    trait Handler [+proto <: Protocol] {
      def handle : PartialFunction[Protocol,Unit] 
      /* can not name the actual protocol type since handler for a subtype also fully supports handling supertype
    besides any message extends other subtype ot the supertype since the scala use matching with case classes
    and these algebraic type realization is seemed excluded from strait scala type system
      */
    }
    
    /*
    ==============
    using scenario
    ==============
    */
    
    trait SumMsg extends Protocol
    case class Sum(op : Int) extends SumMsg
    case class Sub(op : Int) extends SumMsg
    
    trait ProdMsg extends Protocol
    case class Mul(op : Int) extends ProdMsg
    case class Diff(op : Int) extends ProdMsg {
      require (0 != op, "Division by zero is not permited")
    }
    
    /* stackable traites */
    trait NullHandler {
      def handle : PartialFunction[Protocol,Unit] = { case _ => {} }
    }
    
    trait SumHandler extends Handler [SumMsg] with NullHandler{
      var store : Int
      abstract override def handle : PartialFunction[Protocol,Unit] = ({
    case Sum(op) => { this.store += op}
    case Sub(op) => { this.store -= op}
      }: PartialFunction[Protocol,Unit]) orElse super.handle
    }
    
    trait MulHandler extends Handler [ProdMsg] with NullHandler{
      var store : Int
      abstract override def handle : PartialFunction[Protocol,Unit] = ({
    case Mul(op) => {this.store *= op}
    case Diff(op) => {this.store /= op}
      }: PartialFunction[Protocol,Unit]) orElse super.handle
    }
    
    /* concrete classes */
    class SumAccum (var store: Int) extends SumHandler
    
    class MulAccum (var store: Int) extends MulHandler
    
    class ArithmAccum (var store: Int) extends SumHandler with MulHandler
    
    /* producers */
    class ProduceSums (val accum : Handler [SumMsg]) {
      var state : Boolean = true
      def touch() = if (this.state)
    {
      this.state = false
      this.accum.handle(Sum(2))
    } else {
      this.state = true
      this.accum.handle(Sub(1))
    }
    }
    
    class ProduceProds (val accum : Handler [ProdMsg]) {
      var state : Boolean = true
      def touch() = if (this.state)
    {
      this.state = false
      this.accum.handle(Mul(2))
    } else {
      this.state = true
      this.accum.handle(Diff(2))
    }
    }
    
    /* tying together via cake pattern */
    trait ProtocolComp {
      type Proto <: Protocol
    }
    
    trait ProducerComp { this: ProtocolComp =>
      type ProducerT <: {def touch()}
      def getProducer(accum : Handler[Proto]) : ProducerT
    }
    
    trait HandlerComp { this: ProtocolComp =>
      type HandlerT <: Handler[Proto]
      def getHandler(store:Int) : HandlerT
    }
    
    trait AppComp extends ProtocolComp with ProducerComp with HandlerComp {
      val initStore = 1
      def test() {
    val handler = getHandler(initStore)
    val producer = getProducer(handler)
    producer.touch()
      }
    }
    
    /* different examples of compositions */
    
    /* correct usage */
    
    object One extends AppComp{
      override type Proto = SumMsg
      override type ProducerT = ProduceSums
      override type HandlerT = SumAccum
      override def getProducer(accum : Handler[Proto]) = new ProduceSums(accum)
      override def getHandler(store : Int) = new SumAccum(store)
    }
    
    object Two extends AppComp{
      override type Proto = SumMsg
      override type ProducerT = ProduceSums
      override type HandlerT = ArithmAccum
      override def getProducer(accum : Handler[Proto]) = new ProduceSums(accum)
      override def getHandler(store : Int) = new ArithmAccum(store)
    }
    
    object Three extends AppComp{
      override type Proto = SumMsg with ProdMsg
      override type ProducerT = ProduceSums
      override type HandlerT = ArithmAccum
      override def getProducer(accum : Handler[Proto]) = new ProduceSums(accum)
      override def getHandler(store : Int) = new ArithmAccum(store)
    }
    
    /* incorrect usage
    static type checking protects from some kind of logic errors
    */
    
    object Four extends AppComp{
      override type Proto = SumMsg
      override type ProducerT = ProduceProds
      override type HandlerT = SumAccum
      override def getProducer(accum : Handler[Proto]) = new ProduceProds(accum)
      override def getHandler(store : Int) = new SumAccum(store)
    }
    

    最后一个示例没有很好地键入并按预期给出错误:
    mixed.scala:140: error: type mismatch;
    found   : Handler[Four.Proto]
    required: Handler[ProdMsg]
      override def getProducer(accum : Handler[Proto]) = new ProduceProds(accum)
    

    我已经构建了一个灵活的系统,具有简单的组合和扩展,但使用 scala 的案例类而不是 alegraic 类型尽可能类型安全

    我几乎实现了我的目标,但遇到了一个很大的 scala 错误功能:底层 jvm 的类型删除。我使用的结构对于 scala 是非法的,因为我希望参数化特征可以通过“with”子句进行扩展。

    编译器提示
    mixed.scala:53: error: illegal inheritance;
    class ArithmAccum inherits different type instances of trait Handler:
    Handler[ProdMsg] and Handler[SumMsg]
    class ArithmAccum (var store: Int) extends SumHandler with MulHandler
    

    我有什么选择?我无法使用我设计的模式,需要通过可用性找到同等的替代品。有人可以建议替代源代码解决方案吗?是否有 scala 插件(它们似乎存在于编译器)或另一种方法来将 scala 参数化类型的后端从 java 泛型更改为 c++ 之类的代码生成?

    最佳答案

    您的问题不是 JVM 的类型删除,而是 Scala 使用线性化来解决继承问题。

    从 Handler 中移除类型参数 [+proto <: Protocol]。无论如何,handle 方法不使用它。那么你的非法继承错误就会消失。

    关于design-patterns - 具有相同参数化类型的 Scala mixin,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6637933/

    相关文章:

    java - 如何设计接口(interface)以方便 Java 测试?

    c# - 设计模式 C# 存储库

    java - 使用具有 "old"对象引用的 Builder 模式创建新对象

    scala - 使用进纸器确保 Gatling 中的 CSV 记录

    scala - 迭代 RDD 并更新可变集合返回一个空集合

    java - 如何在 Play 2.5.x (Scala) 中禁用弱密码和客户端重新协商

    objective-c - 单例实例与类方法

    java - 扩展 BigDecimal?

    scala - 如何调试 Scala Future?

    scala - Spark DataFrame 计算列