본문 바로가기

책/오브젝트 (완)

10 상속과 코드 재사용

저는 코드를 작성할때 상속이 가능한지, 재사용이 가능한지, 중복은 없는지를 중요하게 여기곤 합니다. 하지만 상속과 재사용 그리고 중복을 피하고자 하는 명확한 이유는 없이 그저 유지보수하기 쉽도록 하는 것이 전부였습니다. 그런 저에게 있어 해당 장은 상속은 어떠한 점에서 좋으며 어떤 점을 조심해야 하는지 알려주는 장이였습니다.

재사용 관점에서 상속이란 클래스 안에 정의된 인스턴스 변수와 메서드를 자동으로 새로운 클래스에 추가하는 구현 기법이다.
...
새로운 클래스의 인스턴스 안에 기존 클래스의 인스턴스를 포함시키는 방법으로 흔히 합성이라고 부른다.

상속과 합성을 왜 사용하며 사용하였을 경우 어떤 이점이 있는지를 알려주는 대목이였습니다.

01 상속과 중복 코드

DRY 원칙

중복 코드가 가지는 가장 큰 문제는 코드를 수정하는 데 필요한 노력을 몇배로 증가시킨다는 것이다.
...
중복 코드를 결정하는 기준은 코드의 모양이 아니다. 모양이 유사하다는 것은 단지 중복의 징후일 뿐이다. 중복 여부를 경정하는 기준은 코드가 변경에 반응하는 방식이다.
...
DRY는 '반복하지 마라'라는 뜻의 Dont Repeat YourSelf의 첫 글자를 모아 만든 용어를 간단히 말해 동일한 지식을 중복하지 말라는 것이다.

DRY 원칙이라는 이름이 있지만 이는 중복하지 말라는 말과 같다고 생각합니다. 코드의 중복이 존재할 경우 동일한 로직에 대해 의미없는 리소스 투입이 일어나기에 기피해야 한다 생각합니다.

 

중복 코드 수정하기

민첩하게 변경하기 위해서는 중복 코드를 추가하는 대신 제거해야 한다. 기회가 생길 때마다 코드를 DRY하게 만들기 위해 노력하라

중복을 제거하기 위해서는 중복이 생기는 부분을 발견하고 이를 하나의 메서드 혹은 클래스 혹은 객체로 추출해야 한다 생각합니다.

 

상속을 이용해서 중복 코드 제거하기

상속을 염두에 두고 설계되지 않은 클래스를 상속을 이용해 재사용하는 것은 생각처럼 쉽지 않다.
...
상속은 결합도를 높인다. 그리고 상속이 초래하는 부모 클래스와 자식 클래스 사이의 강한 결합이 코드를 수정하기 어렵게 만든다.

이전 현장실습을 할 때에도 경험하였지만 상속을 생각하지 않고 코드를 작성한 이후 이를 상속하기 위해 수정하는 일은 여간 번거로운 일이 아니였다 생각합니다. 그렇기에 해당 장을 읽으며 면밀한 설계가 필요하다 생각하였습니다.

 

강하게 결합된 Phone과 NightlyDisountPhone 

상속을 위한 경고 1
자식 클래스의 메서드 안에서 super 참조를 이용해 부모 클래스의 메서드를 직접 호출할 경우 두 클래스는 강하게 결합된다. super 호출을 제거할 수 있는 방법을 찾아 결합도를 제거하라.
...
이처럼 상속 관계로 연결된 자식 클래스가 부모 클래스의 변경에 취약해지는 현상을 가리켜 취약한 기반 클래스 문제라고 부른다.

과거 부모 클래스의 메서드를 사용하기 위해 super를 남발하였던 적이 있었습니다. 당시에는 간단한 프로젝트였기에 큰 문제는 없었지만 만약 그러한 상황이 한번더 발생한다면 책의 내용을 상기하며 조심하고자 합니다.

02 취약한 기반 클래스 문제

상속은 자식 클래스를 점진적으로 추가해서 기능을 확장하는데는 용이하지만 높은 결합도로 인해 부모 클래스를 점진적으로 개선하는 것은 어렵게 만든다.
...
취약한 기반 클래스 문제는 캡슐화를 약화시키고 결합도를 높인다.
...
상속을 사용하면 부모 클래스의 퍼블릭 인터페이스가 아닌 구현을 변경하더라도 자식 클래스가 영향을 받기 쉬워진다.

상속에 대한 문제점을 언급하고 있습니다. 저는 스프링 프레임 워크를 이용해 프로젝트를 진행하며 인터페이스 그리고 이를 상속받아 구현하도록 코드를 작성하였습니다. 그러나 상속의 장점에 대해서만 고려를 하고 단점에 대해서는 고려하지 않았습니다. 추가로 책에서 언급하는 내용은 아마도 추상 클래스에 대한 내용을 말하는 듯 합니다.

 

불필요한 인터페이스 상속 문제

상속을 위한 경고2
상속받은 부모 클래스의 메서드가 자식 클래스의 내부 구조에 대한 규칙을 깨트릴 수 있다.

즉 부모 클래스의 내부 구현을 수정할 경우 문제가 생길 수 있다 언급하고 있습니다. 이러한 내용을 읽을수 록 설계를 치밀하게 하여 코드가 쉽게 수정되는 일이 없도록 하는게 중요할 듯 합니다.

 

메서드 오버라이딩의 오작용 문제

상속을 위한 경고3
자식 클래스가 부모 클래스의 메서드를 오버라이딩할 경우 부모 클래스가 자신의 메서드를 사용하는 방법에 자식 클래스가 결합될 수 있다.
...
상속은 코드 재사용을 위해 캡슐화를 희생한다. 완벽한 캡슐화를 원한다면 코드 재사용을 포기하거나 상속 이외의 다른 방법을 사용해야 한다.

상속을 사용할 때 주의해야 하는 점을 짚어주고 싶습니다. 또한 상속은 무조건적인 해결방법이 아니며 트레이드 오프의 결과물이라고 이야기하고 있습니다.

 

부모 클래스와 자식 클래스의 동시 수정 문제

상속을 위한 경고 4
클래스를 상속하면 결합도로 인해 자식 클래스와 부모 클래스의 구현을 영원히 변경하지 않거나, 자식 클래스와 부모 클래스를 동시에 변경하거나 둘 중 하나를 선택할 수밖에 없다.

부모 클래스와 자식 클래스간 강하게 결합되어 있기 때문에 전체를 수정하거나 혹은 아예 수정하지 않거나 양자택일이라 생각합니다.

03 Phone 다시 살펴보기

추상화에 의존하자

자식 클래스가 부모 클래스의 구현이 아닌 추상화에 의존하도록 만드는 것이다. 정확하게 말하면 부모 클래스와 자식 클래스 모두 추상화에 의존하도록 수정해야 한다.
...
- 두 메서드가 유사하게 보인다면 차이점을 메서드로 추출하라. 메서드 추출을 통해 두 메서드를 동일한 형태로 보이도록 만들 수 있다.
- 부모 클래스의 코드를 하위로 내리지 말고 자식 클래스의 코드를 상위로 올려라. 부모 클래스의 구체적인 메서드를 자식 클래스로 내리는 것 보다 자식 클래스의 추상적인 메서드를 부모 클래스로 올리는 것이 재사용성과 응집도 측면에서 더 뛰어난 결과를 얻을 수 있다.

가장 기억에 남은 대목은 부모 클래스와 자식 클래스 간의 관계에 대해 이야기를 하는 장이였습니다. 특히 부모-자식 클래스간 문제가 생겼을 경우(중복) 자식 클래스의 코드를 부모 클래스로 올리라는 조언은 저에게 많은 생각을 하게끔 해준 글이였습니다.

 

차이를 메서드로 추출하라

먼저 할 일은 두 클래스의 메서드에서 다른 부분을 별도의 메서드로 추출하는 것이다.

개인적으로는 이렇게 추출된 메서드는 별도의 객체에 보관하는 방식을 선호합니다. 그리하면 비록 클래스는 조금 더 많아질지 몰라도 객체의 응집도는 높일수 있다 생각하기 때문입니다.

 

중복 코드를 부모 클래스로 올려라

공통 코드를 옮길 때 인스턴스 변수보다 메서드를 먼저 이동시키는 게 편한데 메서드를 옮기고 나며 그 메서드에 필요한 메서드나 인스턴스 변수가 무엇인지를 컴파일 에러를 토해 자동으로 알 수 있기 때문이다.

메서드를 먼저 옮기고 해당 메서드에서 필요한 것들을 추가하는 식으로 부모 클래스로 옮기라 조언하고 있습니다. 

 

추상화가 핵심이다.

상속 계층이 코드를 진화시키는 데 걸림돌이 된다면 추상화를 찾아내고 상속 계층 안의 클래스들이 그 추상화에 의존하도록 코드를 리팩터링 하라. 차이점을 메서드로 추출하고 공통적인 부분은 부모 클래스로 이동하라.

구체 클래스가 다른 구체 클래스에 의존하지 말고 추상 클래스가 다른 추상 클래스에 의존하라는 이야기라고 생각합니다. 그리고 이는 추상 클래스간의 의존성은 DI를 통해 해결할 수 있다 생각합니다.

04 차이에 의한 프로그래밍

이처럼 기존 코드와 다른 부분만을 추가함으로써 애플리케이션의 기능을 확장하는 방법을 차이에 의한 프로그래밍이라고 부른다.
...
상속은 강력한 도구다. 상속을 이용하면 새로운 기능을 추가하기 위해 직접 구현해야 하는 코드의 양을 최소화할 수 있다.
...
상속의 오용과 남용은 애플리케이션을 이해하고 확장하기 어렵게 만든다.

마지막 문장인 상속의 오용과 남용은 애플리케이션을 이해하고 확장하기 어렵게 만든다는 말에 공감합니다. 저는 과저 오픈소스에 컨트리뷰션을 시도해본 적이 있었으나 과도한 의존성 주입에 의해 코드를 보는 것이 어려웠습니다. 그렇기에 코드를 쉽게 이해하지 못한 경험이 있기에 해당 문장은 저에게 크게 와닿았습니다.

' > 오브젝트 (완)' 카테고리의 다른 글

12 다형성  (1) 2024.02.11
11 합성과 유연한 설계  (0) 2024.02.09
09 유연한 설계  (0) 2024.01.31
08 의존성 관리하기  (1) 2024.01.30
07 객체 분해  (1) 2024.01.24