Java 패키지 레벨 범위가 유용합니까?


11

패키지 범위에 대한 아이디어를 이해하고 때로는 그것을 원한다고 생각했습니다. 그러나, 그것을 사용하려는 심각한 의도로 시작할 때마다 나는 그것이 그것이 필요하다고 생각한 요구에 맞지 않는다는 것을 발견했습니다.

내 주된 문제는 항상 범위를 제한하려는 내용이 동일한 패키지에 포함되지 않는 것 같습니다. 그것들은 개념적으로 모두 연결될 수 있지만, 애플리케이션 내에서 데이터의 논리적 분할은 그것들을 더 큰 패키지의 개별 자식 패키지로 가지고 있습니다.

예를 들어 Mission 모델이있을 수 있으며 missionServices와 같은 다른 Mission 도구 만 일부 방법을 사용하기를 원합니다. 그러나 패키지로 Missions.models 및 Missions.services를 사용하므로 MissionModel과 MissionService가 동일한 패키지 범위가 아닙니다. 패키지에 내가 권한을 갖기를 원하지 않는 수많은 것들을 포함시키지 않고 높은 권한을 갖기를 원하는 것들을 패키지에 적절하게 포함하는 상황은 결코없는 것 같습니다. 패키지 범위 지정의 장점은 거의 모든 패키지를 동일한 패키지에 배치하기 위해 프로젝트 아키텍처를 수정하는 것을 정당화한다고 생각하지 않습니다. 종종 Aspects 또는 일종의 제어 역전은 내가 간단히 패키지 범위 지정을 고려한 모든 문제에 대한 더 나은 접근 방식으로 판명되었습니다.

나는 이것이 모든 Java 개발자들에게 일반적으로 사실이라고 생각하거나 궁금한 일입니다. 실제 환경에서 패키지 범위가 많이 활용됩니까? 사용하기에 좋은 형태로 간주되거나 현대 개발에서 거의 이용되지 않는 레거시 행동으로 보이는 경우가 많이 있습니까?

패키지 개인 범위가 기본값 인 이유에 대해 아무 것도 묻지 않고 기본값에 관계없이 언제 사용해야하는지 묻습니다. 이것이 왜 기본인지에 대한 대부분의 토론은 실제로 패키지 범위가 실제로 유용 할 때 실제로 들어 가지 않고 대신에 일반적으로 사용되는 다른 두 가지 범위가 기본값이 아니어야하는 이유에 대해 간단히 주장하여 패키지가 제거 과정에서 승리합니다. 또한 제 질문은 현재 개발 상태에 관한 것 입니다. 특히, 우리는 다른 툴과 패러다임이 패키지 범위를 덜 유용하게 만드는 시점으로 발전 시켰습니다.


생각 실험 : 패키지가 없다면 Java 프로그램은 어떻게 생겼습니까? 대규모 Java 프로그램 을 보거나 작업하지 않았을 수 있습니다 .
Robert Harvey

java.util.String및 의 코드를 확인하십시오 java.util.Integer.


2
중복 질문에 대해 가장 많이 투표 된 답변은 패키지 개인의 가장 중요한 용도를 다루며, 왜 유용한 지 암시 적으로 대답합니다.

2
나는 복제본에 전혀 확신하지 못한다. 이 질문은 "패키지 범위는 무엇입니까?" 다른 하나는 (다른 것들 중에서) "패키지 범위가 기본으로 제공됩니다. 개인 범위는 무엇입니까?" 또한 듀프에서 허용되는 답변과 여기에서 허용되는 답변은 완전히 다른 점을 만들고 있습니다.
Ixrec

답변:


2

통해 java.util.*패키지 코드를 패키지 수준의 보호하에 작성되었습니다 경우가있다. 예를 들어,이 비트 java.util.String- 자바 6 생성자 :

// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

또는이 getChars 메소드 :

/** Copy characters from this string into dst starting at dstBegin. 
    This method doesn't perform any range checking. */
void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, offset, dst, dstBegin, count);
}

그 이유는 코드 디자이너 (그리고 java.util.*다소 큰 라이브러리라고 생각할 수 있음)는 다양한 안전 (어레이의 범위 검사, 다른 필드에 대한 직접 액세스)의 손실로 더 빠른 성능을 발휘할 수 있기를 원했기 때문입니다. 즉, 공용 메소드를 통해 변경할 수없는 것으로 간주되는 클래스의 일부에 대한 '안전하지 않은'액세스는 인터페이스가 아닌 구현을 의미합니다.

이러한 메소드와 필드가 java.util패키지의 다른 클래스에서만 액세스 할 수 있도록 제한함으로써 이들 메소드와 필드가 서로 밀접하게 연결될뿐 아니라 이러한 구현 세부 사항을 세계에 노출시키지 않습니다.

응용 프로그램에서 작업 중이고 다른 사용자에 대해 걱정할 필요가없는 경우 패키지 수준 보호는 걱정할 필요가 없습니다. 디자인 순도의 다양한 측면 외에도 모든 것을 public만들고 괜찮을 수 있습니다.

그러나 다른 사람이 사용할 라이브러리에서 작업하거나 나중에 리팩토링하기 위해 클래스를 너무 많이 묶지 않으려는 경우 필요에 따라 제공되는 가장 엄격한 수준의 보호를 사용해야합니다. 이것은 종종 의미 private합니다. 그러나 때때로 반복을 피하거나 두 클래스와 해당 구현의 결합을 희생하여 실제 구현을 조금 더 쉽게 만들기 위해 동일한 패키지의 다른 클래스에 약간의 구현 세부 사항을 노출해야하는 경우가 있습니다. 따라서 패키지 레벨 보호.


1
나는 java.util을 살펴 보았고 그것을 보았습니다. 그러나 나는 또한 그것이 큰 도서관이라는 것을 알았습니다. 요즘에는 하위 패키지를 사용하지 않고 큰 라이브러리를 작성하는 경우가 거의 없습니다. 한계를 이끄는 것은 하위 패키지 부서입니다. 나는 java.util이 잘못되었다고 말하지는 않지만, 매우 훌륭한 프로그래머 가 있었기 때문에 패키지 내에서 제한된 캡슐화로 모든 것을 올바르게 할 수 있다고 믿을 수있는 것만으로도 큰 패키지로 벗어날 수있는 것처럼 보입니다 . 나는 비슷한 패키지에서 저와 비슷한 작업을 할 수있는 평범한 개발자들에게 나쁜 일을 할 가능성이 크다고 생각합니다.
dsollen

1
@dsollen Spring, Hibernate 또는 이와 유사한 대형 라이브러리를 파헤 치면 비슷한 것을 발견 할 수 있습니다. 더 가까운 커플 링이 필요한 경우가 있습니다. 하나는 해야 제출물 코드 검토 할 수있는 기회를 가지고 있는지 모두가 정확한지 확인하십시오. 밀접한 연결이 필요하지 않거나 다른 코더를 전혀 믿지 않으면 공용 필드 또는 패키지 또는 보호 필드 또는 방법이 항상 적절한 것은 아닙니다. 그러나 이것이 유용하지 않다는 것을 의미하지는 않습니다.

요컨대, 나는 더 제한된 패키지를 만들고 내 일상의 요구에 대한 최적화 수준 (프로젝트의 95 %의 낭비 일뿐)에 대해 걱정하지 않으려 고합니다. 따라서 제 후속 질문은 패키지 수준 범위가 광범위하게 사용되는 대규모 API에서만 사용되는 매우 구체적인 최적화 가능성입니까? IE는 보호 기능을 그 정도까지 완화하기 위해 필요로하거나 원할 것입니까? 더 작은 사용자 기반을 가진 '표준'API로 작업하는 우수한 프로그래머가이를 사용할 수있는 이유가 있습니까?
dsollen

내 후속 조치 게시물을 충분히 빨리 얻지 못했습니다 :) 나는 당신이 만든 요점을 이해하고 보았습니다. 나는 그것을 토론하지 않습니다. 더 좋은 것은 우리의 나머지 훌륭한 프로그래머에게는 유용하지만 그러한 거대한 API에서 작동하지 않는 사람들에 대한 후속 질문을 제기하는 것입니다. 특히, 실제로 성능을 조정해야 할 때만 수행되는 캡슐화 트레이드 오프를 희생시키면서 최적화하는 것입니다 .
dsollen

2
@ dsollen 성능에 관한 것이 아니라 (구현을 드러내는 이유 중 하나임) 오히려 캡슐화입니다. 당신은에 대한 코드를 반복하지 않도록하기 위해 수행 Integer.toString()하고 String.valueOf(int)세계에 노출하지 않고 코드를 공유 할 수 있습니다. java.util클래스는 오히려 전적으로 자신에게 섬 개별 클래스를 것보다 기능이 더 큰 전체를 제공하기 위해 콘서트에서 일하기 - 그들은 패키지 수준 범위 지정을 통해 패키지와 공유 구현 기능을 함께합니다.

5

때로는 개인 또는 보호 된 메소드의 가시성을 패키지로 에스컬레이션하여 junit-test가 외부에서 액세스해서는 안되는 구현 세부 사항에 액세스 할 수 있도록합니다.

junit-test는 테스트 대상 항목과 동일한 패키지를 가지므로 이러한 구현 세부 사항에 액세스 할 수 있습니다.

  public class MyClass {
    public MyClass() {
        this(new MyDependency());
    }

    /* package level to allow junit-tests to insert mock/fake implementations */ 
    MyClass(IDependency someDepenency) {...}
  }

이것이 "좋은"사용법인지 아닌지 논란의 여지가 있지만 실용적입니다


2
나는 항상 다른 패키지에 테스트를 넣었습니다. 그렇지 않으면 다른 사람이 호출 할 수없는 기본 범위에 몇 가지 주요 메소드를 남겼습니다 ...
soru

나는 결코 메서드를 사용하지 않지만 주입 된 종속성에 사용하는 좋은 기능입니다. 예를 들어 EJB를 테스트 할 때 애플리케이션 서버가 런타임에 삽입 할 Mock 객체로 패키지 레벨 EM을 설정하여 삽입 된 EntityManager를 조롱 할 수 있습니다.
jwenting

protected패키지 범위로 상승해야하는 이유 protected 이미 패키지 액세스를 제공합니다. 약간 이상한 점이지만 protected packagescope void doSomething()(새 키워드가 필요한) 과 같은 복합 범위 선언을 필요로하지 않았다고 생각 합니다.
tgm1024--Monica가

2

사람들이 패키지의 하위 이름처럼 점으로 구분 된 이름을 사용하는 경향이있는 세계에서 특히 유용하지는 않습니다. Java에는 하위 패키지와 같은 것이 없기 때문에 xyinternals는 ab보다 xy에 더 이상 액세스 할 수 없습니다. 패키지 이름이 같거나 다르기 때문에 부분적으로 일치하는 것은 공통점이없는 것과 다릅니다.

원래 개발자는 컨벤션이 사용될 것으로 기대하지 않았으며 Ada 스타일 계층 적 패키지의 복잡성을 추가하지 않고 간단하게 유지하고 싶었습니다.

Java 9 는이를 해결하는 데 도움이됩니다. 모듈에 여러 패키지를 넣고 일부만 내보낼 수 있다는 것입니다. 그러나 이렇게하면 패키지 범위가 훨씬 더 중복됩니다.

이상적인 환경에서는 기본 범위를 '포함하는 모듈이 있으면 모듈 범위, 그렇지 않으면 패키지 범위'로 변경합니다. 그런 다음 모듈 내에서 구현을 공유하는 데 사용하여 내 보낸 인터페이스를 손상시키지 않고 리팩토링 할 수 있습니다. 그러나 이전 버전과의 호환성을 깨뜨 리거나 그렇지 않으면 나쁠 수있는 미묘한 부분이있을 수 있습니다.


Java 9 주석이 흥미 롭습니다. 패키지 헤라 키스를 인식하고 어쨌든 일부 주석 패키지와 관련하여 패키지 범위를 정의하는 간단한 기능 (어노테이션 사용)이 좋을 것이라고 생각합니다.
dsollen

1
@ dsollen 일 것입니다.하지만 첫 번째 성향은 타이어 다리미를 그 위에 금도금하기 시작한다는 것입니다. 내 의견으로는 명확성을 얻는 훨씬 더 나은 첫 번째 단계는 실제 패키지 범위 키워드를 발명하는 것입니다. 그것은 "패키지"일 수도 있습니다 :)
tgm1024--Monica가

2

응집력의 유형 당신의 기술은 정말 당신이 패키지 액세스를 사용하는 것이 어디에 가장 좋은 예가 아니다. 패키지는 인터페이스에서 교환 할 수있는 구성 요소, 모듈을 작성하는 데 편리합니다.

시나리오 의 대략적인 예는 다음과 같습니다 . 코드가 기능적 응집력과 구성 요소 화에 더 가깝도록 재구성되었다고 가정하십시오. 아마도 3 항아리 :

**MissionManager.jar** - an application which uses the Java Service Provider Interface to load some implementation of missions, possible provided by a 3rd party vendor.

**missions-api.jar** - All interfaces, for services and model
    com...missions
        MissionBuilder.java   - has a createMission method
        Mission.java - interface for a mission domain object
    com...missions.strategy
        ... interfaces that attach strategies to missions ...
    com...missions.reports
        .... interfaces todo with mission reports ....

   **MCo-missionizer-5000.jar** -  provides MCo's Missionizer 5000 brand mission implementation.
       com...missions.mco
                  Mission5000.java - implements the Mission interface.
       com...missions.strategy.mco
              ... strategy stuff etc ...

Mission5000.java에서 패키지 레벨을 사용하면 다른 패키지 또는 MissionManager와 같은 컴포넌트가 미션 구현에 긴밀하게 결합 할 수 있는지 확신 할 수 없습니다. 그것은 "M co"의 "제 3 자"제공 컴포넌트만으로 실제로 미션의 특별한 브랜드 구현을 생성합니다. Mission Manager는 MissionBuiler.createMission (...)을 호출하고 정의 된 인터페이스를 사용해야합니다.

이런 식으로 Mission 구현 구성 요소를 교체하면 MissionManager.jar의 구현자가 API를 우회하지 않고 MCo의 구현을 직접 사용한다는 것을 알 수 있습니다.

따라서 MCo-missionizer5000.jar을 경쟁 제품과 교체 할 수 있습니다. (또는 아마도 임무 관리자의 단위 테스트를위한 모의)

반대로 MCo는 독점적 미션 라이저 영업 비밀을 숨길 수 있습니다.

(이것은 구성 요소의 불안정성추상성 아이디어를 시행하는 것과 관련이 있습니다.)


-1

Java의 패키지 범위는 계층 구조로 설계되지 않았으므로 거의 유용하지 않습니다. 우리는 패키지 범위를 전혀 사용하지 않으며 모든 클래스를 공용으로 정의합니다.

대신 패키지 계층 구조 및 사전 정의 된 패키지 이름을 기반으로 자체 액세스 규칙을 사용합니다.

"CODERU-Rules"에 대한 세부 정보 Google에 관심이있는 경우

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