Scala,trait继承和链接,处理专门的方法调用

时间:2015-07-08 04:45:25

标签: scala oop traits

我是使用scala的高级OO技术的新手。我想我知道特质链是如何工作的。我仍然不确定何时继承特质中的其他特征,何时使用自我'类型。如果相关,我也没有使用依赖注入。

编辑:我已经删除了类比并使用了我的实际域名问题,因为看起来我的问题并不清楚。我希望现在这个问题更简单了。

可以按名称搜索每个可搜索实体。如果实体位于物理位置,也可以按其位置进行搜索。如果实体是具有摘要的展览,则可以通过其摘要进行搜索。要进行搜索,我们需要一个权重图来分配每个组件。

我有一个可搜索的特征,以及从中实现的特征:PhysicallySituated,Exhibit。我的代码如下所示:

trait Searchable{ 
  def getWeight() = {
    val name: String 
    println( "getWeight in Searchable" )
    Map( name -> 20 ) 
  }
}

trait PhysicallySituated extends Searchable{
  val city: String
  val country: String

  override def getWeight() = {
    println( "getWeight in PhysicallySituated" )
    super.getWeight ++ Map( city -> 5, country -> 5 )
  }
}

trait Exhibit extends Searchable{
    val summary: String
    override def getWeight() = {
      println( "Regular getWeight in Exhibit" )
      super.getWeight ++ Map( summary -> 3 )
    }
}

class School extends Searchable with PhysicallySituated with Exhibit
class Book   extends Searchable with Exhibit

现在,有时我想根据这是否为“完整搜索”来创建getWeight Exhibit的专用版本。因此,此版本将采用参数getPrecise: Enumeration并返回精确的权重,其中还包括说明。

object Exhibit{
  object Search extends Enumeration{
    val IncludeDescription = Value
    val ExcludeDescription = Value
  }
}

trait Exhibit extends Searchable{
    val summary: String
    override def getWeight() = {
      println( "Regular getWeight in Exhibit" )
      super.getWeight ++ Map( summary -> 3 )
    }

    def getWeight( getPrecise: Exhibit.Search.Value ) = {
      println( "Specialized getWeight in IsGestating" )
      super.getWeight ++  if( getPrecise == Exhibit.Search.IncludeDescription ) Map( summary -> 4 /*4 != 3*/, description -> 2 )  else Map( summary -> 3 )
    }
}

object TraitChaining extends App{
  //Weight depends on order. 

  println( 
    ( new SearchableEntity with Exhibit with PhysicallySituated).
      getWeight( Exhibit.Search.IncludeDescription )  
  )

  println( 
    ( new SearchableEntity with PhysicallySituated with Exhibit).
      getWeight( Exhibit.Search.IncludeDescription )  
  )
}

我希望此服务的目的是,当我进行完整搜索时,有时候我想要在搜索中包含描述,而且我知道可搜索的集合有一些展品太

mycollection.filter( _.isInstanceOf[ Exhibit ] ).map( _.asInstanceOf[ Exhibit ].getWeight( getPrecise = Exhibit.Search.IncludeDescription ) ). ++ mycollection.filter( !_.isInstanceOf[ Exhibit ] ).map( _.getWeight ).sum //I know ++ will change order

我是否以正确的方式思考问题,或者从OO的角度来看,这种模式本身是不是没有?

我的预订是因为两件事:

1)特征继承顺序在这种情况下很重要,主要问题。让我想知道我的问题本身是否适合OOP模式。 2)如果我从中获得了另一个特征,并且它有另一个准确的搜索标准怎么办? (我可以使用@millhouse的答案作为灵感,传递一组标志而不是一个标志,每个特性都担心自己的

如果这不是正确的方法,那么处理这个问题的正确方法是什么?我希望你已经理解了这个问题的意图。只有当您认为自己是OO专家并且非常了解Scala特征时,请回复。我希望一个占用时间的答案实际上会通过提供一些新信息(没有冒犯)来增加价值

2 个答案:

答案 0 :(得分:2)

如@millhouse在您的问题评论中所述,您的建模属性会随trait s而变化。特征解析是在编译时完成的,所以如果你是LivingBeing扩展IsGestating特征,那将永远处于这种状态。

使用您尝试使用的trait被称为Scala中装饰器设计模式的实现。正如this link中报告的那样,在Scala中

  

委托是在编译时静态建立的,但只要我们可以任意组合装饰器代替对象创建,这通常就足够了。

OOP概念比编程语言处于更高的抽象层次。然后,每种编程语言都以不同的方式实现OOP概念,但它们仍然遵循它们。

在您的情况下,您需要建模一些演化机制,允许您更改LivingBeing的状态。例如,您可以使用装饰器模式而不是直接将其应用于LivingBeing,而是将其应用于内部对象,该对象模拟可能随时间改变其属性的状态。

答案 1 :(得分:2)

好的我打算对此进行一次拍摄,但看到我们正在使用Scala,如果不做一些功能性更强的东西并且不那么OO,这似乎是一种耻辱。

在我看来(至少根据你的例子),LivingBeing的权重可以被认为是一些基础,加上应用了多个转换它取决于它是否有翅膀,怀孕,有猎物等等。

那么为什么不这样建模呢?

type WeightTransform = Double => Double

case class LivingBeing(transforms:Set[WeightTransform] = Set(),
                       initialWeight:Double = 100D) {

  // the sequence of transforms is immutable, 
  // so getWeight can be a simple val rather than a def:
  val getWeight = transforms.foldLeft(initialWeight) { case (acc, xform) =>
    xform(acc)
  }
}

我们如何使用这样的东西?好吧,让我们定义一些WeightTransform个实例:

val wingBoost:WeightTransform = _ + 20

val gestatingBoost:WeightTransform = _ + 40

非常简单。现在让我们发布蝙蝠:

val bat = LivingBeing(Set(wingBoost))
// bat.getWeight == 120

val pregnantBat = bat.copy(weightTransforms = Set(wingBoost, gestatingBoost))
// pregnantBat.getWeight == 160

val pregnantBatThatGaveBirth = pregnantBat.copy(weightTransforms = pregnantBat.weightTransforms - gestatingBoost)
// pregnantBatThatGaveBirth.getWeight == 120

有些事情需要注意(好的或坏的,取决于你):

  • 我们已经从在班级定义生物到基于实例的方法
  • 当然你仍然可以编写BuilderFactory个对象/函数来构建这些东西
  • 每个实例只是一个LivingBeing - 类型层次结构完全是平的
  • LivingBeing完全不可变,因此我们会在更改状态(例如,开始或停止怀孕)时制作副本
  • 因为LivingBeing是一个案例类,我们可以免费获得惊人的copy()函数,让我们专注于这些状态转换中的变化
  • 转换是Set,因为:
    • 对于这个简单的例子,排序并不重要
    • 我们可以使用像-这样的精美Scala集合运算符来删除元素
  • WeightTransform是可以独立测试的,没有任何关于与其他任何东西混合的期望(没有super头痛)