2019년 11월 20일 수요일

[java] 상속 대신 구성을 사용하자.

왜 이 글을 쓰는 걸까

나는 자바 개발자로 일을 하면서 주위 사람들과 계속 이야기 하였다. 하지만 다들 수긍을 하는 듯 하면서 절대 바뀌지 않는 것이 있었다. 바로 **상속 대신 구성**이라는 말이다. 꽤나 오랜시간 구성을 사용하자고 설득하였지만 아무도 변화시키지 못했다. 나의 능력이 부족해서 속상하면서도 한 편으로는 내 말을 듣지 않는 것 같은 동료들이 마냥 아쉬웠다.

그런 의미로 나의 주장을 정리하고 가다듬어야 겠다는 생각을 했다.

내가 처음 자바를 공부할 때부터 들어온 말이 있다. 재사용을 위해 상속을 사용하라. 코드의 재사용을 위해서는 상속을 사용해야 한다. 두 개의 코드가 중복되면 상속을 해라. 그런데 뭔가 이상한 것을 느꼈다. 한 명이 말 한 것이 아니며 나에게 자바를 처음 알려준 강사부터 일터에 나가서 까지 그들은 마치 개발의 시작과 끝은 extends라는 키워드만 알면 다 할 수 있는 것으로 설명했다.

마치 상속이라는 단어가 마법의 단어이며, 이것만 있으면 다른 것을 알 필요도 없고 객체지향이건 유지보수 가능한 구조이며 모든 것이 가능하다고 설파했다.
그런데 막상 시간이 지나면 "이건 뭔가 잘못된 구조야", "아! 이건 상속을 좀 잘못했군. 제대로 했어야지", "앗! 이건 못 고친다. 이거 고치면 다 바꿔야해!" 상속은 점점 사람들을 옥죄었고 무엇이 잘못되었는지 몰랐다.

다들 너무 착하다. 사람들은 마치 상속으로 재사용 도모하라는 책들의 말을 믿고 구조가 이상하게 되면 마치 자기 탓인 것 마냥 아파한다. 자기 머리를 쥐어뜯는다. 하지만 끝까지 상속의 잘못은 아니라고 한다.

하지만 이건 그들의 잘못이 아니다 상속이라는 상아탑을 맹신하게하는 분위기가 그들의 도전을 막는다. 상속은 왠지 클래스 사이에 절대 바뀌지 않을 거라는 약속을 하며 새끼손가락을 거는 것 같다. 장기처럼 한 번 두면 무를 수 없는 약속이었다. 나는 이런 약속을 하고 싶지는 않았다. 무조건 깨질 것인데
후에 이 상속 구조를 자유롭게 수정할 수 있을까? 정말 이 구조가 맞는 걸까? 나중에 이 구조가 틀려서 바꿔야 한다면 바꿀 수는 있는 것일까?

없을 것 같았다. 시간이 지나면 지날수록 상속으로 끈끈하게 만들어진 유대관계는 깨기가 힘들어 보였다. 상속계층의 위로 올라갈 수록 그 약속이 많아지기 때문에 수정되면 안된다. 가령 자바에서 가장 위에 있는 객체는 `Object`이다. 이 녀석은 왠만해서는 절대 바뀌지 않는다. 수많은 자식들이 자신을 쳐다보고 있다. 그 자식들(서브클래스)은 당신(Object)이 변하지 않는다는 것만 믿고 재사용을 하기로 한 것이다.

상속은 마치 서브 클래스가 부모 클래스를 위에다가 그대로 복사해서 쓰는 것과 비슷하다. 만약 부모가 바뀐다고 해보자. 서브클래스도 당연히 바뀐 것이다.


2003년도부터 혹은 그 이전부터 상속은 문제였다.

https://www.javaworld.com/article/2073649/why-extends-is-evil.html
someone : If you could do Java over again, what would you change?
Gosling : I'd leave out classes
고슬링은 클래스를 없애고 싶다고 했다. 덧붙이자면 클래스 자체에는 문제가 없으나 extends관계가 문제이며 `implements`관계를 선호하라고 한다.


어떤 개발자는 자신의 99%의 코드에 상속을 사용하지 않았다고 한다

https://codeburst.io/inheritance-is-evil-stop-using-it-6c4f1caf5117
위 블로그에 있는 내용을 인용하겠다.
Using inheritance is not the only way to extend a class behavior.
But definitely is the most dangerous and harmful one.

I do not use inheritance in the 99% of my code.
And I really mean 99%.

- use interfaces to define the contracts between your classes.
- use final classes to implement behavior for those interfaces.
- use composition (through constructor dependency injection) to put things
together and prevent complexity


[이펙티브 자바]에서는 상속 대신 구성을 사용하라고 권고한다.

자세한 건 책을 참고


[오브젝트]에서는 상속을 절대 재사용을 위해 사용하지 말라고 한다.

계층구조를 표현하기 위해 사용해야 한다고 말이다. 즉 타입을 상속하기 위해서 쓰라는 말이다.


C++창시자분도 유튜브로 상속이 없이 어떻게 개발해야 하는지 발표하는 시대가 왔다.

세상은 정말 많이 변했다. 비야넷 아저씨도 변한 세상에 따라 자신의 주장을 바꿨다.

내가 정리한 문제점

내가 생각하는 상속의 문제점은 아래와 같다.
1. equals 구현에 문제가 생긴다(추이성)
2. 상속을 할 때 부모클래스 중에 구현된 기능이 있다면, 그것을 제대로 알고 구현을 해야 한다. 이 말은 하나의 소스코드가 둘로 쪼개져 있는 것과 같다.
3. 부모클래스가 변경되면 자식클래스 또한 전부 점검해야 한다.
4. 자식클래스가 바뀔 때, 부모클래스를 안 볼 수가 없다.
5. 버그를 찾기가 힘들다. 누구의 책임인지 확인하기가 쉽지 않다. 특히 `super`를 함께 쓰게되면 마치 저 클래스 멀리 있는 코드가 내 코드처럼 행동하면서 핑퐁을 하기 시작한다. 위로갔다 내려왔다 롤러코스터를 타게 된다.

그러므로 나는 상속보다는 `Composition` 소위 구성이라는 기능이 기본이 되어야 한다고 생각한다. `Composition`은 상속과 다르게 내부 구현을 알 수 없다. 필요한 부분에만 책임을 위임한다. 내부 소스로직이 수정되었다고 다른 소스에 문제를 일으키지 않는다. 위임한 책임만 완수하면 어떻게 바뀌던 상관이 없다. 하지만 상속은 문제를 끼칠 수 있다. 구성은 그래서 수정에 자유롭다. 아니 아예 전부 뜯어 고쳐서 새로 넣어도 된다. 그렇게 하라고 의존성 주입이 있는 거니까.

방금 구성을 사용해야 하는 다른 이유가 나왔다. 구성은 런타임에 의존성이 바뀔 수 있다. 즉, 프로그래밍이 실행 중에도 내 할일을 다시 집어넣을 수 있다는 말이다. 원래라면 하늘 아래 부모클래스는 하나밖에 가질 수 없고, 그것은 컴파일 이후에는 운명처럼 거스를 수가 없는데 말이다.


결론

글을 쓰면서 내가 틀렸던 건 아닐까 하는 생각으로 정리를 해보았으나.
틀리지 않은 것 같다. 오히려 자신감이 생긴 것 같다.

상속을 쓰기 전에 구성을 할 수 있는지 확인하자. 어떻게 해서든지 구성을 사용하려고 해보자. 코드에 더 믿음이 갈 것이다. 클래스에 final을 붙여서 상속을 막아 보자. 나 자신뿐만 아니라 사람들이 그 코드를 믿게 될 것이다. 상속보다 구성으로 짠 코드를 볼 때 더 별거 아닌 코드라고 생각하는 사람들이 많다. 그것이 목적이다. 더 별거 아닌 것처럼 보이게 더 쉽게 읽을 수 있게 말이다.

댓글 없음:

댓글 쓰기