[디자인 패턴] DI(Dependency Injection) Pattern (1) - IoC(Inversion of Control)

전성훈·2023년 11월 17일
0

DesignPattern

목록 보기
17/18
post-thumbnail

주제: Inversion of Control, IoC란?


  • 제어 역전(Inversion of Control, IoC)은 소프트웨어 설계에서 사용되는 패턴으로, 프로그램의 제어 흐름을 사용자 정의 코드가 아닌 프레임워크나 라이브러리에 위임하는 것을 말합니다.
  • 이는 프로그래밍에서의 종속성을 역전시켜, 코드의 재사용성과 유지보수성을 향상시키는 데 도움이 됩니다.

기존 방식과의 차이

  • 전통적인 프로그래밍에서는 애플리케이션의 메인 흐름과 주요 결정을 사용자가 작성한 코드가 제어했습니다.
  • 반면, 제어 역전에서는 이러한 제어를 외부 프레임워크나 라이브러리에 넘깁니다.

장점

  • 재사용성
    • 동일한 비즈니스 로직이나 컴포넌트를 다양한 컨텍스트에서 재사용할 수 있습니다.
  • 유지보수성
    • 컴포넌트 간의 느슨한 결합을 통해 코드의 유지보수가 용이해집니다.
  • 테스트 용이성
    • 의존성을 외부에서 주입할 수 있어, 단위 테스트가 더 용이해집니다.
  • 확장성
    • 새로운 기능이나 컴포넌트를 쉽게 추가할 수 있습니다.

예시 코드

첫번째

  • 해당 코드를 보면 App의 내부에서 User를 생성하고 start 메서드를 통해 useradd 메서드를 호출하며, 이때 Server을 무엇을 사용할지를 결정합니다.
  • 즉, User 클래스를 확인해보면, Server 클래스를 만들고 UserDatabase에 저장합니다.
// Inversion of Control
class App {
    let user = User()
    
    func start() {
        user.add()
    }
}

class User {
    let database = Server1()
    
    var isValid = true
     
    func add() {
        if isValid {
            database.add(self)
        }
    }
}


class Server1 {
    func add(_ user: User) {
        print("Server1")
    }
}


let app = App()
app.start()

두번째

  • 만약 다른 서버를 활용한다고 한다면 protocol를 통해 이를 관리할 수 있습니다.
  • User내부에 있는 Server 클래스를 Protocol로 변경해주며, User를 초기화할때 설정할 수 있게 코드를 변경해주면 됩니다.
// Inversion of Control
class App {
    let user = User(2)
    
    func start() {
        user.add()
    }
}

class User {
    var database: ServerProtocol!
    
    var isValid = true
     
    init(_ serverType: Int) {
        if serverType == 1 {
            database = Server1()
        } else {
            database = Server2()
        }
    }
    
    func add() {
        if isValid {
            database.add(self)
        }
    }
}

protocol ServerProtocol {
    func add(_ user: User)
}

class Server1: ServerProtocol {
    func add(_ user: User) {
        print("Server1")
    }
}

class Server2: ServerProtocol {
    func add(_ user: User) {
        print("Server2")
    }
}

let app = App()
app.start()
  • 하지만 위 코드를 확인해보면, 아직도 모든 결정을 User 클래스에 진행되고 있다는 것을 확인할 수 있습니다.
  • IoC의 중점은 어떤 Server를 활용할 것인가 이기 때문에 해당 부분의 의존성 역전이 필요합니다.

세번째

// Inversion of Control
class App {
    let user = User(database: Server1())
    
    func start() {
        user.add()
    }
}

class User {
    var database: ServerProtocol!
    
    var isValid = true
     
    init(database: ServerProtocol) {
        self.database = database
    }
    
    func add() {
        if isValid {
            database.add(self)
        }
    }
}

protocol ServerProtocol {
    func add(_ user: User)
}

class Server1: ServerProtocol {
    func add(_ user: User) {
        print("Server1")
    }
}

class Server2: ServerProtocol {
    func add(_ user: User) {
        print("Server2")
    }
}

let app = App()
app.start()
  • 기존의 User 클래스는 클래스 내부에서 database에 대해 설정해줬다면, 해당 부분을 역전시켜서 User 클래스에게 어떤 database를 사용할것인지 알려주는 방식으로 변경해서 의존성을 역전해줬습니다.
  • 이렇게하면 User 클래스는 더이상 database에 대해 생성할 필요가 없어집니다.

출처(참고문헌)

제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.

감사합니다.

0개의 댓글