정적 멤버를 제외한 유틸리티 클래스는 C ++의 안티 패턴입니까?


39

클래스와 관련이없는 함수를 어디에 두어야하는지에 대한 질문 은 C ++에서 유틸리티 함수를 클래스에 결합하는 것이 합리적인지 또는 네임 스페이스에서 자유 함수로 존재하는지 여부에 대한 논쟁을 불러 일으켰습니다.

나는 후자의 옵션이 존재하지 않는 C # 배경에서 왔으므로 자연스럽게 내가 작성한 작은 C ++ 코드에서 정적 클래스를 사용하는 경향이 있습니다. 그러나 몇 가지 의견뿐만 아니라 그 질문에 대해 가장 많이 투표 된 답변은 정적 클래스가 반 패턴임을 암시하는 경우에도 무료 기능을 선호한다고 말합니다. 왜 C ++에서 그렇게됩니까? 적어도 표면에서 클래스의 정적 메소드는 네임 스페이스의 무료 함수와 구별 할 수없는 것처럼 보입니다. 왜 후자를 선호 하는가?

유틸리티 함수 모음에 개인 정적 필드에 저장할 수있는 캐시와 같은 일부 공유 데이터가 필요한 경우 상황이 다릅니 까?


"기능적 분해"반 패턴처럼 들립니다.
user281377

7
짧은 대답 : 이러한 기능을 래핑하기 위해 클래스가 필요하지 않습니다. 무료 함수는 pseudo-OO 구문으로 처리하는 것보다 작업에 훨씬 더 적합합니다. 이는 "순전히 OO"언어로만 필요한 해결 방법입니다.
Chris는 Reinstate Monica가

답변:


39

클래스와 네임 스페이스의 의도를 비교해야한다고 대답합니다. Wikipedia에 따르면 :

수업

객체 지향 프로그래밍에서 클래스는 클래스 인스턴스, 클래스 객체, 인스턴스 객체 또는 단순히 객체라고하는 자체 인스턴스를 만들기위한 청사진으로 사용되는 구조입니다. 클래스는 이러한 클래스 인스턴스가 상태 및 동작을 가질 수 있도록하는 구성 멤버를 정의합니다. 데이터 필드 멤버 (멤버 변수 또는 인스턴스 변수)를 사용하면 클래스 객체가 상태를 유지할 수 있습니다. 다른 종류의 멤버, 특히 메서드는 클래스 객체의 동작을 가능하게합니다. 클래스 인스턴스는 연결된 클래스 유형입니다.

네임 스페이스

일반적으로 네임 스페이스는 보유하고있는 식별자 (이름 또는 기술 용어 또는 단어)에 대한 컨텍스트를 제공하고 다른 네임 스페이스에있는 동종 식별자의 명확성을 허용하는 컨테이너입니다.

이제 함수를 클래스 (정적) 또는 네임 스페이스에 배치하여 달성하려는 목표는 무엇입니까? 네임 스페이스의 정의가 의도를 더 잘 설명한다고 생각합니다. 원하는 것은 함수의 컨테이너입니다. 클래스 정의에 설명 된 기능이 필요하지 않습니다. 클래스 정의의 첫 단어는 " 객체 지향 프로그래밍에서 "이지만 함수 모음에 대한 객체 지향은 없습니다.

아마도 기술적 인 이유가있을 수도 있지만 누군가 Java에서 왔으며 C ++ 인 다중 패러다임 언어를 둘러 보려고 할 때 가장 확실한 대답은 다음과 같습니다. 우리는 이것을 달성하기 위해 OO가 필요하지 않기 때문입니다.


1
+1 "우리는 이것을 달성하기 위해 OO가 필요하지 않습니다"
Ixrec

나는 이것을 OO의 팬이 아니라고 동의하지만 All-static 클래스를 사용하는 것이 유일한 해결책, 예를 들어 네임 스페이스 바꾸기와 같은 몇 가지 상황이 있다고 생각합니다. 수업.
Wael Boutglay

26

나는 그것을 반 패턴이라고 부를 때 매우 조심해야합니다. 네임 스페이스는 일반적으로 선호되지만 네임 스페이스 템플릿이 없으므로 네임 스페이스를 템플릿 매개 변수로 전달할 수 없으므로 정적 멤버 만있는 클래스를 사용하는 것이 일반적입니다.


3
다른 답변은 OP의 마지막 줄에 빛났습니다. 네임 스페이스는 거의 이름이 지정된 범위이므로 액세스 제어가 필요한 경우 클래스 만 사용할 수있는 도구입니다.
cmannett85

2
@ cbamber85는 공정하기 위해 처음 두 답변을 읽은 후에 만 그 줄을 넣었습니다 .
PersonalNexus

@PersonalNexus 아 공정한, 나는 몰랐다!
cmannett85

10
@ cbamber85 : 그것은 사실이 아닙니다. 구현 파일에 "비공개"기능을 사용하면 캡슐화가 향상되므로 사용자는 개인 선언조차 볼 수 없습니다.
UncleBens

2
+1 템플릿은 네임 스페이스에 여전히 기능이 부족한 지점입니다.
Chris는 Reinstate Monica가

13

당시에는 FORTRAN 수업이 필요했습니다. 그때까지 다른 명령형 언어를 알고 있었기 때문에 많은 공부없이 FORTRAN을 할 수있을 것이라고 생각했습니다. 내가 첫 숙제를했을 때, 교수는 나에게 그것을 돌려주고 그것을 다시 해달라고 요청했다. 그는 FORTRAN 문법으로 작성된 파스칼 프로그램은 유효한 제출물로 간주되지 않는다고 말했다.

C ++에서 유틸리티 함수를 호스팅하기 위해 정적 클래스를 사용하는 것은 C ++의 외국 관용구입니다.

질문에서 언급했듯이 C #에서 유틸리티 함수에 정적 클래스를 사용하는 것은 필수적입니다. 독립형 함수는 단순히 C #에서 옵션이 아닙니다. 이 언어는 프로그래머가 다른 방식, 즉 정적 유틸리티 클래스 내에서 독립 함수를 정의 할 수 있도록 패턴을 개발하는 데 필요했습니다. 이것은 한마디로 한 자바 트릭입니다. 예를 들어, .NET의 java.lang.MathSystem.Math 는 거의 동형입니다.

그러나 C ++은 동일한 목표를 달성하기위한 다른 기능인 네임 스페이스를 제공하며 표준 라이브러리 구현에 적극적으로 사용합니다. 정적 클래스의 추가 계층을 추가하는 것은 불필요 할뿐만 아니라 C # 또는 Java 배경이없는 독자에게는 다소 반 직관적입니다. 어떤 의미에서, 당신은 기본적으로 표현 될 수있는 것을 위해 언어에 "대여 번역"을 도입하고 있습니다.

함수가 데이터를 공유해야하는 경우 상황이 다릅니다. 당신의 기능이 더 이상이기 때문에 관련이없는 , 싱글 톤 패턴은 이러한 요구 사항을 해결하는 좋은 방법이된다 없습니다.


6
싱글 톤 추천을 제외하고 +1. 그들은되어 있지 C ++에서 선호! 함수는 상태를 공유해야하는 경우 클래스에있을 수 있지만 실제로 필요한 경우가 아니면 클래스는 싱글 톤이 아니어야 합니다. 그리고 그들은 보통 그렇지 않습니다.
Maxpm

@Maxpm 실수하지 마십시오. singleton은 전역 정적 변수보다 선호됩니다. 그 외에는 "바람직한"것은 없습니다.
dasblinkenlight

2
이 좋은 기사 인 싱글 톤 : 1995 년 이후로 당신이 결코 알지 못했던 문제 해결 . 잘 알려진 인스턴스를 클래스 자체에 결합하면 유연성이 불필요하게 제한되고 아무 것도 제공하지 않는다고 요약되어 있습니다. 대부분의 경우 클래스가 있고 어딘가에 공유 인스턴스가 있지만 싱글 톤으로 만들지 마십시오.
Jan Hudec

나는 "외국 관용구"가 올바른 용어인지 확신하지 못한다. 매우 일반적인 관용구입니다. 나는 꽤 많은 프로그래밍 팀은 ... 스트로브 스트 룹의 E2를 사용하고 생각
닉 카일리을

10

왜 당신은 왜 정적 클래스를 원할까요?

내가 생각할 수있는 유일한 이유는 '모든 것이 클래스입니다'인 다른 언어 (Java 및 C #)에 언어가 필요하기 때문입니다. 이러한 언어는 최상위 함수를 전혀 만들 수 없으므로이를 유지하기위한 트릭을 발명했습니다. 이것이 정적 멤버 함수였습니다. 그것들은 약간의 해결 방법이지만 C ++에는 그러한 것이 필요하지 않습니다. 새로운 최상위 독립 함수를 직접 만들 수 있습니다.

특정 데이터 항목에서 작동하는 함수가 필요한 경우 해당 데이터를 보유하는 클래스에 해당 함수를 번들로 묶는 것이 합리적이지만 함수의 작동을 중지하고 클래스의 데이터에서 작동하는 해당 클래스의 멤버가되기 시작합니다.

특정 데이터 유형에서 작동하지 않는 함수가있는 경우 (클래스가 새로운 데이터 유형을 정의하는 방법이므로 여기에서 단어를 사용합니다.) 실제로 다른 유형으로 클래스에 밀어 넣는 것은 안티 패턴입니다. 시맨틱 목적.


10
실제로-Java의 "모든 정적 멤버가있는 클래스"는 실제로 Java 의 한계를 극복하기 위한 해킹 이라고 말하고 싶습니다 .
Charles Salvia

@CharlesSalvia : 나는 예의 바르고 있었다 :) 나는 main ()도 java 클래스의 일부가되는 것에 대해 같은 나쁜 느낌을 가지고 있지만, 왜 그렇게했는지 이해합니다.
gbjbaanb

이것은 실제로 왜 정적 클래스를 원하는지에 대한 답을 제공하는 것 같습니다. 아니면 내가 당신을 오해합니까? 예를 들어, 한 클래스 (ROM에 저장하려고 함)에서 읽고 다른 클래스 (RAM에 있음)에 의해 읽히는 값을 변경할 수있는 변수를 보유해야합니다. 알려진 램 위치에 배치하는 것만으로는 충분하지 않습니다. 클래스에 래핑 (및 액세서)을 배치하면 액세스 제어를 미세 조정할 수 있습니다. 두 번째 단락에서 설명하는 내용과 거의 같습니다.
iheanyi 2016 년

5

적어도 표면에서 클래스의 정적 메소드는 네임 스페이스의 무료 함수와 구별 할 수없는 것처럼 보입니다.

즉, 네임 스페이스 대신 클래스를 갖는 것은 이점이 없습니다.

왜 후자를 선호 하는가?

한 가지로 그것은 항상 타이핑을 절약 static하지만, 아마도 약간의 이점입니다.

주요 이점은 작업에 가장 강력한 도구라는 것입니다. 클래스는 객체를 만드는 데 사용될 수 있으며 변수 유형 또는 템플릿 인수로 사용할 수 있습니다. 이들 중 어느 것도 함수 모음에 원하는 기능이 아닙니다. 따라서 클래스가 실수로 잘못 사용될 수 없도록 이러한 기능이없는 도구를 사용하는 것이 좋습니다.

네임 스페이스를 사용하면 코드를 사용하는 모든 사용자에게 이것이 함수 모음이며 객체를 생성하는 청사진이 아니라는 것을 즉시 알 수 있습니다.


5

...하지만 여러 의견자는 정적 함수가 안티 패턴임을 암시하더라도 자유 함수를 선호한다고 말합니다. 왜 C ++에서 그렇게됩니까? 적어도 표면에서 클래스의 정적 메소드는 네임 스페이스의 무료 함수와 구별 할 수없는 것처럼 보입니다. 왜 후자를 선호 하는가?

정적 인 수업은 일을 끝내지 만, 4 도어 세단 형 자동차가 할 때 칩과 살사를 위해 53 피트 세미 트럭을 식료품 점으로 운전하는 것과 같습니다. 클래스에는 약간의 추가 오버 헤드가 있으며 클래스의 존재는 클래스를 인스턴스화하는 것이 좋은 아이디어라는 인상을 줄 수 있습니다. C ++에서 제공하는 무료 기능 (그리고 모든 기능이 무료 인 C)은 그렇게하지 않습니다. 그것들은 단지 기능 일뿐입니다.

유틸리티 함수 모음에 개인 정적 필드에 저장할 수있는 캐시와 같은 일부 공유 데이터가 필요한 경우 상황이 다릅니 까?

실제로는 아닙니다. staticC (및 이후 C ++) 의 원래 목적은 모든 범위의 범위에서 사용할 수있는 영구 저장소를 제공하는 것이 었습니다.

int counter() {
    static int value = 0;
    value++;
}

범위를 파일 수준으로 가져 와서 파일 범위가 아닌 모든 좁은 범위에 표시 할 수 있습니다.

static int value = 0;

int up() { value++; }
int down() { value--; }

C ++의 전용 정적 클래스 멤버는 동일한 용도로 사용되지만 클래스 범위로 제한됩니다. counter()위 의 예제 에서 사용 된 기술 은 C ++ 메서드에서도 작동하며 변수가 전체 클래스에 표시 될 필요가없는 경우 실제로 권장하는 방법입니다.


데이터를 공유하지 않는 관련 유틸리티 함수 그룹이 네임 스페이스에 더 잘 그룹화되어서는 안된다고 가정합니다. 그러나 공유 데이터 및 일부 액세스 제어에 액세스해야하는 밀접하게 결합 된 유틸리티 함수 그룹이있는 경우 정적 클래스가 최선의 선택입니다. 함수 내의 정적은 재진입되지 않습니다. 이 컨텍스트에서는 모듈 전역을 사용하는 것이 약간 더 좋지만, 내가 설명한 요구 사항이 있다면 여전히 모든 정적 클래스가 최상의 솔루션이라고 생각합니다.
iheanyi 2016 년

데이터를 공유하면 정적 메소드가없는 클래스로 더 좋을 수 있습니까?
Nick Keighley

@NickKeighley 그것은 하나의 인스턴스를 생성하고 그것을 필요한 모든 것에 전달하거나 클래스를 정적으로 만드는 것이 더 나은지에 대한 토론에서 누가 이기는가에 달려 있습니다.
Blrfl

1

함수가 상태를 유지하지 않고 재진입하는 경우 (언어에 의해 강제되지 않는 한) 클래스 내에서 함수를 밀어 넣는 데 큰 의미가없는 것 같습니다. 함수가 어떤 상태를 유지하는 경우 (예 : 정적 뮤텍스를 통해서만 스레드로부터 안전하게 만들 수있는 경우) 클래스의 정적 메서드가 적절 해 보입니다.


1

그 만드는 ++ C에 대한 아주 근본적인 무언가가 있지만 많은 주제에 대한 담론의 여기, 의미가 namespacesclassES / struct매우 다른 S를.

정적 클래스 (모든 멤버가 정적이고 클래스가 인스턴스화되지 않는 클래스)는 그 자체가 객체입니다. 그것들은 단순히 namespace함수를 포함 하는 것이 아닙니다 .

템플릿 메타 프로그래밍을 사용하면 정적 클래스를 컴파일 타임 객체로 사용할 수 있습니다.

이걸 고려하세요:

template<typename allocator_type> class allocator
{
public:
    inline static void* allocate(size_t size)
    {
        return allocator_type::template allocate(size);
    }
    inline static void release(void* p)
    {
        allocator_type::template release(p);
    }
};

이것을 사용하려면 클래스 안에 포함 된 함수가 필요합니다. 네임 스페이스는 여기서 작동하지 않습니다. 치다:

class mallocator
{
    inline static void* allocate(size_t size)
    {
        return std::malloc(size);
    }
    inline static void release(void* p)
    {
        return std::free(p);
    }
};

이제 그것을 사용하십시오 :

using my_allocator = allocator<mallocator>;

void* p = my_allocator::allocate(1024);
...
my_allocator::release(p);

새로운 할당자가 호환 되는 allocaterelease기능을 노출하는 한, 새로운 할당기로 쉽게 전환 할 수 있습니다.

네임 스페이스로는이 작업을 수행 할 수 없습니다.

클래스의 일부가 되려면 항상 함수가 필요합니까? 아니.

정적 클래스를 사용하는 것이 안티 패턴입니까? 상황에 따라 다릅니다.

유틸리티 함수 모음에 개인 정적 필드에 저장할 수있는 캐시와 같은 일부 공유 데이터가 필요한 경우 상황이 다릅니 까?

이 경우 달성하려는 것은 객체 지향 프로그래밍을 통해 가장 잘 제공 될 수 있습니다.


클래스 정의에 정의 된 메소드가 내재적으로 인라인되어 있으므로 인라인 키워드 사용은 여기서 중복됩니다. en.cppreference.com/w/cpp/language/inline을 참조하십시오 .
Trevor

1

존 카맥이 유명하게 말했다 :

"때때로, 우아한 구현은 단지 함수일뿐입니다. 메소드가 아닙니다. 클래스가 아닙니다. 프레임 워크가 아닙니다. 함수일뿐입니다."

나는 그것을 거의 요약한다고 생각합니다. 수업이 아닌 경우 왜 강의 수업을 하시겠습니까? C ++에는 실제로 함수를 사용할 수있는 고급 기능이 있습니다. Java에서는 모든 것이 방법입니다. 그런 다음 유틸리티 클래스와 많은 클래스가 필요합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.