Es gibt viele Texte zum "Prinzip der Abhängigkeitsumkehr",
Für diejenigen, die sagen, werde ich meinen eigenen Artikel schreiben: "Es war leicht zu verstehen, wenn Sie dies erklärt haben."
github
https://github.com/koboriakira/koboridip
Ein Tool zum Überprüfen der vier Regeln. Das Ergebnis wird wie folgt an die CLI ausgegeben.
$ 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__':
#Holen Sie sich Argument
a = sys.argv[1]
b = sys.argv[2]
#Erstellen Sie eine Rechnerinstanz
calculator = Calculator(int(a), int(b))
#Geben Sie die Ergebnisse jeder der vier Regeln aus
calculator.print()
Es ist ein einfaches Programm. Nachdem Sie der Klasse "Calculator" eine Zahl gegeben haben, lassen Sie die Instanz die "Berechnung (Verarbeitung)" und "Ausgabe" durchführen.
In Bezug auf dieses Produkt gab es die Anfrage "Ich möchte das Ausgabeergebnis im JSON-Format speichern". Daher werden wir die Quelle ändern.
Die Ausgabe wird in die Calculator-Klasse geschrieben. Korrigieren wir sie also.
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))
Nur für den Fall, dass Sie es ausführen, wird der folgende Text in result.json
(formatiert) ausgegeben.
result.json
{
"add":10,
"subtract":6,
"multiply":16,
"divide":4.0
}
Die Klasse "Calculator" führt eine ** Verarbeitung ** der vier Betriebsregeln und eine ** Ausgabe ** des Ergebnisses durch.
Ich entschied, dass es besser wäre, diese zu trennen, und entschied mich daher, eine "Drucker" -Klasse zu erstellen, die für die Ausgabeverarbeitung zuständig ist.
.
└── 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)
Bei der nachfolgenden Richtlinienänderung wurde entschieden, dass "ich sowohl die Ergebnisausgabe an die CLI als auch den Speicher im JSON-Format verwenden möchte". Schalten Sie den Modus wie folgt um.
$ python -m koboridip.main 8 2 simple
>(Ausgabe an CLI)
$ python -m koboridip.main 8 2 json
> (result.Ausgabe json)
Daher wurde die Klasse "Drucker" in zwei Typen unterteilt, damit sie umgeschaltet werden können.
.
└── koboridip
├── calculator.py
├── json_printer.py ->Ausgabe im JSON-Format
├── main.py
├── simple_printer.py ->Ausgabe an 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))
Es liegt an calculator.py
zu entscheiden, welche ausgegeben werden soll.
Die angegebene Zeichenfolge "simple" oder "json" kann durch Speichern in der Variablen "mode" umgeschaltet werden.
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
#Schalten Sie die Ausgabemethode um
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)
Lassen Sie uns auch main.py
ändern, damit wir die Argumente erhalten können.
main.py
import sys
from koboridip.calculator import Calculator
if __name__ == '__main__':
#Holen Sie sich Argument
a = sys.argv[1]
b = sys.argv[2]
#Ausgabemethode
mode = sys.argv[3]
#Erstellen Sie eine Rechnerinstanz
calculator = Calculator(int(a), int(b), mode)
#Geben Sie die Ergebnisse jeder der vier Regeln aus
calculator.print()
Derzeit importiert die Klasse "Rechner" der vier Regeln ** "Verarbeitung" ** die Klasse "Drucker" des Ergebnisses ** "Ausgabe".
Dieser Staat,
** " Rechner
(Verarbeitung) hängt von Drucker
(Ausgabe) ab" **
Es wird ausgedrückt als.
Abhängigkeit (Import) bedeutet, dass ** eine Änderung der Abhängigkeit eine Änderung der Abhängigkeitsquelle erfordert **.
Wie wir in Version 3 gesehen haben, hat dieses Projekt auch die Klasse "Calculator" geändert, um die Ausgabemethode hinzuzufügen (zu ändern).
** Ich wollte nur die Ausgabe ändern, aber ich musste auch die Verarbeitung ändern. ** **.
Nehmen wir an, dass es in Zukunft weitere Anforderungen wie "Ich möchte im CSV-Format ausgeben" und "Ich möchte das Ergebnis an einen Server senden" gibt.
Jedes Mal muss nicht nur die Klasse "Drucker", sondern auch die Klasse "Rechner" einige Änderungen vornehmen.
Auch hier ist es notwendig, die Verarbeitungsfunktion zu ändern, obwohl es keine Änderungen in den Spezifikationen der "Verarbeitung (vier Betriebsregeln)" gibt.
Es ist wichtig, sich hier "unwohl" zu fühlen.
An dieser Stelle können Sie zu dem Schluss kommen: "Sollten wir dann die Abhängigkeit reduzieren, damit sie nicht von der Änderung der Abhängigkeit betroffen ist?"
Es besteht jedoch immer eine Abhängigkeit, da Importe in Python-Projekten nicht verwendet werden können.
Mit anderen Worten, der Einfallsreichtum, den wir brauchen, besteht darin, "angemessene Abhängigkeiten" zu schaffen.
Es bedeutet ** "abhängig von dem mit den wenigsten Änderungen" **.
Ein weiteres Problem bei diesem Projekt ist, dass Calculator
die ** Details ** der Ausgabe kennt.
Der Zweck von "Calculator" ist es, "das Ergebnis ausgeben" zu können, und ob es sich um ein CLI- oder ein JSON-Format handelt, ich möchte vermeiden, mir darüber Sorgen zu machen.
Lassen Sie uns nun die Abhängigkeiten umkehren.
Platzieren Sie die Klasse "Drucker", bei der es sich um eine abstrakte Klasse handelt, in "calculator.py" und importieren Sie die erforderlichen "ABCMeta" und "abstractmethod".
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
#Schalten Sie die Ausgabemethode um
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)
Ändern Sie dann jeweils "SimplePrinter" und "JsonPrinter", um die "Printer" -Klasse zu erben.
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))
Wichtig hierbei ist, dass die "SimplePrinters" von "calculator.py" abhängen.
** Hier wurden die Abhängigkeiten umgekehrt. ** "Ausgabe" hängt von "Verarbeitung" ab.
Natürlich ist es noch nicht perfekt, also entfernen wir den Status, in dem die Klasse "Calculator" von der Klasse "SimplePrinter" abhängt.
Lassen Sie den Konstruktor daher entscheiden, welcher Drucker verwendet werden soll.
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)
Geben Sie dann main.py
an, welcher Drucker verwendet werden soll.
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__':
#Holen Sie sich Argument
a = sys.argv[1]
b = sys.argv[2]
#Ausgabemethode
mode = sys.argv[3]
#Geben Sie die Druckerklasse an ("simple"Da es schwierig zu beurteilen ist, habe ich es anders gemacht)
printer: Printer = JsonPrinter() if mode == 'json' else SimplePrinter()
#Erstellen Sie eine Rechnerinstanz
calculator = Calculator(int(a), int(b), printer)
#Geben Sie die Ergebnisse jeder der vier Regeln aus
calculator.print()
Es gibt keinen Import in berechne.py
, stattdessen gibt es einen Import in simple_printer.py
s.
Damit ist die Abhängigkeitsumkehr abgeschlossen.
Wie erwartet wurde auch die Ausgabe im CSV-Format angefordert.
Bisher war die Klasse "Calculator" auch jedes Mal betroffen, wenn die Ausgabemethode geändert wurde. Mal sehen, was passiert.
.
└── 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__':
#Holen Sie sich Argument
a = sys.argv[1]
b = sys.argv[2]
#Ausgabemethode
mode = sys.argv[3]
#Geben Sie die Druckerklasse an
printer: Printer = JsonPrinter() if mode == 'json' else CsvPrinter(
) if mode == 'csv' else SimplePrinter()
#Erstellen Sie eine Rechnerinstanz
calculator = Calculator(int(a), int(b), printer)
#Geben Sie die Ergebnisse jeder der vier Regeln aus
calculator.print()
Auf diese Weise wurde auch die CSV-Datei ausgegeben.
Sie können sich vorstellen, dass Sie die Ausgabemethode danach leicht ändern können.
Ich hoffe, es hilft Ihnen, das Prinzip der Abhängigkeitsumkehr zu verstehen. Ein letzter Punktzusatz.
Diejenigen, die sagen "Ich verstehe das Prinzip der Umkehrung von Abhängigkeiten!", Werden sofort versuchen, das Design und die Implementierung zu korrigieren und sagen "Dies ist ein Problem!", Wenn sie ein Projekt sehen, das keine angemessene Abhängigkeit zu haben scheint. Ich bin).
Nach dem Refactoring von Version 2 hängt die Klasse "Calculator" beispielsweise von der Klasse "Printer" ab. Daher möchten Sie möglicherweise an dieser Stelle das Prinzip der Abhängigkeitsumkehr anwenden.
Das ist aber verfrüht. Wenn Sie wissen, dass "die Ausgabemethode zu diesem Zeitpunkt so stark erhöht werden kann, wie Sie möchten", sollten Sie sie natürlich anwenden. Wenn sich "die Ausgabemethode wahrscheinlich nicht ändert", wenden Sie ** << hold an 》 ** Ich denke, es kann eine gute Entscheidung sein.
Persönlich möchte ich die Abhängigkeiten von "Details" wie der Ausgabe so schnell wie möglich klären, aber ich denke, es ist wichtig zu denken, dass "Sie es jederzeit ändern können".
Wenn ich Zeit habe, möchte ich über "DI = Dependency Injection" schreiben.
Wenn Sie Anregungen oder Fragen haben, können Sie diese gerne kommentieren.
Recommended Posts