Résumons le degré de couplage entre les modules avec du code Python

Objectif

Je n'ai pas beaucoup touché depuis que j'ai étudié avec des informations de base, et à ce moment-là je n'ai pas essayé d'écrire du code, alors j'ai décidé d'étudier à nouveau.

Quel est le degré de couplage entre les modules?

Cela montre à quel point la relation entre les modules est forte. On dit que l'indépendance des modules peut être augmentée en réduisant le degré de couplage entre les modules. L'indépendance accrue des modules présente les avantages suivants:

Par exemple, jetez un œil au code ci-dessous. Cette fonction détermine s'il s'agit d'une année humide en fonction du calendrier occidental actuel.

Le code avec une faible indépendance du module a une faible réutilisabilité

from datetime import datetime
from calendar import isleap

#Une fonction qui détermine si cette année est une année humide et produit le résultat
def A():
    this_year = datetime.now().year
    if isleap(this_year):
        print('Cette année est une année humide')
    else:
        print('Cette année n'est pas une année humide')

Maintenant, disons que vous voulez incorporer un "mécanisme pour déterminer si il y a 10 ans était une année humide" dans ce module.

from datetime import datetime
from calendar import isleap

#Une fonction qui détermine si cette année est une année humide et produit le résultat
def A():
    this_year = datetime.now().year
    if isleap(this_year):
        print('Cette année est une année humide')
    else:
        print('Cette année n'est pas une année humide')

#Une fonction qui détermine s'il y a 10 ans est une année humide et produit le résultat
def B():
    this_year = datetime.now().year - 10
    if isleap(this_year):
        print('Il y a 10 ans est une année humide')
    else:
        print('Il y a 10 ans n'est pas une année humide')

Le code avec une faible indépendance de module est difficile à tester

Presque le même code est écrit, et cela ne semble pas très bien intuitivement en premier lieu. De plus, il y a des problèmes lors des tests. Supposons que vous souhaitiez saisir les données de test suivantes dans la fonction A et tester si l'année de gonflement est correctement jugée.

données de test Valeur attendue
2000 Cette année est une année humide
2020 Cette année est une année humide
2100 Cette année n'est pas une année humide

Alors, comment le testez-vous?

C'est vrai. Cela ne peut pas être testé. En effet, les deux fonctions A et B ont un mélange de "traitement pour trouver le calendrier actuel" et de "mécanisme pour déterminer l'année du tiède", et leurs fonctions ne sont pas indépendantes.

Par conséquent, il vaut mieux faire cela.

def A(year):
    if isleap(year):
        print('Cette année est une année humide')
    else:
        print('Cette année n'est pas une année humide')

De cette manière, les informations de calendrier doivent être entrées de l'extérieur. Maintenant, que ce soit il y a 10 ans, 100 ans ou 200 ans plus tard, vous n'avez besoin que d'une seule fonction, que vous ayez plusieurs données de test.

Il existe une norme d'évaluation du degré de couplage des modules

Il existe différents types de couplage inter-module.

Critère d'évaluation Degré de couplage entre les modules Indépendance du module
Jointure interne haute Faible
Jointure commune
Jointure externe
Contrôle de la jointure
Combinaison de timbres
Jointure de données Faible haute

Donc, à partir d'ici, je vais le résumer avec un code. Cette fois, je vais le reproduire avec Python.

Jointure interne

C'est presque impossible de nos jours, et c'est difficile à reproduire ... Est-ce comme ça si vous le forcez?

Il existe un tel module. nom d'utilisateur, niveau, attaque, défense, etc. sont définis comme des variables globales.

moduleA.py


username = 'hogehogekun'
level = 25
attack = 20
defence = 5

def show_user_status():
    print('Nom d'utilisateur:' + username)
    print('niveau:' + str(level))
    print('Puissance offensive:' + str(attack))
    print('Puissance de défense:' + str(defence))

Supposons que vous ayez du code qui en profite.

main.py


import moduleA

#Augmentez le niveau de 1
moduleA.level += 1

#Afficher l'état à l'aide de la fonction moduleA
moduleA.show_user_status()

résultat


Nom d'utilisateur: hogehogekun
Niveau: 26
Puissance d'attaque: 20
Défense: 5

La valeur initiale du niveau était de 25, mais elle a augmenté de 1. Il n'y a pas de problème avec le comportement, mais le comportement de moduleA dépend profondément du comportement du module principal.

Jointure commune

Maintenant, disons que vous avez regroupé les informations utilisateur dans une classe et les avez gérées dans une seule liste.

moduleA.py


class User:
    def __init__(self, username, level, attack, defence):
        self.username = username
        self.level = level
        self.attack = attack
        self.defence = defence

    def show_user_status(self):
        print('Nom d'utilisateur:' + self.username)
        print('niveau:' + str(self.level))
        print('Puissance offensive:' + str(self.attack))
        print('Puissance de défense:' + str(self.defence))

#Gérer la liste des utilisateurs dans une liste
user_list = [User('hogehogekun', 75, 90, 80), User('fugafugakun', 10, 5, 7)]

Et supposons qu'il y ait deux fonctions dans mainA qui utilisent ce module.

mainA.py


import moduleA

def funcA():
  del(moduleA.user_list[0])

def funcB():
  print(moduleA.user_list[0].username)


#Exécuter dans l'ordre funcA, funcB
funcA()
funcB()

résultat


fugafugakun

Cette fois, peut-être que funcA a supprimé le premier élément de la liste globale. Au moment où funcB a jeté un coup d'œil, hogehoge avait déjà disparu, ne laissant que fugafuga. Par ailleurs, si funcB fait référence à moduleA.user_list [1], IndexError se produira. De cette manière, dans une jointure commune, si vous modifiez ou supprimez une partie de la structure de données commune, vous devez revoir tous les modules qui font référence à la partie commune.

Jointure externe

C'est très similaire à une jointure commune, mais c'est une perception que les informations partagées sont une collection de données uniques plutôt qu'une structure de données telle qu'une liste ou un objet.

Cette fois, supposons qu'il existe des informations sur le nombre cumulé d'utilisateurs et l'état du service.

moduleA.py


class User:
    def __init__(self, username, level, attack, defence):
        self.username = username
        self.level = level
        self.attack = attack
        self.defence = defence

    def show_user_status(self):
        print('Nom d'utilisateur:' + self.username)
        print('niveau:' + str(self.level))
        print('Puissance offensive:' + str(self.attack))
        print('Puissance de défense:' + str(self.defence))

user_count = 123091 #Nombre cumulé d'utilisateurs
service_status = 200 #État du service

main.py


import moduleA

def funcA():
    print(moduleA.user_count)

def funcB():
    print(moduleA.service_status)

funcA()
funcB()

résultat


123091
200

Ce code n'a aucun problème de comportement. Le nombre d'utilisateurs et l'état du service ont été correctement acquis dans funcA et funcB. Cependant, s'il y a un changement de spécification tel que le service_status de moduleA.py devenant un type caractère au lieu d'un type numérique, il est nécessaire de modifier funcB qui fait référence aux informations correspondantes.

Contrôle de la jointure

La jointure de contrôle utilise des arguments pour contrôler le traitement de la fonction appelée. Dans ce code, différents traitements sont effectués selon que 1 ou 2 est passé à some_command.

moduleA.py


class User:
    def __init__(self, username, level, attack, defence):
        self.username = username
        self.level = level
        self.attack = attack
        self.defence = defence

    def some_command(self, command_id):
        if command_id == 1: #Commande d'affichage d'état
            print('Nom d'utilisateur:' + self.username)
            print('niveau:' + str(self.level))
            print('Puissance offensive:' + str(self.attack))
            print('Puissance de défense:' + str(self.defence))

        elif command_id == 2: #Commande de niveau supérieur
            print(self.username + 'Le niveau a augmenté de 1!')
            self.level += 1

main.py


from moduleA import User

user1 = User('hogehogekun', 40, 20, 20)
user1.some_command(1)
user1.some_command(2)

résultat


Nom d'utilisateur: hogehogekun
Niveau: 40
Puissance d'attaque: 20
Défense: 20
Le niveau de hogehogekun a augmenté de 1!

À première vue, cela a l'air bien parce que les informations appelées la commande sont transmises de l'extérieur. Un autre module qui appelle some_command doit connaître la structure interne de some_command. Par conséquent, le degré de couplage est relativement élevé.

Combinaison de timbres

Classe d'utilisateur comme d'habitude. Cette fois, nous nous concentrerons sur l'échange entre funcA et funcB dans main.py. Appeler funcB dans funcA et passer une liste comme argument ressemble à une sorte de code.

moduleA.py


class User:
    def __init__(self, username, level, attack, defence):
        self.username = username
        self.level = level
        self.attack = attack
        self.defence = defence

    def show_user_status(self):
        print('Nom d'utilisateur:' + self.username)
        print('niveau:' + str(self.level))
        print('Puissance offensive:' + str(self.attack))
        print('Puissance de défense:' + str(self.defence))

Supposons qu'un autre module se comporte comme ceci.

main.py


from moduleA import User

def funcA():
    user_list = [User('hogehogekun', 20, 10, 10), User('fugafugakun', 99, 99, 99), User('piyopiyokun', 99, 99, 99)]
    funcB(user_list)

def funcB(user_list):
    print(user_list[2].username)

funcA()

résultat


piyopiyokun

Il n'y a rien de mal à cela à ce stade, mais si le nombre d'éléments dans funcA change, par exemple, funcB sera affecté.

main.py


def funcA():
    user_list = [User('hogehogekun', 20, 10, 10), User('fugafugakun', 99, 99, 99)]
    funcB(user_list)

def funcB(user_list):
    print(user_list[2].username)

funcA()

résultat


IndexError: list index out of range

Bien que la combinaison de tampons passe la liste entière, le module appelant n'utilise que certaines informations. Cette fois, funcB n'utilise que le troisième élément, même s'il lui est passé une liste avec trois éléments. À ce stade, même s'il y a un changement dans les éléments qui ne sont pas utilisés du côté funcB (le nombre dans ce cas) dans la combinaison de timbres, funcB peut être affecté.

Jointure de données

Minimise les informations transmises entre plusieurs modules. À l'exemple de la combinaison de tampons, cela ressemble à ceci.

moduleA.py


class User:
    def __init__(self, username, level, attack, defence):
        self.username = username
        self.level = level
        self.attack = attack
        self.defence = defence

    def show_user_status(self):
        print('Nom d'utilisateur:' + self.username)
        print('niveau:' + str(self.level))
        print('Puissance offensive:' + str(self.attack))
        print('Puissance de défense:' + str(self.defence))

main.py


def funcA():
    user_list = [User('hogehogekun', 20, 10, 10), User('fugafugakun', 99, 99, 99), User('piyopiyokun', 99, 99, 99)]
    funcB(user_list[2])

def funcB(target_user):
    print(target_user.username)

funcA()

résultat


piyopiyokun

Puisque funcB ne traite que piyopiyokun, les données à transmettre sont uniquement user_list [2] en premier lieu. Si cela réduit le nombre de user_lists, cela devrait affecter funcA mais pas funcB. En premier lieu, la diminution du nombre de user_list signifie que funcA est en cours de modification, de sorte que la plage d'influence peut être considérablement plus petite.

De cette manière, le degré de couplage des modules peut être affaibli en échangeant uniquement les informations nécessaires.

Addendum: Quand je lisais le livre que j'avais, il disait: "Le module appelant pourra manipuler directement les données appelantes avec les arguments reçus." En d'autres termes, il semble que vous devriez passer par référence. Il semblait y avoir des variations dans la façon de penser ici en fonction du développeur et de la méthode de développement.

Résumé

Il existe divers avantages à fixer la résistance des modules à une valeur élevée et le degré de couplage entre les modules à être faible.

Etc!

Recommended Posts

Résumons le degré de couplage entre les modules avec du code Python
Convertir le code de caractère du fichier avec Python3
Décomposons les bases du code Python de TensorFlow
Touchons l'API de Netatmo Weather Station avec Python. #Python #Netatmo
Mesurez la couverture de test du code python poussé sur GitHub.
[Python3] Réécrire l'objet code de la fonction
Lisons le fichier RINEX avec Python ①
Résumons le standard de codage Python PEP8 (1)
Résumons le standard de codage Python PEP8 (2)
[Python] Récupère le code de caractère du fichier
[Python] Lire le code source de Bottle Part 2
Résumé des différences entre PHP et Python
Mathématiques Todai 2016 résolues avec Python
[Note] Exportez le html du site avec python.
La réponse de "1/2" est différente entre python2 et 3
Calculez le nombre total de combinaisons avec python
Vérifiez la date du devoir de drapeau avec Python
Code pour vérifier le fonctionnement de Python Matplot lib
Analyse statique du code Python avec GitLab CI
[Python] Déterminez le type d'iris avec SVM
[Blender x Python] Pensez au code avec des symboles
Simulons la transition du taux d'infection par rapport à la densité de population avec python
Jouons avec Python Receive et enregistrez / affichez le texte du formulaire de saisie
Alignez le nombre d'échantillons entre les classes de données pour l'apprentissage automatique avec Python
Un moyen simple de vérifier la source des modules Python
Extraire le tableau des fichiers image avec OneDrive et Python
Apprenez Nim avec Python (dès le début de l'année).
Détruire l'expression intermédiaire de la méthode sweep avec Python
le zen de Python
Visualisez la gamme d'insertions internes et externes avec python
Récupérer le code retour d'un script Python depuis bat
Calculer le coefficient de régression d'une analyse de régression simple avec python
Utilisons la version Python du module API Confluence.
Utilisons les données ouvertes de "Mamebus" en Python
Résumé du flux de base de l'apprentissage automatique avec Python
Obtenez l'état de fonctionnement de JR West avec Python
Exploitons GPIO de Raspeye avec Python CGI
[Python] Changeons l'URL du site administrateur de Django
J'ai essayé de résumer les opérations de chaîne de Python
J'ai essayé de trouver l'entropie de l'image avec python
Essayez de gratter les données COVID-19 Tokyo avec Python
J'ai essayé la "correction gamma" de l'image avec Python + OpenCV
L'histoire de la mise en œuvre du sujet Facebook Messenger Bot avec python
Exécutons jupyter nativement pris en charge par VS Code avec python3.8
Unifier l'environnement de l'équipe de développement Python en commençant par Poetry
Visualisez les résultats des arbres de décision réalisés avec Python scikit-learn
Calculez des millions de chiffres dans la racine carrée de 2 avec python
Exécutons la commande à temps avec le bot discord
Exécutez l'intelligence de votre propre bibliothèque python avec VScode.
J'ai évalué la stratégie de négociation du système boursier avec Python.
L'histoire du rubyiste aux prises avec Python :: Dict data with pycall
[Homologie] Comptez le nombre de trous dans les données avec Python
Essayez d'automatiser le fonctionnement des périphériques réseau avec Python
Visualisons le nombre de personnes infectées par le virus corona avec matplotlib
Histoire que Python a cessé de travailler avec VS Code (Windows 10)
Réécrivez le nœud d'ajout d'enregistrement de SPSS Modeler avec Python.
Le processus de création et d'amélioration du code Python orienté objet