我是使用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特征时,请回复。我希望一个占用时间的答案实际上会通过提供一些新信息(没有冒犯)来增加价值
答案 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
有些事情需要注意(好的或坏的,取决于你):
Builder
或Factory
个对象/函数来构建这些东西LivingBeing
- 类型层次结构完全是平的LivingBeing
完全不可变,因此我们会在更改状态(例如,开始或停止怀孕)时制作副本LivingBeing
是一个案例类,我们可以免费获得惊人的copy()
函数,让我们专注于这些状态转换中的变化Set
,因为:
-
这样的精美Scala集合运算符来删除元素WeightTransform
是可以独立测试的,没有任何关于与其他任何东西混合的期望(没有super
头痛)