DIP (Dependency Inversion Principle; 의존 역전 법칙)

프로그래머는 “추상화에 의존해야지, 구체화에 의존하면 안된다.". DI(의존성 주입) 역시 이 원칙을 따르는 방법 중에 하나이다.

 

 

 


구현체에 의존

 

 

 

메인 화면과 메모 상세 화면에서 메모 데이터를 조작하고 싶다.

그래서 MainPresenter와 MemoDetailPresenter는 MemoLocalRepository를 가지고 있다.

 

로컬 데이터베이스(임베디드 데이터베이스)를 사용하여 앱을 만드려한다.

MemoLocalRepository는 *RoomDatabase에서 메모 데이터를 받아오도록 작성했다.

*RoomDatabase : Android 임베디드 데이터베이스

 

 

 

개발도중 서버를 사용하는 것으로 요구사항이 변경되었다.

 

이로 인해 MemoLocalRepository를 고쳐야했고 이를 사용중이던 MainPresenter와 MemoDetailPresenter 역시 수정이 필요했다. (의존성)

 

 

 

 


DIP 적용

 

 

위와 같은 일을 막기 위해 구조부터 변경했다.

MemoRepository를 인터페이스로 만들었다.

 

MemoLocalRepository는 MemoRepository 인터페이스상속받아 만들었고

MainPresenter와 MemoDetailPresenter는 MemoRepository 인터페이스로 가지고 있다.

 

사용하는 부분에서도 구현하는 부분에서도 추상화에만 의존하고 있다.

 

이로 인해 MainPresenter와 MemoDetailPresenter는

어디서 어떻게 받아오는지 상관없이 인터페이스 함수대로 데이터를 조작할 수 있다.

 

MemoLocalRepository 대신 MemoRemoteRepository로 변경하던 MemoDummyRepository로

변경하던 상관없이 MemoRepository에만 의존성을 가지고 있으면 된다.

 

 

 

 

 


DI

 

MemoPresenter는 MemoRepsotiroy 인터페이스에만 의존하면 된다.

그럼 MemoRepository는 어디서 어떻게 생성해주지?

 

어디선가 MemoLocalRespository를 생성해서 전달해준다면 

그 생성한 클래스는 결국 구현체에 의존하게 되는데 조삼모사가 아닌가?

 

그래서 DI가 존재한다.

 

전문적으로 생성해서 전달해주는 모듈이 있고

구현체를 몰라도 모듈에 설정한대로 외부에서 구현체를 생성해 전달해주어

사용하는 부분은 구현체에 의존성없이 사용할 수 있다.

 

class MainPresenter(
    private val memoRepository: MemoRepository
)

private val presenter: MainPresenter by inject()

 

위와 같은 코드 형태이다.

by inject()를 통해 외부에서 인스턴스를 만들어 준다.

 

어떤 구현체의 인스턴스인지 명시하지 않고 사용할 수 있어 의존성이 전혀 없다. 

 

DI 역시 DIP의 방법 중에 하나이고

DI를 통해 DIP를 더 잘 지킬 수 있다.

 

 

 

 

 


외부 포스팅

 

좋은 DIP 포스팅

https://medium.com/@lightsoo/dependency-inversion-principle-52a7bb6dd6be

 

Dependency Inversion Principle

본부장님의 클린코드 강의중 Function Structure를 공부하면서 DIP에 대한 설명을 듣는데 이해가 잘 되지않아서 다시 위의 내용을 번역 + DIP강의를 듣고 정리한 문서이다.

medium.com

 

 

 

'SoftwareDesign' 카테고리의 다른 글

ISP  (0) 2020.03.01
LSP  (0) 2020.03.01
OCP  (0) 2020.03.01
SRP  (0) 2020.03.01
SOLID 개요  (0) 2020.03.01

 

 

 

ISP (Interface Segregation Principle; 인터페이스 분리 법칙)

특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.

 

 

 

 


범용 인터페이스

 

캐셔가 필요하지 않은 기능들을 포함한 계산기를 사용하고 있다.

 

 

 


인터페이스 분리

 

 

인터페이스를 작은 단위로 분리함으로 캐셔에게만 맞는 계산기와

신입생에거 필요한 계산기를 인터페이스를 가지고 있을 수 있게 되었다.

 

인터페이스를 작은 단위로 만들게 되면 사용성이나 확장성이 좋아진다.

 

 

 


FIN

 

사실 인터페이스를 범용으로 쓸 일이 없었다.

인터페이스를 사용보다 크게 만들어 문제되는 일도 없었다.

 

다른 포스팅들을 더 읽어보니

'SRP와 같이 인터페이스가 하나의 책임을 가져야 한다'

설명하는 경우가 지배적이다.

 

interface SmartPhoneFunction {
    
    fun call()
    fun sms()
    fun showInternet()
    
}

class SmartPhone : SmartPhoneFunction

 

interface CallFunction {
    fun call()
}

interface SMSFunction {
    fun sms()
}

interface InternetFunction {
    fun showInternet()
}

class SmartPhone : CallFunction, SMSFunction, InternetFunction

 

와 같이 분리하는 예시를 많이 보여준다.

 

이 예시가 더 잘 드러나 보인다.

 

하지만 위에 계산기 예시 역시

NormalCalculator는 기본적인 계산기의 책임을 다하고

ScientificCalculator는 공학 계산기의 책임을 다하는

SRP와 같은 맥락의 예시이다.

 

 

결론은 '인터페이스를 하나의 책임을 갖도록 분리해라.' 가 가장 적합해보인다.

 

 

SOLID에 대해 포스팅하면서 책임이라는 단어가 정말 많이 나온다.

 

그 만큼 중요하지만 책임의 크기를 정하는 것에 대해서도 고민해야할 것이다.

 

무조건 작고 많이 분리한다고 좋은 것도 아니다.

오히려 너무 많은 인터페이스와 클래스가 복잡도를 높일 수가 있다.

 

 

 

 

 

 

 

'SoftwareDesign' 카테고리의 다른 글

DIP  (0) 2020.03.01
LSP  (0) 2020.03.01
OCP  (0) 2020.03.01
SRP  (0) 2020.03.01
SOLID 개요  (0) 2020.03.01

 

 

 

LSP (Liskov Substitution Principle; 리스코브 치환 법칙)

프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.

 

위키의 설명을 보고 이해하려다가 혼란에 빠졌었다.

 

쉽게 설명해서

부모 클래스로 형변환하여 사용해도 아무런 이상없이 사용하면 된다.

자식 클래스 부모 클래스의 만든 전제 내에서 확장하면된다.

 

 

 

 


위반 예시

 

동물은 먹는다. (O)

고양이는 물고기를 먹는다. (O)

고양이 동물이다. (O)

동물은 물고기를 먹는다. (O)

 

직사각형은 가로 세로가 같던 다르던 상관없다. (O)

정사각형은 가로 세로가 같아야 한다. (O)

정사각형 직사각형이다. (O)

직사각형은 가로 세로가 같아야 한다. (X)

 

자식이 부모를 모순되게 만들지 말라는 원칙이다.

 

다형성을 통해 다양한 자식 클래스들을 부모클래스로 통합해 처리하는 로직이 있을 때

자식 중에 로직의 기대를 벗어나게 오버라이딩했다면 문제가 발생할 수 있다.

 

 

 

 


FIN

 

사실 모순을 발생하는 예시가 적으며

발생하더라도 문제가 생기지 않을 확률이 높다.

 

그렇게 신경쓰고 있어야할 원칙은 아닌듯 싶다.

 

 

 

 

'SoftwareDesign' 카테고리의 다른 글

DIP  (0) 2020.03.01
ISP  (0) 2020.03.01
OCP  (0) 2020.03.01
SRP  (0) 2020.03.01
SOLID 개요  (0) 2020.03.01

+ Recent posts