[이펙티브 자바] 아이템19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라

코린이서현이·2025년 2월 9일
0

이펙티브자바

목록 보기
5/6
post-thumbnail

상속용 클래스 만들기

자바 문서 작성하기

주석에 관한 이야기를 잠깐 해보자!

"왜(Why)를 설명하는 주석을 달아라. 무엇(What)은 코드가 보여줄 것이다."
"무엇(WHAT)를 설명하는 주석을 달아라. 어떻게(How)는 코드가 보여줄 것이다."

조금 당황스럽지만 어쨌든 How를 적지 않는 것은 확실해보인다.

그러나 상속용 클래스는 "어떻게" 까지 적어야 한다!
이후 있을지 모르는 오버라이딩으로 인한 오동작을 방지하기 위해서..!!

특히 어떤 메서드가 재정의 될 가능성이 있으면서, 해당 다른 메서드에서도 호출되는 코드라면, 상속관계에서의 재정의가 의도하지 않은 방식으로 동작할 수 있기 때문이다..

재정의 메서드란
pubilc,protected 메서드 중 final이 아닌 모든 메서드를 말하며 결국 재정의 될 수도 있는 메서드를 뜻한다.

자기 사용 코드란
클래스 내부의 다른 메서드에서 호출되는 메서드를 말한다.

java 의 공식 메서드 구현 명세 문서화 태그 : @implSpec

사용 활성화 방법
1. 명령줄에서 직접 사용:

```
bashCopyjavadoc -tag "implSpec:a:Implementation Requirements:" -d docs src/*.java
```
  1. Maven에서 설정:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
        <version>3.6.0</version>
        <configuration>
            <tags>
                <tag>
                    <name>implSpec</name>
                    <placement>a</placement>
                    <head>Implementation Requirements:</head>
                </tag>
            </tags>
        </configuration>
    </plugin>
  2. Gradle에서 설정:

    javadoc {
        options.tags = [ 'implSpec:a:Implementation Requirements:' ]
    }
  3. IDE에서 설정 (예: IntelliJ IDEA):

    -tag "implSpec:a:Implementation Requirements:"

Hook 메서드 제공하기

만약 하위 메서드에서 상위 클래스의 내부 동작 중간에 어떤 동작을 끼워넣고 싶다면 어떻게 하는게 좋을까?

Hook 메서드를 쓴다면 안전하게 메서드의 기능을 확장할 수 있다.
원하는 상위 클래스 메서드 중간에 동작을 하지 않는 Hook 메서드를 호출하는 것이다.
이때 상위 클래스는 오버라이딩할 수 없게 final 키워드를 붙여야한다.

public abstract class Pizza {
   public final void prepare() {
       System.out.println("치즈 추가");
       addToppings();  // Hook
       System.out.println("굽기");
   }

   protected void addToppings() {
   }

}

//하위 클래스 
class PepperoniPizza extends Pizza {
   @Override 
   protected void addToppings() {
       super.addToppings();  // 치즈는 그대로 쓰고
       System.out.println("페퍼로니 추가");
   }
}

생성자에 재정의 가능성이 있는 메서드를 호출하지 않기

하위 클래스의 인스턴스가 생성되기 위해서는 상위클래스의 생성자를 먼저 호출한다는 사실은 모두 아실거라 생각합니다.

만약 상위 클래스 생성자 내부에 하위 클래스에서 오버라이딩 된 메서드를 호출한다면, 오버라이딩되기전 동작이 실행되기 때문이다.

비슷하게 clone, readObject메서드 모두 재정의 가능한 메서드를 호출해서는 안된다.

그렇다면 생성자에서 호출해도 안전한 메서드는 무엇일꺼요?

private,static,final 메서드 처럼 재정의가 불가능한 메서드들이다.

하위클래스를 직접 만들어보기

저자는! 절대적인 규칙이나 판단근거는 없기 때문에, 직접 하위 클래스를 만들어보는 것이 가장 정확한 방법이라고 말한다.
또한 검증을 위해서는 3개의 하위클래스와 하나 이상은 제 3자가 작성해야한다고 말했다.

... 이 부분에 대한 다른 분들의 생각이 궁금합니다! 

예전에 한 어플 대표님께서 "초반에는 2주에 하나씩 어플을 만들었다. 스파게티 코드라고 하더라도, " +
        "출시가 중요했고, 스파게티 코드가 필요한 상황이었다."라고 말씀하셨던 기억이 나네요! 

상속용 클래스와 상속용이 아닌 클래스는 분리하자

위의 복잡한 문제, 특히 자기 사용 코드와 재정의 가능한 메서드가 주는 잠재적인 어려움을 방지하는 좋은 방법은 무엇일까?

바로 상속을 금지하는 것이다.

상속을 금지하는 방법

  1. 클래스를 final로 선언한다.
  2. 생성자를 private, package-private로 선언하고 정적 팩터리 메서드를 둔다.

조금 더 절충적인 대안은 클래스 내부에서는 재정의 가능 메서드를 호출하지 않고, 그런 코드는 삭제하는 것이다.
이럴 경우 하위 클래스에서 해당 메서드를 오버라이딩했더라도, 다른 동작과의 관련이 없어 영향이 적기 때문이다.

+ 보너스 ) 인텔리제이에서 JavaDoc 기능 사용하기

내용이 조금 짤막한 감아서 제가 조금 더 준비했습니다!
공식 자바 코드를 보면 엄청나게 긴 javadoc이 있는 것을 보실 수 있습니다!
더 안전한 코드와 협업을 위해서라면 작성 안 할 이유가 없죠! 편리한 방법까지 소개해드릴게요!

  1. File -> Settings -> Editor -> Live Templates
  1. Template Group으로 그룹 생성과 Live Template으로 템플릿 생성
  1. 내부 채우기
  • Abbreviation : 단축어로 이후 자바doc 사용에 단축어 + tap으로 이용하게 된다.
  • Template Text : 기본틀, 변수로 지정하고 싶다면 앞뒤로 $ 붙이기
  1. 변수명 지정
    기본적으로 저장할 수 있는 유저명, 클래스명, 날짜를 저장해둔다. 저장할 값은 Skip도 눌러주기!
  1. 사용할 수 있는 곳 지정하기
    우리는 java에서 쓸 것이기 때문에 아래 두개를 지정하면 됩니다!
  1. 사용해보기
    위에서 지정한 단축어 + Tap 키를 누르면 자동으로 템플릿이 생성됩니다!!

아래는 제가 쓴 클래스용 탬플릿과 메서드용 탬플릿 들입니다!

/**
* $Class$
*
* $DESC$
*
* @author $USER$
* @since $DATE$
*/

/**
* $DESC$
*
* @params $PARAMS$
* @return $RETURN$
*
* @author $USER$
* @since $DATE$
*/

주석을 하나하나 통일하는 것도 참 귀찮은 일인데, 해당 기능을 사용하면 더 편리하게 협업할 수 있지 않을까 싶습니다 ~!


해당 글은 카카오테크스터디 중에 작성한 글입니다 🙇‍♀️
제가 정리하지 않은 나머지 글은 고마운 팀원분들이 정리해주시고 계십니다 🫶

더 많은 글을 읽고 싶다면 아래 깃허브를 참고해주세요!

🔗 카카오테크 스터디의 이펙티브 자바

profile
포기만 하지 않는다면 언젠간 도달한다!

0개의 댓글