代码之家  ›  专栏  ›  技术社区  ›  Sandor Murakozi

scala中的混合物与成分

  •  74
  • Sandor Murakozi  · 技术社区  · 14 年前

    在Java世界中(更确切地说,如果没有多重继承/混合),经验法则很简单:“赞成对象组合优于类继承”。

    我想知道如果你也考虑混音,特别是在斯卡拉,它是否会/如何改变?
    混合被认为是多重继承的一种方式,还是更多的类组合?
    是否还有一个“有利于对象组合而非类组合”(或相反的方法)的准则?

    我已经看到很多例子,当人们使用(或滥用)混合时,对象组合也可以完成任务,我不总是确定哪一个更好。在我看来,你可以用它们实现非常相似的事情,但也有一些不同之处,例如:

    • 可见性-与mixins一起,所有东西都成为公共API的一部分,而与composition不同。
    • 冗长-在大多数情况下,mixin不那么冗长,使用起来也比较容易,但情况并非总是如此(例如,如果在复杂的层次结构中也使用self-types)

    我知道简短的回答是“视情况而定”,但可能有一些典型的情况,当这或那是更好的。

    到目前为止,我可以提出一些指导原则的例子(假设我有两个特征a和b,a想使用b的一些方法):

    • 如果您想用B中的方法扩展A的API,那么就混合,否则就合成。但是,如果我创建的类/实例不是公共API的一部分,那么它就没有帮助。
    • 如果您想使用一些需要混合的模式(例如 Stackable Trait Pattern )那么这是一个简单的决定。
    • 如果您有循环依赖关系,那么自我类型的混合可以有所帮助。(我尽量避免这种情况,但并不总是那么容易)
    • 如果您想要一些动态的运行时决定如何进行组合,那么对象组合。

    在许多情况下,混合似乎更容易(和/或更不冗长),但我确信它们也有一些缺陷,如“上帝等级”和两篇Artima文章中描述的其他缺陷: part 1 , part 2 (顺便说一句,在我看来,大多数其他问题与scala无关/不太严重)。

    你有更多这样的暗示吗?

    2 回复  |  直到 6 年前
        1
  •  39
  •   jub0bs    6 年前

    如果只在类定义中混合抽象特性,然后在对象实例化时混合相应的具体特性,那么在scala中就可以避免人们使用mix-in的许多问题。例如

    trait Locking{
       // abstract locking trait, many possible definitions
       protected def lock(body: =>A):A
    }
    
    class MyService{
       this:Locking =>
    }
    
    //For this time, we'll use a java.util.concurrent lock
    val myService:MyService = new MyService with JDK15Locking 
    

    这个结构有几点值得推荐。首先,由于需要不同的特征功能组合,它可以防止类的爆炸。第二,它允许简单的测试,因为可以创建和混合“什么都不做”的具体特性,类似于模拟对象。最后,我们完全隐藏了所使用的锁定特性,甚至是正在进行的锁定,不让我们的服务消费者看到。

    由于我们已经克服了混合输入的大部分声称的缺点,我们仍然需要权衡。 在混合物和成分之间。对于我自己来说,我通常根据假设的委托对象是否完全由包含对象封装,或者它是否可能被共享并拥有自己的生命周期来做出决定。锁定为完全封装的委托提供了一个很好的例子。如果您的类使用锁对象来管理对其内部状态的并发访问,则该锁完全由包含对象控制,并且它及其操作都不会作为类的公共接口的一部分进行公布。对于这种完全封装的功能,我使用mix-in。对于共享的东西,如数据源,使用合成。

        2
  •  10
  •   VonC    14 年前

    您没有提到的其他差异:

    • 特性类没有任何独立的存在:

    ( Programming Scala )

    如果您发现某个特定的特性最常被用作其他类的父特性,从而使子类表现为父特性,那么考虑将该特性定义为类,以使这种逻辑关系更加清晰。
    (我们说 表现为 而不是 是一个 ,因为前者是更精确的继承定义,基于Liskov替换原则——例如,参见[Martin2003]。

    【Martin2003】:Robert C.Martin,《敏捷软件开发:原则、模式和实践》,Prentice Hall,2003年。

    • 混合蛋白 trait )没有构造函数参数。

    因此 advice, still from Programming Scala :

    避免特性中的具体字段可以初始化为合适的默认值。
    改用抽象字段 或者将特性转换为具有构造函数的类 .
    当然,无状态特性不存在初始化方面的任何问题。

    它是 良好的面向对象设计,从构造过程完成时开始,实例应始终处于已知的有效状态。 .

    最后一部分,关于 最初的 状态 对于一个对象,通常有助于确定给定概念的类(和类组成)和特征(和混合)。