Как сохранить единоличную ответственность при использовании самостоятельного ввода в Scala? - PullRequest
0 голосов
/ 05 января 2019

Использование self-type для инъекций зависимости заставляет выставлять открытый метод других признаков, которые нарушают принцип единой ответственности. Давайте поговорим с примером

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.

.

Как мне избежать этого и продолжать использовать собственный тип и шаблон торта?

Ответы [ 2 ]

0 голосов
/ 05 января 2019

Ответственность за предоставление вывода по-прежнему лежит на компоненте, реализующем Output. Тот факт, что ваш класс обеспечивает к нему доступ, ничем не отличается от:

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

Конечно, вы можете сделать out приватным здесь, но вы также можете защитить .output в ConsoleOutput, если вы не хотите, чтобы он был доступен извне.

(Ответ на ваш комментарий в другом ответе таков: если вы также хотите использовать его «автономно», то вы создадите его подкласс и сделаете output общедоступным в подклассе).

0 голосов
/ 05 января 2019

Тип личности здесь не очень актуален. Наследование от другого класса открывает открытые методы этого класса независимо от любого типа себя. Таким образом, можно сказать, что любое наследование класса с открытыми методами нарушает принцип единой ответственности.

Если 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 ConsoleOutput имеет метод output для типа Object и метод doSomething для типа Sample. Поэтому он несет ответственность за обеспечение реализации обоих этих методов. Тот факт, что реализация output находится в ConsoleOuput, не имеет отношения к типу и, следовательно, не имеет отношения к тому, кто за него отвечает.

Объект Sample with ConsoleOutput может легко переопределить реализацию output, и в этом случае он будет явно отвечать за этот метод, а не ConsoleOutput. Тот факт, что Sample with ConsoleOutput решает не изменять реализацию output, не означает, что он не несет за это ответственности. Обязанности объекта не меняются при изменении реализации.

Объяснение Принципа единой ответственности

Этот принцип является первым из пяти SOLID принципов разработки программного обеспечения. Как объясняет Википедия: «Принцип единой ответственности [] гласит, что каждый модуль или класс должен нести ответственность за одну часть функциональности, предоставляемой программным обеспечением, и что ответственность должна быть полностью заключена в класс».

Другими словами, не делайте этого:

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 - это принцип подстановки Лискова , который гласит, что функциональность объекта должна зависеть исключительно от типа и не должна зависеть от реализации. Вы должны иметь возможность изменить реализацию и по-прежнему использовать объект таким же образом с теми же результатами.

Поскольку функциональность объекта полностью определяется типом объекта, обязанности объекта также полностью определяются типом. Изменение реализации не меняет обязанностей.

...