2010-03-17 4 views
18

여기 내 샘플 추상적 인 싱글 톤 클래스입니다 :Java로 추상 싱글 톤 클래스를 구현하려면 어떻게해야합니까?

public abstract class A { 
    protected static A instance; 
    public static A getInstance() { 
     return instance; 
    } 
    //...rest of my abstract methods... 
} 

그리고 여기에 구체적인 구현 :

public class B extends A { 
    private B() { } 
    static { 
     instance = new B(); 
    } 
    //...implementations of my abstract methods... 
} 

불행하게도 나는 실행 클래스 B의 정적 코드를 얻을 수 없기 때문에 인스턴스 변수 절대로 설정되지 않습니다. 나는 이것을 시도 :

Class c = B.class; 
A.getInstance() - returns null; 

ClassLoader.getSystemClassLoader().loadClass("B"); 
A.getInstance() - return null; 

모두 실행 정적 코드가 실행되지 없구요 이클립스 디버거에서이. 정적 코드가 실행되도록하는 유일한 방법은 B의 생성자에 대한 액세스 가능성을 public으로 변경하고 호출하는 것입니다.

나는이 테스트를 실행하기 위해 Ubuntu 32bit에서 sun-java6-jre를 사용하고 있습니다.

답변

19

초록 Singleton? 나에게 생동감이 들리지 않는다. Singleton 패턴은 private 생성자를 필요로하므로 서브 클래 싱을 불가능하게 만듭니다. 디자인을 재고해야합니다. Abstract Factory pattern은 특정 목적에 더 적합 할 수 있습니다.

+0

싱글 톤에서 개인 생성자의 목적은 다른 누군가가 그것을 인스턴스화하는 것을 방지하기위한 것입니다. 여기에서이 작업을 수행했습니다. 추상 클래스를 인스턴스화 할 수 없으며 하위 클래스에는 전용 생성자가 있습니다. – Simon

+0

하위 클래스를 추상 전용으로 설정하고 전용 생성자 만 사용해야합니다. – BalusC

+0

싱글 톤은 개인 생성자를 필요로하지 않습니다 (공용 생성자를 사용하여이 질문에 대한 해결책을 보려면 내 대답을 참조하십시오). – ryvantage

4

A.getInstance()은 정적으로 연결되어 있으므로 파생 인스턴스를 호출하지 않습니다.

개체의 생성을 실제 개체 자체와 분리하고 특정 클래스 형식을 반환하는 적절한 factory을 만듭니다. 예제 코드가 주어진다면 매개 변수화 방법이 명확하지 않습니다. 어떤 인수를 통해 매개 변수화되었거나 클래스 선택이 정적입니까?

싱글 톤 btw를 다시 생각해 볼 수 있습니다. 일반적으로 반 패턴이며 테스트중인 클래스가 해당 클래스의 인스턴스를 싱글 톤으로 제공하므로 테스트 (특히)에 고통을줍니다. 더미 구현을 제공하거나 각 테스트에 대해 새 인스턴스를 쉽게 작성할 수는 없습니다.

+0

A.getInstance()는 인스턴스 변수를 반환하기 때문에 파생 인스턴스를 호출 할 필요가 없습니다. 파생 된 인스턴스 (클래스 B)는 인스턴스 변수를 설정합니다. 정적 코드 블록이 실행되지 않는 것을 제외하고는. 이 문제를 해결할 수없는 경우 팩토리 패턴이 유일한 옵션 일 수 있습니다. – Simon

4

싱글 톤은 종류가 yucky입니다. 가능한 경우 avoid을 원하지 않는 상속을 추상합니다. 전반적으로 당신이하려고하는 것이 simplest possible way이라면 다시 생각해보아야합니다. 그렇다면 팩토리가 테스트 인스턴스를 쉽게 대체하도록 할 수있는 반면에 싱글 톤이 아닌 공장을 사용해야합니다 (싱글 톤은 unit tests으로 대체하기가 어렵습니다.).

일단 팩토리로 구현하기 시작하면 추상적 인 것이 자체를 정렬합니다 (인터페이스가 필요하거나 인터페이스 대신 쉽게 제외 될 수 있음). 문제의 다른 사람에게

+0

+1은 싱글 톤이 안티 패턴이라는 것을 지적합니다. –

+1

싱글 톤은 그들의 위치를 ​​가지고 있지만, 일반적으로 당신이하는 일에 더 좋은 디자인 패턴이 있습니다. 로그가 아마도 싱글 톤에 대해 생각할 수있는 가장 좋은 예일 것입니다.이 방법을 사용하면 변수를 전달할 필요가없고 어디서나 액세스 할 수 있습니다. –

+0

로그는 주입으로 훨씬 잘 수행 할 수 있습니다. 싱글 톤 로그는 라인이 어떤 클래스에서 왔는지를 쉽게 알 수 없지만 Log l = new Log (this.getClass()) 또는 일부 varient는이를 나타낼 수 있습니다. 주입과 함께 그것은 단지 @ 유도 로그 또는 비슷한 것입니다. 싱글 톤은 로깅을 위해서조차도 테스트하기가 더 어렵습니다. 다시 한 번 싱글 톤은 최악의 선택에 불과합니다. (그러나 인정할 만하게 사용 가능합니다.) –

2

또한이 A에서 instance 필드 만 전체 VM에서 하나 싱글을 가질 수 있다는 것을 의미 가지는 지적했다. 당신은 또한이 경우

public class C extends A { 
    private C() { } 
    static { 
     instance = new C(); 
    } 
    //...implementations of my abstract methods... 
} 

을 ... 다음 중의 B 또는 C이 이길 것이다 마지막으로로드되는, 그리고 상대방의 싱글 인스턴스가 손실됩니다.

이것은 일을하는 단지 나쁜 방법입니다.

+0

감사합니다. 이것은 내 실제 문제가 내 인터페이스의 여러 구현 중 노출해야하는 것을 선택하는 방법이라는 것을 강조합니다. 공장이나 외관은 좋은 방법과 같습니다. – Simon

8

글쎄, 당신의 포스트에서, 명확하게 명시되지 않았더라도, 추상적 인 클래스가 두 가지 다른 역할을하도록하려는 것 같습니다. 역할은 다음과 같습니다

  • 여러 대체 가능한 구현을 가질 수 (싱글) 서비스에 대한 추상적 인 공장의 역할,
  • 서비스 인터페이스 역할,

플러스 당신이 원하는 싱글 톤이되는 서비스는 클래스 전체 패밀리에서 '싱글 톤 (singletoness)'을 시행합니다. 어떤 이유로 서비스 인스턴스를 캐싱하기에 충분하지 않습니다.

괜찮습니다.

"우려 사항 분리"와 "싱글 톤 및 단위 테스트가 서로 잘 어울리지 않기"때문에 누군가는 매우 나쁘다고 말합니다.

다른 사람들은 당신이 가족들에게 올바른 아이들을 인스턴스화하는 책임을 부여하고 또한 더 유창한 인터페이스를 전반적으로 노출하기 때문에 다른 어떤 것도하지 않는 공장을 중재 할 필요가 없기 때문에 다른 사람들은 괜찮다고 말합니다. 정적 메서드입니다.

부모 팩터 리 메소드가 반환해야하는 구현을 선택해야하는 책임을 자녀에게 부여한 후에 잘못된 점이 있습니다. 모든 어린이들에게 단순히 추상 수퍼 클래스로 푸시되고 중앙 집중화 된 것을 위임 할뿐만 아니라 다른 컨텍스트에서 사용되는 패턴을 함께 혼합한다는 것을 보여주기 때문에 디자인면에서 문제가 있습니다. 클래스 클라이언트는 얻을 것이다) 및 팩토리 메서드 (자식 팩토리는 클라이언트가 얻을 수있는 것을 선택합니다.)

팩토리 메서드는 "인스턴스"메서드를 구현하거나 재정의 할 때 중심이므로 팩터 리 메서드에서는 필요하지 않지만 사용할 수도 없습니다. 정적 메서드 나 생성자에 대한 재정의와 같은 것은 없습니다.

그래서 초기 행동을 선택하는 추상 싱글 톤의 초기 또는 좋지 않은 아이디어로 돌아가 보면 초기 문제를 해결할 수있는 몇 가지 방법이 있습니다. 다음 중 하나가 될 수 있지만 나쁘지는 않습니다. 당신은 찾고 있었다 :

public abstract class A{ 
    public static A getInstance(){ 
     if (...) 
     return B.getInstance(); 
     return C.getInstance(); 
    } 

    public abstract void doSomething(); 

    public abstract void doSomethingElse(); 

} 

public class B extends A{ 
    private static B instance=new B(); 

    private B(){ 
    } 

    public static B getInstance(){ 
     return instance; 
    } 

    public void doSomething(){ 
     ... 
    } 
    ... 
} 

//do similarly for class C 

부모는 또한 반사를 사용할 수 있습니다.

테스트 친화적이고 확장 성이 뛰어난 솔루션은 단순히 싱글 톤이 아닌 "싱글 톤 모방 (singleton mimiking)"정적 getInstance()를 노출 할 수있는 "개인"및 추상 부모로 문서화되는 일부 내부 패키지로 패키징 된 자식을 갖는 것입니다. 해당 클라이언트가 항상 동일한 서비스 인스턴스를 갖도록 강제하는 하위 인스턴스를 캐시합니다.

관련 문제