Today
-
Yesterday
-
Total
-
  • 엑세스 함수(getter/setter)를 올바로 사용하기
    Programmer/Programming 2017. 2. 13. 13:05

    객체의 속성을 읽고 쓰는 일관된 표현 방식을 제공하라. 엑세스 함수를 추천한다.[각주:1]

     

    엑세스 함수를 사용하면 의존성 깨짐의 걱정 없이 구현을 바꿀 수 있다.

    Null을 할당하는 것을 허용하지 않거나 객체의 속성을 외부에서 바꿀 수 없는[각주:2] 규칙이 더해질 수 있다.
    메모이제이션(memoization)[각주:3]과 같은 기능을 추가할 수도 있다.

    자료형과 알고리듬이 변경되기도 한다.

     

    엑세스 함수를 사용했으면, 이것을 이용하는 의존 모듈의 수정 없이 규칙과 기능을 추가할 수 있다.

    추상적인 자료형을 사용한 엑세스 함수는 객체의 자료형 변경을 용이하게 한다.

    위 이유는 명확하니 굳이 설명할 필요가 없다.

     

    모호한 상황에서도 엑세스 함수 강제가 정당한지 생각해보겠다.

    멤버 변수의 자료형, 알고리듬의 변경이 없을 것이 확실하면, 공개해도 괜찮을까?
    아래 소개할 일관된 접근법을 망치기 때문에 이런 상황에서조차 피하는 것이 좋다.

     

    일관된 방법을 제공하라.

    엑세스 함수와 공개 변수를 같이 쓰면 일관성이 깨진다.[각주:4]
    일관성이 없으면 접근 방식(변수에 직접? 엑세스 함수 사용?)에 주의력을 소진해야 한다.
    일관된 접근 방식을 제공하면 그 방식 하나만 기억하면 명확하다.

    h = t.getHours();   // getter
    m = t.minutes;      // get a public field
    t.setSeconds(sec);  // setter
    t.minutes = min;    // set a public field

    시간은 엑세스 함수로 가져와야 하고, 분은 직접 읽어와야 한다.
    일관되지 않는 모든 것은 더럽다.

     

    같은 논리로 모든 멤버 변수를 공개할 수 있다면 엑세스 함수를 제공하지 않아도 괜찮다.

    (C++에서 언제 어떻게 struct를 사용하는가?)

     

    결론적으로 객체의 속성을 읽고 쓸 때는 언제나 엑세스 함수를 사용하라.

     

    getter/setter에 대한 오해

    setter/getter 쓴다고 정보은닉이 되는 게 아니고 사실은 정보은닉을 망치게 됩니다.
    저는 getter/setter 많이 쓴 것은 나쁜 냄새(bad smell)로 봅니다.
    getter setter는 객체지향과 거리가 멀며 써야 할 곳은 한정되어 있다

     

    문맥과 떨어져서 해석하면,
    "엑세스 함수보다는 공개 변수를 사용하는 것이 좋다"
    라고 오해할 수 있다.
    이는 잘못된 해석이다.

     

    의존성의 문제를 지적하는 여러 글이 있으니 생략하고, 다른 관점을 이야기하겠다.

    getter/setter의 무분별한 노출은 객체의 구현과 상관없이 객체의 상태를 직접 변경할 수 여지를 줄 수 있다.

    공개 변수 사용은 당연히 좋지 않고, getter/setter도 가능하면 적게 공개하여 이런 문제를 방지해야 한다.

     

    예를 들어 설명하겠다.

    밥을 먹으면 배부른 상태가 되어야 한다.

    foo.eat();
    assert(foo.isFull() == true);

    반면, 객체 밖에서 배부른 상태를 직접 변경하는 것은 바람직하지 않다.

    foo.setFull(true);   // 바람직하지 않다.
    foo._isFull = true;  // 공개 변수 역시 마찮가지로 좋지 않다.

     

    엑세스 함수를 남발하지 말라.

    엑세스 함수를 많이 노출해야 한다면 설계에 문제가 있다고 봐야 한다.
    이것이 나쁜 냄새다.
    가능하면 초기값이나 구현 독립적인 부분을 제외하고는 직접적인 상태 변경이 없어야 한다.
    외부에서 엑세스 함수를 최소한으로 사용하도록 설계한다.

     

    프러퍼티나 엑세스 함수 자동 생성 모듈을 사용하자.

    엑세스 함수가 비난받는 이유 중에 하나는 보일러플레이트 코드(boilerplate code)의 문제이다.
    데이터 접근에 별다른 동작이 필요하지 않는 경우가 대부분이다.[각주:5]
    엑세스 함수라는 규칙에 맞추려면 이러한 반복 코드를 꽤 만들어야 한다.
    이는 매우 귀찮은 작업이다.

    사실 그럴 필요는 없다.
    프러퍼티를 사용하거나, 엑세스 함수를 자동으로 생성하는 모듈[각주:6] 을 사용하면 된다.

     

    C#의 프러퍼티:

    public class Date {
        private int year = 2017;
        public int Year { get; set; }    // 기본 getter/setter
    
        private int month = 2;
        public int Month
        {
            get { return month; }
            set    // 단순하지 않은 getter/setter
            {
                if ((value > 0) && (value < 13))
                {
                    month = value;
                }
            }
        }
    }

    Java의 Lombok 사용하기:

    public class Date {
        @Getter @Setter
        private int year = 2017;           // getter/setter를 자동으로 생성
    
        @Getter private int month = 2;     // getter를 자동으로 생성
        public int setMonth(int value) {   // 단순하지 않은 setter를 별도 구현
            if ((value > 0) && (value < 13)) {
                month = value;
            }
        }
    }

     

     

    2018/01/17 - [Practice/Python, Perl] - 파이썬 프러퍼티 사용하여 일관되게 속성에 접근하라

     

    1. 프러퍼티를 제공하는 언어(C#, Python, ...)는 예외이다. 가능하면 이 언어에서는 프러퍼티를 사용한다.핵심은 섞어쓰지 않는데 있다. [본문으로]
    2. 엑세스 함수에서 사본을 생성하고 이를 반환하는 식으로 구현 [본문으로]
    3. 값을 캐싱하여 성능을 높이는 기법 [본문으로]
    4. 일관성을 유지하는 것은 매우 중요하다. 코딩 규약이나 UI 디자인 가이드 등으로 이것을 강조하는데는 이유가 있다. [본문으로]
    5. setter는 단순히 값을 복사하고, getter는 그냥 반환하면 된다. [본문으로]
    6. 자바에서는 Lombok이 유명하다. [본문으로]

     

    댓글

Designed by Tistory.