Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 带有继承和混合的Scala可测试代码_Java_Scala_Unit Testing_Oop_Testability - Fatal编程技术网

Java 带有继承和混合的Scala可测试代码

Java 带有继承和混合的Scala可测试代码,java,scala,unit-testing,oop,testability,Java,Scala,Unit Testing,Oop,Testability,我已经用Java开发了很多代码,并且涉猎了Groovy和Haskell,这使我现在接触到了Scala 我对Scala的功能方面感觉比较舒服,但我发现自己对Scala中的面向对象设计有点动摇,因为它感觉与Java有点不同,特别是由于traits/mix-ins 我的目标是编写尽可能可测试的代码,这在我的Java开发中一直转化为关注 尽可能不可变 更喜欢由构造函数注入状态 总是选择合成而不是继承(严重受其影响,并且可能对其反应过度) 现在我正试图在这个新的Scala领域站稳脚跟,我很难弄清楚我应

我已经用Java开发了很多代码,并且涉猎了Groovy和Haskell,这使我现在接触到了Scala

我对Scala的功能方面感觉比较舒服,但我发现自己对Scala中的面向对象设计有点动摇,因为它感觉与Java有点不同,特别是由于traits/mix-ins

我的目标是编写尽可能可测试的代码,这在我的Java开发中一直转化为关注

  • 尽可能不可变
  • 更喜欢由构造函数注入状态
  • 总是选择合成而不是继承(严重受其影响,并且可能对其反应过度)
现在我正试图在这个新的Scala领域站稳脚跟,我很难弄清楚我应该采取什么方法,特别是我是否应该开始为某些目的使用继承

Scala编程(Wampler和Payne;O'Reilly,第二版)有一节有考虑事项(“良好的面向对象设计:离题”),我已经读了很多关于这方面的文章,但我没有看到明确提到可测试性的设计考虑。本书就如何使用继承提供了以下建议:

  • 抽象基类或特征在一个级别上由具体类(包括case类)子类化
  • 除了以下两种情况外,混凝土类从来都不是子类:
    • 与traits(…)中定义的其他行为混合的类
    • 仅测试版本以促进自动单元测试
  • <> LI>当子类似乎是正确的方法时,考虑将行为划分为特征并混合这些特征。
  • 决不跨父子类型边界拆分逻辑状态
  • 一些研究也表明了这一点

    所以本质上我有两个问题:

  • 即使考虑到可测试性,是否存在使用继承更好的常见情况

  • 混合插件是否提供了增强代码可测试性的好方法


  • 我已经做了一个可以体验使用混合插件和组成的组合

    所以,举例来说,使用组件将行为混合到特定的特征中。下面的示例显示了一个类中使用多个dao层特征的结构

    trait ServiceXXX {
      def findAllByXXX(): Future[SomeClass]
    }
    
    trait ServiceYYY {
      def findAllByYYY(): Future[AnotherClass]
    }
    
    trait SomeTraitsComponent {
      val serviceXXX: ServiceXXX
      val serviceYYY: ServiceYYY
    }
    
    trait SomeTraitsUsingMixing { 
      self: SomeTraitsComponent => 
    
      def getXXX() = Action.async {
        serviceXXX.findAllByXXX() map { results => 
          Ok(Json.toJson(results))
        }
      }
    
      def getYYY() = Actiona.async {
        serviceYYY.findAllByYYY() map {results => 
          Ok(Json.toJson(results))
        }
      }
    }
    
    之后,您可以声明一个具体组件,并通过示例将其绑定到伴随对象:

    trait ConreteTraitsComponent extends SomeTraitsComponent {
      val serviceXXX = new ConcreteServiceXXX
      val serviceYYY = new ConcreteServiceYYY
    }
    
    object SomeTraitsUsingMixing extends ConreteTraitsComponent
    
    使用此模式可以轻松创建测试组件,并使用mock测试您的tait/类的具体行为:

    trait SomeTraitsComponentMock {
      val serviceXXX = mock[ServiceXXX]
      val serviceYYY = mock[ServiceYYY]
    }
    
    object SomeTraitsUsingMixingMock extends SomeTraitsComponentMock
    

    在您的规范中,您可以使用ScalaMock声明控制服务的结果,您引用的Q/A中的trait用法实际上是处理混合trait所提供的灵活性

    在本例中,当您显式扩展一个trait时,编译器会在编译时锁定类和超类的类型。在本例中,MyService是一个
    LockingFlavorA

    trait Locking { // ... }
    
    class LockingFlavorA extends Locking { //... }
    
    class MyService extends LockingFlavorA {
    
    }
    
    当您使用键入的自引用时(如您所指的Q/a所示):

    。。
    锁定
    可以指
    锁定
    本身,或
    锁定
    的任何有效子类。然后作者在调用站点混合了锁定实现,而没有为此明确创建新类:

    val myService: MyService = new MyService with JDK15Locking
    
    我认为,当他们说您可以简化测试时,他们实际上是在谈论使用此功能来模拟我们Java开发人员通常对组合和模拟对象所做的事情。您只需制作一个模拟的
    锁定
    实现,并在测试期间将其混合,然后为运行时制作一个真正的实现

    对于您的问题:这比使用模拟库和依赖项注入更好还是更差?这很难说,但我认为最终很大程度上取决于一种技术或另一种技术与代码库其余部分的配合程度

    如果您已经在使用组合和依赖注入来获得良好的效果,我认为继续使用这种模式可能是一个好主意

    如果您刚刚起步,还没有真正需要所有这些功能,或者还没有从哲学上确定依赖项注入适合您,那么您可以在运行时复杂性方面以很小的成本从混合中获得很多好处

    我认为真正的答案将被证明是高度情境化的

    TL;DR低于

    问题1)我认为这是一个情境上有用的替代合成/副合成的方法,但我不认为它提供了除了简单性以外的任何主要好处


    问题2)是的,它可以提高可测试性,主要是通过trait实现来模拟模拟对象。

    谢谢您的回答。你能更详细地解释一下为什么你选择将
    concreteTraitComponent
    作为一个特征而不是一个类吗?我更喜欢尽可能长地将一个函数单元声明为一个特征,因为在scala中,只能从多个特征中进行。谢谢。我真的很欣赏这个答案,因为它为我的参考文献提供了很好的注释,并直接回答了问题。没问题,Scala是一种有趣的语言,很高兴了解这些更深奥的语言特性如何与世界上人们已经在做的事情相适应。这是个好问题。嗯。。。从它巨大的零分来看,我想它本可以更好。但是我得到了我的答案,这是最重要的部分。哈哈,人们都是自私自利的,包括我在内。我把它记下来了,我真的认为这是一个好问题。
    val myService: MyService = new MyService with JDK15Locking