티스토리 뷰

# 주의 #

뇌피셜, 개인적인 생각이 상당부분 포함되어 있고 글도 상당히 뒤죽박죽입니다.

또 틀린 부분이 있을지도 모릅니다.

무엇보다 굉장히 장문이라 1줄짜리 정의를 원하는 사람에게 적합하지 않습니다

 

 


객체지향(Object Oriented Programming, OOP). 객체를 지향한다.

 

객체가 뭔가?

 

그 답은 클래스가 뭔가? 에서부터 출발한다.

 

클래스는 대개 객체를 정의하는 설계도 또는 틀으로 객체를 생성하는데 사용된다고 흔히 이야기 한다.

 

"붕어빵 틀은 클래스이고 붕어빵 틀로 찍어낸 붕어빵들이 객체" 라는 설명은 객체지향을 공부한 사람이라면 반드시 한번쯤은 들어봤을 설명이다.

 

여기서 특별히 한 클래스를 통해 생성된 객체를 찝어서 해당 클래스의 인스턴스(Instance)라고 부르기로 한다. 

인스턴스랑 객체가 막 다른건 아니고 그냥 이름만 좀 특별하게 붙여준 것이다.

 

이 클래스는 속성기능을 가진다.

속성은 필드로, 기능은 메서드로 표현된다. 그리고 이들을 아울러 클래스의 "멤버"라고 부르기로 한다.

(필드 - 클래스 변수, 인스턴스 변수, 지역 변수)

 

여기까지는 맛보기.

이제부터 본격적인 시작이다.


 

모두가 평등했던 클래스들의 세계에 "상속(extends)"이라는 개념이 등장한다.

 

상속이라는 것은 물려주는 사람과 물려받는 사람이 있다. 주는 사람이 위, 받는 사람이 아래.

누가 나보다 위고, 누가 나보다 아래인지를 구분할 수 있게 되었다. 

 

하위 클래스는 상위 클래스의 멤버를 모두 물려받고 이를 확장해 사용할 수 있게 된다.

** 이는 한 클래스가 다른 클래스를 멤버로 갖는 "포함 관계"와는 다르다 **

 

이 상속 관계에 있어 가장 근본이 되는 개념은, 상속 관계에 있는 두 클래스 간에 "A는 B이다 (IS-A)" 라는 개념이 성립해야 한다는 것이다.

당연한 이야기이다. 상속 관계에서 하위 클래스는 상위 클래스의 멤버를 모두 갖는다고 했다. 그럼 적어도 하위 클래스는 상위 클래스랑 같거나 더 큰 확장 관계에 있게 된다. 

 

하위 클래스는 상위 클래스보다 같거나 더 크다.

즉 상위 클래스에 있는 내용은 하위 클래스들도 갖고 있다는 것이다.

그렇기에 상위 클래스에는 하위 클래스들이 가지고 있는 공통적인 속성들이 정의된다. 그렇게 되면 상위 클래스에 있는 내용은 굳이 하위 클래스에 두번 적을 필요가 없다. 어차피 물려받기 때문이다. 하위 클래스는 상위 클래스로부터 기능을 물려받고, 하위 클래스는 이를 토대로 기능을 확장한다.

 

그렇기에 상속은 코드의 중복을 제거하고, 상위 클래스의 기능을 재사용 및 확장해서 사용할 수 있도록 만들어줄 수 있다.

 

상속은 단순히 하위 클래스가 상위 클래스의 멤버를 물려받는 것에 끝나는 개념이 아니다.

상-하위 개념이 등장함에 따라서, 하위 클래스가 상위 클래스를 대체할 수 있다는 "다형성"의 개념이 나타나게 된다.

아까 말했듯이 상위 클래스는 하위 클래스들이 가지고 있는 공통적인 속성이 정의되어있기 때문에 하위 클래스들은 당연히 상위 클래스의 기능과 속성을 모두 갖고있기 때문이다.

 

아무튼 그렇다.

이제 하위 클래스가 상위 클래스를 대체할 수 있게 되었다. 이는 클래스들의 세상에 매우 혁신적인 일이었다.

 

상위 클래스에는 공통적인 기능이 정의되어있고, 하위 클래스는 이 공통적인 기능을 모두 수행할 수 있다.

상위 클래스에는 공통적인 기능. 하위 클래스에서 구현

이를 다시 말하면 하위 클래스들의 공통점을 모아 하나의 상위 클래스에 정의해 둔다는 이야기로도 볼 수 있다. 

 

하위 클래스들의 공통점을 잘 간추려서 상위 클래스 하나에 모으는 과정, 이를 전문 용어로 "추상화" 라 부르기로 한다.

 

이를 깨달은 상위 클래스는, 굳이 자신이 기능을 전부 구현해야 하는가에 대한 의문을 품는다.

자신을 상속받는 하위 클래스가 어차피 기능 구현을 다시 할 수 있기 때문이다. (이를 메서드 오버라이딩이라고 한다)

그렇기에 상위 클래스는 아예 클래스 내부에 어떠한 기능이 있다 하고 정의만 해두고, 즉 추상화만 해두고 내부는 텅텅 비워놓는 지경에 이르렀다.

나는 정의만 할테니 구현은 하위 클래스 너네들이 알아서 하라는 것이다. 

 

이렇게 추상적으로 개념 정의만 해놓고 구현은 텅텅 비워둔 메서드를 추상 메서드라 부르고, 추상 메서드를 하나 이상 포함하고 있는 클래스를 추상 클래스로 부르기로 한다.

추상 클래스도 말 그대로 클래스이고, 속성과 기능(필드와 메서드)를 갖는다. 

 

추상 클래스를 상속받는 하위 클래스는, 추상 클래스가 정의한 추상 메서드를 반드시 구현해야 한다.

 

하지만 추상 클래스도 역시 클래스일 뿐. 일반 클래스를 상속하는 것과 크게 다르지 않다.

"A는 B이다 (IS-A 관계)" 관계가 동일하게 성립한다는 이야기이다.

하지만 유일하게 다른 것이 있으니, 추상 클래스에서 구현되지 않은 추상 메서드가 이를 상속받는 하위 클래스에서 "반드시" 구현되어야 한다는 것이다. 

만일 구현하지 않으면 여전히 해당 메서드는 추상 메서드로 남고, 추상 클래스를 상속받은 하위 클래스도 추상 클래스가 된다. (추상 클래스는 인스턴스를 생성할 수 없기 때문에 추상 메서드를 전부 구현한 이후에야 인스턴스를 생성할 수 있다)

 

결론적으로 추상 클래스를 상속한다는 것은, 기존 상속의 개념을 그대로 가져가면서 하위 클래스로 하여금 추상 메서드를 구현하는 것을 강제하도록 하는 효과를 얻는데 목적이 있다.

일반 상속과 동일하게 불필요한 중복을 제거하고, 상위 클래스를 확장해서 사용하는데 그 의도가 있다.

 

 

하지만 상속이라는 것은 꽤 불편한 점이 많다.

"A는 B이다" 개념이 성립하는 클래스들보다 성립하지 않는 클래스들이 훨씬 많다는 것이다.

또한 자바는 다중상속을 금지하고 있기 때문에 "A는 B이면서 C이다" 라는 관계를 만들 수가 없다.

 

이를 보완하기 위해 등장한 것이 바로 "인터페이스" 되겠다.

인터페이스는 추상 메서드들의 모임이다. 추상 메서드들의 모임이라는 것은 구현되어 있는 메서드가 아예 없다는 뜻이다.

인터페이스를 상속하는 하위 클래스들은 인터페이스가 정의해둔 추상 메서드들을 전부 구현해야 한다. 

 

이처럼 구현되어있는 메서드가 아예 없는 경우, 추상 메서드밖에 없는 경우는 곰곰히 생각해보면 다중 상속이 안 될 이유가 없다. 자바에서 다중상속을 불가능하게 한 이유는 하위 클래스가 두개의 상위 클래스를 상속받을 때, 이 두 상위 클래스가 동일한 메서드나 필드를 갖고있는 경우, 하위 클래스는 둘 중에 대체 무엇을 골라야 하는지 알 수 없기 때문에 이러한 일을 막기 위해 근본적으로 차단해 놓은 것이다. (이를 죽음의 다이아몬드 문제 라고 한다)

 

하지만 인터페이스는?

두개의 상위 인터페이스 모두 추상 메서드로만 이루어져 있다. 둘다 텅텅 비어있다는 뜻이다.

그렇다면 하위 클래스는 두 인터페이스 중 뭐를 고르더라도 어차피 둘 다 비어있기때문에 뭘 고르던 간에 내가 구현을 해야한다.

 

그렇기에 추상 메서드들만이 모인 인터페이스는 다중상속이 가능하도록 하는데, 다중 상속이 가능하다고 하면 일반 클래스 간의 상속과 비교하여 용어에 혼란이 오기 때문에 클래스가 인터페이스를 상속하는 것은 특별하게 상속 대신 "구현(implement)" 이라는 용어를 사용하기로 한다. *** 뇌피셜 ***

 

즉 클래스는 인터페이스를 "구현" 한다고 부르며, 클래스는 인터페이스를 "다중 구현" 할 수 있다고 부르기로 한다.

한편 클래스는 클래스를 "상속"할 수 있으며, 클래스는 클래스를 "다중 상속" 할 수 없다.

 

인터페이스는 클래스와 같이 "A는 B이다" 관계가 성립하지 않는 곳에서도 사용할 수 있다.

그니까 서로 연관없는 클래스들이 같은 인터페이스를 구현할 수 있다는 것이다. 그렇기에 주로 어떤 용도로 사용되게 되냐면 "한 클래스가 어떠한 동작을 할 수 있는지? (-able), (HAS-A 관계)" 에 대한 공통적인 기능을 정의해두는 용도로 사용되게 된다.

 

예를 들어서 사람 클래스는 숨을 쉴 수 있다. 또한 강아지 클래스도 숨을 쉴 수 있다.

그런데 사람 클래스와 강아지 클래스 사이에는 "A는 B이다" 관계가 성립하지 않는다. 하지만 둘 사이에는 "숨을 쉴 수 있다"라는 명백한 공통점이 존재한다.

 

그렇기에 이러한 공통점을 뽑아내기 위해서 인터페이스가 사용되고,  "숨을 쉴 수 있다" 라는 공통점을 Breathable 인터페이스를 하나 만들어 그곳에 추상 메서드로서 정의해 두는 것이다.

그렇다면 "숨을 쉴 수 있는" 클래스들은 모두 Breathable 인터페이스를 각자의 방식대로 구현하면 된다. 서로 연관 없는 클래스들 간의 공통점을 뽑아내는데 인터페이스가 사용되는 것이다.

이는 약간만 관점을 달리 보면 어떠한 클래스가 어떠한 역할을 수행할 수 있는지를 나타내는 식별자(?) 역할도 할 수 있다.

Breathable 인터페이스를 구현하고 있는 클래스는 "모두 숨을 쉴 수 있다" 라는 정보를 클래스 내부의 코드를 들여다보지 않아도 구현 중인 인터페이스의 이름만 봐도 이해할 수 있다.

 

그리고 이러한 개념으로 생각해봤을때 인터페이스가 왜 다중 구현이 가능한지도 이해할 수 있다.

사람 클래스는 숨만 쉴 수 있는 것이 아니다. 걸을 수도 있고, 밥을 먹을 수도 있다. 다양한 메서드들이 존재하는데, 이런 메서드들 역시 인터페이스로서 분리될 수 있다. Eatable 인터페이스라던지, Walkable 인터페이스라던지 식으로 말이다.

그렇게 되면 사람 클래스는 Breathable, Eatable, Walkable 인터페이스 3개를 동시에 구현하게 된다. 하지만 아무 문제 없다. 어차피 3개의 인터페이스 모두 정의만 되어있을 뿐 내부는 텅텅 비어있기 때문이다.

 

결론적으로 인터페이스는 무엇을 할 수 있는지에 대해 "역할"을 정의한 것이고, 클래스는 이를 각자 "구현" 하는 구조이다.

= "역할"과 "구현"이 분리되었다.

 

이는 즉 어떠한 역할을 모집하는 경우, 그 역할(인터페이스)을 구현하는 클래스라면 누구든지 들어갈 수 있다는 것이다.

이게 바로 다형성이다.

 

그리고, 그 역할을 어떻게든 수행하기만 하면, 즉 결과를 잘 내놓기만 하면 역할을 구현하는 클래스가 내부적으로 어떻게 동작하는지는 전혀 알 필요가 없다. 역할을 맡긴 측에서는 결과물이 중요한 것이지 구현 클래스의 동작 방식은 전혀 알 필요가 없는 것이다. 또 알게 해서 좋을 것도 하나 없다.

그렇기에 각자 구현 클래스들은 자신이 가지고 있는 멤버들을 숨기고, 꼭 필요한 멤버들만 외부에 노출하게 되는데, 이를 "캡슐화" 라고 부른다.

 

 

결국 "상속"으로부터 등장한 클래스 간의 상하 관계 개념

하위 클래스가 상위 클래스를 대체할 수 있다는 "다형성"의 개념을 불러왔고,

하위 클래스들의 공통점을 모아 하나의 상위 클래스에 정의해 둔다는 "추상화"의 개념을 불러왔다.

또한 각자의 클래스들은 자신의 동작방식을 숨기고 꼭 필요한 멤버들만 외부로 노출하는 "캡슐화"의 개념도 등장했다.

 

이렇게 객체지향의 4대 근본 상속 / 다형성 / 추상화 / 캡슐화 개념이 모두 등장하게 된다.

 

결국 객체지향의 근본 개념은 "상속"에서부터 출발했고, 추상 클래스와 인터페이스 역시 상속에 뿌리가 있다는 것을 알 수 있다.

 

 

그리고 마침내. 추상 클래스와 인터페이스의 차이가 무엇인지를 얘기할 수 있게 되었다.

 

추상 클래스는 딱딱하다. 상위 클래스에 이미 기본적으로 만들어져 있는 내용이 있고, 하위 클래스는 이를 그대로 본받아 거기에 내용을 덧붙여 사용한다. (이를 어렵게 말하면 둘이 강하게 결합한다고 한다)

반면 인터페이스는 유연하다. 인터페이스는 텅텅 비어있기 때문에 하위 클래스는 내 방식대로 자유롭게 구현한다. "결과만 잘 나오면" 어떤 식으로 구현했는 지는 전혀 신경쓰지 않는다.

 

당연히 딱딱한 것보다 유연한 것이 좋다. 인터페이스를 적극적으로 활용하는 것이 객체지향의 핵심이고, 스프링 프레임워크에서 DI, IoC의 근본 개념이 된다.

유연하면서 객체지향 냄새가 나는개발. 인터페이스가 있기 때문에 가능한 이야기이다.


 

뭔가 갑자기 삘을 확 받아서 이렇게 장문의 글을 쓰게 되었다. 이렇게 길어질 줄은 몰랐는데.

 

추상 클래스와 인터페이스의 차이가 뭐냐? 라는 질문을 받았을 때, 나는 이 질문에 대한 명쾌한 답을 내놓을 수 있는 상태가 아니었다. 그냥 생김새가 다른데요, 이름이 다르네요 같은 누구나 아는 답변이 아니라 왜 만들어졌고, 각자가 어디에 어떤 쓰임새로 쓰이는지에 대해 시원하게 답을 못한다는 것이었다.

 

그래서 뭔가 이전에 배웠던 내용들을 다시한번 돌아보고 싶었다. 

글을 쓰고 난 이후에 지금 다시 질문을 받으면 시원하게 답변할 수 있는가? 그건 또 아닌 것 같지만 이전보다는 훨씬 나아진 것 같다. 그리고 면접에 대한 대비를 조금 해야겠다는 것을 많이 느꼈다. 

위 질문과도 마찬가지로 뭔가 근본적인, 추상적인 개념에 대해 물으면 뭐라 한마디로 요약해서 답하기가 어려운 경우가 많다. 머릿속으로는 대충 뭔지 알겠는데, 뭔가 내뱉기는 하는데 정작 핵심을 찌르는 내용을 말하지는 못한다..

 

여전히 갈 길이 멀다

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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 31
글 보관함