Je suis tombé sur un conteneur DI lors du développement avec Laravel, et je pensais que je n'avais jamais utilisé DI en développant avec Rails, donc je vais résumer pourquoi DI n'est pas courant dans Ruby (Rails).
TL;DR ―― Puisque Ruby peut changer dynamiquement la dépendance sur l'instance, DI est fondamentalement inutile. ―― Reportez-vous presque à l'article ci-dessous, donc si vous parlez anglais, veuillez lire cet article. LEGOs, Play-Doh, and Programming
DI DI (Dependency Injection) est une technologie qui élimine la dépendance entre les instances appelée injection de dépendance. Il est pratique pour remplacer des objets fictifs pendant les tests, car vous pouvez injecter des instances dépendantes de l'extérieur. Le framework DI est une technique qui réalise un couplage lâche et une agrégation élevée en laissant le conteneur DI créer et injecter des instances. Certains frameworks DI sont couramment utilisés pour les applications natives (je ne connais pas iOS), prenons donc Java comme exemple.
Java
public class A {
//L'instance B est générée à l'intérieur de A et le degré de connexion est élevé
public Client client = new B();
}
interface Client {
String outputSelf();
}
public class B implements Client {
@Override
public String outputSelf() {
return "B";
}
}
public class C implements Client {
@Override
public String outputSelf() {
return "C";
}
}
DI résout ce problème, et il en existe plusieurs types, mais en prenant l'injection de constructeur comme exemple, cela ressemble à ce qui suit. A perd la substance de B et devient faiblement couplé.
public class A {
public Client client;
public void A(Client client) {
this.client = client;
}
}
Cependant, dans ce cas, l'appelant est responsable de la création de l'instance à injecter dans A, et les responsabilités sont réparties, donc lors de l'utilisation du conteneur DI (ex. Dagger2), cela ressemble à ceci. Le conteneur DI est chargé d'injecter la dépendance à l'activité, y compris A.
public class A {
public Client client;
@Inject
public void A(Client client) {
this.client = client;
}
}
Ruby Comme expliqué jusqu'ici, DI facilite le remplacement d'objets en le rendant dépendant de l'interface au lieu de la classe concrète (instance). Ensuite, comme dans la conclusion du début, qu'est-ce que cela signifie qu'un mécanisme comme un conteneur DI n'est pas nécessaire dans Ruby? Même si vous souhaitez effectuer une injection, la fonction de langage d'origine de Ruby peut suffisamment réaliser un couplage lâche et une agrégation élevée. Il semble que l'utilisation d'un conteneur DI ne peut qu'ajouter de la complexité.
Comme pour l'exemple Java, si une injection de constructeur est effectuée, ce sera comme suit.
class A
def initialize(options={})
@client_impl = options[:client] || B
end
def new_client
@client_impl.new
end
end
class B end
class C end
Reflection Une technologie qui lit ou réécrit la structure du programme lui-même lors du processus d'exécution du programme. Dans cet article, il s'agit d'une méthode de réécriture dynamique de l'entité de client, qui est membre de la classe A, de l'extérieur.
Ruby
L'article original indique que Ruby est la langue suivante.
The very Ruby language itself is designed for this: closures, super-simple introspection of objects, runtime modification of existing objects, and the use of modules for extending classes and objects all tend to result in an environment that is simple, malleable, and extensible.
Par conséquent, si vous écrivez dynamiquement comme Ruby,
class A
def new_client
client.new
end
def client
B
end
end
class B end
class C end
Même l'instance créée peut être facilement modifiée.
#La méthode d'instance peut être modifiée dynamiquement
def A.new.client
C
end
Java La réflexion est également prise en charge en Java, vous pouvez donc remplacer les membres de l'instance comme suit, mais ...
class D {
public static void main(String args[]){
try {
A a_instance = new A();
Class a = a_instance.getClass();
Field field = a.getDeclaredField("client");
System.out.println("before reflection:" + ((Client) field.get(a_instance)).outputSelf());
field.setAccessible(true);
field.set(a_instance, new C());
System.out.println("after reflection:" + ((Client) field.get(a_instance)).outputSelf());
} catch (Exception e) {}
}
}
Par rapport au cas de l'injection, il est extrêmement difficile à comprendre et la complexité augmente.
Ruby est plus dynamique que des langages comme Java, et même une fois créé, il est facile de changer et de changer les dépendances. Par conséquent, il semble que l'introduction d'un mécanisme tel qu'un conteneur DI, qui était nécessaire dans un langage statique, ne ferait qu'augmenter la complexité.
Recommended Posts