언제 개인 / 이너 클래스를 사용해야합니까?


21

명확히하기 위해, 내가 묻는 것은

public class A{
    private/*or public*/ B b;
}

vs.

public class A{
    private/*or public*/ class B{
        ....
    }
}

나는 분명히 하나 또는 다른 것을 사용해야하는 몇 가지 이유를 생각할 수 있지만 실제로보고 싶은 것은 장단점이 학문이 아니라는 것을 보여주는 설득력있는 예입니다.


2
대답은 언어 및 핵심 라이브러리에서 제공하는 시설에 따라 다릅니다. C # 언어를 가정하면 StyleCop으로이를 실행하십시오. 이 클래스를 자체 파일로 분리하는 것에 대한 경고가 표시됩니다. 즉, MSFT의 충분한 사람들이 귀하가 사용한 예제 중 하나를 사용할 필요가 없다고 생각합니다.
Job

학문적 예가 충분하지 않다면 설득력있는 예가 무엇입니까?

@Job StyleCop은 내부 클래스가 외부에서 보이는 경우에만 경고를 표시합니다. 비공개 인 경우 완전히 괜찮으며 실제로 많은 용도가 있습니다.
julealgon

답변:


20

일반적으로 클래스가 외부 인터페이스의 일부가 아닌 다른 클래스의 내부 구현 세부 정보 인 경우에 사용됩니다. 나는 그것들을 c 스타일 구조체에 대한 별도의 구문이없는 언어로 내부 데이터 구조를 더 깨끗하게 만드는 데이터 전용 클래스로 보았습니다. 이벤트 핸들러 또는 작업자 스레드와 같이 고도로 사용자 정의 된 단일 메소드 파생 오브젝트에도 유용합니다.


2
이것이 내가 사용하는 방법입니다. 기능적 관점에서 클래스가 다른 클래스의 개인 구현 세부 사항에 지나지 않는 경우 개인 메서드 또는 필드와 마찬가지로 선언해야합니다. 다른 곳에서는 절대 사용하지 않는 쓰레기로 네임 스페이스를 오염시킬 이유가 없습니다.
Aaronaught

9

트리, 목록, 그래프 등을 작성한다고 가정하십시오. 노드 또는 셀의 내부 세부 사항을 외부 세계에 노출해야하는 이유는 무엇입니까?

그래프 또는 목록을 사용하는 사람은 나중에 언젠가 (예 : 배열 기반 구현에서 포인터 기반으로 변경) 클라이언트를 사용하여 변경하려는 경우 구현이 아닌 인터페이스에만 의존해야합니다. 데이터 구조 ( 각각 )는 새로운 구현에 적합하도록 코드를 수정해야합니다.

대신 개인 내부 클래스에서 노드 또는 셀의 구현을 캡슐화하면 클라이언트가 데이터 구조의 인터페이스가 유지되는 한 결과적으로 코드를 강제로 조정하지 않고도 필요할 때마다 구현을 자유롭게 수정할 수 있습니다. 손대지 않은.

데이터 구조 구현의 세부 사항을 숨기면 보안 이점도 생길 수 있습니다. 클래스를 배포하려는 경우 인터페이스 파일 만 컴파일 된 구현 파일과 함께 사용할 수있게되므로 실제로 배열이나 포인터를 사용하고 있는지 아무도 알 수 없기 때문에 구현을 위해 코드를 오용하거나 검사 할 수 없기 때문에 어떤 종류의 착취 또는 적어도 지식으로부터 응용 프로그램을 보호하십시오. 실제적인 문제 외에도 그러한 경우에 매우 우아한 솔루션이라는 사실을 과소 평가하지 마십시오.


5

제 생각 에는 특정 작업을 허용하기 위해 클래스에서 간략하게 사용되는 클래스에 내부 정의 클래스를 사용하는 것이 공정한 경찰 입니다.

예를 들어, 두 개의 관련되지 않은 클래스로 구성된 데이터 목록을 바인딩해야하는 경우 :

public class UiLayer
{
    public BindLists(List<A> as, List<B> bs)
    {
        var list = as.ZipWith(bs, (x, y) => new LayerListItem { AnA = x, AB = y});
        // do stuff with your new list.
    }

    private class LayerListItem
    {
        public A AnA;
        public B AB;
    }
}

다른 클래스에서 내부 클래스를 사용하는 경우 별도의 클래스를 사용해야합니다. 내부 클래스에 논리가 포함되어 있으면이를 별도로 배치해야합니다.

기본적으로 데이터 객체에 구멍을 뚫는 데는 훌륭하지만 실제로 로직을 포함 해야하는 경우 유지 관리가 어렵습니다. 변경해야 할 경우 어디를 찾아야하는지 알아야하기 때문입니다.


2
C #을 가정하면 여기에서 튜플을 사용할 수 없습니까?
Job

3
@Job 물론이지만 때로는 tuple.ItemX코드가 명확하지 않습니다.
Lasse Espeholt 2016 년

2
@Job yup, lasseespeholt가 그 권리를 얻었습니다. 차라리 {string Title의 내부 클래스를보고 싶습니다. 문자열 설명; } Tuple <string, string>보다.
Ed James

그 시점에서 그 클래스를 자체 파일에 넣는 것이 합리적이지 않습니까?
Job

부모 클래스의 과제를 벗어나지 않는 작업을 수행하기 위해 일부 데이터를 간단히 묶기 위해 실제로 사용하는 경우는 아닙니다. 적어도 내 의견으로는 아닙니다!
Ed James

4
  • 일부 언어 (예 : Java)의 내부 클래스는 포함 클래스의 객체와 연결되어 있으며 자격을 갖추지 않고 멤버를 사용할 수 있습니다. 가능한 경우 다른 언어 기능을 사용하여 다시 구현하는 것보다 명확합니다.

  • 다른 언어 (예 : C ++)의 중첩 클래스에는 그러한 넥타이가 없습니다. 클래스에서 제공하는 범위 및 접근성 컨트롤을 사용하기 만하면됩니다.


3
실제로 Java에는이 두 가지 가 있습니다.
Joachim Sauer

3

내부 클래스가있는 경우 핫 스왑이 작동하지 않으므로 Java에서 내부 클래스를 피하십시오. 이러한 고려 사항에 대한 코드를 손상시키는 것이 정말로 싫어하기 때문에 이것을 싫어하지만 Tomcat이 다시 시작됩니다.


이 문제가 어딘가에 기록되어 있습니까?
gnat

Downvoter : 내 주장이 잘못 되었습니까?
케빈 클라인

1
귀하의 답변에 잘못된 내용이 아니라 세부 정보가 없기 때문에 하향 투표했습니다. 세부 정보가 없으면 어설 션을 확인하기가 어렵습니다. 이 문제에 대해 더 추가하면 정보가 꽤 흥미롭고 유용 할 수 있기 때문에 공감으로 되돌릴 수 있습니다.
gnat

1
@ gnat : 나는 약간의 연구를했으며 이것이 잘 알려진 것처럼 보이지만 Java HotSwap의 한계에 대한 명확한 참조를 찾지 못했습니다.
kevin cline

우리는 회의론자에 있지 않습니다. SE :) 단지 몇 가지 참고가 될 것입니다. 그것은 확실 할 필요는 없습니다
gnat

2

개인 정적 내부 클래스의 사용 중 하나는 메모 패턴입니다. 기억해야 할 모든 데이터를 개인 클래스에 넣고 일부 함수에서 Object로 반환합니다. 외부의 누구도 (반사, 역 직렬화, 메모리 검사없이) 그것을 조사 / 수정할 수 없지만 클래스에 되돌려주고 상태를 복원 할 수는 없습니다.


2

개인 내부 클래스를 사용하는 한 가지 이유는 사용하는 API가 특정 클래스에서 상속을 요구하기 때문에 해당 클래스의 지식을 외부 클래스의 사용자에게 내보내고 싶지 않기 때문일 수 있습니다. 예를 들면 다음과 같습니다.

// async_op.h -- someone else's api.
struct callback
{
    virtual ~callback(){}
    virtual void fire() = 0;
};

void do_my_async_op(callback *p);



// caller.cpp -- my api
class caller
{
private :
    struct caller_inner : callback
    {
        caller_inner(caller &self) : self_(self) {}
        void fire() { self_.do_callback(); }
        caller &self_;
    };

    void do_callback()
    {
        // Real callback
    }

    caller_inner inner_;

public :
    caller() : inner_(*this) {}

    void do_op()
    {
        do_my_async_op(&inner_);
    }
};

여기에서 do_my_async_op는 콜백 유형의 객체를 전달해야합니다. 이 콜백에는 API가 사용 하는 공개 멤버 함수 서명이 있습니다.

do_op ()가 outer_class에서 호출되면 자체 내부 포인터 대신 필요한 콜백 클래스에서 상속되는 전용 내부 클래스의 인스턴스를 사용합니다. 외부 클래스에는 콜백을 외부 클래스의 전용 do_callback 멤버 함수 로 분류하기위한 목적으로 외부 클래스에 대한 참조가 있습니다.

이것의 장점은 아무도 "public (fire)"멤버 함수를 호출 할 수 없다는 것입니다.


2

Depth의 C #에서 Jon Skeet에 따르면 중첩 클래스를 사용하는 것이 완전히 게으른 스레드 안전 싱글 톤 (5 번째 버전 참조) 을 구현하는 유일한 방법이었습니다 (.NET 4까지).


1
그것의 초기화 온 디맨드 홀더 관용구 , 자바 또한 개인 정적 내부 클래스를 필요로한다. JMM FAQ , JCiP 16.4의 Brian Goetz 및 JavaOne 2008의 Joshua Bloch에 의해 권장됩니다. 보다 효과적인 Java (pdf) "정적 필드의 고성능을 위해 ..."
gnat

1

개인 및 내부 클래스는 캡슐화 수준을 높이고 구현 세부 정보를 숨기는 데 사용됩니다.

C ++에서는 개인 클래스 외에도 클래스 cpp의 익명 네임 스페이스를 구현하여 동일한 개념을 달성 할 수 있습니다. 이것은 구현 세부 사항을 숨기거나 개인화하는 데 유용합니다.

내부 또는 개인 클래스와 같은 아이디어이지만 파일의 컴파일 단위 외부에서는 완전히 볼 수 없으므로 캡슐화 수준이 훨씬 높습니다. 헤더 파일에 아무것도 나타나지 않거나 클래스 선언에 외부 적으로 표시됩니다.


-1에 대한 건설적인 의견?
hiwaylon

1
나는 공감하지 않았지만 당신이 실제로 질문에 대답하지 않았다고 생각할 것입니다 : 당신은 개인 / 내부 수업을 사용할 이유를주지 않습니다. 당신은 그것이 어떻게 작동하고 C ++에서 비슷한 일을하는 방법을 설명하고 있습니다.
Simon Bergot

과연. 나는 내와 다른 대답 (구현 세부 정보 숨기기, 캡슐화 수준 증가 등)에 의해 분명하다고 생각했지만 일부를 명확히하려고 노력할 것입니다. @Simon에게 감사합니다.
hiwaylon

1
좋은 편집. 그리고 이런 종류의 반응 때문에 화를 내지 마십시오. SO 네트워크는 신호 대 잡음비를 개선하기위한 정책을 시행하려고합니다. 어떤 사람들은 질문 / 응답 범위에 대해 매우 엄격하고, 언젠가는 (예를 들어 자신의 downvote 주석에 의해) 좋은 것을 잊지
사이먼 Bergot을

@Simon 감사합니다. 의사 소통 품질이 좋지 않아 dissapointing입니다. 웹의 익명의 베일 뒤에 "엄격한"상태를 유지하는 것이 너무 쉬운 것일까 요? 오 잘 새로운 것 같아요. 긍정적 인 피드백에 다시 한번 감사드립니다.
hiwaylon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.