어댑터 패턴이란?
어댑터를 다들 아실꺼라 생각한다. 어댑터는
기본적으로 이러한 친구들을 어댑터라 한다.
그렇다면 어댑터 패턴이란 무엇일까?
일반적으로 알고있는 어댑터와 비슷하다 생각하면 편할 듯 하다.
예시를 한번 들어보겠다.
비트코인 지갑을 컴퓨터에 연결할 수 있는 type-A의 USB에 저장해 놓았다. 그런데 나는 맥북밖에 없다;; 5분 뒤 비트코인이 반토막 날 것이라는 엄청난 비밀 정보를 들은 나는 지금 가격에 팔아야 하는데 지갑이 없으니 내가 비트코인을 가지고 있다는 것을 증명할 방법이 없다. 하지만 이를 가능하게 해줄 어댑터 라는 것이 있다. type-A의 USB를 type-C를 사용하는 맥북에 연결을 할 수 있게 해주는 위 사진의 어댑터 말이다. 정말 다행이다. 이렇게 실용적인 기기가 있다니!
프로그래밍에서의 어댑터 패턴도 마찬가지이다.
동물은 걸을 수 있고 짖을 수 있으며 밥을 먹고 응아를 할 수 있다.
오리 또한 걸을 수 있고 짖을 수 있으며 밥을 먹고 응아를 할 수 있다.
오리는 동물이라 할 수 있다.
개와 고양이 또한 마찬가지 일 테다.
감이 벌써 왔을 수도 있다.
컴퓨터가 직접적으로 type-A USB와 호환이 되지 않는다면 어댑터를 사용해 이 둘을 연결해 줄 수 있다.
코드에서 직접적으로 오리라는 친구와 호환이 되지 않는다면 어댑터 패턴을 사용해 이를 연결해 줄 수 있다.
정말 다행이다. 이렇게 실용적인 패턴이 있다니!
장점
- 인터페이스를 사용하기에 인터페이스가 가지는 장점을 모두 가진다.
- 호환성의 문제로 사용할 수 없는 구조체를 어댑터 패턴의 사용으로 극복할 수 있다.
- 함수의 검사가 효율적으로 가능하다.
단점
- 인터페이스를 사용하기에 인터페이스가 가지는 단점을 모두 가진다.
- 인터페이스가 가진 속성을 하나하나 제작해 주어야함.
Go 에서의 인터페이스 사용 방법
언어는 Go 사용할 것이지만 다른 언어를 사용하는 이 글을 보는 사람들도 어렵지 않게 이해가 가능할 것이다.
일딴 인터페이스의 사용 방법을 익혀보자.
package main
import (
"errors"
"fmt"
)
type animal interface {
걷기()
짖기()
먹기(food string) error
응아() (string, error)
}
type duck struct {
이름 string
배부름상태 bool
}
func (duck) 걷기() {
fmt.Println("뒤뚱 뒤뚱")
}
func (duck) 짖기() {
fmt.Println("꽥꽥!!")
}
func (d *duck) 먹기(food string) error {
if d.배부름상태 {
return errors.New("ERROR : 배부른 상태")
}
d.배부름상태 = true
fmt.Printf("%s(을)를 먹었습니다. %s(은)는 배부른 상태가 됐습니다.\n", food, d.이름)
return nil
}
func (d duck) 응아() (string, error) {
if !d.배부름상태 {
return "", errors.New("ERROR : 조건이 충족되지 않음")
}
d.배부름상태 = false
return fmt.Sprintf("%s의 응아", d.이름), nil
}
var 꽥돌이 animal = &duck{"꽥돌이", false}
func main() {
꽥돌이.걷기()
꽥돌이.짖기()
_, err := 꽥돌이.응아()
if err != nil {
fmt.Println(err)
}
err = 꽥돌이.먹기("상추")
if err != nil {
fmt.Println(err)
}
err = 꽥돌이.먹기("물고기")
if err != nil {
fmt.Println(err)
}
shit, err := 꽥돌이.응아()
if err != nil {
fmt.Println(err)
}
fmt.Println(shit)
}
실행 결과
다른 언어에서와 같이 duck을 animal 인터페이스 타입으로 지정했기에 걷기, 짖기, 먹기, 응아 함수를 모두 오버라이딩 해 주지 않는다면 에러를 일으킨다. 따라서 인터페이스의 함수를 모두 일일이 지정해 주어야 한다.
Go에서의 어댑터 패턴 사용 방법
package main
import (
"errors"
"fmt"
)
type family struct {
}
func (f *family) 분양받기(object animal) string {
return fmt.Sprintf("%s(이)가 가족이 됐습니다.", object)
}
type animal interface {
걷기()
짖기()
먹기(food string) error
응아() (string, error)
}
type duck struct {
이름 string
배부름상태 bool
}
func (d *duck) String() string {
return d.이름
}
func (duck) 걷기() {
fmt.Println("뒤뚱 뒤뚱")
}
func (duck) 짖기() {
fmt.Println("꽥꽥!!")
}
func (d *duck) 먹기(food string) error {
if d.배부름상태 {
return errors.New("ERROR : 배부른 상태")
}
d.배부름상태 = true
fmt.Printf("%s(을)를 먹었습니다. %s(은)는 배부른 상태가 됐습니다.\n", food, d.이름)
return nil
}
func (d duck) 응아() (string, error) {
if !d.배부름상태 {
return "", errors.New("ERROR : 조건이 충족되지 않음")
}
d.배부름상태 = false
return fmt.Sprintf("%s의 응아", d.이름), nil
}
type dog struct {
이름 string
배부름상태 bool
}
func (d *dog) String() string {
return d.이름
}
func (dog) 걷기() {
fmt.Println("look at my hap py happy guy oh just a happy happy happy dog")
}
func (dog) 짖기() {
fmt.Println("와우! 바우!")
}
func (d *dog) 먹기(food string) error {
if d.배부름상태 {
return errors.New("ERROR : 배부른 상태")
}
d.배부름상태 = true
fmt.Printf("%s(을)를 먹었습니다. %s(은)는 배부른 상태가 됐습니다.\n", food, d.이름)
return nil
}
func (d dog) 응아() (string, error) {
if !d.배부름상태 {
return "", errors.New("ERROR : 조건이 충족되지 않음")
}
d.배부름상태 = false
return fmt.Sprintf("%s의 응아", d.이름), nil
}
var 꽥돌이 animal = &duck{"꽥돌이", false}
var 바둑이 animal = &dog{"바둑이", false}
func main() {
바둑이.걷기()
바둑이.짖기()
_, err := 바둑이.응아()
if err != nil {
fmt.Println(err)
}
err = 바둑이.먹기("사료")
if err != nil {
fmt.Println(err)
}
err = 바둑이.먹기("고기")
if err != nil {
fmt.Println(err)
}
shit, err := 바둑이.응아()
if err != nil {
fmt.Println(err)
}
fmt.Println(shit)
var fam family
fmt.Println(fam.분양받기(꽥돌이))
fmt.Println(fam.분양받기(바둑이))
}
실행 결과
실행 결과에서 볼 수 있듯이 family의 분양받기 함수는 animal을 매개변수로 갖는다. 그로 인해 animal 인터페이스를 구현한 duck과 dog타입인 꽥돌이와 바둑이 모두 이 함수의 매개변수로서 작동할 수 있는것을 볼 수 있다.
결론
결과적으로 인터페이스를 적극 활용한 패턴이라 이해할 수 있다. 이러한 어댑터 패턴으로 종속성을 줄인 코드는 유닛 테스트를 보다 간편하게 할 수 있게 도와주며 보다 OOP를 활용한 코드를 완성시키는데 큰 도움을 준다.
'Study > Design Pattern' 카테고리의 다른 글
[OOP] 객체지향 설계 5원칙 SOLID (SRP, OCP) (0) | 2022.08.13 |
---|---|
SOLID (0) | 2022.03.18 |
[Design Pattern] 싱글톤 패턴(Singleton Pattern) with Golang (0) | 2021.08.26 |