루비에서는 DI 코드를 만들 수 있고 의존성을 보내는 주체는 개발자이다.
프레임워크를 사용했을 시 의존성을 프레임워크가 보내주는 것이다.(IoC)
또한 레일즈 이야기를 하자면, 스프링에서는 DI를 제공해주는 반면, 레일즈에서는 DI를 제공하지 않는다.
왜냐하면 스프링처럼 빈(스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트)을 관리하면서 외부 모듈이나 클래스가 필요한 클래스에 주입을 해주는 기능이 없기 때문이다.
그래서 레일즈에서는 다른 모듈이나 클래스에 접근하고 싶을 때, 직접 생성해서 접근한다.
이러한 이유로 결합도가 높은 코드가 나올 수 밖에 없는 것이다.
1. 의존성 이야기
잘 디자인된 객체는 하나의 책임만 지고 있다. 그래서 복잡한 환경을 표현하기 위해 다른 객체와 협업을 할 수밖에 없다. 서로 협업을 하려면 다른 객체에 대한 지식이 필요하고, 이 지식은 의존성을 만들어 낸다.
이러한 의존성을 잘 관리해야 프로그램을 잘 만들어 낼 수 있다.
1) 의존한다는 표현
의존성이 높다는 것은 하나의 객체를 수정했을 때 다른 객체들을 뒤따라 수정해야 한다면 후자는 전자에 의존적이다.
2. rails에서 다른 오브젝트에 접근하는 방법
class TestController
def index
ts = TestService.new
end
end
이렇게 직접 생성해서 접근한다. 하지만 스프링에서는 생성자로 빈을 주입받아 사용할 수 있다.
3. 루비에서 DI 코드
여기에서 왜 DI를 지키지 않았을 때 발생하는 문제가 있는지 확인해보자.
1) DI를 지키지 않는 경우
class TestDi1
attr_reader :data, :distance
def initialize(data,distance)
@data = data
@distance = distance
end
def move
data + TestDi2.new(distance).go
end
end
class TestDi2
attr_reader :distance
def initialize(distance)
@distance = distance
end
def go
distance
end
end
위에 코드처럼 클래스는 두 개인데, 거의 한 클래스처럼 움직이는 것처럼 보인다. 왜냐하면 TestDi1 class에서 받은 distance로 TestDi2 클래스의 파라미터로 넣어주기 때문이다.
이렇게 되면 만약에 다른 인스턴스 변수를 추가하게 되면 변경을 여러 번해야한다.
class TestDi1
attr_reader :data, :distance, :type
def initialize(data,distance, type)
@data = data
@distance = distance
@type = type
end
def move
data + TestDi2.new(distance, type).go
end
end
class TestDi2
attr_reader :distance, :type
def initialize(distance, type)
@distance = distance
@type = type
end
def go
distance
end
end
위에 코드에서 볼 수 있는 것처럼 type 인스턴스 변수 하나를 추가했는데, 두 클래스를 한 클래스처럼 여러 번 수정을 해야한다.
이러한 코드를 보고 결합도가 높다고 표현한다.
2) DI를 지키는 경우
class TestDi1
attr_reader :data, :test_di2
def initialize(data,test_di2)
@data = data
@test_di2 = test_di2
end
def move
data + test_di2.go
end
end
class TestDi2
attr_reader :distance
def initialize(distance)
@distance = distance
end
def go
distance
end
end
이렇게 외부에서 TestDi2 클래스를 주입받으면 TestDi1 클래스 입장에서 이 클래스가 어떠한 인스턴스 변수가 필요한지 알 필요가 없다.
TestDi1 클래스는 이 클래스의 함수만 호출하면 되는 것이다.
이렇게 설계하면 변경에 TestDi2 클래스만 수정해도 TestDi2 클래스의 메서드를 호출하는데 전혀 문제가 없다.
class TestDi1
attr_reader :data, :test_di2
def initialize(data,test_di2)
@data = data
@test_di2 = test_di2
end
def move
data + test_di2.go
end
end
class TestDi2
attr_reader :distance, :type
def initialize(distance, type)
@distance = distance
@type = type
end
def go
distance
end
end
위에 코드처럼 TestDi2 클래스만 수정해도 TestDi1 클래스에는 전혀 문제가 없다.
스프링에서도 DI는 이러한 이유로 인해 사용하는 것이다.
4. DI의 장점
1) 각 오브젝트는 서로가 어떠한 로직을 가지고 있는지 몰라도 되고 그냥 함수만 호출하면 되는 것이다.
2) 결합도가 낮다.
3) 확장성
- 미래를 견뎌낼 수 있는 애플리케이션을 만드는 데 핵심적인 요소
4) 변화에 강하다.
'ruby' 카테고리의 다른 글
| instance variable encapsulation (0) | 2021.12.30 |
|---|---|
| 상속 vs 믹스인(mixin) (0) | 2021.12.28 |
| 블록 (0) | 2021.12.26 |
| 루비에서 람다식이란 (0) | 2021.12.26 |
| ruby 특징 (0) | 2021.12.26 |