오늘도 삽질중

클린코드 책을 읽고서 Chatper. 경계 with Adapter Pattern이란? 본문

기타

클린코드 책을 읽고서 Chatper. 경계 with Adapter Pattern이란?

Choi3950 2020. 12. 4. 12:22
반응형

본 포스팅은 개인정리 목적으로 작성된 글입니다.

잘못된 정보가 있을 수 있으며, 피드백은 겸허히 받겠습니다.



클린아키텍처 chapter. 경계는 이런 내용을 설명하고 있다.

시스템에 들어가는 모든 소프트웨어를 직접 개발하는 경우는 드물다.

때로는 패키지를 사고, 때로는 오픈소스를 이용한다.

또는 사내 다른팀이 제공하는 컴포넌트를 이용한다.

어떤식으로든 외부 코드를 우리 코드에 깔끔하게 통합해야만 한다.




외부코드 잘못된 사용 예시

val sensors: HashMap<String,Sensor> = HashMap()
val sensor = sensors[sensorId] as Sensor
//...
//...
getDataInRefresh(sensor)

이렇게 사용하면 안된다.

여러 화면이나 기능에서 HashMap을 사용하여 데이터를 전달할 경우에 만약 HashMap 인터페이스가 내부적으로 기능이 변경되었다면 모든 코드를 수정해야 한다.





클린코드 책에서 권장하는 사용 방법

class Sensors {

private val sensors: HashMap<String,Sensor> = HashMap()


fun getSensor(id : String) = sensors[id] as Sensor
//.....
//.....
}

HashMap 인터페이스를 래퍼클래스 내에서 사용한다.

HashMap 인터페이스가 변경되어도 해당 래퍼클래스만 수정하면 된다.






내용정리


외부 라이브러리를 import 해서 사용할 경우 래퍼클래스로 만들어 관리하는 것을 권장한다.

외부 라이브러리의 버전이 변경된 경우 내부 동작이 바뀔 수 있으므로 변경이 이루어져야 한다면 래퍼클래스만 수정하도록 설계한다.

외부 라이브러리를 우리 프로젝트에 적용하기 전, 간단한 테스트 케이스를 작성해 진행해 본다.


아직 존재하지 않는 코드 (예를들어  개발이 되지 않은 APi) 를 사용할 경우 자체적으로 인터페이스를 정의하여 사용하자.(Adapter Pattern 권장)



이러한 작업을 해야하는 이유는 무엇인가?

통제가 불가능한 외부 패키지에 의존하는 대신 통제가 가능한 우리 코드에 의존하는 편이 훨씬 좋기 때문이다.




Adapter Pattern이란 무엇인가?

그 전에 Adapter 가 무엇인지 생각해보면 개념을 이해하기 쉽다.

Adapter 란 다른 전기나 기계 장치를 서로 연결해서 작동할 수 있도록 만들어 주는 결합 도구이다.

=> 보통 서로 다른 전압이나 콘센트 모양이 다른 플러그를 꽂을 수 있도록 중간사이에 연결해주는 장치(돼지코)


위 내용을 기반으로 생각해보면 프로그래밍 관점에서 Adapter Pattern이 이해하기 쉬워진다.

위키 : 클래스의 인터페이스를 사용자가 기대하는 다른 인터페이스로 변환하는 패턴이다. 


즉 기존에 정의된 인터페이스를 클라이언트 단에서 사용할 수 있도록 변환한다.


쉽게 생각하면 B를 A처럼 포장하여 A로 사용할 수 있게 하는 패턴이다.


왜 사용해야 하는가?

기존의 클래스를 재사용하려고 하나, 그 인터페이스가 원하는것과 동일하지 않을 때




Adapter Pattern 예제


시나리오

자동차 정비리스트 A와 B가 존재한다.

체크리스트 A : 엔진오일교환 , 타이어교환, 배터리교환

체크리스트 B : 엔진오일교환, 범퍼교환


interface CheckListA {

fun engineOilChange()

fun tireChange()

fun batteryChange()

}
interface CheckListB {

fun engineOilChange()

fun bumperChange()

}




체크리스트 A를 참조하는 A CarCenter 가 존재하고 , 체크리스트 B를 참조하는 B CarCenter 가 존재한다.


class ACarCenter : CheckListA {

private val TAG = "A CarCenter :: "

override fun engineOilChange() {
Log.e(TAG , "엔진 오일 교환")
}

override fun tireChange() {
Log.e(TAG , "타이어 교환")
}

override fun batteryChange() {
Log.e(TAG , "배터리 교환")
}

}
class BCarCenter : CheckListB {

private val TAG = "B CarCenter :: "

override fun engineOilChange() {
Log.e(TAG , "엔진 오일 교환")
}

override fun bumperChange() {
Log.e(TAG , "엔진 오일 교환")
}

}




이제 어댑터 부분이다.

class CarCenterAdapter : CheckListA {

private val TAG = "Adapter ::"

private val checkListB: CheckListB

constructor(checkListB: CheckListB) {
this.checkListB = checkListB
}

override fun engineOilChange() {
checkListB.engineOilChange()
}

override fun tireChange() {
Log.e(TAG , "지원되지 않습니다.")
}

override fun batteryChange() {
Log.e(TAG , "지원되지 않습니다.")
}

}

체크리스트 A를 참조하면서 생성자로 체크리스트 B를 받는다.

체크리스트 B는 B CarCenter 가 참조를 하는데, B CarCenter는 타이어와 배터리 교환을 지원하지 않는다.



이제 메인에서 사용해보자.

class MainActivity : AppCompatActivity() {



override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)


val aCarCenter = ACarCenter()
val bCarCenter = CarCenterAdapter(BCarCenter())

aCarCenter.engineOilChange()
aCarCenter.batteryChange()
aCarCenter.tireChange()

bCarCenter.engineOilChange()
bCarCenter.batteryChange()
bCarCenter.tireChange()

}

}

잘보면 bCarCenter 는 어댑터를 선언하여 사용하고 있다.





실행결과





반응형
Comments