DI Container est un système qui transmet automatiquement une instance du type approprié à l'argument du constructeur lors de la création d'une instance d'une classe. Vous pouvez facilement implémenter des modèles DI et exprimer du code faiblement couplé.
Si vous souhaitez en savoir plus sur les modèles DI, veuillez vous référer à un autre article. En fait, c'est très facile à faire, alors je ferai de mon mieux.
Je vais lier le référentiel terminé. Dépôt
DEMO
Enregistrez dans la classe DI que c'est la classe d'implémentation, ou que cette classe sera utilisée dans une seule tonne. Après cela, le flux consiste à demander à la classe DI de créer une instance de cette classe.
Regardez la démo de calmar.
class Animal:
def __init__(self, kind: str, name: str):
self.kind = kind
self.name = name
def __str__(self):
return "I am {kind}. My name is {name}".format(kind=self.kind, name=self.name)
class Cat(Animal):
def __init__(self, name: str = "Cathy"):
super().__init__('Cat', name)
di = DI()
di.register(Animal, Cat)
print(di.resolve(Animal))
# > I am Cat. My name is Cathy.
class Person:
def __init__(self, name: str, role: str):
self.name = name
self.role = role
def __str__(self):
return "I am {name}. My role is {role}.".format(name=self.name, role=self.role)
nukumizu = Person('Nukumizu', 'Actor')
di.register_singleton(Person, nukumizu)
print(di.resolve(Person))
# > I am Nukumizu. My role is Actor.
Les exigences minimales pour un conteneur DI sont les trois suivantes.
--Classe de conteneur pour enregistrer la classe ou le singleton correspondant
Jetons un œil au code réel dans l'ordre.
Container Commençons par la classe Container. Cette classe fournit les fonctionnalités suivantes:
Jetons un coup d'œil au code réel.
container.py
class DIContainer:
concrete_table = {}
singleton_table = {}
def register(self, base_cls: type, concrete_cls: type):
if not issubclass(concrete_cls, base_cls):
raise TypeError('Concrete class is required {} not {}.'.format(base_cls, concrete_cls))
self.concrete_table[base_cls] = concrete_cls
def register_singleton(self, t: type, instance: object):
if not isinstance(instance, t):
raise TypeError('Instance type is required {} not {}.'.format(t, type(instance)))
self.singleton_table[t] = instance
def get(self, t: type):
if self.is_registered_concrete(t):
return self.concrete_table[t]
return t
def get_singleton(self, t: type):
if self.is_registered_singleton(t):
return self.singleton_table[t]
raise KeyError('{} is not registered as singleton.'.format(t))
def is_registered_singleton(self, t: type):
return t in self.singleton_table.keys()
def is_registered_concrete(self, t: type):
return t in self.concrete_table.keys()
def is_registered(self, t: type):
return self.is_registered_concrete(t) or self.is_registered_singleton(t)
def clear(self):
self.concrete_table.clear()
self.singleton_table.clear()
Puisque je ne m'inscris que honnêtement, je pense qu'il n'y a pas d'endroit pour expliquer en particulier. Je suis désolé si le code est difficile à lire.
Resolver Ensuite, regardons la classe Resolver. Cette classe fournit les fonctionnalités suivantes:
Il n'y en a que deux.
Jetons un coup d'œil au code réel.
resolver.py
from container import DIContainer
class Resolver:
def __init__(self, container: DIContainer):
self.container = container
def resolve(self, cls: type):
if self.container.is_registered_singleton(cls):
return self.container.get_singleton(cls)
cls = self.container.get(cls)
init_args = self.resolve_init_args(cls)
return cls(**init_args)
def resolve_init_args(self, cls: type):
init_args_annotations = get_init_args_annotations(cls)
defaults = get_init_default_values(cls)
result = {}
args_count = len(init_args_annotations)
for key, t in init_args_annotations.items():
if self.container.is_registered(t) or len(defaults) < args_count:
result[key] = self.resolve(t)
else:
result[key] = defaults[len(defaults) - args_count]
args_count -= 1
return result
def get_init_args_annotations(cls: type):
if hasattr(cls.__init__, '__annotations__'):
return cls.__init__.__annotations__
return {}
def get_init_default_values(cls: type):
if hasattr(cls.__init__, '__defaults__'):
result = cls.__init__.__defaults__
return [] if result is None else result
return []
Je vais expliquer get_init_args_annotations en dehors de la classe. Tout ce dont vous avez besoin pour créer un DIContainer est l'information sur l'argument du constructeur. Cela ne commencera pas sans cela. La façon d'obtenir les informations du constructeur en python est annotations. Vous pouvez maintenant obtenir un tableau associatif avec le nom d'argument Key et le type de classe Value. Un exemple est présenté ci-dessous.
class Person:
def __init__(self, name: str, age: int):
pass
print(Person.__init__.__annotaions__)
# {'name': <class 'str'> , 'age': <class 'int'>}
Créez une instance de la classe basée sur les informations obtenues par \ __ annotations__.
Maintenant, vérifions le flux autour du processus de génération.
C'est comme générer des classes de manière récursive. Maintenant que les fonctions de conteneur DI sont terminées, vous pouvez l'utiliser en créant une façade.
di.py
from container import DIContainer
from resolver import Resolver
class DI:
def __init__(self):
self.container = DIContainer()
self.resolver = Resolver(self.container)
def register_singleton(self, t: type, instance: object):
self.container.register_singleton(t, instance)
def register(self, base_cls: type, concrete_cls: type):
self.container.register(base_cls, concrete_cls)
def resolve(self, t: type):
return self.resolver.resolve(t)
def clear(self):
self.container.clear()
À ce stade, vous pouvez utiliser DI Container comme DEMO.
Une fois que vous l'aurez compris, vous aurez moins de chances d'oublier comment l'utiliser. Connaissant le modèle DI, j'ai résolu quelques questions sur la gestion des problèmes liés à l'interface.
C'était incroyablement pratique de ne pas avoir à vérifier comment générer des classes une par une en introduisant DI Container dans notre propre bibliothèque sur le lieu de travail précédent. Bien sûr, il y a des inconvénients, mais c'est toujours pratique, je vais donc l'utiliser quand il pourra être utilisé.
Je vais lier le référentiel terminé. Dépôt
Je vous serais reconnaissant si vous pouviez me faire savoir s'il y a des lacunes.
Merci de rester avec nous jusqu'à la fin.
Recommended Posts