[PYTHON]

Ich denke darüber nach, die Anti-Muster zusammenzufassen, die ich oft bei der Arbeit sehe. Diesmal ist es ein Verstoß gegen Demeters Gesetz. Der Code ist Scala, aber für Ruby, Python und Java ist er im Grunde der gleiche. Die Themen sind unten. * Code verletzt * Vorerst beheben

Code, der gegen Demeters Gesetz verstößt

Demeters Gesetz besagt, dass Sie nichts anderes als "direkt instanziiert" oder "als Argument übergeben" verwenden sollten.

class Profile(name: String, age: Int) {
  def showProfile(): Unit = println(s"my name is $name (age: $age)")
}

class Applicant(id: String, val profile: Profile)

object Order {
  //Verstößt gegen Demeters Gesetz
  def showApplicant(applicant: Applicant): Unit = {
    applicant.profile.showProfile()
  }
}

"Antragsteller" wird als Argument übergeben, aber in der Methode, Dies ist ein Verstoß, da die Methode "showProfile ()" ausgeführt wird, nachdem das "Profil" mit "Bewerber.profil" abgerufen wurde.

Weil ich Bewerber.profile.showProfile () machen möchte Ich denke, Sie sollten zögern, val like class Applicant (id: String, val profile: Profile) hinzuzufügen und ** Eigenschaften mit getter ** zu veröffentlichen.

Das Formular, das Sie aufrufen möchten, lautet wie folgt.

  def showApplicant(applicant: Applicant): Unit = {
    applicant.showProfile()
  }

Hier wird das Folgende beschrieben.

Vererbungslösung

class Profile(name: String, age: Int) {
  def showProfile(): Unit = println(s"my name is $name (age: $age)")
}

//Der Antragsteller erbt das Profil
class Applicant(id: String, name: String, age: Int) extends Profile(name, age)

object Order {
  def showApplicant(applicant: Applicant): Unit = {
    applicant.showProfile()
  }
}

↑ Es fühlt sich seltsam an, also beheben Sie es mit der Übertragungsmethode.

Lösung durch Übertragungsmethode (Delegierung)

class Profile(name: String, age: Int) {
  def showProfile(): Unit = println(s"my name is $name (age: $age)")
}

//Da das Profil nicht mehr von Getter veröffentlicht werden muss
//Ich habe den Wert entfernt.
class Applicant(id: String, profile: Profile) {
  //Übertragungsmethode
  def showProfile(): Unit = profile.showProfile()
}

object Order {
  def showApplicant(applicant: Applicant): Unit = {
    applicant.showProfile()
  }
}

Über den Test

Testen Sie nun die Order.showApplicant () -Methode, die durch die ** Übertragungsmethode ** geändert wurde.

object SimpleTest extends App {
  //Erstellen Sie das gewünschte Objekt
  val profile = new Profile("taro", 22)
  val applicant = new Applicant("001", profile)
  
  //Führen Sie die zu testende Methode aus
  Order.showApplicant(applicant)
}

Im obigen Beispiel ist es einfach, eine "Antragsteller" -Instanz zu erstellen. Betrachten Sie jedoch beispielsweise den Fall, in dem "Antragsteller" verschiedene Klassen aggregiert und die Instanziierung umständlich ist, wie unten gezeigt.

Wichtig ist, dass ** das Testziel die Order.showApplicant () -Methode ist, eine Übertragungsmethode. Es ist also gut, wenn Sie bestätigen können, dass Sie sie aufgerufen haben. Das bedeutet **.

object ComplexTest extends App {
  //Komplexe Instanziierung
  val profile1 = ...
  val profile2 = ...
  val profile3 = ...
  val applicant = new Applicant("001", profile1, profile2, profile3, ...)

  //Ich möchte nur die zu testende Methode ausführen.
  //Instanzgenerierung ist ein Ärger.
  Order.showApplicant(applicant)
}

Refactoring zur Erleichterung des Testens

Solange die Methode "Order.showApplicant ()" ausgeführt werden kann, kann der Inhalt des Arguments "Antragsteller" beliebig sein. Legen Sie also die Schnittstelle so ein, dass "Order.showApplicant ()" von der Zusammenfassung abhängt.

//Halte es abstrakt
trait IApplicant {
  def showProfile(): Unit
}

class Applicant(id: String, profile: Profile) extends IApplicant {
  def showProfile(): Unit = profile.showProfile()
}

object Order {
  //Der Antragstellertyp ist jetzt IApplicant und hängt von der Abstraktion ab.
  def showApplicant(applicant: IApplicant): Unit = {
    applicant.showProfile()
  }
}

Der für das Argument der Order.showApplicant () -Methode erforderliche "Antragsteller" kann wie folgt generiert werden, was den Test etwas erleichtert.

object RefactorComplexTest extends App {
  //Komplexe Instanziierung
  val applicant = new IApplicant {
    override def showProfile(): Unit = println("Komm schon. geeignet. Komm schon")
  }
  
  Order.showApplicant(applicant)
}

Wenn Sie weiterhin gegen das Gesetz von Demetel verstoßen, wie unten gezeigt, ist es schwierig zu testen. Ich persönlich denke, wenn Sie gegen das Gesetz von Demetel verstoßen, sollten Sie es beheben.

Sie können es mit einem Modell testen, aber ich persönlich denke, dass es besser ist, das Design zu überprüfen, bevor Sie das Modell verwenden.

  def showApplicant(applicant: Applicant): Unit = {
    //Es ist schwierig zu testen, ob Sie weiterhin gegen Demeters Gesetz verstoßen
    applicant.profile.showProfile()
  }

Recommended Posts