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

대연.

·

2021. 4. 15. 00:03

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. 작성을 마쳤다면 생성한 웹페이지를 읽어보자.

0개의 댓글