본문 바로가기

『객체지향 사실과 오해(2)』추상화

by Recstasy 2019. 8. 28.

추상화

 화장실 표지판에 그려진 남녀 그림은 세계 공용이다. 외국어를 모르더라도 남자,여자 화장실을 찾을 수 있는 이유는 '기호' 덕분이다. 기호는 대개 복잡하지 않다. 복잡한 부분을 제외한 뒤, 공통적인 부분만을 그려냈기 때문이다. 또한, 버스, 지하철 노선을 간단하게 노선도로 정리한 것 역시 '기호와 상징'의 좋은 사례다. 이렇게 공통적인 부분을 뽑아낸 것을 '추상화'라 하며, 우리는 매일 추상화 된 서비스와 기호들을 접하고 있다. 

 

객체지향 설계에서 추상화는 너무나 중요하다. 추상화를 통해 복잡성을 제거할 수 있기 때문이다. 어떻게보면 객체지향 패러다임은 객체라는 추상화를 통해 현실의 복잡성을 극복한 방식이다. 그렇다면, 추상화는 어떠한 방식으로 구현할 수 있을까? 추상화의 구현방식은 다음 2가지 정도로 요약할 수 있다. 

 


1] 구체적인 사물들 간의 공통점은 취하고 차이점은 버리는 일반화를 통해 단순하게 한다

2] 중요한 부분을 강조하기 위해 불필요한 세부사항을 제거함으로써 단순하게 만든다


 

 사람은 '추상화'를 활용한 덕분에 효율적으로 살아갈 수 있다. 가령, 운전면허가 있으면 차종에 상관없이 모든 승용차를 운전할 수 있다. '부가티', '람보르기니 우라칸', '페라리 슈퍼페스트', 등...과 같은 독특한 모델들도 추상화의 테두리 내에서는 결국 '자동차'일 뿐이다. 그리고 자동차라면, 모두 공통적인 '개념(운전대, 미션, 악셀, 브레이크, 등.. )'을 갖는데, 객체지향 설계에서는 이를 '클래스'라 한다. 또, 클래스는 여러가지 인스턴스(복사)를 만드는데 이렇게 만들어진 복사본이 바로 객체이다. 따라서 'G바겐(벤츠)', '콰트로포르테(마세라티)', 'x7(BMW)'와 같은 차들은 모두 '자동차'라는 클래스에 속하며, 자동차의 객체 인스턴스들이라 할 수 있다.

 

 객체지향 설계를 요약하자면, 결국 개념을 뽑아서 정리한 뒤 클래스로 분류작업을 하는 작업이라 볼 수 있다. 여기서 분류란, 객체에 특정한 개념을 적용하는 작업인 셈이다. 분류는 객체지향의 가장 중요한 개념이며, 어떤 객체를 어떤 개념으로 분류할지에 따라 객체지향 설계의 품질이 결정된다. 만일 객체를 적절하게 분류하지 못하면, 향후 유지보수가 어려워지고, 변경에 취약해진다. 반면, 분류를 잘하게되면, 개념정리가 깔끔해지고, 개념이 확실할수록 협력 관계가 명확해진다. 그러므로 객체지향 설계에서 가장 중요한 것은 데이터나 상태가 아니다. 객체의 '행동'이다. 상태는 행동의 결과로 초래된 부수효과를 쉽게 표현하기 위해 도입한 추상적인 개념일 뿐이다. 객체를 창조할 때 가장 중요하게 고려해야 하는 것은 객체가 이웃하는 객체와 협력하기 위해 어떤 행동을 해야 할지를 결정하는 것이다. 즉, 객체가 협력을 위해 어떤 책임을 지녀야 하는지를 결정하는 것이 객체지향 설계의 핵심이다. 

 

 개념은 행동에서 시작된다. 결국 어떤 경우건 행동이 우선이다. 객체가 어떤 행동을 하느냐에 따라 객체의 타입(개념)이 결정되며, 객체의 타입은 객체의 내부 표현과 관련없다. 가령, 객체의 내부 표현방식이 다르더라도 어떤 객체들이 동일하게 행동한다면 그 객체들은 동일한 타입에 속한다고 할 수 있다. 결과적으로 동일한 책임을 수행하는 일련의 객체는 동일한 타입에 속한다고 말할 수 있다. 어떤 객체가 타입에 속한 다른 객체와 동일한 행동을 한다면 그 객체는 같은 타입으로 분류할 수 있고, 해당 객체가 어떤 데이터를 갖고 있는지는 설계자의 관심사항이 아니다. 만일 그 객체가 다른 객체와 동일한 데이터를 갖고 있더라도 다른 행동을 한다면 그 객체들은 서로 다른 타입으로 분류되어야만 한다.

 

객체의 타입을 결정하는 것은 객체의 행동뿐이라는 사실은 몇번 강조해도 지나치지 않다. 

 

 

 

 

다형성

 같은 타입에 속한 객체는 행동만 동일하다면 서로 다른 데이터를 가질 수 있다. 여기서 동일한 행동이란 동일한 책임을 의미하며, 동일한 책임이란 동일한 메시지 수신을 의미한다. 따라서 동일한 타입에 속한 객체는 내부의 데이터 표현이 다르더라도 동일한 메시지를 수신하고 이를 처리할 수 있다. 다만 내부의 표현 방식이 다르기 때문에 동일한 메시지를 처리하는 방식이 서로 다를 수밖에 없는데 이를 다형성이라 한다. 다형성이란, 동일한 요청에 대해 서로 다른 방식으로 응답할 수 있는 능력을 의미한다. 

 

 

 

 

 

유연한 설계

 객체지향 설계에 있어 '유연성'이란 말을 빼놓을 수 없는데, 유연한 설계란 말은 어떤 의미일까? 예를들어, 마트에서 키오스크와 같은 무인계산기로 계산하는 상황을 떠올려보자. 마트에서 물건을 사는 고객은 무인계산대에서 계산만 할 뿐이며, 마트의 데이터에는 접근하는 행위를 할 수 없다.(하려해도 불가능) 만일 캐셔가 있다면, 캐셔 역시 고객의 카드로 물건값을 계산만 할뿐 재고량이나 본사 내부정보에 접근할 수 없다. 객체들은 '약속된 행위'를 통해 각자 역할과 책임을 다할 뿐이다. 그리고 이는 훌륭한 객체지향 설계의 원칙과 맞닿은 개념이다. 훌륭한 객체지향 설계는 외부에 행동만을 제공하고 데이터는 행동 뒤로 감춰야 한다. 손님 객체가 마트의 내부데이터에 접근할 수 없고, 캐셔가 고객의 사적정보를 알 수 없는 것처럼 오직 객체끼리의 행동(메시지)만이 고려대상이다. 

 

 만일 데이터가 캡슐의 벽을 뚫고 객체의 인터페이스(메시지)를 오염시키는 순간 객체의 분류 체계는 급격히 위험에 노출되고 하나의 행동에 여러 객체가 영향을 받는 식의 유연하지 못한 설계가 되어버린다. 그러므로 행동에 따라 객체를 분류하기 위해서는 항상 객체가 내부적으로 관리해야 하는 데이터가 아니라 객체가 외부에 제공해야 하는 행동을 먼저 생각해야 한다. 객체지향 설계자는 객체가 외부에 제공해야 하는 책임을 먼저 결정하고, 그 책임을 수행하는 데 적합한 데이터를 나중에 결정한 후, 책임을 수행하는데 필요한 외부 인터페이스 뒤로 데이터를 감춰야(캡슐화) 한다. 데이터를 먼저 결정하고 객체의 책임을 결정하는 방법은 유연하지 못한 설계의 대명사이며, 이를 타개하고자 '책임-주도 설계'라고 부르는 객체 지향 설계가 고안됐다.

 

 객체를 결정하는 것은 오직 행동뿐이다. 데이터는 단지 행동을 따를 뿐이다. 이것이 객체를 객체답게 만드는 가장 핵심적인 원칙이다.

 

 

 

 

정적인 모델 VS 동적인 모델

 객체지향 설계에서 가장 중요한 키워드는 '클래스'가 아니라 '객체'다. 클래스는 일종의 개념으로써 실제 프로그램의 구동상황에 직접적으로 관여하지 않는다. 클래스는 명세일 뿐이다. 클래스의 상태와 행동을 복사한 객체들의 다형성이 어플리케이션을 움직이게 한다. 그렇다면 클래스는 왜 필요하며, 굳이 클래스를 만들 이유는 또 뭘까? 

 

 가령, 제과점을 떠올려보자. 제빵관련 공부를 하지 않았다면, 제과점에 있는 모든 종류의 빵을 구분하기란 쉽지 않다. 그러나 사람들은 다양한 종류의 빵들에 혀를 내두르고 있는 상황에서도 대략적으로 자신이 좋아하는 빵을 결국 찾아낸다. 비슷한 종류의 빵들이 각각 다른 바구니에 담겨있기 때문이다. 소보로처럼 보이는 빵은 특정 바구니에만 담겨 있고, 피자빵, 치즈빵, 식빵, 단팥빵 등.... 비슷한 '종'의 빵들이 구분되어 각각 바구니별로 분류되어 있다. 이 때문에 사람들은 빵 이름을 정확하게 알 필요가 없다. 이 분류를 좀더 확장한다면, 다음과 같다.

 

       「포장된 빵」                                                       「포장되지 않은 빵」                                                「케익」       

1] '포장된 빵'

2] '포장되지 않은 빵'

3] '케익'

 

 빵의 종류가 아무리 많더라도 '포장된 빵', '포장되지 않은 빵', '케익'으로 구분한다면 제과점 제품(빵)의 구조를 빠르게 파악할 수 있다. '포장유무'에 따라 확실하게 구분한다면, 이는 '정적 모델'에 비유할 수 있다. 그런데 다음과 같이 '포장'이 된 빵인지 아닌지 구분이 확실치 않는 '에그 타르트'같은 모델이 있다.

 

 또한 '피자+치즈+식빵 + 등...'의 조합으로  빵의 종류 및 구분이 모호한(상태가 조금씩 변해가는?) 빵들이 있을 수 있다. 분명,  큰 틀에서 보자면, '포장되지 않은 빵'에 속하지만, 계산할 때 포장되면서 상태가 변하는 빵이 존재한다. 이를 객체지향에서는 '동적 모델'이라 한다.

 

사람이 해석할 수 있는 복잡함에는 한계가 있다. 따라서 객체지향 설계는 정적 모델과 동적 모델을 만들어(UML 다이어그램) 전체를 바라보는 관점, 미시적 변화를 볼 수 있는 관점을 제공한다. 상황에 따라 변하는 상태를 표시한 설계도가 필요할 때가 있고, 클래스들의 책임을 명확하게 명시한(객체들의 공통점을 뽑고, 차이점을 제거한 타입을 정리) 정적 설계도가 필요할 경우가 있다. 일반적으로 객체의 상태 변경을 추적하고 디버깅하는 테스트에서는 객체의 동적인 모델을 제시한 설계도(UML)가 필요하고, 클래스를 작성하는 시점에서는 시스템 전체를 정리한 정적인 설계도(UML)가 중요하다. 결론적으로, 유연하고 아름다운 설계는 객체의 동적·정적인 관점이 모두 필요하다.

 

댓글

최신글 전체

이미지
제목
글쓴이
등록일