2010-04-30 6 views
7

비공개 형식을 반환하는 팩터 리 메서드가 거의없고이 비공개 형식의 변수를 제공하는 메서드 집합을 페어링하면 어떻게됩니까? NetBeans에서 제목이 표시된 경고 메시지가 나타납니다.공용 API를 통해 비공개 형식 내보내기

결과 공개 API에는 두 가지 쌍의 메소드 쌍만 포함됩니다. 그 이유는 내 유형 계층을 봉인 (스칼라에서 seald 클래스처럼)하고 사용자가 팩토리 메소드를 통해 이러한 유형을 인스턴스화 만하도록 허용하기 위해서입니다. 그래서 우리는 어떤면에서 DSL을 얻습니다.

예를 들어, 일정 필드의 컨트랙트로 표시된 Schedule 클래스. RangeSet, Singleton, List, FullSet과 같은 NumberSet 인터페이스를 루트로하는 몇 가지 유형의 제한이 있습니다. 우리는 이러한 유형을 공개하고 Schedule이 어떻게 상호 작용하는지 공개하지 않습니다. 우리는 단지 사용자의 명세를 원할뿐입니다. 그래서 우리는 NumberSet을 private 패키지로 만듭니다.

NumberSet singleton(int value); 
NumberSet range(int form, int to); 
NumberSet list(NumberSet ... components); 

및 생성 일정 개체에 대한 몇 가지 방법 :

Schedule everyHour(NumberSet minutes); 
Schedule everyDay(NumberSet minutes, NumberSet hours); 

사용자는 방식으로 사용할 수 있습니다 :

Schedule s = Schedule.everyDay(singleton(0), list(range(10-15), singleton(8))); 

을 수업 일정에서 우리는 몇 가지 제약 공장 방법을 만들 수 나쁜 생각인가요?

답변

9

아이디어 자체가 이상합니다. 그러나 루트 유형 (여기 : NumberSet) 패키지를 비공개로 설정하면 작동하지 않습니다. 해당 유형을 공개하거나 대신 공용 인터페이스를 사용하십시오. 실제 구현 유형을 숨길 수 있어야합니다. 편집 공개 API에서 숨겨진/개인 루트 유형을 노출

public abstract class NumberSet { 

    // Constructor is package private, so no new classes can be derived from 
    // this guy outside of its package. 
    NumberSet() { 
    } 
} 

public class Factories { 

    public NumberSet range(int start, int length) { 
     return new RangeNumberSet(start, length); 
    } 

    // ... 
} 

class RangeNumberSet extends NumberSet { 
    // ... must be defined in the same package as NumberSet 
    // Is "invisible" to client code 
} 

실수입니다. 다음 시나리오를 고려하십시오.

package example; 

class Bar { 
    public void doSomething() { 
      // ... 
    } 
} 

public class Foo { 

    public Bar newBar() { 
     return new Bar(); 
    } 
} 

이 API를 사용하는 클라이언트 응용 프로그램을 고려하십시오. 클라이언트는 무엇을 할 수 있습니까? 변수 example 외부의 모든 클래스에서 해당 유형을 볼 수 없으므로 유형이 Bar 인 변수를 올바르게 선언 할 수 없습니다. public 메서드를 Bar 인스턴스에서 호출 할 수도 없습니다. 모름부터, 그러한 공용 메서드가 존재합니다 (클래스를 볼 수는 없으며, 노출 된 멤버는 물론). 따라서 고객이 할 수있는 최선의 방법은 다음과 같습니다.

Object bar = foo.newBar(); 

본질적으로 쓸모가 없습니다. 다른 점은 위에 정의 된 코드에서와 같이 private 패키지가 아닌 public 인터페이스 (또는 추상 클래스)를 갖는 것입니다. 이 경우 클라이언트는 실제로 변수를 NumberSet 변수로 선언 할 수 있습니다. 생성자가 숨겨져 있지만 정의 된 공용 API에 액세스 할 수 있으므로 자체 인스턴스를 만들거나 하위 클래스를 파생시킬 수 없습니다.

다시 편집 클라이언트의 관점에서 볼 때 "특징없는"값, 즉 클라이언트가 원하는 API를 정의하지 않는 값을 원한다고해도 여전히 좋은 생각입니다. 위에서 설명한 방식으로 공개 기본 유형을 노출합니다. 그리고 컴파일러가 유형 검사를 수행하고 클라이언트 코드가 (적절하게 선언 된) 변수에 이러한 값을 임시로 저장할 수있게하는 목적으로 만 사용하십시오.

클라이언트가 해당 유형의 API 메소드를 호출하지 않도록하려면 다음을 수행하십시오. 귀하의 (다른) 공개 기본 유형에 공개 API를 제공하지 못하도록 방해하는 방법은 없습니다. 아무 것도 선언하지 마세요. "빈"추상 기본 클래스를 사용하십시오 (클라이언트의 관점에서 볼 때 비어 있습니다. 모든 흥미로운 방법은 개인 패키지이므로 숨겨집니다). 그럼에도 불구하고 공용 기본 유형을 제공해야하거나 일반 값 Object을 반환 값으로 사용해야하지만 컴파일 타임에 오류 검사를 포기해야합니다.

규칙 : 클라이언트가 값을 얻고 다른 API로 전달하기 위해 클라이언트가 어떤 메소드를 호출해야한다면 클라이언트는 실제로 이라는 마술 특수 값을 알고 있습니다. 그리고 그것은 어떤 식 으로든 그것을 처리 할 수 ​​있어야합니다 (적어도 "통과하십시오"). 값을 처리하기 위해 클라이언트에 적절한 유형을 제공하지 않으면 (전적으로 적절한) 컴파일러 경고를 제외하고는 아무 것도 사지 않습니다.

public abstract class Specification { 

    Specification() { 
     // Package private, thus not accessible to the client 
     // No subclassing possible 
    } 

    Stuff getInternalValue1() { 
     // Package private, thus not accessible to the client 
     // Client cannot call this 
    } 
} 

위의 클래스는 클라이언트 코드와 관련하여 "비어 있습니다". Object이 이미 제공하는 물건을 제외하고는 유용한 API를 제공하지 않습니다. 클라이언트가이 유형의 변수를 선언 할 수 있고 컴파일러가 유형 검사를 수행 할 수있는 주요 이점은 다음과 같습니다. 그러나 프레임 워크는이 유형의 구체적인 인스턴스를 만들 수있는 유일한 장소로 남아 있으므로 프레임 워크는 모든 값을 완벽하게 제어 할 수 있습니다.

+0

루트 유형을 package-private로 설정하면 왜 작동하지 않습니까? –

+0

예.하지만이 유형을 보지 못하는 클라이언트입니다. 팩토리 메소드를 통해 스펙을 지정하고 스케쥴 오브젝트의 팩토리 메소드를 전달하면됩니다. –

0

나는이 작업을 수행하는 두 가지 방법을 생각하지만, 둘 다 정말 작동하지 않을 수 있습니다

  1. 개인 패키지로 NumberSet를 선언하지만, 현재 대신 Object를 사용하도록 NumberSet 유형을 사용하는 공개 API 방법을 변경하고, 구현시에는 Object에서 NumberSet까지 인수를 캐스팅해야합니다. 이것은 호출자가 항상 올바른 종류의 객체를 전달하는 것에 의존하며 ClassCastException에 속합니다.

  2. 이없는 방법과 공공 NumberSet 인터페이스를 구현하고 내부적으로 필요한 방법을 NumberSet를 구현하고 정의 개인 InternalNumberSet 패키지. 형식 캐스팅은 다시 한번 NumberSet에서 InternalNumberSet까지 인자를 전달합니다. 이전 방법보다 낫지 만 누군가 NumberSet을 구현하는 클래스를 만들고 InternalNumberSet을 구현하지 않으면 결과는 ClassCastException이됩니다.

나는 개인적으로 공개로 NumberSet 인터페이스를 선언해야한다고 생각합니다. 인터페이스에서 getter를 노출하는 데 실제적인 해가 없으며 구현 클래스를 변경 불가능하게 만들려면 final로 선언하고 setter를 노출하지 마십시오.

관련 문제