W2AS2L
2023. 6. 30. 12:21
2023. 6. 30. 12:21
상속이란?
- 자식 클래스가 부모클래스를 상속받아 설계된 구조를 일컫는다. 상속으로 이루어진 관계에서는 부모 클래스의 멤버 변수나 메서드를 사용할 수 있다.
자식 클래스는 부모 클래스의 모두 접근이 가능한가?
- 모두 접근이 가능한 것은 아니다. 부모 클래스의 멤버 변수나 메서드의 접근자가 private가 아닌 public 이나 protected일 경우메나 사용할 수 있다.
protected 접근자는 왜 사용할까?
- 우선, public 과 protected의 차이를 살펴보자
- public 접근자는 외부에 노출이 가능하다.
- public 접근자는 자식 클래스가 접근할 수 있다.
- protected 접근자는 외부에 노출되지 않는다.
- protected 접근자는자식 클래스가 접근할 수 있다.
- 두 접근자 모두 자식 클래스에게 노출이 되지만 외부에 노출 여부에 차이가 있다.\
- 변경 가능성이 열려있다는 것을 암시
- 상속받은 자식 클래스에서 구현을 완성시켜야 한다는 것
상속은 언제 사용해야 할까?
- SOLID 원칙의 리스코프 치환 원칙을 통해서 해답을 얻을 수 있다.
- 리스코프 치환 원칙은 상속받은 자식 클래스는 부모 클래스를 대체할 수 있는 경우에만 상속을 해야한다고 명시하고 있다.
- 자식 클래스가 부모 클래스를 대체할 수 있는 경우는 부모 클래스의 외부로 노출되는 메서드를 자식 클래스에서도 같은 의미로 제공되어야 한다는 것을 의미함. → IS-A 관계가 성립
상속으로 얻을 수 있는 장점은 무엇이 있을까?
- 상속 관계에서는 외부로부터 다형성을 보장하면서 클래스 내부 구현 코드를 모두 구현하지 않고 공통된 로직을 그대로 사용할 수 있다.
- 클래스 타입에 따라 변경되는 로직만 일부분 구현하면 된다는 것이 장점
- 하지만 이러한 장점도 java8 부터 인터페이스의 디폴트 메서드 기능이 나오면서 인터페이스내에서 로직 구현이 가능하여 상속의 장점이 약화되었다고 할 수 있다.
추상 클래스는 왜 사용할까?
- 추상 클래스는 protected 접근자를 사용하는 이유를 더 명확하게 나타내기 위해서 사용한다고 볼 수 있다.
- 부모 클래스에서는 변경되는 로직을 abstract로 정의하여 내부 로직은 구현하지 않고 상속받은 자식 클래스에서는 무조건 구현할 수 있게 만들어 변경되어야 할 부분과 변경되지 않을 부분을 더 명확히 구분할 수 있게 되었다.
- 컴파일 단계에서 자식 클래스가 abstract 메서드를 오버라이딩 하지 않을 경우에는 에러가 발생하므로 런타임 단계에서 발생할 수 있는 예외를 명확하게 확인할 수 있는 장점이 있다.
- 이러한 추상 클래스를 사용한 대표적인 사례가 템플릿 메서드 패턴이라고 할 수 있다. 템플릿 메서드 패턴은 외부로 노출되는 메서드는 그대로이나 내부에서 변경되는 로직만 별도로 추상화하여 외부에서 필요한 전략을 선택하여 내부 구현만 변경되도록 노출하는 디자인 패턴이다.
잘못된 상속 방법
- 어떤 이는 상속은 코드를 재사용 하기 위해 사용한다고 주장하기도 한다. 이는 전형적인 상속의 잘못된 사용 사례이다.
코드 재사용을 위해 상속을 하면 어떠한 단점이 있나?
캡슐화를 위반할 수 있다.
- 부모의 public 메서드는 외부에 노출하기 위한 용도로 사용된다. 그러나 자식 클래스에서도 부모 클래스의 public 메서드는 외부로 노출되기 때문에 자식 클래스에서 의도하지 않는 동작을 수반할 수 있게 되며 캡슐화를 위반하게 된다.
설계가 유연하지 않게된다.
- 상속으로 인해 결합도가 높아지면 다음과 같은 두 가지 문제점이 발생
- 하나의 기능을 추가하거나 수정하기 위해 불필요하게 많은 수의 클래스를 추가하거나 수정해야 한다.
- 단일 상속만 지원하는 언어에서는 상속으로 인해 오히려 중복 코드의 양이 늘어날 수 있다.
합성이란?
- 합성은 객체가 다른 객체의 참조자를 얻는 방식으로 런타임시에 동적으로 이뤄진다. 이는 보통 has-a 관계라고 일컫는다. 따라서 다른 객체의 참조자를 얻은 후 그 참조자를 이용해서 객체의 기능을 이용하기 때문에 해당 객체의 인터페이스만을 바라보게 됨으로써 캡슐화가 잘 이뤄질 수 있다.
합성은 언제 사용해야 할까?
- 구현 코드를 재사용하고 싶을 때 사용하면 유리하다. 또한 합성으로 사용된 코드는 사용하는 클래스에 따라 외부로 노출시킬 수 있고 내부로 캡슐화 할 수도 있어 클래스 특성에 맞게 캡슐화를 할 수 있다.
- 합성을 사용하고 인터페이스 타입을 사용한다면 런타임 시에 외부에서 필요한 전략에 따라 교체하며 사용할 수 있으므로 좀 더 유연한 설계를 할 수 있다.
- 대표적인 사례가 전략 패턴이다.
합성의 단점은?
- 합성은 객체 간의 관계가 수직관계가 아닌 수평관계가 된다.
- 따라서 큰 시스템에서 많은 부분에 걸쳐 합성이 사용될 때 객체나 메서드 명이 명확하지 않으면 코드가 가독성이 떨어지고 이해하기 어려워지게 된다.
- 합성을 사용할 시 용도에 따라 클래스들을 패키지로 분리하고 각각의 사용 용도가 명확하게 드러나도록 인터페이스를 잘 설계해야 한다.
결론
- 상속을 사용하고 싶다면 단순히 코드를 재사용 하는 용도가 아닌 부모 클래스를 대체할 수 있는 IS-A 관계인지 고려해야 한다. IS-A 관계에서도 변경되는 부분이 있다고 하면 protected 접근자나 abstract 를 사용하여 자식 클래스에게 명확하게 전달해야 한다.
- 단순히 코드를 재사용하고 싶다면 합성을 고려해보자 합성을 사용하면 코드 재사용도 가능할 뿐더러 캡슐화도 지킬 수 있다. 또한 다양한 전략에 따라 런타임시에 교체도 가능하여 유연한 설계가 가능하다. 단 합성을 하려는 클래스에 너무 많은 기능들이 정의되어 있거나 합성하는 인터페이스의 기능이 단일 책임보다 많은 책임을 가진 설계라면 분리해야할 필요가 있다.