본문 바로가기
golang

[golang] go언어의 의존성 주입(di)과 제어의 역전(ioc)

by devjh 2021. 8. 15.
반응형

이번게시글에서는 go 언어에서의 제어의 역전과 의존성 주입에 대해 정리합니다.

 

구현방식은 아래 게시글의 첫번째 방법을 사용하였습니다.

https://frozenpond.tistory.com/123

 

[golang] go언어를 객체지향처럼 사용하는방법

golang은 객체지향 언어가 아닙니다. import만 하면 어디서든 변수, 메서드에 함부로 접근할수 있습니다. 개인적으로 뭔가 불안하고 어색하고 불편합니다. 객체지향처럼 만들어봤습니다.(현재 저희

frozenpond.tistory.com

 

1. 의존성 주입이란

dependency injection이라고 부르는 의존성 주입은 

하나의 모듈에서 의존관계를 가지는 모듈을 내부에서 생성하는것이 아니라, 외부에서 주입해주는 방식을 말합니다.

결합도를 낮추기위해 내부에서는 추상에 의존하고 외부에서는 구현체를 주입해줍니다.

 

 

2. 제어의 역전이란

inversion of control 또는 dependency inversion이라고 부르는 제어의 역전(의존성 역전)은 interface를 사용하여 구현됩니다.

의존성을 가지는 모듈을 호출할때 인터페이스를 통해 호출하며

상속관계(의존성)가 제어의 흐름과 반대가 되는 현상을 말합니다.

다형성을 안전하게 구현함으로써 소프트웨어의 품질을 높이고 결합도를 낮추는 아키텍쳐입니다.

 

 

 

3. 예시

두가지 아키텍쳐 설계방식은 말로만 들으면 이해할 수 없습니다.

간단한 예시를 만들어봤습니다.

 

리모콘이 있습니다.

리모콘은 키다 끄다 의 역할만 수행하면 되지

티비를 킬지, 에어콘을킬지 컴퓨터를 킬지를 리모콘은 몰라도 되며 리모콘이 정하지 않습니다.(리모콘 내부에서 new를 하지 않습니다.)

의존성을 주입받되, interface로 아규먼트 패싱을 하면 완성됩니다.(구현이아닌 추상에 의존)

제어의 방향이 역전되 있어서(화살표방향이 반대) 안전하게 다형성 구현이 가능합니다.

 

4. 구현

(github.com/jaeho310/golang-di-ioc) 에서 확인가능합니다.

(1). 패키지구조

|-- interfaces
|   `-- remote_controller.go
|-- machine
|   |-- air_conditioner.go
|   |-- computer.go
|   `-- tv.go
`-- main.go

 

(2). remote_controller.go

package interfaces

// 리모콘은 Machine이라는 의존성이 필요합니다.
// 외부에서 생성할때 주입해줍니다.
type RemoteController struct {
	machineImpl Machine
}

type Machine interface {
	TurnOn() (string, error)
}

func (RemoteController) New(machine Machine) *RemoteController {
	return &RemoteController{machine}
}

// 리모콘은 뭐를 키는지 몰라도 됩니다.
func (remoteController *RemoteController) TrunOnMachine() (string, error) {
	return remoteController.machineImpl.TurnOn()
}

리모콘 구조체는 기계인터페이스 의존성이 필요합니다.

그것이 TV가 될지 Computer가 될지 에어컨이될지 리모콘은 몰라도 됩니다.

내부에서 Machine 구현체를 New를 해주지않고 외부에서 생성해줄때 주입을 해줘야합니다.(IOC, DI)

 

(3). air_conditioner.go

package machine

type AirConditional struct{}

func (AirConditional) New() *AirConditional {
	return &AirConditional{}
}

func (airConditional *AirConditional) TurnOn() (string, error) {
	return "에어콘을 킵니다", nil
}

 

 

(4). computer.go

package machine

type Computer struct{}

func (Computer) New() *Computer {
	return &Computer{}
}

func (*Computer) TurnOn() (string, error) {
	return "컴퓨터를 킵니다", nil
}

 

(5). tv.go

package machine

type Tv struct{}

func (Tv) New() *Tv {
	return &Tv{}
}

func (*Tv) TurnOn() (string, error) {
	return "TV를 킵니다", nil
}

에어컨, 컴퓨터, 티비가 Machine 인터페이스의 구현체가 될수 있도록 TurnOn을 구현해줍니다.

Golang은 상속이 존재하지않아 자동구현이 까다롭습니다. 오타에 주의합니다.

 

 

(6). main.go

package main

import (
	interfaces "di-ioc/interfaces"
	machine "di-ioc/machine"
	"fmt"
)

func main() {

	// tv를 구현체로 설정하겠습니다.
	typeOfController := "tv"
	myRemoteController := getRemoteController(typeOfController)

	// err처리를 상위 스코프에서 마무리 하도록 작성했습니다.
	result, err := myRemoteController.TrunOnMachine()
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println(result)
}

// IOC와 DI를 해줍니다.
// 리모콘은 생성자에서 Machine 인터페이스를 받습니다.
// 원하는 구현체를 주입해줍니다.
func getRemoteController(
	typeOfController string) *interfaces.RemoteController {
	if typeOfController == "tv" {
		return interfaces.RemoteController{}.New(injectAirConditional())
	}
	if typeOfController == "airconditional" {
		return interfaces.RemoteController{}.New(injectTv())
	}
	return interfaces.RemoteController{}.New(injectComputer())
}

func injectAirConditional() *machine.AirConditional {
	return machine.AirConditional{}.New()
}

func injectTv() *machine.Tv {
	return machine.Tv{}.New()
}

func injectComputer() *machine.Computer {
	return machine.Computer{}.New()
}

리모콘은 생성자에서 TurnOn이 구현된 Machine 구현체를 주입받아야합니다.

상황에 맞게 에어컨, 컴퓨터, TV를 주입 해줍니다.

반응형

댓글