[PYTHON] Codes que je vois souvent en travaillant chez SIer et comment les corriger - violation de la loi de Demeter

Je pense résumer les anti-patterns que je vois souvent en travaillant. Cette fois, c'est une violation de la loi de Demeter. Le code est Scala, mais c'est fondamentalement le même pour Ruby, Python et Java.

Les sujets sont ci-dessous.

Code qui enfreint la loi de Demeter

La loi de Demeter est que vous ne devez rien utiliser d'autre que "directement instancié" ou "passé comme argument".

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 {
  //Viole la loi de Déméter
  def showApplicant(applicant: Applicant): Unit = {
    applicant.profile.showProfile()
  }
}

ʻApplicantest passé comme argument, mais dans la méthode, Cela enfreint parce que la méthodeshowProfile ()est exécutée après avoir récupéré leprofile dans ʻapplicant.profile.

ʻApplicant.profile.showProfile () parce que je veux le faire Je pense que vous devriez hésiter à ajouter val like class Applicant (id: String, val profile: Profile) ʻet ** publier les propriétés avec getter **.

Le formulaire que vous souhaitez appeler est le suivant.

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

Ici, ce qui suit est décrit.

Solution d'héritage

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

//Le candidat hérite du profil
class Applicant(id: String, name: String, age: Int) extends Profile(name, age)

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

↑ Cela semble étrange, alors corrigez-le avec la méthode de transfert.

Solution par méthode de transfert (délégation)

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

//Puisque le profil n'a plus besoin d'être publié par les getters
//J'ai enlevé le val.
class Applicant(id: String, profile: Profile) {
  //Méthode de transfert
  def showProfile(): Unit = profile.showProfile()
}

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

À propos du test

De là, considérons le test de la méthode ʻOrder.showApplicant () `modifiée par la ** méthode de transfert **.

object SimpleTest extends App {
  //Créez l'objet requis
  val profile = new Profile("taro", 22)
  val applicant = new Applicant("001", profile)
  
  //Exécuter la méthode testée
  Order.showApplicant(applicant)
}

Dans l'exemple ci-dessus, il est facile de créer une instance ʻApplicant, mais par exemple, considérons le cas où ʻApplicant agrège diverses classes et l'instanciation est délicate, comme indiqué ci-dessous.

L'important est que ** la cible du test soit la méthode ʻOrder.showApplicant () `, qui est une méthode de transfert, donc c'est bien si vous pouvez confirmer que vous l'avez appelée. Cela signifie **.

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

  //Je veux juste exécuter la méthode testée,
  //La génération d'instances est un problème.
  Order.showApplicant(applicant)
}

Refactoring pour faciliter les tests

Tant que la méthode ʻOrder.showApplicant () peut être exécutée, le contenu de l'argument ʻapplicant peut être n'importe quoi. Donc, prenez en sandwich l'interface pour que ʻOrder.showApplicant () `dépende du résumé.

//Gardez-le abstrait
trait IApplicant {
  def showProfile(): Unit
}

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

object Order {
  //Le type de candidat est désormais IApplicant et dépend de l'abstraction.
  def showApplicant(applicant: IApplicant): Unit = {
    applicant.showProfile()
  }
}

Le ʻapplicantrequis pour l'argument de la méthode ʻOrder.showApplicant ()peut être généré comme suit, ce qui rend le test un peu plus facile.

object RefactorComplexTest extends App {
  //Instanciation complexe
  val applicant = new IApplicant {
    override def showProfile(): Unit = println("Allons. adapté. Allons")
  }
  
  Order.showApplicant(applicant)
}

Si vous continuez à violer la loi de Demetel comme indiqué ci-dessous, ce sera difficile à tester, donc je pense personnellement que si vous enfreignez la loi de Demetel, vous devriez la réparer.

Vous pouvez le tester en utilisant une maquette, mais je pense personnellement qu'il est préférable de revoir la conception avant d'utiliser la maquette.

  def showApplicant(applicant: Applicant): Unit = {
    //Il est difficile de tester si vous continuez à enfreindre la loi de Demeter
    applicant.profile.showProfile()
  }

Recommended Posts

Codes que je vois souvent en travaillant chez SIer et comment les corriger - violation de la loi de Demeter
Codes que je vois souvent en travaillant chez SIer et comment les corriger - violation de la loi de Demeter
Comment traiter l'erreur "Impossible de charger le module" canberra-gtk-module "qui apparaît lorsque vous exécutez OpenCV
Comment gérer les erreurs lors de l'installation de whitenoise et du déploiement sur Heroku
Comment gérer les erreurs lors de l'installation de Python et de pip avec choco
Comment créer un environnement d'exécution Python et Jupyter avec VSCode
Convertissez l'image du visage en une qualité d'image élevée avec PULSE afin que vous puissiez voir les pores et la texture