티스토리 뷰
Photo by Austin Distel on Unsplash
드디어 마지막 Dependency Inversion Principle 이다.
- Single Responsibility Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
Dependency Inversion Principle
"High-level modules should not depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions." - Robert C. Martin
상위레벨 모듈이 하위레벨 모듈에 의존해서는 안되며, 둘 다 추상화에 의존해야 한다 디테일은 추상화에 의존해야 한다.
이게 Go 에서는 어떻게 이해하면 될까?
이미 둘러본 네 개의 원칙을 잘 따랐다면
1) 하나의 잘 명시된 책임/목적을 수행하는 패키지들로 잘 나누었을 것이고 (SRP)
2) 의존성은 인터페이스로 잘 표현했을 것이며(LSP), 오직 함수가 원하는 행위만으로 되어 있을 것이다. (ISP)
→ 한마디로 더 이상 할 건 거의 남아있지 않았다.
여기서 말하는 것은 import graph의 구조이다. Go의 import graph 는 acyclic이다. 돌고돌아 서로가 서로를 의존해서는 안된다는 것이며, 그렇지 않으면 컴파일 에러가 나며, 이건 단순히 에러를 넘어서서 디자인에 심각한 문제가 있다는 것을 의미한다.
잘 디자인된 Go 프로그램의 import graph는 넓고 얕아야 한다. 즉, 다양한 패키지를 import 하되, 패키지에 패키지가 꼬리를 물며 import 하지 않아야 한다는 것이다. 아래 예제와는 정확히 어떤 연관이 있다고 볼 수 있을지 통찰은 못했다.
유튜브의 예제를 보자
유튜브 링크: https://youtu.be/AKdvlr-RzEA?t=413
문제가 있는 버전: https://play.golang.org/p/2RLJmOT1-sI
문제를 해결한 버전: https://play.golang.org/p/uNO0zNvrG4f
문제가 있는 버전부터 보자
1. UserRepository 구조체가 문제의 핵심이다.
2. 필드로서 MySQL 타입을 받고 있다. depend on details!
3. 이제 UserRepository 구조체의 GetUsers() 메서드는 MySQL에서 PostgreSQL로 변경시 구현을 모두 바꿔줘야 한다.
문제를 어떻게 해결할 수 있을까?
1. 핵심은 DBConn 인터페이스이다. Query() 라는 메서드만 구현하면 되는 녀석이다.
2. 그리고, UserRepository는 DBConn 인터페이스를 만족하는 db라는 필드를 가진다.
3. UserRepository 구조체의 GetUsers() 메서드는 자신의 필드의 Query() 메서드를 활용해서 리턴값을 만들어낸다.
→ 무엇보다 중요한 것은 GetUsers()를 DB에 따라 수정할 필요가 없어졌다는 것이다
main() 함수를 보면 UserRepository 구조체는 다른 두 구조체 인스턴스인 mysqlDB와 postgreSQLDB 둘다 db 필드에 받을 수 있다. 둘 다 DBConn을 만족하기 때문이다.
SOLID Go Design 마무리
SOLID 원칙 각각은 디자인에 대한 강력한 표현이지만 또한 하나의 테마로 볼 수 있다.
Single Responsibility Principle은 함수, 타입, 메서드들이 자연스럽게 응집하는 것들을 모아 패키지로 만들라는 것이다.
Open/Closed Principle은 임베딩을 이용해 단순한 타입들을 모아 복잡한 구조체를 만들라는 것이다.
Liskov Substitution Principle은 패키지간의 의존성을 인터페이스로 표시하라는 것이다. 인터페이스가 요구하는 행위(메서드)가 적을 수록 구현이 명확하고 좋다.
Interface Substittution Principle은 LSP에서 좀 더 나아가 필요한 behavior 만을 정의한 함수와 메서드를 쓰라는 것이다. 인터페이스가 구현하라고 요구하는 메서드가 작을 수록 SRP를 지킬 가능성도 높아진다.
Dependency Inversion Principle은 패키지의 의존성에 대한 정보를 컴파일 타임이 아닌 런타임으로 옮기라는 말이다. 무슨 소리인가 싶지만, 인터페이스를 이용하면 런타임에 어떤 녀석이 들어오더라도 인터페이스를 만족하면 동작하게 한다는 것이다.
곰곰이 보면 Go에서의 SOLID의 핵심은 인터페이스이다.
"interfaces let you apply the SOLID principles to Go programs."
결국은 디커플링이라고 볼 수도 있다. 인터페이스를 이용해서 패키지가 무엇을 제공하는지 보여주는 것이다. (무엇을 하고 있는지가 아니라)
"Design is the art of arranging code that needs to work today, and to be easy to change forever."- Sandi Metz
'golang' 카테고리의 다른 글
Golang으로 Min Heap을 구현해보자 - container/heap 패키지 사용 (0) | 2021.05.04 |
---|---|
Golang으로 Min Heap을 구현해보자 (0) | 2021.05.04 |
SOLID in GO - Interface Segregation Principle (0) | 2021.04.05 |
SOLID in GO - Liskov Substitution Principle (0) | 2021.04.04 |
SOLID in GO - Open/Closed Priciple (0) | 2021.04.01 |
- Total
- Today
- Yesterday
- ChatGPT
- 노션
- go
- intellij
- OpenAI
- 제이펍
- 독서후기
- 클린 애자일
- pool
- 인텔리제이
- 체호프
- 잡학툰
- github
- folklore
- Gin
- postgres
- Shortcut
- Bug
- 영화
- bun
- strange
- API
- JIRA
- notion
- 2023
- solid
- websocket
- golang
- agile
- 독서
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |