[PYTHON] Écrivez clairement du code qui augmente le branchement conditionnel

introduction

Parfois, lors de l'écriture de code, un branchement conditionnel peut se produire de manière multiplicative (combinée). Par exemple

etc.

Le thème de cet article est de savoir comment être résilient aux changements sans compromettre la visibilité dans ces situations. L'exemple de code est montré en Python, mais l'idée n'est pas limitée au langage de programmation, et si l'implémentation est un langage orienté objet, la même implémentation peut être réalisée.

Exemple) Prix des films

Ici, je voudrais prendre un exemple de prix des films. Par exemple, il y a des adultes en général, des étudiants et des personnes âgées comme acheteurs, et les prix changent selon l'heure de la journée (jour, spectacle tardif).

Pendant la journée Dernier spectacle
Général 1800 yens 1300 yens
étudiant 1 500 yens 1 500 yens
Sénior 1100 yens 1100 yens

Considérons qu'il existe une règle de taux d'actualisation pour le taux standard (1800 yens), et l'application de cette règle est exprimée en code.

Règle de taux de remise à partir du montant standard

Jours de la semaine En semaine (spectacle tardif)
Général +-0 yen -500 yen
étudiant -300 yens -500 yens
Sénior -700 yens -700 yens

Lorsque rédigé selon la procédure

Dans un tel cas, si vous écrivez simplement avec un branchement conditionnel procédural, ce sera douloureux comme suit.

from datetime import datetime
import enum


#Classification des spectateurs
class ViewerType(enum.Enum):
  ADULT = "adult"
  STUDENT = "student"
  SENIOR = "senior"


#Rembourser les frais de visionnage en fonction de la classification du spectateur, de l'heure de début du film et des frais standard
def charge(viewer_type: ViewerType, movie_start_at: datetime  base_charge: int) -> int:
  #Spectacle tardif après 20h00
  if movie_start_at.hour < 20:
    if viewer_type == ViewerType.ADULT:
      return base_charge
    elif viewer_type == ViewerType.STUDENT:
      return base_charge - 300
    else:
      return base_charge - 700

  if viewer_type == ViewerType.ADULT or viewer_type == ViewerType.STUDENT:
    return base_charge - 500
  else:
    return base_charge - 700

À première vue, les perspectives générales sont médiocres et je pense qu'il est difficile de dire si elle est correctement mise en œuvre. De plus, lorsque les types d'acheteurs tels que les membres inscrits augmentent ou que les règles relatives aux frais supérieurs changent, il est difficile de savoir où ajouter le traitement.

Considérez le branchement conditionnel comme un contexte

Dans un tel cas, vous pouvez obtenir une meilleure vue en considérant ** l'événement (les données qui sont la source du branchement conditionnel) ** qui affecte le processus que vous voulez faire en tant que ** contexte **.

Dans ce cas, l '** heure de début de l'émission **, qui est le critère pour savoir s'il s'agit d'une émission tardive, est exprimée en code comme contexte ** pour déterminer le prix ** final.

from datetime import datetime
import dataclasses

@dataclasses.dataclass
class MovieStartAtContext:
  movie_start_at: datetime

  def is_late_show(self) -> bool:
    return self.movie_start_at.hour >= 20

Pour le moment, j'ai pu exprimer le contexte de ** si l'heure de début de l'émission est une émission tardive **, mais il ne sert à rien d'étendre cela dans le code de procédure ci-dessus.

L'important est de rendre ce ** jugement et code exécutable **.

from datetime import datetime
import dataclasses

@dataclasses.dataclass
class MovieStartAtContext:
  movie_start_at: datetime

  def is_late_show(self) -> bool:
    return self.movie_start_at.hour >= 20

  #Calcul des prix par spectateur
  #
  #La méthode appelée par le spectateur change selon qu'il s'agit d'une émission tardive ou non.
  #
  # -Pour les spectacles tardifs: late_show_charge()
  # -Pendant la journée:      normal_charge()
  def charge_for(self, viewer: Viewer, base_charge: int) -> int:
    if self.is_late_show():
      return viewer.late_show_charge(base_charge)

    return viewer.normal_charge(base_charge)

En faisant cela, il suffit de définir les méthodes de calcul pour l'émission tardive et le tarif régulier pour chaque type de spectateur (il est de la responsabilité du côté contexte de l'heure de l'émission d'appeler la méthode appropriée).

Implémentation d'une logique de calcul de charge pour chaque spectateur

La mise en œuvre du calcul des frais pour chaque téléspectateur est la suivante.

class Viewer:
  def normal_charge(self, base_charge: int) -> int:
    pass

  def late_show_charge(self, base_charge: int) -> int:
    pass


class AdultViewer(Viewer):
  def normal_charge(self, base_charge: int) -> int:
    return base_charge

  def late_show_charge(self, base_charge: int) -> int:
    return base_charge - 500


class StudentViewer(Viewer):
  def normal_charge(self, base_charge: int) -> int:
    return base_charge - 300

  def late_show_charge(self, base_charge: int) -> int:
    return base_charge - 500


class SeniorViewer(Viewer):
  def normal_charge(self, base_charge: int) -> int:
    return base_charge - 700

  def late_show_charge(self, base_charge: int) -> int:
    return base_charge - 700

Vous pouvez voir que le tableau des règles de taux d'actualisation est exprimé dans le code presque tel quel.

Règle de taux de remise à partir du montant standard

Jours de la semaine En semaine (spectacle tardif)
Général +-0 yen -500 yen
étudiant -300 yens -500 yens
Sénior -700 yens -700 yens

Intégration de code

Enfin, lorsque vous intégrez le code que vous avez défini jusqu'à présent, votre fonction d'origine charge () ressemble à ceci:

class ViewerFactory:
  viewer_mapping = {
    ViewerType.ADULT: AdultViewer(),
    ViewerType.STUDENT: StudentViewer(),
    ViewerType.SENIOR: SeniorViewer()
  }

  @classmethod
  def create(cls, viewer_type: ViewerType) -> Viewer:
    return cls.viewer_mapping[viewer_type]


def charge(viewer_type: ViewerType, movie_start_at: datetime, base_charge: int) -> int:
  context = MovieStartAtContext(movie_start_at)

  viewer = ViewerFactory.create(viewer_type)

  return context.charge_for(viewer, base_charge)

En faisant cela, lorsque le type de membre inscrit mentionné dans l'exemple augmente, vous pouvez définir une nouvelle classe de téléspectateurs, et même si une nouvelle catégorie de frais telle que les frais de vacances augmente, Je pense que vous pouvez l'implémenter sans vous perdre dans l'emplacement de montage.

Résumé

Lorsque le branchement conditionnel se produit par multiplication

J'espère que vous pourrez rendre votre code plus propre et plus facile à étendre. J'espère que cela sera utile pour la mise en œuvre.

Recommended Posts

Écrivez clairement du code qui augmente le branchement conditionnel
Ecrire l'entrée standard dans le code
Ecrire Spigot dans VS Code
Une doublure qui produit quatre-vingt-dix-neuf en Python
J'ai eu du mal avec le branchement conditionnel dans les modèles de Django.