Summarize Ruby and Dependency Injection

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.

Conclusion

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

Summarize Ruby and Dependency Injection
Ruby and Gem
[Ruby] Classes and instances
Symbols and Destructive Ruby
[Ruby] Big Decimal and DECIMAL
Ruby classes and instances
Ruby inheritance and delegation
Ruby variables and methods
Dagger2 --Android Dependency Injection
What is DI (Dependency Injection)
DI: What is Dependency Injection?
GraphQL Ruby and actual development
Ruby syntax errors and countermeasures
About Ruby hashes and symbols
Dagger2 --Android Dependency Injection
What is DI (Dependency Injection)
DI: What is Dependency Injection?
Summarize Ruby and Dependency Injection
Ruby C extension and volatile
About Ruby and object model
[Ruby] Singular methods and singular classes
About Ruby classes and instances
Ruby variables and functions (methods)
Ruby methods and classes (basic)
Creating Ruby classes and instances
[Ruby] Singular methods and singular classes
[Ruby] Difference between get and post
[Ruby] present/blank method and postfix if.
[Ruby] Difference between is_a? And instance_of?
Ruby standard input and various methods
Dependency Injection to understand step by step
About Ruby single quotes and double quotes
[Ruby] then keyword and case in
[Ruby basics] split method and to_s method
About Ruby product operator (&) and sum operator (|)
Write keys and values in Ruby
[Ruby] If and else problems-with operators-
[Ruby] Boolean values ​​and logical operators
Project ruby and rails version upgrade
About object-oriented inheritance and about yield Ruby