[Effective Java 3/E] ITEM 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라

    Effective Java 표지. Source : https://blog.insightbook.co.kr/

    *알림 : *
    Effective Java 3판은 Java 9까지 도입된 언어적 기능을 중심으로 서술되어 있습니다. 10버젼 이후의 Java 개발을 하시는 분들은 우회적인 접근법 대신 Java 언어 내 새로 도입된 기능이 더 간결하고 좋을 수 있습니다.

    해당 포스팅은 SSAFY 내 책읽기 스터디의 활동을 통해 작성된 포스팅입니다.
    https://github.com/kjsu0209/JavaBook
    https://medium.com/javabook

     

    JavaBook – Medium

    Documentation space of our book study.

    medium.com

     

    kjsu0209/JavaBook

    책읽기 스터디. Contribute to kjsu0209/JavaBook development by creating an account on GitHub.

    github.com

    doc 주석이란?

    예제 1 : Java 8의 ArrayList doc 주석

    /**
     * Resizable-array implementation of the <tt>List</tt> interface.  Implements
     * all optional list operations, and permits all elements, including
     * <tt>null</tt>.  In addition to implementing the <tt>List</tt> interface,
     * this class provides methods to manipulate the size of the array that is
     * used internally to store the list.  (This class is roughly equivalent to
     * <tt>Vector</tt>, except that it is unsynchronized.)
     *
     * <p>The <tt>size</tt>, <tt>isEmpty</tt>, <tt>get</tt>, <tt>set</tt>,
     * <tt>iterator</tt>, and <tt>listIterator</tt> operations run in constant
     * time.  The <tt>add</tt> operation runs in <i>amortized constant time</i>,
     * that is, adding n elements requires O(n) time.  All of the other operations
     * run in linear time (roughly speaking).  The constant factor is low compared
     * to that for the <tt>LinkedList</tt> implementation.
     *
     * <p>Each <tt>ArrayList</tt> instance has a <i>capacity</i>.  The capacity is
     * the size of the array used to store the elements in the list.  It is always
     * at least as large as the list size.  As elements are added to an ArrayList,
     * its capacity grows automatically.  The details of the growth policy are not
     * specified beyond the fact that adding an element has constant amortized
     * time cost.
     *
     * <p>An application can increase the capacity of an <tt>ArrayList</tt> instance
     * before adding a large number of elements using the <tt>ensureCapacity</tt>
     * operation.  This may reduce the amount of incremental reallocation.
     *
     * <p><strong>Note that this implementation is not synchronized.</strong>
     * If multiple threads access an <tt>ArrayList</tt> instance concurrently,
     * and at least one of the threads modifies the list structurally, it
     * <i>must</i> be synchronized externally.  (A structural modification is
     * any operation that adds or deletes one or more elements, or explicitly
     * resizes the backing array; merely setting the value of an element is not
     * a structural modification.)  This is typically accomplished by
     * synchronizing on some object that naturally encapsulates the list.
     *
     * If no such object exists, the list should be "wrapped" using the
     * {@link Collections#synchronizedList Collections.synchronizedList}
     * method.  This is best done at creation time, to prevent accidental
     * unsynchronized access to the list:<pre>
     *   List list = Collections.synchronizedList(new ArrayList(...));</pre>
     *
     * <p><a name="fail-fast">
     * The iterators returned by this class's {@link #iterator() iterator} and
     * {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:</a>
     * if the list is structurally modified at any time after the iterator is
     * created, in any way except through the iterator's own
     * {@link ListIterator#remove() remove} or
     * {@link ListIterator#add(Object) add} methods, the iterator will throw a
     * {@link ConcurrentModificationException}.  Thus, in the face of
     * concurrent modification, the iterator fails quickly and cleanly, rather
     * than risking arbitrary, non-deterministic behavior at an undetermined
     * time in the future.
     *
     * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
     * as it is, generally speaking, impossible to make any hard guarantees in the
     * presence of unsynchronized concurrent modification.  Fail-fast iterators
     * throw {@code ConcurrentModificationException} on a best-effort basis.
     * Therefore, it would be wrong to write a program that depended on this
     * exception for its correctness:  <i>the fail-fast behavior of iterators
     * should be used only to detect bugs.</i>
     *
     * <p>This class is a member of the
     * <a href="{@docRoot}/../technotes/guides/collections/index.html">
     * Java Collections Framework</a>.
     *
     * @author  Josh Bloch
     * @author  Neal Gafter
     * @see     Collection
     * @see     List
     * @see     LinkedList
     * @see     Vector
     * @since   1.2
     */
    
    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
    //...
    }

    /** ... */의 형태를 띄고 있으며, 메서드 혹은 클래스 선언 위에 작성할 때 JavaDoc이 자동으로 문서화를 해주는 특별한 주석이다. 대다수의 IDE에서 doc 주석을 인지하고 설명을 툴팁 형태로 표기해주는 기능을 가지고 있다.

    예제 2 : Eclipse에서 JavaDoc 주석을 표기하는 기능

    https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c34fd058-bdee-459c-97b6-cfc9534d93f3/Untitled.png

    참고하기 : How to Write Doc Comments for the Javadoc Tool

    많은 개발자들이 이 API 문서를 참고하기 때문에, API 유저가 혼란에 빠지지 않도록 문서를 잘 작성하는 것이 중요하다.

    API 문서화 규칙

    1. 공개된 모든 클래스, 인터페이스, 메서드, 필드 선언에doc comment를 달아야 한다.
    2. 다른 사람이 유지보수 할 때를 대비해서 공개되지 않은 요소들에도 doc comment를 다는 것이 좋다.
    3. 기본 생성자엔 doc comment를 달 수가 없으니, 공개된 클래스는 기본 생성자를 사용하면 안된다.
    4. 메서드용 문서화 주석에는 해당 메서드와 클라이언트의 규약을 명료하게 기술하여야 한다.
      • 메서드를 호출하기 위한 전제조건(precondition)
      • 전제 조건으로 인해 발생할 수 있는 비검사 예외는 @throws 태그로 암시적으로 기술하며, @param 태그로 전제조건에 의해 영향받는 매개변수에 기술할 수도 있다.
      • 메서드가 성공적으로 수행된 후 만족해야 하는 사후조건(postcondition)
      • 부작용(side effect)
      • 백그라운드 스레드를 시작시키는 메서드는 API User가 알 수 있도록 그 사실을 기술하는 등의 예시가 있다.
    5. 해당 메서드가 어떻게(How) 동작하는 지가 아닌, 무엇을(What) 하는지 기술하여야 한다.
    6. 모든 매개변수에 @param, void가 아니라면 @return, 발생할 가능성이 있는 모든 예외에 @throws 태그를 붙인다.
       public class DocExamples<E> {
           // 메서드 주석 (333-334쪽)
           /**
            * Returns the element at the specified position in this list.
            *
            * <p>This method is <i>not</i> guaranteed to run in constant
            * time. In some implementations it may run in time proportional
            * to the element position.
            *
            * @param  index index of element to return; must be
            *         non-negative and less than the size of this list
            * @return the element at the specified position in this list
            * @throws IndexOutOfBoundsException if the index is out of range
            *         ({@code index < 0 || index >= this.size()})
            */
       }
    7. 예제 3 : DocComment 예시
    8. doc comment엔 HTML 태그를 사용할 수 있다.
    9. @code 태그는 감싼 주석을 JavaDoc 내 코드블럭을 생성하여 코드로 출력하며 다른 HTML 태그를 무시한다.
    10. 여러 줄의 코드 주석을 작성하고 싶다면태그로 감싸면 된다. 예를 들면{@code (여러줄의 코드)}와 같이 작성하면 된다.
    11. 클래스를 상속용으로 설계할 때 자기사용 패턴(self-use pattern)에 대해서 알려주어야 한다.@implSpec 태그를 이용하며, 해당 메서드와 하위 클래스 사이의 계약(contract)을 설명하여 하위 클래스가 이 메서드를 상속하거나 super 키워드로 호출할 때 어떻게 동작하는지 명확히 해야한다.
       /**
            * Returns true if this collection is empty.
            *
            * @implSpec This implementation returns {@code this.size() == 0}.
            *
            * @return true if this collection is empty
            */
       public boolean isEmpty() {
               return this.size() == 0;
       }
      this.size()는 상속 후 메서드 재정의시 문제가 될 소지가 충분히 있다.
    12. 주의할 점은 JavaDoc 명령줄이 -tag "implSpec:a:Implemetntation Requeirements:" 스위치를 켜지 않으면 @implSpec을 무시해버린다.
    13. 예제 4 : @implSpec으로 자기사용 패턴 기술하기
    14. 해당 메서드를 올바르게 재정의하는 방법을 문서에 기술하지 않으면, API User의 코드에 끔찍한 버그를 유발할 수 있다.(ITEM 15 참조)
    15. <, >, & 등의 HTML 메타문자를 포함시키려면 @literal을 사용하자.
       /**
           * A geometric series converges if {@literal |r| < 1}
           */
    16. 예제 5 : HTML 메타문자 표기하기
    17. 각 doc comment의 첫 문장은 해당 요소의 요약으로 간주된다.@literal로 감싸면 마침표를 포함한 요약 설명을 마칠 수 있다.예제 6 : 올바른 요약설명 예시return the number가 아닌 returns the number로 3인칭 취급함을 주의하자(영어에 능숙한 사람에겐 당연한 얘기일 수 있다).예제 7
    18. Instant—An instantaneous point on the time-line. Math.PI—The double value that is closer than any other to pi, the ratio of the circumference of a circle to its diameter.
    19. 클래스, 인터페이스의 요약 설명은 '인스터스'를 설명해야 하고, 필드는 필드 자신을 설명해야 한다.
    20. ArrayList(int initialCapacity) — Constructs an empty list with the specified initial capacity. Collection.size() — Returns the number of elements in this collection
    21. 영어로 요약설명을 작성할 경우 2인칭 문장이 아닌 3인칭으로 작성하고, 동작을 설명하는 주어가 없는 동사구여야 한다.
    22. 첫 문장을 공들여 써야하며, 대상의 기능을 고유하게 기술해야 한다. 또한 처음 발견되는 {<.><space or tab or 줄바꿈><다음문장 시작(소문자가 아닌 문자로 시작하는 문장)>}을 기준으로 첫 문장을 구분하기 때문에 사용에 유의해야 한다. "김철수와 Mr. 코난의 asdf"이란 문장은, "김철수와 Mr."까지만 요약설명으로 간주되는 문제가 있다.
    23. 색인으로 감쌀 용어는 @index 태그를 사용한다.(Java 9~)
      This method complies with the {@index IEEE 754} standard.
    24. 예제 8 : @index 태그 사용하기
    25. 제네릭 타입, 제네릭 메서드를 문서화할 때는 모든 타입 매개변수에 주석을 달아야 한다.
      /**
       * An object that maps keys to values. A map cannot contain
       * duplicate keys; each key can map to at most one value.
       *
       * (Remainder omitted)
       *
       * @param <K> the type of keys maintained by this map
       * @param <V> the type of mapped values
       */
      public interface Map<K, V> { ... }
    26. 예제 9 : 제네릭 타입/메서드의 매개변수 주석
    27. 열거 타입은 상수들에도 주석을 달아야 한다.예제 10 : Enum 타입의 doc comment 작성하기
    28. /** * An instrument section of a symphony orchestra. */ public enum OrchestraSection { /** Woodwinds, such as flute, clarinet, and oboe. */ WOODWIND, /** Brass instruments, such as french horn and trumpet. */ BRASS, /** Percussion instruments, such as timpani and cymbals. */ PERCUSSION, /** Stringed instruments, such as violin and cello. */ STRING; }
    29. public 메서드도 마찬가지로 주석을 달아야 한다.
    30. annotation 타입을 문서화 할 때에는, 멤버들과 타입 자기자신 모두에 주석을 달아야 한다.
              /**
           * Indicates that the annotated method is a test method that
           * must throw the designated exception to pass.
           */
          @Retention(RetentionPolicy.RUNTIME)
          @Target(ElementType.METHOD)
          public @interface ExceptionTest {
              /**
               * The exception that the annotated test method must throw
               * in order to pass. (The test is permitted to throw any
               * subtype of the type described by this class object.)
               */
              Class<? extends Throwable> value();
          }
      타입은 타입을 다는 것의 의미를 동사구로, 필드는 명사구로 설명한다.
    31. 예제 11 : annotation doc comment 작성하기
    32. 패키지를 설명하는 문서화 주석은 package-info.java에 작성한다.
    33. 모듈 설명을 기술하는 module-info.java도 마찬가지다.
    34. thread safe와 Serializable을 누락하지 말아야 한다.
    35. 위 두 가지는 반드시 기술되어야 하며, thread safe 여부와 관계없이 무조건 기술하여야 한다. 직렬화 가능한 클래스는 직렬화 형태에 대해서 기술하여야 한다.
    36. @inheridDoc 태그는 상위 타입의 문서화 주석 일부를 상속하는데 사용한다.
    37. 유지보수가 수월한 측면은 있지만, 제약이 조금 있고 까다롭다. 참고하기 : Oracle 공식 문서
    38. 전체 아키텍쳐의 설명이 필요하면 문서의 링크를 제공하자.
    39. 각각의 요소에 대해서만 doc comment를 작성할 수 있을 뿐 전체적인 아키텍쳐나 상호작용하는 API의 복잡한 상황을 기술하기는 어렵다. 별도의 문서로 분리하고 링크를 제공하자.
    40. JavaDoc 생성시 -Xdoclint 플래그는 문법 오류를 검사한다.checkstyle 같은 IDE 플러그인도 대안이 될 수 있으며, W3C-validator와 같은 HTML 유효성 검사 도구도 사용할 수 있다.
    41. 책 전반을 관통하는 컴파일 타임에 에러잡기(?)와 유사하다.
    42. -html5 플래그는 HTML 5로 문서를 생성한다.
    43. 작성을 마쳤다면 생성한 웹페이지를 읽어보자.

    댓글