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.
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.
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')
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 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.
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.
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.
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.
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é.
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é.
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.
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