Je m'inquiétais beaucoup de la conception et de l'architecture ces jours-ci, mais j'ai écrit ma propre opinion sur la question de savoir si Python, un langage à typage dynamique, peut également implémenter la loi de l'inversion des dépendances. Mon opinion n'est pas tout à fait correcte, mais j'espère que cela aide.
Tout d'abord, nous devons comprendre le ** polymorphisme ** avant de pouvoir comprendre la loi de l'inversion des dépendances. L'explication est tirée de Wikipedia.
Le polymorphisme décrit la nature du système de types d'un langage de programmation, et pour chaque élément du langage de programmation (constantes, variables, expressions, objets, fonctions, méthodes, etc.) ils appartiennent à plusieurs types. Fait référence à la propriété de pardonner. Aussi appelé polymorphisme, polymorphisme, polymorphisme, diversité.
Le contenu étant difficile à comprendre, je vais l'expliquer en détail. L'essence est la partie de "** fait référence à la propriété de leur permettre d'appartenir à plusieurs types **". Dans le cas d'un langage typé dynamiquement tel que Python, ce n'est pas si conscient, mais dans le cas d'un langage typé statiquement, il n'est fondamentalement pas autorisé à appartenir à plusieurs types car les types de variables sont limités.
Par exemple, voyez l'exemple ci-dessous. (Code C ++, mais je ne pense pas que le contenu soit difficile)
main.cpp
#include <iostream>
using namespace std;
int main()
{
int a = 1;
string b = "hello";
a = b; // error
}
La variable a est définie par int et la variable b est définie par string. J'essaye "d'assigner une variable de type chaîne b à une variable de type int a" avec "a = b". Cette opération entraînera une erreur. Ceci est dû au fait que ** la variable a est définie comme un type int et ne peut pas appartenir à plus d'un type **.
En d'autres termes, les variables etc. ne peuvent pas appartenir à plusieurs types en principe. Cependant, dans le cas du polymorphisme, le contenu qui lui permet d'appartenir à plusieurs types est expliqué dans la partie citée de Wikipédia.
Jetons un coup d'œil à un exemple de polymorphisme. (Code C ++, mais vous n'avez pas besoin de comprendre le code lui-même)
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();
}
Les parties importantes sont les suivantes.
Man* taro = new Man("taro", 12);
Woman* hanako = new Woman("hanako", 23);
Human* human = taro;
Une variable appelée taro de la classe Man est affectée à la variable appelée human définie dans la classe Human. J'ai mentionné dans l'explication ci-dessus qu'une variable ne peut appartenir qu'à un seul type (classe), mais la variable human semble appartenir à la fois à la classe Human et à la classe Man. (Strictement parlant, il n'appartient qu'à l'Homme)
La raison pour laquelle cela est possible est que la classe Man est une classe dérivée de la classe Human. La classe Man et la classe Human sont liées, donc l'image est que vous permettez aux choses liées d'être traitées de la même manière.
Je pense que le mérite du polymorphisme est que vous pouvez écrire du code propre parce que vous pouvez traiter les choses avec les mêmes propriétés de la même manière. (Je n'expliquerai pas d'exemples spécifiques. Je suis désolé.)
Comme connaissance préalable, il est nécessaire de comprendre un peu «abstrait» et «concret». Je vais vous expliquer un peu les mots.
Abstract peut être reformulé comme une interface, mais c'est essentiellement une définition de type (qui peut ne pas être tout à fait correcte). Par exemple, considérez la fonction suivante.
main1.py
def add_number(a: int, b: int) -> int:
"""Renvoie le résultat de l'ajout de deux arguments"""
return a + b
Une fonction appelée add_number définit une interface qui prend un argument de int et modifie le résultat de int. D'un point de vue abstrait, je ne pense pas à l'intérieur de l'implémentation, donc le nom de la fonction est add_number, mais je m'en fiche si la multiplication est effectuée au lieu de l'addition à l'intérieur. Ne considérez que les entrées et sorties (interfaces).
Plus précisément, nous pensons à l'intérieur de l'implémentation. Par conséquent, nous devons déterminer si l'implémentation interne de la fonction add_number est une addition ou une multiplication.
L'explication des connaissances préalables est devenue longue. De plus, il n'y a presque pas de code Python ... Tout d'abord, le sens du mot «dépendance» est déroutant, je vais donc l'expliquer à partir de là.
Par exemple, consultez le code ci-dessous.
main2.py(Extrait)
def main():
taro: Man = Man('taro', 12)
taro.print_gender()
taro.print_info()
Il semble que vous utilisez une classe appelée Man dans la fonction principale. Cet état peut être dit "la fonction principale dépend de la classe Man". En d'autres termes, si le contenu de la classe Man est modifié, la fonction principale peut également être affectée.
Maintenant, citons l'explication de la loi de l'inversion de dépendance à partir de wikipedia.
Dans la conception orientée objet, le principe de l'inversion de dépendance ou le principe de l'inversion de dépendance [1](principe d'inversion de dépendance) est un terme qui fait référence à une forme spécifique pour maintenir les modules logiciels faiblement couplés. Suivant ce principe, les dépendances traditionnelles des modules de niveau supérieur qui définissent le comportement du logiciel vers les modules de niveau inférieur sont inversées, de sorte que les modules de niveau supérieur peuvent rester indépendants des détails d'implémentation des modules de niveau inférieur. Devenir A. Les modules de niveau supérieur ne doivent pas dépendre des modules de niveau inférieur. Les deux devraient s'appuyer sur des abstractions. B. Les résumés ne doivent pas reposer sur des détails. Les détails doivent reposer sur l'abstraction.
Une autre explication ésotérique. .. .. Tout d'abord, les mots "supérieur" et "inférieur", mais dans l'exemple précédent, la fonction principale est la classe supérieure et la classe Man est la classe inférieure. En d'autres termes, «les modules de niveau supérieur ne doivent pas dépendre des modules de niveau inférieur» signifie spécifiquement «la fonction principale ne doit pas dépendre de la classe Man». Ensuite, la partie que «les deux doivent dépendre d'abstractions», en d'autres termes, cela signifie que cela doit dépendre de l'interface (type) plutôt que de l'implémentation interne.
C'est presque le même que le code ci-dessus, mais voyez l'exemple ci-dessous.
main3.py(Extrait)
def main():
taro: Human = Man('taro', 12)
taro.print_gender()
taro.print_info()
La différence est que la fonction ** main a remplacé sa dépendance sur la classe Man par une dépendance sur la classe Human **. Jetons maintenant un coup d'œil aux implémentations des classes Human et Man. (Le contenu est un extrait, le texte intégral est à la fin)
main3.py(Extrait)
@dataclass
class Human(metaclass=ABCMeta):
name: str
age: int
#Méthode abstraite
@abstractmethod
def print_gender(self) -> None:
pass
#Méthode commune
def print_info(self) -> None:
print(f'name: {self.name}, age: {self.age}')
class Man(Human):
def print_gender(self):
print('I am man.')
La classe Human est la classe de base et ne contient pas de logique métier complexe. En d'autres termes, on peut l'appeler une classe abstraite. Au contraire, la classe Man est une classe concrète car c'est une classe qui inclut une implémentation interne. La différence entre la classe Human et la classe Man est que la classe Human change moins fréquemment et la classe Man change plus souvent. La raison est, bien sûr, que la classe Human ne définit que l'interface (pas exactement, mais ...), et la classe Man contient l'implémentation interne et la logique métier.
À ce stade, vous pouvez voir les avantages de changer la fonction principale d'une classe Man (concrète) à une classe humaine (abstraite). Si vous dépendez de la classe Man, la fonction principale est affectée par les changements dans la classe Man, donc la fréquence des changements dans la classe Man est élevée, donc elle est grandement affectée. Au contraire, si cela dépend de la classe Humaine, elle sera affectée si la classe Humaine est modifiée, mais elle sera moins affectée car la fréquence de changement est inférieure à celle de la classe Homme.
Cela signifie que le fait de s'appuyer sur des abstractions vous permet d'écrire du code plus robuste aux changements.
Vous pouvez, mais ce n'est pas parfait. Plus précisément, cela est possible en utilisant ensemble la classe abstraite abc, lint et l'outil de vérification de type. Cependant, dans le cas de Python, même si les informations de type sont incorrectes, le programme lui-même fonctionnera, il n'est donc pas possible d'atteindre le même niveau que les autres langages à typage statique. Si vous visez un niveau supérieur, je pense que cela peut être réalisé en alignant soigneusement l'environnement de développement en équipe et en vérifiant le type et les peluches en pré-engagement. Je pense qu'il offre des fonctions qui ne posent pas de problème pour une utilisation au niveau pratique.
Je l'ai écrit à la hâte, donc je pense que c'est très facile à comprendre. Ce domaine est un domaine difficile à comprendre, j'espère donc qu'il sera utile autant que possible. Si vous avez des questions dans les commentaires, nous vous répondrons dans la mesure du possible. Il serait utile que vous signaliez des erreurs ou des conseils.
main3.py
from abc import ABCMeta, abstractmethod
from dataclasses import dataclass
@dataclass
class Human(metaclass=ABCMeta):
name: str
age: int
#Méthode abstraite
@abstractmethod
def print_gender(self) -> None:
pass
#Méthode commune
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