티스토리 뷰
오늘은 의존성 주입(DI)에 대해 공부한 내용을 정리해보려 합니다.
https://ko.wikipedia.org/wiki/의존성_주입
의존성 주입이란?
객체지향 프로그래밍에서 의존성이 있다는 것은 클래스 간에 의존 관계가 있다는 것을 의미합니다. 즉, 다음과 같이 A라는 클래스에서 B클래스 객체를 생성해 사용하면 의존관계가 형성되는것이죠.
class B: A {
}
class A {
let b = B() // 의존 관계 형성
}
의존관계가 형성되면 한 클래스가 바뀔 때 다른 클래스가 영향받게 되는데. 이러한 의존관계를 약하게 만들기 위한 방법이 의존성 주입입니다. 객체의 생성과 사용의 관심을 분리함으로써 가독성과 코드 재사용성을 높이는데 그 의의가 있습니다.
만약 메일을 사용하는 User라는 클래스가 있고, 객체를 외부에서 주입하는 것이 아니라 내부에서 생성한다면, 사용자의 메일을 바꿔줄 때마다 매번 해당 클래스를 수정해줘야 됩니다.
class Naver {
var name: String {
return "Naver"
}
var domain: String {
return "naver.com"
}
}
class Gmail {
var name: String {
return "Google"
}
var domain: String {
return "gmail.com"
}
}
class User {
var mail = Naver()
}
// 만약 기존 Naver에서 Gmail로 바꾸려면 User 클래스로 가서 Naver() -> Gmail()로 수정
class User {
var mail = Gmail()
}
내부에서 객체를 생성하게 되면, 변경사항이 있을 때마다 해당 클래스로 가서 직접 수정해줘야 하는 번거로움도 있지만, 코드 재활용에도 문제가 생기게 됩니다. 만약 각각 gmail , naver를 사용하는 user 객체를 만들어야 하면, 다음과 같이 작성해줘야 하니까요
class NaverUser {
var mail = Naver()
}
class GmailUser {
var mail = Gmail()
}
let naverUser = NaverUser()
let gmailUser = GmailUser()
하지만, 다음과 같이 EmailType이라는 Protocol을 만들고 객체를 외부에서 생성해 주입해주게 된다면, 의존성(결합도)은 낮아지고 가독성과 코드 재사용성은 높일 수 있게 됩니다.
protocol EmailType {
var name: String { get }
var domain: String { get }
}
class Naver: EmailType {
var name: String {
return "Naver"
}
var domain: String {
return "naver.com"
}
}
class Gmail: EmailType {
var name: String {
return "Google"
}
var domain: String {
return "gmail.com"
}
}
class User {
var mail: EmailType
init(email: EmailType) {
self.mail = email
}
}
let naver = Naver()
let gmail = Gmail()
let naverUser = User(email: naver)
let gmailUser = User(email: gmail)
또 다른 예로 네트워크 통신을 예로 들 수 있는데요.
만약 다음과 같이 서버에 요청을 보내는 APIRequester를 만든다고 가정했을 때, URL에 대한 정보를 외부에서 생성해 주입해주는것이 아니라 내부에서 생성하게 되면 URL 별로 APIRequester를 만들어야 할지도 모릅니다.
// 의존성 주입 X -> 세부 사항을 내부에서 직접 결정(내부에서 객체 생성)하기 때문에 APIRequester와 Router간에
// 의존 결합도가 높아지고 코드 재사용성이 나빠짐
// (Router에 대한 구체적 구현은 따로 명시하진 않았지만 Router는 url 정보를 갖고 있습니다.)
struct APIRequester {
typealias Completion<T> = (Result<T, AFError>) -> Void
let router: Router = Router.fetchUserInfo
func getRequest<T: Codable> (completion: @escaping Completion<T>) {
let request = AF.request(router.url, method: .get, headers: nil)
request.responseDecodable(of: T.self) { response in
completion(response.result)
}
}
}
// Router가 달라지면 APIRequester를 여러개 작성해야함...
struct APIRequester2 {
typealias Completion<T> = (Result<T, AFError>) -> Void
let router: Router = Router.fetchBookInfo()
func getRequest<T: Codable> (completion: @escaping Completion<T>) {
let request = AF.request(router.url, method: .get, headers: nil)
request.responseDecodable(of: T.self) { response in
completion(response.result)
}
}
}
// 사용
func fetchUserInfo(completion: @escaping Completion<LoginResponse>) {
APIRequester().getRequest(completion: completion)
}
func fetchBookInfo(completion: @escaping Completion<BookInfo>) {
APIRequester2().getRequest(completion: completion)
}
하지만 다음과 같이 외부에서 생성해 주입해주게 되면, 객체간 의존결합도가 낮아지고 코드의 재사용성이 높아지게 됩니다.
// 의존성 주입 -> 세부 사항을 외부에서 결정해서 주입해주기 때문에 코드 재사용성을 높일 수 있음.
// 외부에서 Router 주입 (아래 코드에서 Router는 url 정보를 갖고 있음)
struct APIRequester {
typealias Completion<T> = (Result<T, AFError>) -> Void
let router: Router
init(with router: Router) {
self.router = router
}
func getRequest<T: Codable> (completion: @escaping Completion<T>) {
let request = AF.request(router.url, method: .get, headers: nil)
request.responseDecodable(of: T.self) { response in
completion(response.result)
}
}
}
// 사용
func fetchUserInfo(completion: @escaping Completion<LoginResponse>) {
let router: Router = .fetchUserInfo
APIRequester(with: router).getRequest(completion: completion)
}
func fetchBookInfo(completion: @escaping Completion<BookInfo>) {
let router: Router = .fetchBookInfo()
APIRequester(with: router).getRequest(completion: completion)
}
잘못된 내용 혹은 오기가 있다면 꼭 댓글 남겨주시길 바랍니다!! 감사합니다 :)
참고)
https://ko.wikipedia.org/wiki/의존성_주입
https://mangkyu.tistory.com/150
'공부' 카테고리의 다른 글
모바일 개발에서의 Clean Architecture (0) | 2023.05.14 |
---|---|
메모이제이션(Memoization)기법 Python으로 알아보기 (1) | 2020.10.19 |
객체지향 5원칙(SOLID) 정리 (0) | 2020.10.15 |