类型类的工厂方法需要scala设计建议

时间:2015-09-22 21:16:01

标签: java scala

我正在尝试将java webapp转换为scala。我一直在努力解决Type Mismatch错误,这对于确定问题的原因或者我不知道如何阅读它们没有多大帮助。我试过在下面的帖子中询问整个设计中的问题,但看起来这并没有帮助我。
Scala type mismatch errors when using type classes and factory methods

所以我把我的问题分解成小块。希望这将导致最终的解决方案。有人可以帮我解决DataContext和DataContextBuilder之间的契约吗?

从上面的帖子中取出的部分:

trait DBObject
trait AggDBObject extends DBObject 
trait RawDBObject extends DBObject 
class Dimensions1Agg extends AggDBObject 
class Dimensions2Agg extends AggDBObject 
class Dimensions1Raw extends RawDBObject

trait IDataContext[A <: DBObject]  {
    var XsummaryData: A = _
    var XByDateData : Map[String, A] = _
    ... more feilds ..

    def restrictAccess = {.. some impl ..}
}

trait IDataContextBuilder[A <: DBObject] {
    def initDataPoints(dataContext: IDataContext[A]): Unit
}
class Dimension1AggContextBuilder extends IDataContextBuilder[Dimension1Agg] {
 .. override method impl..
}


object IDataContext {
  def apply(sId: Int, mId: Int): DataContext[_ <: DBObject]  = { 

    (sId, mId) match {

      case (1, 1) => {
        new DataContext[Dimension1Agg]()
      }
      case (1, 2)  => {
        new DataContext[Dimension1Raw]()
      }
    }
  }
}

我正在创建的对象是否有正确的返回类型的apply方法?如果我删除它然后我在调用站点“val dataContext:DataContext [_&gt;:Dimension1Agg with Dimension1Raw&lt;:DBObject] with Product with Serializable {..}”

获得一些复杂的返回类型
object IDataContextBuilder {

  def apply(sId: Int, daId: Int): IDataContextBuilder[_ <: DBObject]  = {

    (sId, daId) match {

      case (1, 1) => {
        new Dimension1AggContextBuilder

      }
      case (1, 2) => {
        new Dimension1RawContextBuilder
      }
    }
  }
}

您是否看到上述工厂方法的一般问题?

无论如何,在将DataContext传递给DataContextBuilder

时出现以下错误

类型不匹配; found:IDataContext [_ $ 1]其中type $ 1&lt ;:需要DBObject:IDataContext [ $ 19]

上面是_ $ 1和_ $ 19,因为scalac无法确定类型?

编辑:
将站点调用为接收用户请求参数的DataService类,编排所有上述组件并撰写响应。我知道它不起作用,但我正在尝试进行渐进式重构。 DataService检查请求参数,将该信息传递给工厂方法以创建多个DataContext和DataContextBuilder以及DataWorker;调用所有DataContextBuilder的initDataPoints方法,等待它们完成;调用所有Dataworker的generateView方法,等待它们完成,最后组成响应。

1)将每个度量的映射初始化为DataContext var measureToDCMap = MapInt,IDataContext [_&lt;:DBObject] 2)对于每个measuer,它执行以下操作以填充DataContext

Get concrete DataContext from its factory method
Get concrete DataContextBuilder from its factory method
Call DataContextBuilder's initDataPoints method

3)此时所有DataContext都填充了相应的DimensionData。

4)为每个矩阵的每个度量的每个视图初始化dataView映射(每天平均推文,每个主题的平均推文,推文率百分位等)

var dataView = MapInt,Map [Int,Map [Int,List [DataView]]] 5)对于每个视图,它执行以下操作以生成最终可查看响应

Get concrete ViewWorker . i.e. Metrix1ViewWorker or MetrixViewWorker
Call getData method

6)产生最终答案

2 个答案:

答案 0 :(得分:1)

这是一种简化它的方法。我拿出了一些额外的接口等等:

trait DBObject
trait AggDBObject extends DBObject
trait RawDBObject extends DBObject
class Dimensions1Agg extends AggDBObject
class Dimensions2Agg extends AggDBObject
class Dimensions1Raw extends RawDBObject

class DataContext[A <: DBObject] {
  var XsummaryData: A = _
  var XByDateData : Map[String, A] = _
  def restrictAccess = {
    // .. some impl ..
  }
}

object DataContext {

  def buildDim1Agg() : DataContext[Dimensions1Agg] = {
    // Put the custom code for Dim1 you had at `initDataPoints`
    // ...
    new DataContext[Dimensions1Agg]()
  }

  // Add other builder methods if you have lots of custom code, or functions shared across builders.

  /** This is your main builder function. 
    * Note that per your request, it is not polymorphic, but tied to a specific type of DBObject.  */
  def apply(sId: Int, mId: Int): DataContext[_ <: DBObject]  = {

    (sId, mId) match {
      case (1, 1) => buildDim1Agg()
      case (1, 2)  => buildDim1Agg()
        //... add more cases and a default case.
    }
  }
}

您没有发布会调用所有这些内容的代码,并且缺少某些上下文,所以我做了很多假设。

特别是,如果你愿意在这里做实际的OOP,你可以将每个DBObject类的自定义代码移动到该特定类的构造函数中(每个类都知道如何初始化自己)并拥有一个更通用的DataContext关心它存储的具体类型:

trait DBObject
trait AggDBObject extends DBObject
trait RawDBObject extends DBObject

class Dimensions1Agg extends AggDBObject {
  // Put the custom code you had at `initDataPoints`
}

class Dimensions2Agg extends AggDBObject {
  // Put the custom code you had at `initDataPoints`
}

class Dimensions1Raw extends RawDBObject {
  // Put the custom code you had at `initDataPoints`
}


class DataContext(var summaryData: DBObject) {

  var XByDateData : Map[String, DBObject] = _

  def restrictAccess = {
    // .. some impl ..
  }
}

object DataContext {
  /** This is your main builder function.
    * This version IS polymorphic, your data context holds a DBObject of the right type, but callers don't need to know which one. */
  def apply(sId: Int, mId: Int): DataContext  = {

    (sId, mId) match {
      case (1, 1) => new DataContext(new Dimensions1Agg())
      case (1, 2)  => new DataContext(new Dimensions2Agg())
      //... add more cases and a default case.
    }
  }
}

希望它有所帮助。

答案 1 :(得分:0)

  

无论如何,在将DataContext传递给DataContextBuilder

时出现以下错误
type mismatch; found : IDataContext[_$1] where type $1 <: DBObject required: IDataContext[$19] is _$1 and _$19 because scalac can't determine types?

在这种情况下,DataContext.apply可以返回DataContext[Dimension1Agg]IDataContextBuilder.apply返回Dimension1RawContextBuilder。如果您将相同的参数传递给两者,则不会发生这种情况,但类型允许它,因此scalac正确拒绝该程序。我在这种情况下所做的是避免存在性返回类型,例如

object DataContext {
  def apply(sId: Int, mId: Int)[A <: DBObject]: DataContext[A] = ...

  // or separate methods returning DataContext[Dimension1Agg] etc
}

如果可能的话。