2

使用 self-type 进行依赖注入,导致暴露其他 trait 的 public 方法,破坏了单一职责主体。让我用例子来说话

trait Output {
  def output(str: String): Unit
}

trait ConsoleOutput extends Output {
  override def output(str: String): Unit = println(str)
}

class Sample {
  self: Output =>

  def doSomething() = {
    // I do something stupid here!
    output("Output goes here!")
  }
}

val obj = new Sample with ConsoleOutput
obj.output("Hey there")

我的Sample班级依赖于Output特质,当然我想在我的班级中使用Output特质方法。Sample但是通过上面的代码示例,我的Sample类公开output了并非来自其功能的方法并打破了Sample.

我怎样才能避免它并继续使用自我类型和蛋糕模式?

4

2 回答 2

1

提供输出的责任仍然在于实现的组件Output。您的类提供对它的访问这一事实与以下内容没有什么不同:

  class Foo(val out: Output)
  new Foo(new ConsoleOutput{}).out.output

当然,您可以在out此处设为私有,但如果您也不想从外部访问它,您也可以在其中进行.output保护。ConsoleOutput

(您在另一个答案中的评论的答案是,如果您还想“独立”使用它,那么您将其子类化,并output在子类中公开)。

于 2019-01-05T13:01:13.373 回答
1

self 类型在这里并不重要。从另一个类继承公开该类的公共方法,而不管任何自身类型。因此,任何从具有公共方法的类的继承都可以说是打破了单一职责原则。

如果trait它打算用于依赖注入,那么它应该使其方法protected不被暴露。

trait Output {
  protected def output(str: String): Unit
}

trait ConsoleOutput extends Output {
  protected override def output(str: String): Unit = println(str)
}

评论接受的答案

接受的答案声称“提供输出的责任仍然在于实现的组件Output”。这是不正确的,并且显示了类型和实现之间的混淆。

对象的行为是由它的类型指定的,而不是它的实现(里氏替换原则)。类型是告诉用户对象可以做什么的合同。因此,指定职责的是类型,而不是实现

类型Sample with ConsoleOutputoutput来自Object类型的方法和来自类型的doSomething方法Sample。因此,它有责任提供这两种方法的实现。outputin的实现ConsoleOuput与类型无关,因此与谁负责它无关。

Sample with ConsoleOutput对象可以轻松地覆盖 的实现,output在这种情况下,它显然会负责该方法,而不是ConsoleOutput. Sample with ConsoleOutput选择不改变实现的事实output并不意味着它不对它负责。当实现改变时,对象的职责不会改变。

单一职责原则的解释

该原则是软件工程的五个SOLID原则中的第一个。正如 Wikipedia 解释的那样,“单一职责原则 [] 指出每个模块或类都应该对软件提供的单个功能部分负责,并且该职责应该完全由类封装。”

换句话说,不要这样做:

class MultipleResponsibilities {
   def computePi(places: Int): List[Int]
   def countVowels(text: String): Int
}

但改为这样做:

class PiComputer {
  def computePi(places: Int): List[Int]
}

class VowelCounter {
   def countVowels(text: String): Int
}

computePi并且countVowels是程序功能的不同部分,因此它们应该封装在不同的类中。

第三个 SOLID 原则是Liskov 替换原则,它说对象的功能应该完全取决于类型,并且不应该受到实现的影响。您应该能够更改实现并仍然以相同的方式使用对象并获得相同的结果。

由于对象的功能完全由对象的类型定义,因此对象的职责也完全由类型定义。改变实施并不会改变责任。

于 2019-01-05T08:28:59.333 回答