본문 바로가기

기타/객체지향의 탄생(2013)

객체지향의 탄생-인터페이스

인터페이스는 산의 맑은 공기와 같다. 등산할때는 맑은 공기를 마실 수 있지만, 맑은 공기의 고마움은 쉽게 인식하지 못한다. 등산을 마치고 도시속에 찌든 공기를 마실때 기침을 콜록일 때야 고마움을 깨닫곤 한다. 인터페이스는 산의 맑은 공기처럼 늘 객체지향 개발자와 함께 하는 존재이며, 무의식중에 쓰는 존재이지만, 인터페이스의 유익함과 고마움을 쉽게 인식하지 못한다.

 

 

꼬마때 삼국지를 열심히 읽었다. 삼국지는 꼬마가 꿈꾸는 갖가지 상상력을 소설로 모두 담았다. 넓은 대륙, 영웅 호걸 장수들의 무협 소설 같은 활약, 천재 모사들의 머리 싸움, 영웅들의 사랑까지~ 담을 수 있는 '판타지' 는 모두 담은 소설이니 영웅들을 동경하며 열심히 읽었다.

 

나는 천재 모사들의 머리 싸움을 좋아했다. 제갈량이나 조조가 전장을 보는 시야는 너무도 넓어서 다양한 시나리오를 모두 염두하고 치밀한 작전을 펼친다. 제갈량이나 조조는 후퇴할때도 머리쓰면서 그럴 듯 하게 철수한다. 천재 모사들이 아닌 일반 가신이나 힘만 좋은 장수들은 당장의 앞만 보다가 상황을 그르치곤 한다.

 

나는 마치 신의 입장에서 삼국지를 읽었기 때문에 상황을 그르치는 장수들을 욕하곤 했다. 그러나 개발자 입장에서 나는 상황을 그르치는 장수들과 크게 다르지 않았다. 나도 처음에 프로그래밍 할때는 프로젝트 전체 입장에서 짜는게 아니라 지금 내가 집중하고 있는 클래스가 제대로 작동하면 그만이었다.

 

시간이 흘러 경력이 쌓일수록 클래스 안의 로직이 아닌 전체를 보기 시작했다. 설계 관점에서 보기 시작했다. 마치 초보운전자가 안의 운전 조작에만 신경쓰느라고 바깥 운전 흐름은 보지 못하다가 어느 덧 전후좌우를 다 보는 감각으로 운전을 해내는 것과 비슷하다.

 

객체지향에서 설계 관점으로 보기 시작했다는 것은 클래스 로직 중심이 아닌 인터페이스 설계 관점으로 보기 시작했다는 말과 같다. 제갈량 같은 개발자가 되고 싶다면 인터페이스 설계 관점에서 보는 눈부터 키워야 한다.

 

 

인터페이스는 약속이다. 인터페이스는 나는 너희들에게 이러이러한 기능을 제공하겠다는 약속이다. 다른 모든 객체들은 인터페이스가 약속한 기능들을 믿고 인터페이스에 약속된 기능을 호출한다. 이때 인터페이스에 약속된 기능의 실제 구현은 '자식 클래스' 에서 담당하게 된다. 인터페이스를 호출하는 '클라이언트 클래스'는 인터페이스에만 의존하고 있지만 실제로 클라이언트 클래스가 받는 객체는 '자식 클래스' 이다.

 

이 그림은 인터페이스를 활용한 전형적인 클래스 다이어그램이다. 클라이언트는 휴대폰 인터페이스에만 의존하며, 휴대폰 인터페이스에서 약속한 기능을 호출한다. 클라이언트 클래스는 휴대폰 인터페이스에만 의존하여 있지만, 실제로 클라이언트 클래스가 받게될 객체는 겔럭시, 아이폰, 넥서스 객체중 하나가 될 것이다.

 

실행 클래스는 모든 프로그램을 시작하고 제어하는 main() 메소드를 포함한 클래스이다. 실행 클래스는 빨간 화살표처럼 프로그램을 구성하는 클래스를 모두 알고 있다. 실행 클래스는 클라이언트 객체를 생성할 때, 휴대폰 인터페이스의 기능을 실제로 구현한 클래스 겔럭시, 아이폰, 넥서스중 하나를 인자로 넘겨줄 것이다.

 

- 실행 클래스

public static void main( String[] args ) throws Exception {

    …..

    Clinet client1 = new Client(iphone); // 1

    …...

}

 

- 클라이언트 클래스

public class Client {

Handphone handphone = null; // 2

public Client(Handphone handphone) {

this.handphone = handphone; // 3

}

…..

}

 

1. 실행 클래스에서 Client 객체를 생성하면서 핸드폰 관련 클래스중 어느 자식 클래스를 사용할지 결정하여 인자로 넘겨준다. 실행 클래스는 어느 자식 클래스를 사용할지 다 알아야 하기 때문에 자식 클래스에도 의존하고 있다.

2. Client 객체는 Handphone 인터페이스를 속성으로 가졌다.

3. 1.의 절차에 의해 휴대폰 구현 객체 blackjack이 생성자로 넘어왔다. 클라이언트 클래스는 Handphone 객체만 알고 있지만 Handphone객체의 실제 구현은 blackjack 클래스가 선택되어서 인자로 넘어온 것이다.

 

이 그림과 코드는 인터페이스, 상속, 다형성 요소가 들어간 객체지향을 이해하는 핵심이 되는 부분이다. 앞으로 계속 반복 설명되니 계속 음미한다.

 

 

내가 프로그래밍을 하면서 겪었던 혼란은, 복잡하게 생각할 필요 없이 하나의 클래스만 만들어 그곳에 기능도 선언하고, 그 기능에 해당하는 로직도 직접 구현하는것이 더 깔끔하게 프로그램 짜는것이 아닐까라고 생각하곤 했다.

 

특히 의문은 처음 웹 프로젝트를 진행할 때 들었다. JSP 하나에 다 넣어서 하나의 파일로 하는게 오히려 깔끔한것이 아닌가. 왜 JSP 하나당 XML파일 클래스 파일 다른 설정 파일 등 파일 개수가 많아야 하는가. 심지어 어느 프로젝트는 하나의 JSP당 6개 이상의 파일이 필요한적도 있다. 나는 당시 깔끔한 코딩의 기준을 파일의 개수가 적을수록 깔끔하고 유지보수하기도 편하다고 생각했다. 사실 지금와서 생각하면 파일 개수가 많아도 유지보수가 불편하긴 하다.

 

순수 자바 클래스에서는 왜 굳이 인터페이스와 클래스로 나누는가 라고 생각한적이 있다. 당시 나는 '클래스의 수만 적으면 깔끔'하다고 생각했다.

 

 

예를 들어 휴대폰 클래스 하나에 겔럭시, 아이폰, 넥서스, 기타 휴대폰 객체에 의존하고 관련 로직을 꾸역 꾸역 넣으면 어떻게 될까. 저 그림을 코드로 구현하면 얼마나 지저분할지 생각해보자. 코드의 확장성과 유지보수 관점에서 생각해볼만 하다. 코드의 기능을 변경하거나 확장할 때 기존 코드를 직접 고쳐야 해서 확장성이 떨어지고, 기존 코드를 수정할 가능성이 높고, 복잡한 기존 코드에서 에러 없이 수정하기도 힘들기 때문에 유지보수성도 떨어진다.

 

 

객체지향 개발에서 인터페이스를 쓰면, '인터페이스의 기능을 쓰려는 클라이언트 클래스'가 인터페이스에 정의된 '메소드 목록'만 알고 내부는 어떻게 구현되었는지 자세히 알 필요가 없다는 것을 강조하는 효과가 있다.

 

클라이언트 클래스는 인터페이스의 메소드 정의만 보고 메소드의 구현은 제대로 되었다고 가정한다. 실제 메소드의 구현은 인터페이스를 상속받은 자식 클래스에서 담당할 것이다. 클라이언트 클래스는 자식 클래스에게 의존할 필요도 없고 의존해서도 안된다.

 

인터페이스를 정의하면 여러가지 지저분한 로직이 함께 포함된 '클래스'보다 좀더 '직관적'으로 '메소드 목록'를 클라이언트 클래스에게 알려주는 효과가 있다. 자동차 영업사원이 초보 여자 고객에게 차를 권유하면서 차의 내부 작동 원리까지 설명할 필요는 없다. 초보 여자 고객의 판단과 마케팅에 도움될만한 직관적이고 간결한 설명만 있으면 되는것과 마찬가지이다.

 

이점은 구현 클래스의 로직을 숨기는 효과도 있다. 클라이언트 클래스로부터 구현 클래스와 구현 클래스의 로직을 숨길 때 얻는 효과는, 구현 클래스를 다른 클라이언트가 필요 이상으로 의존하고 조작하는 점을 막을 수 있다. 그래서 좋은 프로그램을 판단하는 기준인 '응집도를 높이고 결합도를 낮추는 효과'가 있다.

 

구현클래스와 구현 클래스의 로직을 숨기는 효과와 함께 다양한 구현 클래스와 로직을 쉽게 바꾸거나 확장 할 수 있기 때문에 '유연함'과 '확장성'을 높이는 효과가 있다.

 

클라이언트 클래스가 인터페이스만 알고 구현클래스는 모르는 상태이다. 이때 '클라이언트'와 '인터페이스'를 조작하는 '실행 클래스'가 '클라이언트' 에게 어떤 ' 구현 클래스를' 쓰게 할 것인지 선택하여 선택한 '구현 클래스'를 넘겨준다.

 

예를들어 산골이라는 개발자가 객체지향을 공부하기 위해 책을 읽고 있었다. 만약 개발자 산골이가 객체지향 책 인터페이스를 구현한 클래스에 직접 의존한다면, 개발자 산골이는 직접 의존한 객체지향 책들만 읽을 수 있다. 개발자 산골이가 자신이 아는 책 말고 다른 책도 읽으려면 자신의 생각을 뜯어 고쳐야 하기 때문에(개발자 산골이 클래스의 내부 소스를 수정해야 하기 때문에), 개발자 산골이보고 다른 책도 읽어보라고 설득하기 어렵다.(기존 클래스의 소스 수정은 어렵고 위험한 작업이다.)

 

 

위의 객체지향 지식 습득에 문제가 있는 개발자 산골이를 표현한 것으로 개발자 산골이가 헤드 퍼스트 시리즈1을 읽다가 다른 책을 읽으려면 개발자 산골이 내부 소스를 직접 수정해야 한다. 이미 개발된 내부소스를 다시 수정하는 일은 피해야 한다.

 

기존 개발된 소스를 수정하는 일 자체가 나는 이 소스를 제대로 개발하지 못했다는 얘기가 된다. 이미 건설된 건축물을 고친다면 그것이 제대로 지은 건축물은 아니다. 기존 개발된 소스를 수정하면 수정한 소스와 연관된 다른 클래스의 소스에 영향을 끼친다.

 

그러나 객체지향 책 인터페이스 만을 참조한다면 어떤 책을 읽을것인지 프로그램이 실행 될때, 입맛에 맛게 고를 수 있다. 마치 개발자 산골이가 열린 생각으로 여러 책을 읽어보겠다는 긍정적인 마음과 같다.

 

바람직한 객체지향 책 읽기 구성, 전형적인 인터페이스-상속-다형성을 활용한 클래스 구성이며 Strategy Pattern이기도 하다. 그림처럼 인터페이스(또는 추상 클래스)를 활용하여 상속 객체를 만들고 상속 객체를 폴리모피즘을 이용하여 동적으로 객체를 생성하는 구성이야말로 객체지향의 효과를 볼 수 있는 바람직한 구성이다.

 

또한 Strateg Patter n이라는 디자인 패턴의 출발이 되며, 스프링 프레임워크의 IoC(Inversion Of Control, 제어 역행)기능에서도 항상 사용되는 클래스 구성이다. 이 클래스 구성의 중심에 인터페이스가 존재한다. 그래서 인터페이스는 객체지향의 정수이고 객체지향의 꽃이다.

 

 

인터페이스의 다른 특징으로는 자바에서 다중 상속이 가능한 효과가 있다. 자바에서 상속은 여러 부모 클래스를 동시에 상속받는것이 금지되어 있다. 아마 다중 상속이 가능하면 지저분한 기형 객체 그룹이 많이 생길 것 같다. 그러나 인터페이스를 쓰면 여러 메소드 특징을 가진 부모 인터페이스를 동시에 상속 받게 되어, 해당 객체 그룹 특징에 맞는, 적당하고 유연한 설계가 가능할 것이다.

 

내가 처음 객체지향에 대해 배울때는 어려운 공학 이론 배우듯이, 객체란 무엇이고, 클래스란 무엇이고, 속성은 무엇이고, 이런 개념만 암기하려고 했기 때문에 객체지향 장점을 제대로 활용하기 어려웠다. 그러나 지금에 와서 느낀것은 무엇보다 객체 구성 요소를 골고루 알고,

 

특히 인터페이스의 정확한 뜻을 알고 용도를 이해해야 객체지향 개발을 제대로 하는 밑거름이 된다. 지금까지 클래스 로직 구현에만 신경 썼다면, 인터페이스 설계를 잘하는 관점으로 시야를 넓혀야 한다.

 

덧 ) 이 객체지향의 탄생 원고는 제가 책으로 내려다가 일단 잘 안되었는데요. 이유는 비문이 많다. 단락내 주제가 중복된다. 어떤 상황 설명을 과장한다.등 입니다. 그래도 원고를 일단 블로그에 몽땅 풀어보고 언젠가 제대로 교정해서 다시 도전할 생각입니다. 비문이 많다. 단락내 주제가 중복된다. 어떤 상황 설명을 과장한다. 이점을 감안해서 읽고 객체지향을 이해하는데 도움이 되셨으면 좋겠습니다. 의견도 주셨으면 좋겠습니다. 원고 조금만 교정하면 괜찮을것 같은 출판사 관계자분의 피드백도 환영합니다. 특별한 일 없으면 매주 월수 발행 예정입니다.