본문 바로가기
Study/클린코드

[클린코드] 8장. 경계

by Nahwasa 2023. 1. 2.

스터디 메인 페이지

 

- ☆ 표시가 붙은 부분은 스터디 중 나온 얘기 혹은 제 개인적인 생각이나 제가 이해한 방식을 적어놓은 것으로, 책에서 말하고자 하는 바와 다를 수 있습니다.

- 모든 이미지의 출처는 클린 코드(로버트 C. 마틴 저) 책 입니다.

 


 

8장 경계

 외부 코드 사용하기

  • 패키지 제공자나 프레임워크 제공자는 적용성을 최대한 넓히려 애쓴다.
  • 사용자는 자신의 요구에 집중하는 인터페이스를 바란다.
  • 그러다보니 외부 코드를 그대로 사용하면 필요하지 않은 기능까지 제공하게 된다.
  • 이러한 경계 인터페이스를 이용할 때는 이를 이용하는 클래스나 클래스 계열 밖으로 노출되지 않도록 주의한다.
  • ☆ 약간 이 경우와 주제가 다른 내용이긴 하지만, 자바에서도 필요하지 않은 기능까지 제공하는 경우가 있다.

e.g. 자바의 Stack은 일반적으로 잘못된 코드로 알려져있다. 스택이라는 자료구조는 원래 한쪽 방향으로 넣고, 한쪽방향으로 뺄 수 있어야 한다. 즉 LIFO (마지막에 들어간게 먼저 나온다)가 지켜져야 한다. 하지만 자바의 Stack은 코드 재사용을 위해 Vector를 상속했고, 이에 따라 문제가 발생한다. 이하의 경우 마지막에 들어간 '3'이 pop으로 나오길 원하지만, add은 Vector의 함수를 사용한 것으로 스택 자료구조의 기본 조건을 충족하지 못하고 2가 출력되어 버린다.

Stack<Integer> stack = new Stack<>();
stack.push(0);
stack.push(1);
stack.push(2);
stack.add(0, 3);
System.out.println(stack.pop());

 

이건 Stack이 아래와 같이 Vector를 상속해 구현했기 때문이다.

 

  이 책의 주제와는 약간 다르지만, 아무튼 코드 재사용을 위해 상속(inheritance)을 사용하면 위와 같이 문제가 발생할 수 있다. 코드 재사용은 합성(composition)을 사용하는 것이 더 좋은 방법이다. 아래처럼 새로 Stack을 짜면 문제가 해결된다.

public class Stack<E> {
    private Vector<E> vector = new Vector<>();

    public E push(E item) {
        vector.addElement(item);
        return item;
    }

    public E top() {
        if (vector.isEmpty())
            throw new EmptyStackException();
        return vector.elementAt(vector.size() - 1);
    }

    public E pop() {
        E obj = top();
        vector.removeElementAt(vector.size() - 1);
        return obj;
    }
}

 

학습 테스트

  • 우리쪽 코드를 작성해 외부 코드를 호출하는 대신 먼저 간단한 테스트 케이스를 작성해 외부 코드를 익히는게 좋다.
  • 이 경우 학습 자체 이상의 의미를 가지는데, 새 버전이 우리 코드와 호환되지 않으면 학습 테스트가 이 사실을 곧바로 밝혀낼 수 있다.

 

ADAPTER 패턴으로 API 사용을 캡슐화해 API가 바뀔 때 수정할 코드를 한 곳으로 모아두면 편하다.

  • ☆ 이하 해드퍼스트 디자인 패턴책의 캡쳐이다. 어댑터 패턴을 어떻게 사용하는지 확인할 수 있다.

 

깨끗한 경계

  • 경계에 위치하는 코드는 깔끔히 분리한다.
  • 기대치를 정의하는 테스트 케이스를 작성한다.
  • 외부 패키지를 호출하는 코드를 가능한 줄여 경계를 관리하자. (새로운 클래스로 경계를 감싸거나 ADAPTER 패턴을 사용해 우리가 원하는 인터페이스를 패키지가 제공하는 인터페이스로 변환)

댓글