[클린코드] 6장 : 객체와 자료 구조

목록으로 돌아가기

비공개로 정의한 변수를 왜 조회 및 설정 함수로 공개하는 지에 대한 의문 및 이야기

  • 자료 추상화
    • 구현을 감추기 위해서는 추상화를 해야한다.
    • 비공개로 정의한 변수의 조회 및 설정 함수 등을 공개한다면, 이는 구현을 외부로 노출하는 것이다.
    • 추상 인터페이스를 제공하여 사용자에게 구현을 노출하지 않고 자료를 조작할 수 있어야 한다. 이것이 클레스다.
    • ex ) 구현을 외부에 노출 / 구현을 숨김

        public interface Vehicle {
          double getFuelTankCapacityInGallons();
          double getGallonsOfGasoline();
        }
      
        public interface Vehicle {
          double getPercentFuelRemaining();
        }
      



  • 자료 / 객체 비대칭
    • 자료와 자료 구조는 근본적으로 상반된다.
    • 절차적인 코드는 기존 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽다.
    • 객체 지향 코드는 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉽다.
    • 반면, 절차적인 코드는 새 자료구조를 추가하기 어렵고, 객체 지향 코드는 새 함수를 추가하기 어렵다.
    • 이 둘은 상반되기 때문에, 새 자료 타입이 필요하다면 클래스와 객체 지향 기법을, 새 함수가 필요하다면 자료 구조와 절차적인 코드를 사용해야한다.

  • 디미터 법칙
    • 모듈은 자신이 조작하는 객체의 속사정을 몰라야한다는 법칙
    • 기차 충돌 - 객체가 한 줄로 이어진 기차처럼 보이는 코드 (디미터 법칙 어김)

        **final** String outputDir **=** ctxt**.**getOptions**().**getScratchDir**().**getAbsolutePath**();**
      
    • 잡종 구조 - 객체와 자료 구조가 섞인 잡종 구조 (디미터 법칙 어김) getter, setter, 공개 변수 등이 섞인 구조
    • 구조체 감추기 - 객체를 함수 내에서 줄줄이 호출하는 형식

        Option options = foo.getOption();
        File files = options.getFile();
      



  • 자료 전달 객체
    • DTO(Data Transfer Object) - 공개 변수만 있고 함수가 없는 클래스
    • Bean 구조 - 비공개 변수와 getter, setter 함수가 있는 클래스
    • 활성 레코드 - 공개 및 비공개 변수와 getter, setter, 그리고 탐색 함수가 있는 클래스

본문에 대한 예시

자료 추상화에 대한 예시를 가져와 보았다.

기존 코드는 모델 자료형에 대해 다음과 같이 정의하였다.

export interface BookListModel {
  No: number
  일련번호: string
  도서명: string
  저자: string
  출판사: string
  대출가능여부: boolean
  대출인: string
}

이는 책에 따르면 외부에 구현을 노출한 예시가 된다. 구현을 숨기기 위해서는 추상화를 진행해야 하는데, 바꾸자면 다음과 같이 바꿀 수 있다.

export interface BookListModel {
  whoBorrow : string
	bookId : string
	writerAndPublisher : string
}

최대한 기존 변수들을 추상화 하였다. 이러면 구현에 대한 것을 외부에 숨길 수는 있으나, 사실 구체성이 떨어지기 때문에 properity로 사용하기에 적합한가는 많이 어렵다. 또한 props보다 직관성이 떨어지기 때문에 추상화를 하기 위해서는 작명에 보다 신중해야 할 것이다.

다음은 reactjs-popup의 구현 일부이다.

export interface PopupProps {
    trigger?: JSX.Element | ((isOpen: boolean) => JSX.Element);
    open?: boolean;
    disabled?: boolean;
    nested?: boolean;
    defaultOpen?: boolean;
    on?: EventType | EventType[];
    children: React.ReactNode;
    position?: PopupPosition | PopupPosition[];
    offsetX?: number;
    offsetY?: number;
    arrow?: boolean;
    modal?: boolean;
    lockScroll?: boolean;
    closeOnDocumentClick?: boolean;
    closeOnEscape?: boolean;
    repositionOnResize?: boolean;
    mouseEnterDelay?: number;
    mouseLeaveDelay?: number;
    onOpen?: (event?: React.SyntheticEvent) => void;
    onClose?: (event?: React.SyntheticEvent | KeyboardEvent | TouchEvent | MouseEvent) => void;
    contentStyle?: React.CSSProperties;
    overlayStyle?: React.CSSProperties;
    arrowStyle?: React.CSSProperties;
    className?: string;
    keepTooltipInside?: boolean | string;
}

이 역시 구현을 추상화 하였다고 보기에는 다소 어렵다. 만약 이 코드가 추상화를 하였다고 하더라도 그 정도에 대해서는 코드를 작성하는 이에게 전적으로 맡겨야 할 일이다. 따라서, 추상화에 대한 정도는 사실 모호하기 때문에 코드를 작성하는 이에게 그 정도를 맡겨야 하지만, 구현을 외부에 숨기기 위해서 아예 날 것의 구현을 표현하는 것은 삼가야 한다.

다음으로, 본문에 대한 예시를 찾기 전에 생각했던 바로 기차 충돌 및 getter, setter를 구조에 섞은 것이 많을 것이라고 예상하였다. 하지만 다행히 이에 대한 코드는 작성하지 않아, 클린 코드를 어느 정도는 지키고 있다는 안도감이 들었다.

author-profile
Written by 상 한규

댓글