Ich habe mir heutzutage viel Sorgen um Design und Architektur gemacht, aber ich habe meine eigene Meinung darüber geschrieben, ob Python, eine dynamisch typisierte Sprache, auch das Gesetz der Abhängigkeitsumkehr implementieren kann. Meine Meinung ist nicht absolut richtig, aber ich hoffe es hilft.
Zuerst müssen wir den ** Polymorphismus ** verstehen, bevor wir das Gesetz der Abhängigkeitsumkehr verstehen können. Die Erklärung stammt aus Wikipedia.
Polymorphismus beschreibt die Art des Typsystems einer Programmiersprache und gehört für jedes Element der Programmiersprache (Konstanten, Variablen, Ausdrücke, Objekte, Funktionen, Methoden usw.) mehreren Typen an. Bezieht sich auf das Eigentum der Vergebung. Auch Polymorphismus, Polymorphismus, Polymorphismus, Diversität genannt.
Da der Inhalt schwer zu verstehen ist, werde ich ihn ausführlich erläutern. Die Essenz ist der Teil von "** bezieht sich auf die Eigenschaft, dass sie mehreren Typen angehören dürfen **". Bei einer dynamisch typisierten Sprache wie Python ist sie nicht so bewusst, aber bei einer statisch typisierten Sprache darf sie grundsätzlich nicht zu mehreren Typen gehören, da die Variablentypen begrenzt sind.
Siehe zum Beispiel das folgende Beispiel. (C ++ - Code, aber ich denke nicht, dass der Inhalt schwierig ist)
main.cpp
#include <iostream>
using namespace std;
int main()
{
int a = 1;
string b = "hello";
a = b; // error
}
Die Variable a wird durch int definiert, und die Variable b wird durch einen String definiert. Ich versuche, einer Variablen vom Typ int eine Zeichenfolgentypvariable b mit "a = b" zuzuweisen. Dieser Vorgang führt zu einem Fehler. Dies liegt daran, dass ** Variable a als int-Typ definiert ist und nicht zu mehr als einem Typ gehören kann **.
Mit anderen Worten, Variablen usw. können grundsätzlich nicht zu mehreren Typen gehören. Im Fall von Polymorphismus wird der Inhalt, der es ihm ermöglicht, zu mehreren Typen zu gehören, im zitierten Teil von Wikipedia erläutert.
Schauen wir uns ein Beispiel für Polymorphismus an. (C ++ - Code, aber Sie müssen den Code selbst nicht verstehen)
main.cpp
#include <iostream>
using namespace std;
class Human{
public:
string name;
int age;
Human(const string _name, const int _age){
name = _name;
age = _age;
};
virtual void print_gender(){};
void print_info(){
cout << "name: " << name << ", age: " << age << endl;
};
};
class Man: public Human{
public:
Man(const string _name, const int _age): Human(_name,_age){};
void print_gender() override {
cout << "I am man." << endl;
};
};
class Woman: public Human{
public:
Woman(const string _name, const int _age): Human(_name,_age){};
void print_gender() override {
cout << "I am woman." << endl;
};
};
int main()
{
Man* taro = new Man("taro", 12);
Woman* hanako = new Woman("hanako", 23);
Human* human = taro;
human->print_gender();
human->print_info();
}
Die wichtigen Teile sind wie folgt.
Man* taro = new Man("taro", 12);
Woman* hanako = new Woman("hanako", 23);
Human* human = taro;
Eine Variable namens Taro der Man-Klasse wird der in der Human-Klasse definierten Variablen Human zugewiesen. Ich habe in der obigen Erklärung erwähnt, dass Variablen nur zu einem Typ (Klasse) gehören können, aber die Variable Mensch scheint sowohl zur Klasse Mensch als auch zur Klasse Mensch zu gehören. (Genau genommen gehört es nur dem Menschen)
Der Grund, warum dies möglich ist, ist, dass die Man-Klasse eine abgeleitete Klasse der Human-Klasse ist. Die Man-Klasse und die Human-Klasse sind verwandt. Das Bild ist also, dass Sie zulassen, dass verwandte Dinge auf die gleiche Weise behandelt werden.
Ich denke, der Vorteil des Polymorphismus besteht darin, dass Sie sauberen Code schreiben können, weil Sie Dinge mit denselben Eigenschaften auf dieselbe Weise behandeln können. (Ich werde keine konkreten Beispiele erklären. Es tut mir leid.)
Als Vorwissen ist es notwendig, ein wenig über "abstrakt" und "konkret" zu verstehen. Ich werde ein wenig über die Worte erklären.
Abstract kann als Schnittstelle umformuliert werden, ist jedoch im Grunde eine Typdefinition (die möglicherweise nicht genau korrekt ist). Betrachten Sie beispielsweise die folgende Funktion.
main1.py
def add_number(a: int, b: int) -> int:
"""Gibt das Ergebnis des Hinzufügens von zwei Argumenten zurück"""
return a + b
Eine Funktion namens add_number definiert eine Schnittstelle, die ein Argument von int verwendet und das Ergebnis von int ändert. Aus abstrakter Sicht denke ich nicht an das Innere der Implementierung, daher lautet der Funktionsname add_number, aber es ist mir egal, ob die Multiplikation anstelle der Addition innerhalb erfolgt. Berücksichtigen Sie nur die Ein- und Ausgänge (Schnittstellen).
Insbesondere denken wir über das Innere der Implementierung nach. Daher sollten wir prüfen, ob die interne Implementierung der Funktion add_number Addition oder Multiplikation ist.
Die Erklärung der vorausgesetzten Kenntnisse ist lang geworden. Außerdem gibt es fast keinen Python-Code ... Zuallererst ist die Bedeutung des Wortes "Abhängigkeit" verwirrend, deshalb werde ich es von dort aus erklären.
Siehe zum Beispiel den folgenden Code.
main2.py(Auszug)
def main():
taro: Man = Man('taro', 12)
taro.print_gender()
taro.print_info()
Es scheint, dass Sie in der Hauptfunktion eine Klasse namens Man verwenden. Dieser Zustand kann als "die Hauptfunktion hängt von der Man-Klasse ab" bezeichnet werden. Mit anderen Worten, wenn der Inhalt der Man-Klasse geändert wird, kann auch die Hauptfunktion betroffen sein.
Lassen Sie uns nun die Erklärung des Gesetzes der Abhängigkeitsumkehr aus Wikipedia zitieren.
Im objektorientierten Design ist das Prinzip der Abhängigkeitsumkehr oder das Prinzip der Abhängigkeitsinversion 1 ein Begriff, der sich auf eine bestimmte Form bezieht, um Softwaremodule lose gekoppelt zu halten. Nach diesem Prinzip werden die traditionellen Abhängigkeiten von Modulen höherer Ebene zu Modulen niedrigerer Ebene, die das Softwareverhalten definieren, umgekehrt, sodass die Module höherer Ebene unabhängig von den Implementierungsdetails der Module niedrigerer Ebene gehalten werden können. Werden A. Module höherer Ebene sollten nicht von Modulen niedrigerer Ebene abhängen. Beide sollten sich auf Abstraktionen stützen. B. Abstracts sollten sich nicht auf Details stützen. Details sollten auf Abstraktion beruhen.
Eine andere esoterische Erklärung. .. .. Zuallererst die Wörter "oben" und "unten", aber im vorherigen Beispiel ist die Hauptfunktion die obere und die Man-Klasse die untere. Mit anderen Worten bedeutet "Module der oberen Ebene sollten nicht von Modulen der unteren Ebene abhängen", was speziell bedeutet, dass "die Hauptfunktion nicht von der Man-Klasse abhängen sollte". Als nächstes bedeutet der Teil, dass "beide von Abstraktionen abhängen sollten", mit anderen Worten, dass er eher von der Schnittstelle (Typ) als von der internen Implementierung abhängen sollte.
Es ist fast das gleiche wie der obige Code, aber siehe das folgende Beispiel.
main3.py(Auszug)
def main():
taro: Human = Man('taro', 12)
taro.print_gender()
taro.print_info()
Der Unterschied besteht darin, dass die ** Hauptfunktion ihre Abhängigkeit von der Man-Klasse durch eine Abhängigkeit von der Human-Klasse ** ersetzt hat. Schauen wir uns nun die Implementierungen der Klassen Human und Man an. (Der Inhalt ist ein Auszug, der vollständige Text steht am Ende)
main3.py(Auszug)
@dataclass
class Human(metaclass=ABCMeta):
name: str
age: int
#Abstrakte Methode
@abstractmethod
def print_gender(self) -> None:
pass
#Gängige Methode
def print_info(self) -> None:
print(f'name: {self.name}, age: {self.age}')
class Man(Human):
def print_gender(self):
print('I am man.')
Die Human-Klasse ist die Basisklasse und enthält keine komplexe Geschäftslogik. Mit anderen Worten, es kann eine abstrakte Klasse genannt werden. Im Gegenteil, die Man-Klasse ist eine konkrete Klasse, da es sich um eine Klasse handelt, die eine interne Implementierung enthält. Der Unterschied zwischen der Human-Klasse und der Man-Klasse besteht darin, dass sich die Human-Klasse weniger häufig und die Man-Klasse häufiger ändert. Der Grund ist natürlich, dass die Human-Klasse nur die Schnittstelle definiert (nicht genau, aber ...) und die Man-Klasse die interne Implementierung und Geschäftslogik enthält.
An dieser Stelle sehen Sie die Vorteile des Wechsels der Hauptfunktion von einer Man-Klasse (konkret) zu einer Human-Klasse (abstrakt). Wenn Sie von der Man-Klasse abhängig sind, wird die Hauptfunktion von den Änderungen in der Man-Klasse beeinflusst, sodass die Häufigkeit von Änderungen in der Man-Klasse hoch ist und daher stark beeinflusst wird. Im Gegenteil, wenn es von der menschlichen Klasse abhängt, wird es beeinflusst, wenn die menschliche Klasse geändert wird, aber es wird weniger betroffen sein, da die Änderungshäufigkeit geringer ist als die der Man-Klasse.
Wenn Sie sich also auf Abstraktionen verlassen, können Sie Code schreiben, der gegenüber Änderungen robuster ist.
Sie können, aber es ist nicht perfekt. Insbesondere ist es möglich, die abstrakte Klasse abc, lint und type check tool zusammen zu verwenden. Im Fall von Python funktioniert das Programm selbst, selbst wenn die Typinformationen falsch sind, so dass es nicht möglich ist, das gleiche Niveau wie bei anderen statisch typisierten Sprachen zu erreichen. Wenn Sie ein höheres Niveau anstreben, kann dies meiner Meinung nach erreicht werden, indem Sie die Entwicklungsumgebung als Team gründlich ausrichten und den Typ und die Flusen durch Vorabbindung überprüfen. Ich denke, dass es Funktionen bietet, die für die praktische Anwendung kein Problem darstellen.
Ich habe es in Eile geschrieben, daher denke ich, dass es sehr leicht zu verstehen ist. Dieser Bereich ist schwer zu verstehen, daher hoffe ich, dass er so hilfreich wie möglich ist. Wenn Sie Fragen in den Kommentaren haben, werden wir so viel wie möglich beantworten. Es wäre hilfreich, wenn Sie auf Fehler oder Ratschläge hinweisen könnten.
main3.py
from abc import ABCMeta, abstractmethod
from dataclasses import dataclass
@dataclass
class Human(metaclass=ABCMeta):
name: str
age: int
#Abstrakte Methode
@abstractmethod
def print_gender(self) -> None:
pass
#Gängige Methode
def print_info(self) -> None:
print(f'name: {self.name}, age: {self.age}')
class Man(Human):
def print_gender(self):
print('I am man.')
class Woman(Human):
def print_gender(self):
print('I am woman.')
def main():
taro: Human = Man('taro', 12)
taro.print_gender()
taro.print_info()
Recommended Posts