개인용 메소드는 언제 개인 데이터에 액세스하기 위해 공개 경로를 가져야합니까?


11

개인용 메소드는 언제 개인 데이터에 액세스하기 위해 공개 경로를 가져야합니까? 예를 들어,이 불변의 '멀티 플라이어'클래스를 가지고 있다면 (나는 조금 생각했다.)

class Multiplier {
public:
    Multiplier(int a, int b) : a(a), b(b) { }
    int getA() const { return a; }
    int getB() const { return b; }
    int getProduct() const { /* ??? */ }
private:
    int a, b;
};

구현할 수있는 두 가지 방법이 있습니다 getProduct.

    int getProduct() const { return a * b; }

또는

    int getProduct() const { return getA() * getB(); }

여기에 의도의 값을 사용하기 때문에 a하는, 즉를 얻을 a 사용하여 getA()구현하는 것은 getProduct()나에게 청소기를 보인다. a수정하지 않으면 사용 하지 않는 것이 좋습니다. 내 관심사는 내가 이런 식으로 작성된 코드를 자주 보지 못한다 a * b는 것 getA() * getB()입니다. 내 경험에 따르면 보다 일반적인 구현 입니다.

개인 메소드가 직접 무언가에 액세스 할 수있을 때 공개 방식을 사용해야합니까?

답변:


7

그것은의 실제 의미를 따라 a, b그리고 getProduct.

게터의 목적은 객체의 인터페이스를 동일하게 유지하면서 실제 구현을 변경할 수 있도록하는 것입니다. 예를 들어, 하루 getAreturn a + 1;되면 변경 사항이 getter로 현지화됩니다.

실제 시나리오 사례는 때때로 getter와 관련된 생성자를 통해 할당 된 상수 지원 필드보다 더 복잡합니다. 예를 들어, 필드의 값은 코드의 원래 버전의 데이터베이스에서 계산되거나로드 될 수 있습니다. 다음 버전에서는 성능을 최적화하기 위해 캐싱이 추가 될 수 있습니다. 경우 getProduct계산 된 버전을 계속 사용, 그것은 캐싱의 혜택을하지 않습니다 (또는 메인테이너는 두 번 같은 변화를 할 것입니다).

가에 대한 완벽한 이해하게되면 getProduct사용 ab직접 그들을 사용합니다. 그렇지 않으면 나중에 유지 관리 문제를 방지하기 위해 게터를 사용하십시오.

getter를 사용하는 예 :

class Product {
public:
    Product(ProductId id) : {
        price = Money.fromCents(
            data.findProductById(id).price,
            environment.currentCurrency
        )
    }

    Money getPrice() {
        return price;
    }

    Money getPriceWithRebate() {
        return getPrice().applyRebate(rebate); // ← Using a getter instead of a field.
    }
private:
    Money price;
}

현재 getter에 비즈니스 로직이 포함되어 있지 않지만 오브젝트를 초기화 할 때 데이터베이스 작업을 수행하지 않기 위해 생성자의 로직이 getter로 마이그레이션되는 것을 제외하지 않습니다.

class Product {
public:
    Product(ProductId id) : id(id) { }

    Money getPrice() {
        return Money.fromCents(
            data.findProductById(id).price,
            environment.currentCurrency
        )
    }

    Money getPriceWithRebate() {
        return getPrice().applyRebate(rebate);
    }
private:
    const ProductId id;
}

나중에 캐싱이 추가 될 수 있습니다 (C #에서는 Lazy<T>코드를 짧고 쉽게 만들 수 있습니다 .C ++에 해당하는 것이 있는지 모르겠습니다).

class Product {
public:
    Product(ProductId id) : id(id) { }

    Money getPrice() {
        if (priceCache == NULL) {
            priceCache = Money.fromCents(
                data.findProductById(id).price,
                environment.currentCurrency
            )

        return priceCache;
    }

    Money getPriceWithRebate() {
        return getPrice().applyRebate(rebate);
    }
private:
    const ProductId id;
    Money priceCache;
}

두 변경 사항은 모두 getter와 backing 필드에 초점을 맞추 었으며 나머지 코드는 영향을받지 않습니다. 대신에 getter 대신 필드를 사용한 getPriceWithRebate경우 변경 사항도 반영해야합니다.

개인 필드를 사용하는 예 :

class Product {
public:
    Product(ProductId id) : id(id) { }
    ProductId getId() const { return id; }
    Money getPrice() {
        return Money.fromCents(
            data.findProductById(id).price, // ← Accessing `id` directly.
            environment.currentCurrency
        )
    }
private:
    const ProductId id;
}

게터는 간단합니다. readonly미래에는 변하지 않을 것으로 예상되는 상수 (C #과 유사 ) 필드를 직접 표현한 것입니다. 따라서 간단하게 유지하고 현장에 직접 액세스하십시오.

또 다른 이점은 getId이전 코드와 같이 외부에서 사용되지 않는 것으로 보이는 경우 향후에 제거 될 수 있다는 것입니다.


나는 당신에게 줄 수없는 한 개인 필드를 사용하도록 예를 들어 당신이 선언이 주로하기 때문에, 하나 이럴되지 않기 때문에 const:를 나는 컴파일러가 인라인된다는 것을 의미합니다 가정 getId어쨌든 전화를하고 당신이 어느 한 방향으로 변경할 수 있습니다. (그렇지 않으면 나는 getter 사용해야하는 이유 전적으로 동의한다 .) 그리고 속성 구문을 제공하는 언어에서, 지원 필드보다는 속성을 직접 사용하지 않는 이유는 훨씬 적다.
Mark Hurd

1

일반적으로 변수를 직접 사용합니다. 클래스 구현을 변경할 때 모든 멤버를 변경해야합니다. 변수를 직접 사용하지 않으면 변수에 의존하는 코드를 올바르게 분리하기가 더 어려워지고 멤버를 읽기가 더 어려워집니다.

게터가 실제 논리를 구현하는 경우에는 물론 다릅니다.이 경우 논리를 사용해야하는지 여부에 따라 다릅니다.


1

나는 다른 방법으로가 아니라 DRY 를 준수하기 위해 공개 메소드를 사용하는 것이 바람직하다고 말하고 싶습니다 .

귀하의 경우 접근 자에 대한 간단한 지원 필드가 있지만 지연 로딩 코드와 같은 특정 논리를 가질 수 있으며 해당 변수를 처음 사용하기 전에 실행해야합니다. 따라서 필드를 직접 참조하는 대신 접근자를 호출하려고합니다. 이 경우에는 이것을 갖지 않아도 단일 규칙을 따르는 것이 좋습니다. 이렇게하면 로직을 변경 한 경우 한 곳에서만 변경하면됩니다.


0

이 작은 수업은 단순성이 이깁니다. 그냥 a * b를 사용합니다.

훨씬 더 복잡한 것을 위해, "최소한"인터페이스를 전체 공개 API의 다른 모든 함수와 명확하게 분리하려면 getA () * getB ()를 사용하는 것이 좋습니다. 훌륭한 예제는 C ++의 std :: string입니다. 103 개의 멤버 함수가 있지만 그중 32 개만 실제로 프라이빗 멤버에 액세스 해야 합니다. 복잡한 클래스가있는 경우 모든 "비 핵심"함수가 "코어 API"를 일관되게 적용하면 구현을 테스트, 디버그 및 리팩토링하기가 훨씬 쉬워 질 수 있습니다.


1
복잡한 수업이 있다면 반창고가 아니라 고쳐야합니다.
DeadMG

동의했다. 아마도 20-30 개의 함수 만있는 예제를 선택했을 것입니다.
Ixrec

1
"103 기능"은 약간의 청어입니다. 인터페이스가 복잡하다면 오버로드 된 메소드를 한 번 계산해야합니다.
Avner Shahar-Kashtan

나는 완전히 동의하지 않습니다. 서로 다른 과부하는 서로 다른 의미와 인터페이스를 가질 수 있습니다.
DeadMG

이 "작은"예에서도 getA() * getB()중장기 적으로 더 좋습니다.
Mark Hurd
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.