Rendre les chaînes intelligentes en Python

Jan 10 2023
Application de la conception axée sur le domaine
Les bonnes idées architecturales ne sont pas réservées aux grands projets compliqués. Que vous ayez entendu parler de Domain Driven Design ou non, je vais vous montrer comment améliorer votre projet, grand ou petit, en appliquant les principes de base aux chaînes que vous avez fait circuler.
Photo par xandtor sur Unsplash

Les bonnes idées architecturales ne sont pas réservées aux grands projets compliqués. Que vous ayez entendu parler de Domain Driven Design ou non, je vais vous montrer comment améliorer votre projet, grand ou petit, en appliquant les principes de base aux chaînes que vous avez fait circuler. Il est temps de collecter ces morceaux de code de validation et d'analyse que vous avez éparpillés partout et de les intégrer dans une belle unité réutilisable.

Nous allons juste effleurer la surface de la conception pilotée par domaine ici. Vous voudrez obtenir une copie du livre du même nom d'Eric Evans à la fin de cet article. Le concept de base est que votre modèle d'objet est mieux développé en utilisant le langage du domaine métier. Les règles métier sont encodées dans le modèle. Ceci est séparé du code d'application et d'infrastructure. Nous allons nous en tenir à un exemple simple de la façon dont vous pouvez appliquer cette idée aux chaînes de votre code.

J'ai trouvé que les cordes étaient une cible simple pour commencer et si c'est tout ce que vous faites, vous en profiterez. Aujourd'hui, nous avons une méthode pour obtenir l'emplacement du terminal d'un vol en fonction de son numéro de vol, comme LA8165. Nous pouvons traiter le numéro de vol comme une chaîne :

def getTerminal(flight_number): 
  return db.getTerminal(flight_number)

def getTerminal(flight_number): 
  # Normalize input to a clean string
  flight_number = str(flight_number).upper().strip()
  if not flight_number:
    raise ValueError(“Flight Number can not be empty.”)

  return db.getTerminal(flight_number)

class FlightNumber(object):
  def __init__(self, flight_number):
    # Normalize input to a clean string
    flight_number = str(flight_number).upper().strip()
    if not flight_number:
      raise ValueError(“Flight Number can not be empty.”)
    self.flight_number = flight_number

  # The value when this is cast to a string or used in a formatted string
  def __str__(self):
    return self.flight_number

def getTerminal(flight_number): 
  flight_number = FlightNumber(flight_number)
  return db.getTerminal(str(flight_number))

Nous allons maintenant explorer tous les avantages subtils de cette façon de penser :

  1. Notre nouvelle classe est incroyablement facile à tester car elle ne touche pas aux systèmes de fichiers ou aux réseaux ou à tout ce dont nous avons besoin de nous moquer.
  2. Nous avons un endroit unique pour ajouter de la documentation.
  3. Nous pouvons le remplir de toutes les règles et de l'analyse que nous voulons rendre complet.
  4. Toute personne nouvelle dans le projet apprendra facilement ce qu'est un numéro de vol, très précisément.
  5. Tous ces objets peuvent aller dans un dossier de domaine pour les rendre réutilisables et détectables.
  6. Le code d'appel est plus facile à lire et les demandes d'extraction sont plus simples.
  7. Les erreurs seront détectées plus tôt dans la pile

class FlightNumber(object):
  """
  Represents an International Air Transport Association flight number. Ex — LA8165
  The format is two uppercase letters for the airline followed by up to four digits. 
  """
  def __init__(self, flight_number):
  # Normalize input to a clean string
  flight_number = str(flight_number).upper().strip()
  if not flight_number:
    raise ValueError("Flight Number can not be empty.")
  self.airline_code = flight_number[0:2].strip()
  if not self.airline_code.isalpha():
    raise ValueError(f"Airline Code {self.airline_code} must be two letters.")
  # Throws ValueError already if not a valid int
  self.numeric_part = int(flight_number[2:])
  if self.numeric_part < 1 or self.numeric_part > 9999:
    raise ValueError(f”Flight Number {self.numeric_part} must be from 1 to 9999.”)
  if self.numeric_part == 666:
    raise ValueError(f"Seriously, airlines don’t use this flight number.")

  def __str__(self):
    return f"{self.airline_code}{self.numeric_part}"

Les références

  • Conception axée sur le domaine