I came across a DI container while developing with Laravel, and I thought that I had never used DI because I was developing with Rails, so I will summarize why DI is not common in Ruby (Rails).
TL;DR --Ruby can dynamically change the dependency on the instance, so basically DI is unnecessary. ――Almost refer to the article below, so if you can speak English, please read this article. LEGOs, Play-Doh, and Programming
DI DI (Dependency Injection) is a technology that eliminates the dependency between instances called dependency injection. Since you can inject dependent instances from the outside, it is convenient for replacing mock objects at the time of testing. The DI framework is a technique that realizes loose coupling and high aggregation by letting the DI container create and inject instances. There are some DI frameworks that are commonly used for native apps (I don't know iOS), so let's take Java as an example.
Java
public class A {
//B instance is generated inside A and the degree of coupling is high.
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 solves this problem, and there are several types, but taking constructor injection as an example, it looks like the following. A loses the substance of B and becomes loosely coupled.
public class A {
public Client client;
public void A(Client client) {
this.client = client;
}
}
However, in this case, the caller is responsible for creating the instance to be injected into A, and the responsibilities are distributed, so when using the DI container (ex. Dagger2), it looks like this. The DI container is responsible for all the dependency injection to Activity, including A.
public class A {
public Client client;
@Inject
public void A(Client client) {
this.client = client;
}
}
Ruby As explained so far, DI makes it easy to replace objects by making it dependent on the interface instead of the concrete class (instance). Then, as in the conclusion at the beginning, what does it mean that a mechanism like a DI container is unnecessary in Ruby? Even if you want to perform injection, Ruby's original language function can sufficiently achieve loose coupling and high aggregation. It seems that using a DI container can only add complexity.
As with the Java example, if constructor injection is performed, it will be as follows.
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 A technology that reads or rewrites the structure of the program itself during the execution process of the program. In this article, it is a method to dynamically rewrite the entity of client which is a member of class A from the outside.
Ruby
The original article states that Ruby is the following language.
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.
Therefore, if you write dynamically like Ruby,
class A
def new_client
client.new
end
def client
B
end
end
class B end
class C end
Even the created instance can be easily changed.
#Instance methods can be changed dynamically
def A.new.client
C
end
Java Since reflection is also supported in Java, it is possible to replace the members of the instance as follows, but ...
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) {}
}
}
Compared to the case of injection, it is overwhelmingly difficult to understand and the complexity increases.
Ruby is more dynamic than languages like Java, and even once created instances are easy to change and dependents to change. Therefore, it seems that introducing a mechanism such as a DI container, which was necessary in a static language, would only increase the complexity.
Recommended Posts