ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • # Java의 상속 개념 : inheritance, composite
    각종 학습 요약/Java 2022. 5. 12. 16:24

    Java의 상속 개념

    상속에 대해 설명을 잘하는 것 같진 않지만, 적어도 장단점은 꼭 알아보고 상속 개념을 활용하면 좋겠다는 마음으로 글을 써본다. 이 글에도 끄트머리 즈음(아마도..)에 장단점을 다룬다.

    상속이란?


    두 클래스 간 만들어질 수 있는 연관 관계 중 하나다. 둘 중 개념의 범위가 좀 더 넓고 하나를 포괄하고 있는 클래스를 상위 클래스(혹은 부모 클래스)라고 하고, 좀 더 특정적이고 구체적인 개념의 클래스를 하위 클래스(혹은 자식 클래스)라고 한다.

    개념적인 설명이 잘 와닿지 않는다면 역시 비유로 가자.

    언제 상속관계가 필요할까?


    IS-A or HAS-A / 상속(inheritance)와 포함(composite)이라는 키워드를 알아야 한다.

    닉은 사람이다 (닉 == 사람, is-a)
    닉은 심장을 갖고 있다 (닉 ⊃ 심장, has-a)

    이거야 당연한 전제니까 빠르게 이해됐을 거라 생각한다. 한걸음 더 나아가서 반대로도 생각해보자.

    닉은 심장이 아니다. 그럴 뿐더러, 심장도 닉이 아니다. 개념이 서로 일치하거나 포함되는 건 전혀 아니지만, 닉이 존재하는데 있어 심장이 필요하기에, 필요할 때 필요에 따라 조립하여 존재하면 되는 일이다.

    닉은 사람이다. 하지만 사람은 닉이 아니다(닉만이 사람인 것은 아니다). 그말인 즉, '닉'과 '사람' 사이에는 개념이 일치되는 부분이 있고, '사람'이 '닉'보다 좀 더 상위의 개념이라는 것을 쉽게 알 수가 있다.
    '사람'이란 개념 없이 '닉'을 정의하기란 모호한 작업이 될 수 있다. '사람'이란 개념 없이, '닉' 말고 '홍길동'이 등장하게 되면, '닉'과 '홍길동'은 매우 유사하면서도 전혀 다른 것으로 구분할 수밖에 없다.

    이럴 때, is-a 관계임이 명확하고 그 안에서 상위/하위 개념의 구분이 명확할 때 우리는 상속 관계를 정의해볼 수 있다.
    (물론 이것도 100% 정확한 것은 아니다. 구체적인 내용은 '상속의 규칙' 파트를 확인하자.)

    포함(composition, has-a) 관계의 객체의 특징은, 포괄하는 객체에서 포함되는 객체가 필요할 때 생성해서 사용하고, 포괄하는 객체가 생명주기를 다할 때에 포함되는 객체도 동시에 소멸한다는 특징이 있다.

    한 가지 더, 집약관계(Aggregation)라는 관계도 존재하는데, 이건 직접 알아보자(아니면 뒤쪽에 추상클래스 이야기 할 때에...).

    그래서 상속하면 뭘하는 건데


    상속을 하면 상위개념(클래스)의 기능(행동, 메소드)을 확장할 수가 있다. 하위개념(클래스)의 특성에 맞게 좀 더 구체화/재구성 할 수 있다는 말이다.

    사람은 숨을 쉰다. 들숨(습) 날숨(후-)을 반복한다.
    닉도 숨을 쉰다. 근데 좀 특이하게, '습습후-습 후-후-습후-' 하고 쉰다. 물론 그렇다고 그게 숨쉬는게 아닌 건 아니다.

    숨쉬기를 구현하는 건 꽤나 어려운 일일 것이다. 먼저 혈중 산소농도를 감안해서 필요한 숨쉬기 템포를 조절해야 할 것이고, 체온도 고려해야 할 것이고, 운동강도에 따라서도 달라질 것이다. 만약 고산지대나 저지대에 있다면? 호르몬을 고려한다면? ...이렇게 생각하다보면 한도 끝도 없다. 인체의 신비란...

    그런데 만약 닉을 구현하면서 숨쉬기라는 동작을 만들어야 하는데, 또 처음부터 새롭게 만들어야한다면? 정말 끔찍한 일일 것이다.
    사람 개념을 상속받은 닉은 이 정도만 해도 될 것 같다.

    class 닉 {
        @Override // 이건 상위클래스에서 본딴 행동(메소드)을 여기(하위클래스) 안에서 재정의 한다는 뜻이다.
        public void 숨쉬기() {
            super.숨쉬기(setBreathTempo("습습후-습 후-후-습후-"));
        }
    }

    숨쉬기에 대한 거의 모든 속성(변수)와 동작(메소드) 구현 없이, 상위 클래스에서 허용한 '숨쉬기 박자 맞추기' 기능만을 이용해 닉만의 특별한 숨쉬기를 구현했다.
    반대로 말하면, 특별히 재정의하고 싶은 부분 외에는 물려받은 기능을 그대로 사용할 수 있다. 위의 클래스 안에는 setBreathTempo()라는 메소드를 정의하지 않고도 사용하고 있는 모습을 볼 수 있다.

    만약 이렇게 상속을 통해 구현하지 않고, 닉에서 자체적으로 숨쉬기를 전부 구현한다면? 만약 '사람' 클래스와 정말 똑같이 구현해낸다면 문제가 없을지도 모른다. 하지만 하나라도 빠지면, 적어도 숨쉬기 영역에서 닉은 사람의 역할을 하지 못하게 될 것이다.

    이건 하나의 예시일 뿐이지만, 이런 편리함과 안전함 때문에 상속을 구현하는 것이다. 다 예시를 들 순 없지만, 어떤 장단점들이 있는지 아래에서 살펴보자.

    상속의 장단점


    1. 장점:
      • 코드 재사용성 증가(메소드 따로 정의할 필요 없음, 공통적으로 사용하는 멤버 따로 정의할 필요 없음)
      • 적은 양의 코드로 새로운 기능을 하는 클래스들을 만들 수 있음.
      • 객체지향은 곧 관계 중심의 코드였다, 즉, 객체지향적인 구조를 설계할 수 있다.
    2. 단점:
      • 보통 많은 블로그에서 '상속의 장점'이라고 표현되는 단점: 유지보수. 유지보수는 쉬워지는 게 아니라 어려워진다. '유지보수'는 상속의 장점이 아니라 단점이다. '코드 중복이 사라진다'는 것이 곧 '유지보수가 쉬워진다'는 의미가 되는 것은 결코 아니기 때문이다.
      • 예를 들어, 상위클래스의 어떤 부분을 수정한 후에도 하위클래스가 정상적으로 동작할거라고 장담할 수 있을까? 그럴 수 없을 것이다.
      • 상위클래스에 버그가 있다면? 하위클래스 또한 버그를 일으킬 것이다.
      • 여러 상속관계에 있는 하위클래스의 소스를 파악중이라면? 코드 한 줄의 동작을 확인하기 위해 상위클래스 전부를 열어봐야 할 수도 있다.
      • 하위클래스를 작성한 사람들이 일관성 있게 상속이 되었다고 장담할 수도 없다. 물려받은 상위클래스만 보고 '당연히 이렇게 작동하겠지'라고 판단했다면, 그건 단지 착각일 지도 모른다.
      • 상위 클래스의 모든 기능을 필요로 하지 않을 수 있지만, 하위 클래스는 무조건 물려받아야만 한다(오버라이드의 강제성).

    (이쯤 보면 아셨겠지만 난 상속이 싫다. ㅋㅋ 머리아프다...)

    extends와 implements


    Java에는 상속을 위한 두가지 키워드가 있다. extendsimplements다. 둘 다 상속관계를 위한 키워드다(사실 난 implements는 상속이 아니라 구현관계 아닌가 생각하지만...).
    extends는 클래스를 상속하는 키워드며, 단 하나의 부모만을 상속받을 수 있다.
    implements는 인터페이스를 상속하는 키워드며, 하나의 클래스가 여러 구현을 받을 수 있다.

    상속의 규칙


    피터 코드라는 훌륭한 분께서 좋은 상속을 위한 규칙을 정리해놓으신 것이 있다. 다음과 같다.

    1. 상위-하위 클래스의 관계가 '역할 수행' 관계라면 상속을 사용해선 안된다.
      • '직장인'은 '사람'이다. 어떤 순간에는 그렇다. '편의점 손님'도 '사람'이다. 어떤 순간에는 그렇다. '어떤 순간'이 아닌 다른 상황이 주어진다면? 어떤 순간의 역할이 'is-a'로 표현된다고 해서 상속 관계를 적용해서는 안된다.
    2. 하위 클래스 간 역할 변경이 필요하지 않아야 한다. 절대로.
      • '직장인'은 어떤 순간에는 '편의점 손님'이 되기도 한다. '편의점 손님'은 '직장인'이 되기도 한다. 둘 다 '사람'을 상속하고 있음에도 서로 객체를 변환하는 작업을 필요로 한다. 둘 중 하나로 책임을 고정하든가 절대로 변환할 경우를 만들어선 안된다. 1번과도 비슷한 느낌이다(그만큼 중요하시다는거지).
    3. 하위 클래스가 상위 클래스의 책임을 무시해서는 안된다. 책임은 곧 역할이고 기능이다. 안 쓸 거면 상속하지 말란 말이다.
    4. 일부 기능만을 재사용할 목적으로 상속해서는 안된다.
      • 상위 클래스를 유틸 클래스(유용한 공통 기능을 담고있는 객체)처럼 쓸 거면 상속하지 마라.
      • 3번의 연속이다. 그마만큼 중요하시다는 거지.
    5. 하위 클래스는 '역할', '트랜잭션', '디바이스' 등을 특수화해야 한다. - 이게 무슨 소리일까?
      • 나도 설명할 수 있을만큼 정확히는 모르겠다. 좋은 말씀 하신거지 뭐.
      • 하지만 아무 설명도 하지 않으면 안되니, 보충해서 공부할 키워드를 남긴다. : 집약 관계(Aggregation)

    어우 해야 할 말이 너무 많아서
    일단 한번 끊고 간다. 끝!

    댓글

Designed by Tistory.