Il existe de nombreux textes sur le "principe du renversement des dépendances",
Pour ceux qui disent, j'écrirai mon propre article: "C'était facile à comprendre si vous expliquiez cela."
github
https://github.com/koboriakira/koboridip
Un outil pour vérifier les quatre règles. Le résultat est envoyé à la CLI comme suit.
$ python -m koboridip.main 8 2
8 + 2 = 10
8 - 2 = 6
8 * 2 = 16
8 / 2 = 4.0
.
└── koboridip
├── calculator.py
└── main.py
calculator.py
class Calculator():
def __init__(self, a: int, b: int) -> None:
self.a = a
self.b = b
def print(self) -> None:
print(f'add: {self.a + self.b}')
print(f'subtract: {self.a - self.b}')
print(f'multiply: {self.a * self.b}')
print(f'divide: {self.a / self.b}')
main.py
import sys
from koboridip.calculator import Calculator
if __name__ == '__main__':
#Obtenir l'argument
a = sys.argv[1]
b = sys.argv[2]
#Créer une instance de calculatrice
calculator = Calculator(int(a), int(b))
#Sortir les résultats de chacune des quatre règles
calculator.print()
C'est un programme simple. Après avoir donné un nombre à la classe Calculator
, laissez l'instance faire le" calcul (traitement) "et la" sortie ".
Concernant ce produit, il y avait une demande que "je veux enregistrer le résultat de sortie au format json". Par conséquent, nous modifierons la source.
La sortie est écrite dans la classe Calculator, donc corrigeons-la.
calculator.py
import json
from typing import Dict
class Calculator():
def __init__(self, a: int, b: int) -> None:
self.a = a
self.b = b
def print(self) -> None:
# print(f'add: {self.a + self.b}')
# print(f'subtract: {self.a - self.b}')
# print(f'multiply: {self.a * self.b}')
# print(f'divide: {self.a / self.b}')
result: Dict[str, int] = {
"add": self.a + self.b,
"subtract": self.a - self.b,
"multiply": self.a * self.b,
"divide": self.a / self.b
}
with open('result.json', mode='w') as f:
f.write(json.dumps(result))
Juste au cas où, lorsque vous l'exécutez, le texte suivant sera affiché dans result.json
(formaté).
result.json
{
"add":10,
"subtract":6,
"multiply":16,
"divide":4.0
}
La classe Calculator
effectue ** le traitement ** des quatre règles de fonctionnement et ** la sortie ** du résultat.
J'ai décidé qu'il serait préférable de les séparer, j'ai donc décidé de créer une classe Imprimante
chargée du traitement de la sortie.
.
└── koboridip
├── calculator.py
├── main.py
└── printer.py
printer.py
import json
from typing import Dict
class Printer():
def print(self, add, subtract, multiply, divide) -> None:
result: Dict[str, int] = {
"add": add,
"subtract": subtract,
"multiply": multiply,
"divide": divide
}
with open('result.json', mode='w') as f:
f.write(json.dumps(result))
calculator.py
from koboridip.printer import Printer
class Calculator():
def __init__(self, a: int, b: int) -> None:
self.a = a
self.b = b
def print(self) -> None:
add = self.a + self.b
subtract = self.a - self.b
multiply = self.a * self.b
divide = self.a / self.b
printer = Printer()
printer.print(add, subtract, multiply, divide)
Dans le changement de politique qui a suivi, il a été décidé que «je veux utiliser à la fois la sortie des résultats vers CLI et le stockage au format json». Changez de mode comme suit.
$ python -m koboridip.main 8 2 simple
>(Sortie vers CLI)
$ python -m koboridip.main 8 2 json
> (result.sortie json)
Par conséquent, la classe Imprimante
a été divisée en deux types afin de pouvoir être commutée.
.
└── koboridip
├── calculator.py
├── json_printer.py ->Sortie au format json
├── main.py
├── simple_printer.py ->Sortie vers CLI
simple_printer.py
class SimplePrinter():
def print(self, add, subtract, multiply, divide) -> None:
print(f'add: {add}')
print(f'subtract: {subtract}')
print(f'multiply: {multiply}')
print(f'divide: {divide}')
json_printer.py
import json
from typing import Dict
class JsonPrinter():
def print(self, add, subtract, multiply, divide) -> None:
result: Dict[str, int] = {
"add": add,
"subtract": subtract,
"multiply": multiply,
"divide": divide
}
with open('result.json', mode='w') as f:
f.write(json.dumps(result))
C'est à calculator.py
de décider lequel afficher.
La chaîne spécifiée "simple" ou "json" peut être commutée en la stockant dans la variable mode
.
calculator.py
from koboridip.simple_printer import SimplePrinter
from koboridip.json_printer import JsonPrinter
class Calculator():
def __init__(self, a: int, b: int, mode: str) -> None:
self.a = a
self.b = b
self.mode = mode
def print(self) -> None:
add = self.a + self.b
subtract = self.a - self.b
multiply = self.a * self.b
divide = self.a / self.b
#Changer la méthode de sortie
if self.mode == 'json':
json_printer = JsonPrinter()
json_printer.print(add, subtract, multiply, divide)
elif self.mode == 'simple':
simple_printer = SimplePrinter()
simple_printer.print(add, subtract, multiply, divide)
Modifions également main.py
pour que nous puissions obtenir les arguments.
main.py
import sys
from koboridip.calculator import Calculator
if __name__ == '__main__':
#Obtenir l'argument
a = sys.argv[1]
b = sys.argv[2]
#Méthode de sortie
mode = sys.argv[3]
#Créer une instance de calculatrice
calculator = Calculator(int(a), int(b), mode)
#Sortir les résultats de chacune des quatre règles
calculator.print()
Actuellement, la classe "Calculatrice" des quatre règles ** "traitement" ** importe la classe "Imprimante" du résultat ** "sortie".
Cet état,
** " Calculatrice
(traitement) dépend de Imprimante
(sortie)" **
Il est exprimé comme.
Dépendance (importation) signifie que ** une modification de la dépendance nécessite une modification de la source de dépendance **.
Comme nous l'avons vu dans la version 3, ce projet a également modifié la classe Calculator
pour ajouter (changer) la méthode de sortie.
** Je voulais juste changer la sortie, mais je devais aussi changer le traitement. ** **
Supposons qu'il y ait plus de demandes telles que «Je veux sortir au format csv» et «Je veux envoyer le résultat à un serveur» à l'avenir.
A chaque fois, non seulement la classe Printer
mais aussi la classe Calculator
sont obligées d'apporter des modifications.
Encore une fois, même s'il n'y a pas de changement dans les spécifications de «traitement (quatre règles de fonctionnement)», il est nécessaire de modifier la fonction de traitement.
Il est important de se sentir «mal à l'aise» ici.
À ce stade, vous pouvez arriver à la conclusion: "Alors, devrions-nous réduire la dépendance afin qu'elle ne soit pas affectée par le changement de dépendance?"
Cependant, il existe toujours une dépendance car les importations ne peuvent pas être utilisées dans les projets Python.
En d'autres termes, l'ingéniosité dont nous avons besoin est de créer des «dépendances appropriées».
Cela signifie ** "en fonction de celui avec le moins de changements" **.
Un autre problème avec ce projet est que Calculator
connaît les ** détails ** de la sortie.
Le but de Calculator
est de pouvoir" sortir le résultat ", et que ce soit au format CLI ou json, je veux éviter de m'inquiéter à ce sujet.
Maintenant, inversons les dépendances.
Placez la classe Printer
, qui est une classe abstraite, dans calculator.py
, et importez les ʻABCMeta et ʻabstract method
requises.
calculator.py
from abc import ABCMeta, abstractmethod
from koboridip.simple_printer import SimplePrinter
from koboridip.json_printer import JsonPrinter
class Printer(metaclass=ABCMeta):
@abstractmethod
def print(self, add, subtract, multiply, divide):
pass
class Calculator():
def __init__(self, a: int, b: int, mode: str) -> None:
self.a = a
self.b = b
self.mode = mode
def print(self) -> None:
add = self.a + self.b
subtract = self.a - self.b
multiply = self.a * self.b
divide = self.a / self.b
#Changer la méthode de sortie
if self.mode == 'json':
json_printer = JsonPrinter()
json_printer.print(add, subtract, multiply, divide)
elif self.mode == 'simple':
simple_printer = SimplePrinter()
simple_printer.print(add, subtract, multiply, divide)
Puis changez chacun de SimplePrinter
et JsonPrinter
pour hériter de la classe Printer
.
simple_printer.py
from koboridip.calculator import Printer
class SimplePrinter(Printer):
def print(self, add, subtract, multiply, divide) -> None:
print(f'add: {add}')
print(f'subtract: {subtract}')
print(f'multiply: {multiply}')
print(f'divide: {divide}')
json_printer.py
import json
from typing import Dict
from koboridip.calculator import Printer
class JsonPrinter(Printer):
def print(self, add, subtract, multiply, divide) -> None:
result: Dict[str, int] = {
"add": add,
"subtract": subtract,
"multiply": multiply,
"divide": divide
}
with open('result.json', mode='w') as f:
f.write(json.dumps(result))
L'important ici est que les SimplePrinters
dépendent de calculator.py
.
** Ici, les dépendances ont été inversées. ** "Sortie" dépend du "Traitement".
Bien sûr, ce n'est pas encore parfait, nous allons donc supprimer l'état où la classe Calculator
dépend de la classe SimplePrinter
.
Par conséquent, laissez le constructeur décider quelle imprimante utiliser.
calculator.py
from abc import ABCMeta, abstractmethod
class Printer(metaclass=ABCMeta):
@abstractmethod
def print(self, add, subtract, multiply, divide):
pass
class Calculator():
def __init__(self, a: int, b: int, printer:Printer) -> None:
self.a = a
self.b = b
self.printer = printer
def print(self) -> None:
add = self.a + self.b
subtract = self.a - self.b
multiply = self.a * self.b
divide = self.a / self.b
self.printer.print(add, subtract, multiply, divide)
Puis laissez main.py
spécifier quelle imprimante utiliser.
main.py
import sys
from koboridip.calculator import Calculator, Printer
from koboridip.json_printer import JsonPrinter
from koboridip.simple_printer import SimplePrinter
if __name__ == '__main__':
#Obtenir l'argument
a = sys.argv[1]
b = sys.argv[2]
#Méthode de sortie
mode = sys.argv[3]
#Spécifiez la classe d'imprimante ("simple"Puisqu'il est difficile de juger, je l'ai fait autrement)
printer: Printer = JsonPrinter() if mode == 'json' else SimplePrinter()
#Créer une instance de calculatrice
calculator = Calculator(int(a), int(b), printer)
#Sortir les résultats de chacune des quatre règles
calculator.print()
Il n'y a pas d'importation dans Calculate.py
, mais il y a une importation dans simple_printer.py
s.
Ceci termine l'inversion de dépendance.
Comme prévu, une sortie au format csv a également été demandée.
Auparavant, la classe Calculator
était également affectée chaque fois qu'il y avait un changement dans la méthode de sortie, mais voyons ce qui se passe.
.
└── koboridip
├── calculator.py
├── csv_printer.py
├── json_printer.py
├── main.py
└── simple_printer.py
csv_printer.py
import csv
from typing import List
from koboridip.calculator import Printer
class CsvPrinter(Printer):
def print(self, add, subtract, multiply, divide) -> None:
result: List[List] = []
result.append(["add", add])
result.append(["subtract", subtract])
result.append(["multiply", multiply])
result.append(["divide", divide])
with open('result.csv', 'w') as f:
writer = csv.writer(f)
writer.writerows(result)
main.py
import sys
from koboridip.calculator import Calculator, Printer
from koboridip.json_printer import JsonPrinter
from koboridip.simple_printer import SimplePrinter
from koboridip.csv_printer import CsvPrinter
if __name__ == '__main__':
#Obtenir l'argument
a = sys.argv[1]
b = sys.argv[2]
#Méthode de sortie
mode = sys.argv[3]
#Spécifiez la classe d'imprimante
printer: Printer = JsonPrinter() if mode == 'json' else CsvPrinter(
) if mode == 'csv' else SimplePrinter()
#Créer une instance de calculatrice
calculator = Calculator(int(a), int(b), printer)
#Sortir les résultats de chacune des quatre règles
calculator.print()
En faisant cela, le fichier csv était également sorti.
Vous pouvez imaginer que vous pouvez facilement changer la méthode de sortie après cela.
J'espère que cela vous aidera à comprendre le principe du renversement de la dépendance. Un dernier supplément de point.
Ceux qui disent "Je comprends le principe de l'inversion des dépendances!" Essaieront immédiatement de corriger la conception et l'implémentation, en disant "C'est un problème!" Quand ils voient un projet qui ne semble pas avoir de dépendance appropriée. Je suis).
Par exemple, après le refactoring de la version 2, la classe Calculator
dépend de la classe Printer
, donc à ce stade, vous voudrez peut-être appliquer le principe d'inversion de dépendance.
Mais c'est prématuré. Bien sûr, si vous savez que "la méthode de sortie peut augmenter autant que vous le souhaitez" à ce moment, vous devez l'appliquer, mais d'un autre côté, si "la méthode de sortie est peu susceptible de changer", appliquez ** << maintenir 》 ** Je pense que cela peut être une bonne décision.
Personnellement, je voudrais trier les dépendances des «détails» tels que la sortie le plus tôt possible, mais je pense qu'il est important de penser au moins que «vous pouvez le changer à tout moment».
Si j'ai le temps, j'aimerais écrire sur "DI = Dependency Injection" tel quel.
Si vous avez des suggestions ou des questions, n'hésitez pas à commenter.
Recommended Posts