Java에서 인수가있는 싱글 톤


142

나는 Wikipedia에서 Singleton 기사를 읽고 있었고이 예제를 보았습니다.

public class Singleton {
    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    /**
     * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
     * or the first access to SingletonHolder.INSTANCE, not before.
     */
    private static class SingletonHolder { 
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

이 싱글 톤의 동작 방식이 정말 마음에 들지만, 생성자에 인수를 통합하도록 조정하는 방법을 볼 수 없습니다. Java에서이를 수행하는 선호되는 방법은 무엇입니까? 이런 식으로해야합니까?

public class Singleton
{
    private static Singleton singleton = null;  
    private final int x;

    private Singleton(int x) {
        this.x = x;
    }

    public synchronized static Singleton getInstance(int x) {
        if(singleton == null) singleton = new Singleton(x);
        return singleton;
    }
}

감사!


편집 : 나는 Singleton을 사용하려는 열망으로 논쟁의 폭풍을 시작했다고 생각합니다. 내 동기 부여를 설명하고 누군가가 더 나은 아이디어를 제안 할 수 있기를 바랍니다. 그리드 컴퓨팅 프레임 워크를 사용하여 작업을 병렬로 실행하고 있습니다. 일반적으로 다음과 같은 것이 있습니다.

// AbstractTask implements Serializable
public class Task extends AbstractTask
{
    private final ReferenceToReallyBigObject object;

    public Task(ReferenceToReallyBigObject object)
    {
        this.object = object;
    }

    public void run()
    {
        // Do some stuff with the object (which is immutable).
    }
}

결과는 데이터에 대한 참조를 모든 작업에 전달하더라도 작업이 직렬화되면 데이터가 계속 복사되는 것입니다. 내가하고 싶은 일은 모든 작업에서 객체를 공유하는 것입니다. 당연히 클래스를 다음과 같이 수정할 수 있습니다.

// AbstractTask implements Serializable
public class Task extends AbstractTask
{
    private static ReferenceToReallyBigObject object = null;

    private final String filePath;

    public Task(String filePath)
    {
        this.filePath = filePath;
    }

    public void run()
    {
        synchronized(this)
        {
            if(object == null)
            {
                ObjectReader reader = new ObjectReader(filePath);
                object = reader.read();
            }
        }

        // Do some stuff with the object (which is immutable).
    }
}

보시다시피, 여기에서도 다른 파일 경로를 전달하면 첫 번째 경로가 전달 된 후에 아무것도 의미가 없다는 문제가 있습니다. 이것이 내가 아이디어를 좋아하는 이유입니다. 답변에 게시 된 상점에 입니다. 어쨌든 run 메소드에 파일을로드하는 논리를 포함시키는 대신이 논리를 Singleton 클래스로 추상화하고 싶었습니다. 나는 또 다른 예를 제공하지는 않지만 아이디어를 얻길 바랍니다. 내가하려는 일을 더 우아하게 수행 할 수있는 방법에 대한 귀하의 아이디어를 들려주세요. 다시 감사합니다!


1
공장 패턴은 당신이 원하는 것입니다. 이상적으로 그리드 작업은 다른 작업과 완전히 독립적이어야하며 실행하고 결과를 반환하는 데 필요한 모든 데이터를 전송해야합니다. 그러나 이것이 항상 가장 적합한 솔루션은 아니므로 데이터를 파일로 직렬화하는 것이 그렇게 나쁘지는 않습니다. 싱글 톤 전체가 약간 청어 인 것 같습니다. 싱글 톤을 원하지 않습니다.
oxbow_lakes

2
그러한 수하물과 함께 제공되는 싱글 톤이라는 용어를 사용한 것은 매우 불행한 일입니다. 이 패턴에 대한 적절한 용어는 실제로 인턴입니다. 인터 닝은 추상 값이 하나의 인스턴스로만 표시되도록하는 방법입니다. 문자열 인턴은 가장 일반적인 사용법입니다 : en.wikipedia.org/wiki/String_intern_pool
notnoop

테라코타를보고 싶을 수도 있습니다. 클러스터 전체에서 개체 ID를 유지 관리합니다. 이미 클러스터에있는 데이터에 대한 참조를 보내면 다시 직렬화되지 않습니다.
Taylor Gautier 2016 년

21
싱글 톤 패턴을 사용해야하는지의 문제를 제쳐두고, 여기에있는 거의 모든 대답은 인수를 제공하는 목적이 가치에 의해 구별되는 "다중 싱글 톤"을 만들 수있게하는 것이라고 가정하는 것 같습니다. 상기 파라미터의 그러나 또 다른 가능한 목적은 제공하는 액세스 는 IS 외부 개체에 대한 유일한 종류가 싱글 톤 클래스 '의 객체 고유의 인스턴스를 의지 적 필요. 따라서 이러한 액세스를 위해 제공된 매개 변수를 "다중 싱글 톤 인스턴스"를 작성하려는 매개 변수와 구별해야합니다.
Carl

2
"매개 변수가있는 단일 시나리오"에 대한 또 다른 시나리오 : 첫 번째 예정된 요청 (스레드)과 함께 제공되는 정보를 기반으로 고유 한 변경 불가능한 싱글 톤을 작성하는 웹 응용 프로그램입니다. 요청의 도메인은 예를 들어 싱글 톤의 동작을 결정할 수 있습니다
fustaki

답변:


171

내 요점을 매우 명확하게 설명하겠습니다 . 매개 변수가있는 싱글 톤은 싱글 톤이 아닙니다 .

정의에 따르면 싱글 톤은 한 번만 인스턴스화하려는 객체입니다. 생성자에 매개 변수를 공급하려는 경우 싱글 톤의 요점은 무엇입니까?

두 가지 옵션이 있습니다. 단일 데이터를 일부 데이터로 초기화하려면 인스턴스화 후 다음 과 같이 데이터로로드 할 수 있습니다 .

SingletonObj singleton = SingletonObj.getInstance();
singleton.init(paramA, paramB); // init the object with data

싱글 톤이 수행하는 작업이 반복되고 매번 다른 매개 변수를 사용하는 경우 실행중인 기본 메소드에 매개 변수를 전달할 수도 있습니다.

SingletonObj singleton = SingletonObj.getInstance();
singleton.doSomething(paramA, paramB); // pass parameters on execution

어쨌든 인스턴스화는 항상 매개 변수가 없습니다. 그렇지 않으면 싱글 톤이 싱글 톤이 아닙니다.


1
+1 이것이 코딩 할 때 아마 그렇게 할 것입니다. C #에서는 속성을 사용합니다. Java는 아마 이것과 같습니다.
Zack

131
죄송합니다. 사실이 아닙니다. 홀 응용 프로그램 런타임에 대해 동일하게 유지되는 동적으로 작성된 매개 변수를 전달해야하는 상황이 있습니다. 따라서 싱글 톤 내에서 상수를 사용할 수 없지만 생성 될 때 해당 상수를 전달해야합니다. 구멍 시간 동안 동일한 상수를 한 번 통과 한 후. 생성자 내에서 특정 상수가 필요한 경우 setter가 작업을 수행하지 않습니다.
masi

1
저자가 말했듯이 @masi는 싱글 톤이 아닙니다. 상수 동적 분석을 통과해야하는 경우 상수가 다른 이러한 클래스를 많이 만들어야 할 수도 있습니다. 따라서 싱글 톤에는 아무런 의미가 없습니다.
Dmitry Zaytsev

53
응용 프로그램의 전체 수명 동안 하나의 클래스 인스턴스 만 필요하지만 시작시 해당 인스턴스에 값을 제공해야하는 경우 더 이상 싱글 톤이 아닌 이유는 무엇입니까?
오스카

4
"매개 변수를 생성자에게 공급하려고하면 싱글 톤의 요점은 무엇입니까?" "전체 응용 프로그램을 단일 인스턴스로 만들면 명령 줄 인수의 요점은 무엇입니까?"라고 말할 수도 있습니다. 그 대답은 많은 의미가 있습니다. 클래스가 실제로 main 메소드에서 args []를 수신하는 Main 클래스 인 경우를 제외하고는 이것이 싱글 톤 클래스와는 상당히 다르다고 말할 수 있습니다. 막연한 마지막 주장은 이것이 상당히 예외적 인 상황이라는 것입니다.
Dreamspace President

41

다양한 매개 변수가있는 객체를 인스턴스화하고 재사용하려면 팩토리 와 같은 것이 필요하다고 생각합니다 . 동기화를 사용하여 구현 HashMap하거나 ConcurrentHashMap매개 변수 ( Integer예를 들어)를 'singleton'매개 변수화 가능 클래스에 매핑 하여 구현할 수 있습니다 .

대신 싱글 톤이 아닌 일반 클래스를 사용해야하는 시점에 도달 할 수 있습니다 (예 : 10.000 개의 다르게 매개 변수화 된 싱글 톤이 필요함).

이러한 상점의 예는 다음과 같습니다.

public final class UsefulObjFactory {

    private static Map<Integer, UsefulObj> store =
        new HashMap<Integer, UsefulObj>();

    public static final class UsefulObj {
        private UsefulObj(int parameter) {
            // init
        }
        public void someUsefulMethod() {
            // some useful operation
        }
    }

    public static UsefulObj get(int parameter) {
        synchronized (store) {
            UsefulObj result = store.get(parameter);
            if (result == null) {
                result = new UsefulObj(parameter);
                store.put(parameter, result);
            }
            return result;
        }
    }
}

더 나아가서, enum고정 수의 정적 변형 만 허용하더라도 Java 는 매개 변수가있는 싱글 톤으로 간주되거나 사용 될 수 있습니다.

그러나 분산 1 솔루션 이 필요한 경우 측면 캐싱 솔루션을 고려하십시오. 예 : EHCache, Terracotta 등

1 아마 여러 대의 컴퓨터에 여러 개의 VM을 스패닝 의미한다.


예, 이것이 바로 내가 필요한 것입니다. 대단히 감사합니다! 필자의 예제에서 인수를 처리하는 방식이 그다지 의미가 없지만 동의하지 않았습니다. oxbow_lakes의 답변에 대한 내 설명을 참조하십시오.

1
이것은 싱글 톤 이 아닙니다 . 이제 둘 이상이 있습니다. LOL
oxbow_lakes 2016 년

@ 스콧 : 유발이 아래에 제안한 것과 같은 것을 제안하고 싶습니다. 좀 더 의미가 있고 '진정한'싱글 톤이 있습니다. 편집
Zack

코드에서 이름을 편집하는 사람이 아무도 없기를 바랍니다. 나는 이것이 초보자에게 정말 혼란 스럽다고 상상할 수 있습니다. 동의하지 않을 경우 롤백
oxbow_lakes 2016 년

예, 우리는 그들을 멀티 트론 (Multitron)이라고 부르면서도 여전히 OPHO가 원하는 첫 번째 목표 IMHO와 같은 목표를 달성 할 수있었습니다.
akarnokd

22

인스턴스화와 가져 오기를 분리하기 위해 구성 가능한 초기화 방법을 추가 할 수 있습니다.

public class Singleton {
    private static Singleton singleton = null;
    private final int x;

    private Singleton(int x) {
        this.x = x;
    }

    public static Singleton getInstance() {
        if(singleton == null) {
            throw new AssertionError("You have to call init first");
        }

        return singleton;
    }

    public synchronized static Singleton init(int x) {
        if (singleton != null)
        {
            // in my opinion this is optional, but for the purists it ensures
            // that you only ever get the same instance when you call getInstance
            throw new AssertionError("You already initialized me");
        }

        singleton = new Singleton(x);
        return singleton;
    }

}

그런 다음 Singleton.init(123)앱 시작과 같이 한 번만 호출 하여 구성 할 수 있습니다 .


13

일부 매개 변수가 필수임을 표시하려는 경우 빌더 패턴을 사용할 수도 있습니다.

    public enum EnumSingleton {

    INSTANCE;

    private String name; // Mandatory
    private Double age = null; // Not Mandatory

    private void build(SingletonBuilder builder) {
        this.name = builder.name;
        this.age = builder.age;
    }

    // Static getter
    public static EnumSingleton getSingleton() {
        return INSTANCE;
    }

    public void print() {
        System.out.println("Name "+name + ", age: "+age);
    }


    public static class SingletonBuilder {

        private final String name; // Mandatory
        private Double age = null; // Not Mandatory

        private SingletonBuilder(){
          name = null;
        }

        SingletonBuilder(String name) {
            this.name = name;
        }

        public SingletonBuilder age(double age) {
            this.age = age;
            return this;
        }

        public void build(){
            EnumSingleton.INSTANCE.build(this);
        }

    }


}

그런 다음 다음과 같이 생성 / 인스턴스화 / 매개 변수화 할 수 있습니다 .

public static void main(String[] args) {
    new EnumSingleton.SingletonBuilder("nico").age(41).build();
    EnumSingleton.getSingleton().print();
}

6

" 매개 변수가있는 싱글 톤은 싱글 톤아닙니다 "문이 완전히 올바르지 않습니다 . 코드 관점이 아닌 응용 프로그램 관점에서이를 분석해야합니다.

단일 응용 프로그램 실행에서 단일 객체 클래스를 만들기 위해 단일 클래스를 빌드합니다. 매개 변수가있는 생성자를 사용하면 코드를 유연하게 만들어 응용 프로그램을 실행할 때마다 싱글 톤 객체의 일부 속성을 변경할 수 있습니다. 이것은 싱글 톤 패턴을 위반하지 않습니다. 코드 관점에서 볼 때 위반으로 보입니다.

디자인 패턴은 유연하고 확장 가능한 코드를 작성하는 데 도움이되고 좋은 코드를 작성하는 데 방해가되지 않습니다.


12
이것은 OP 질문에 대한 답변이 아니며 의견이어야합니다.
Thierry J.

5

getter 및 setter를 사용하여 변수를 설정하고 기본 생성자를 비공개로 설정하십시오. 그런 다음 사용하십시오.

Singleton.getInstance().setX(value);

1
왜 투표가 거부되었는지 이해하지 마십시오. 올바른 답변 tbh. : /
Zack

13
그것은 쓰레기 답변이기 때문에. 예를 들어, 초기 관리자의 초기 사용자 이름과 비밀번호가 생성자 인수 인 시스템을 상상해보십시오. 자, 이것을 싱글 톤으로 만들고 말한대로하면 관리자를위한 게터와 세터를 얻습니다. 원하는 것은 아닙니다. 따라서 어떤 경우에는 귀하의 옵션이 유효 할 수 있지만 실제로 일반적인 질문에 대답하지는 않습니다. (예, 내가 설명한 시스템에서 작업 중이며 아니오, 과제가 "여기에 싱글 톤 패턴 사용"이라고 표시되어 있지 않다면 싱글 톤 패턴을 사용하지 않았을 것입니다)
Jasper

5

로거 작성 / 검색 방법을 언급 한 사람이 아무도 없습니다. 예를 들어, 아래는 Log4J 로거 를 검색 하는 방법을 보여줍니다 .

// Retrieve a logger named according to the value of the name parameter. If the named logger already exists, then the existing instance will be returned. Otherwise, a new instance is created.
public static Logger getLogger(String name)

이 indirections의 몇 가지 수준이있다, 그러나 중요한 부분은 아래 방법을 거의 작동 방법에 대한 모든 것을 알려줍니다. 해시 테이블을 사용하여 종료 로거를 저장하고 키는 이름에서 파생됩니다. 이름으로 로거가 존재하지 않으면 팩토리를 사용하여 로거를 작성한 다음 해시 테이블에 추가합니다.

69   Hashtable ht;
...
258  public
259  Logger getLogger(String name, LoggerFactory factory) {
260    //System.out.println("getInstance("+name+") called.");
261    CategoryKey key = new CategoryKey(name);
262    // Synchronize to prevent write conflicts. Read conflicts (in
263    // getChainedLevel method) are possible only if variable
264    // assignments are non-atomic.
265    Logger logger;
266
267    synchronized(ht) {
268      Object o = ht.get(key);
269      if(o == null) {
270        logger = factory.makeNewLoggerInstance(name);
271        logger.setHierarchy(this);
272        ht.put(key, logger);
273        updateParents(logger);
274        return logger;
275      } else if(o instanceof Logger) {
276        return (Logger) o;
277      } 
...

4

Bill Pugh의 주문형 홀더 이디엄 초기화 를 사용하는 싱글 톤 패턴 수정 . 이것은 특수 언어 구조 (예 : 휘발성 또는 동기화)의 오버 헤드없이 스레드 안전합니다.

public final class RInterfaceHL {

    /**
     * Private constructor prevents instantiation from other classes.
     */
    private RInterfaceHL() { }

    /**
     * R REPL (read-evaluate-parse loop) handler.
     */
    private static RMainLoopCallbacks rloopHandler = null;

    /**
     * SingletonHolder is loaded, and the static initializer executed, 
     * on the first execution of Singleton.getInstance() or the first 
     * access to SingletonHolder.INSTANCE, not before.
     */
    private static final class SingletonHolder {

        /**
         * Singleton instance, with static initializer.
         */
        private static final RInterfaceHL INSTANCE = initRInterfaceHL();

        /**
         * Initialize RInterfaceHL singleton instance using rLoopHandler from
         * outer class.
         * 
         * @return RInterfaceHL instance
         */
        private static RInterfaceHL initRInterfaceHL() {
            try {
                return new RInterfaceHL(rloopHandler);
            } catch (REngineException e) {
                // a static initializer cannot throw exceptions
                // but it can throw an ExceptionInInitializerError
                throw new ExceptionInInitializerError(e);
            }
        }

        /**
         * Prevent instantiation.
         */
        private SingletonHolder() {
        }

        /**
         * Get singleton RInterfaceHL.
         * 
         * @return RInterfaceHL singleton.
         */
        public static RInterfaceHL getInstance() {
            return SingletonHolder.INSTANCE;
        }

    }

    /**
     * Return the singleton instance of RInterfaceHL. Only the first call to
     * this will establish the rloopHandler.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @return RInterfaceHL singleton instance
     * @throws REngineException
     *             if REngine cannot be created
     */
    public static RInterfaceHL getInstance(RMainLoopCallbacks rloopHandler)
            throws REngineException {
        RInterfaceHL.rloopHandler = rloopHandler;

        RInterfaceHL instance = null;

        try {
            instance = SingletonHolder.getInstance();
        } catch (ExceptionInInitializerError e) {

            // rethrow exception that occurred in the initializer
            // so our caller can deal with it
            Throwable exceptionInInit = e.getCause();
            throw new REngineException(null, exceptionInInit.getMessage());
        }

        return instance;
    }

    /**
     * org.rosuda.REngine.REngine high level R interface.
     */
    private REngine rosudaEngine = null;

    /**
     * Construct new RInterfaceHL. Only ever gets called once by
     * {@link SingletonHolder.initRInterfaceHL}.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @throws REngineException
     *             if R cannot be loaded.
     */
    private RInterfaceHL(RMainLoopCallbacks rloopHandler)
            throws REngineException {

        // tell Rengine code not to die if it can't
        // load the JRI native DLLs. This allows
        // us to catch the UnsatisfiedLinkError
        // ourselves
        System.setProperty("jri.ignore.ule", "yes");

        rosudaEngine = new JRIEngine(new String[] { "--no-save" }, rloopHandler);
    }
}

정적 참조는 조심하지 않으면 메모리 누수가 발생할 수 있기 때문에 finally { RInterfaceHL.rloopHandler = null; }에 좋은 아이디어라고 생각합니다 getInstance. 귀하의 경우 문제가 아닌 것처럼 보이지만 전달 된 객체가 크고 RInterfaceHLctor가 일부 값을 가져 와서 참조하지 않는 시나리오를 상상할 수 있습니다.
TWiStErRob 12

아이디어 : return SingletonHolder.INSTANCE에서 잘 작동 getInstance합니다. 외부 클래스는 이미 내부 클래스의 내부를 알고 있기 때문에 캡슐화가 필요하지 않다고 생각합니다. 밀접하게 결합되어 있습니다. rloopHandler전화하기 전에 초기화 가 필요 하다는 것을 알고 있습니다. 내부 클래스의 개인 항목은 외부 클래스에서 간단하게 사용할 수 있기 때문에 개인 생성자는 효과가 없습니다.
TWiStErRob

1
링크가 끊어졌습니다. en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom 을 참조하고 있습니까?
Jorge Lavín 2016 년

3

당신이하려는 일을 성취하는 방법을 이해할 수없는 이유는 아마도 당신이하려는 일이 실제로 의미가 없기 때문일 것입니다. getInstance(x)다른 인수 로 호출 하고 싶지만 항상 같은 객체를 반환합니까? 전화 할 때 당신은 어떤 동작을 원하지되어 getInstance(2)다음과 getInstance(5)?

동일한 객체이지만 내부 값이 다르기를 원합니다. 이것이 여전히 싱글 톤 인 유일한 방법 인 경우, 생성자를 전혀 신경 쓰지 않아도됩니다. getInstance()객체의 나갈 때 값을 설정하기 만하면 됩니다. 물론 싱글 톤에 대한 다른 모든 참조는 이제 다른 내부 값을 가짐을 이해합니다.

반면에 다른 객체 를 원 getInstance(2)하고 getInstance(5)반환하려면 Singleton 패턴을 사용하지 않고 Factory 패턴을 사용합니다.


3

귀하의 예에서는 싱글 톤을 사용하지 않습니다. Singleton.getInstance가 실제로 정적이라고 가정하면 다음을 수행하십시오.

Singleton obj1 = Singleton.getInstance(3);
Singleton obj2 = Singleton.getInstance(4);

그런 다음 obj2.x의 값은 4가 아니라 3입니다.이 작업을 수행해야하는 경우 일반 클래스로 만드십시오. 값 수가 작고 고정 된 경우을 사용하는 것이 enum좋습니다. 과도한 객체 생성에 문제가있는 경우 (보통 그렇지 않은 경우), 값을 캐싱하는 것을 고려할 수 있습니다 (메모리 누수의 위험없이 캐시를 작성하는 방법이 분명하므로 소스를 확인하거나 도움을 얻을 수 있음).

싱글 톤을 너무 쉽게 과도하게 사용할 수 있으므로이 기사읽어보십시오 .


3

Singletons가 안티 패턴 인 또 다른 이유는 권장 사항에 따라 개인 생성자를 사용하여 작성하면 서브 클래스를 작성하고 특정 단위 테스트에서 사용하기가 매우 어렵다는 것입니다. 예를 들어 레거시 코드를 유지 관리하는 데 필요합니다.


3

컨텍스트 역할을하는 Singleton 클래스를 만들려면 구성 파일을 갖고 instance () 내의 파일에서 매개 변수를 읽는 것이 좋습니다.

프로그램을 실행하는 동안 Singleton 클래스를 공급하는 매개 변수가 동적으로 제공되는 경우 Singleton 클래스에 다른 인스턴스를 저장하는 정적 HashMap을 사용하여 각 매개 변수에 대해 하나의 인스턴스 만 생성되도록하십시오.


1

이것은 싱글 톤은 아니지만 문제를 해결할 수있는 것일 수 있습니다.

public class KamilManager {

  private static KamilManager sharedInstance;

  /**
   * This method cannot be called before calling KamilManager constructor or else
   * it will bomb out.
   * @return
   */
  public static KamilManager getInstanceAfterInitialized() {
    if(sharedInstance == null)
        throw new RuntimeException("You must instantiate KamilManager once, before calling this method");

    return sharedInstance;
}

  public KamilManager(Context context, KamilConfig KamilConfig) {
    //Set whatever you need to set here then call:
  s  haredInstance = this;
  }
}

1

"상태를 사용하여 싱글 톤을 만드는 방법"으로 문제를 해결하면 상태를 생성자 매개 변수로 전달할 필요가 없습니다. 단일 인스턴스를 얻은 후 상태를 초기화하거나 set 메소드를 사용하는 게시물에 동의합니다.

또 다른 질문은 상태와 싱글 톤을 갖는 것이 좋은가?


1

우리는 이런 식으로 할 수 없습니다 :

public class Singleton {

    private int x;

    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    /**
     * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
     * or the first access to SingletonHolder.INSTANCE, not before.
     */
    private static class SingletonHolder { 
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(int x) {
        Singleton instance = SingletonHolder.INSTANCE;
        instance.x = x;
        return instance;
    }
}

1

어떤 사람들이 주장 할 수는 있지만 생성자에 매개 변수가있는 싱글 톤이 있습니다.

public class Singleton {

    private static String aParameterStored;

    private static final Singleton instance = new Singleton("Param to set");

    private Singleton() {
        // do nothing
    }

    private Singleton(String param) {
        aParameterStored = param;
    }

    public static Singleton getInstance() {
        return instance;
    }

    /*
     * ... stuff you would like the singleton do
     */
}

싱글 톤 패턴 말한다 :

  • 싱글턴 클래스의 인스턴스가 하나만 존재하는지 확인하십시오.
  • 해당 인스턴스에 대한 글로벌 액세스를 제공합니다.

이 예제와 관련이 있습니다.

왜 속성을 직접 설정하지 않습니까? 매개 변수가있는 생성자를 갖는 싱글 톤을 얻는 방법을 보여주는 교과서 사례이지만 일부 상황에서는 유용 할 수 있습니다. 예를 들어 상속 사례에서 싱글 톤이 일부 슈퍼 클래스 속성을 설정하도록 강제합니다.


0

나는 이것을 대답으로 게시하는 것이 무서워하지만 아무도 이것에 대해 생각하지 않는 이유를 이해하지 못합니다. 아마도이 대답도 이미 이해하지 못했습니다.

public class example  {
    private volatile static example instance;

    private String string;
    private int iInt = -1; //any number you know you don't want to use here

  private example() {

    //In case someone uses the private method to create a new Instance
    if (instance != null){
      throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
    }
  }

  public synchronized static example getIsntance(){
    if(instance == null){
      instance = new example();
    }
    return instance;
  }

public void methodDoingWork(){
    if(checkInit()){
      //DoSome
    }
  }

  private boolean checkInit(){
    boolean filled = (this.string != null) && (this.iInt != -1);
    return filled;
  }

  public void setString(String string) {
    if(this.string == null){
      this.string = string;
    }else{
      throw new RuntimeException("You try to override an already setValue"); 
    }
  }

  public void setiInt(int iInt) {
    if(this.iInt == -1){
      this.iInt = iInt;
    }else{
      throw new RuntimeException("You try to override an already setValue");
    }
  }
}

getInstance()매번 동일한 인스턴스를 반환하기 때문에 이것이 효과가 있다고 생각합니다. 이것이 많이 잘못되면 삭제하겠습니다.이 주제에 관심이 있습니다.


-1

이것이 일반적인 문제라고 생각합니다. 싱글 톤의 "초기화"를 싱글 톤의 "get"에서 분리하면 작동 할 수 있습니다 (이 예에서는 이중 검사 잠금 변형을 사용함).

public class MySingleton {

    private static volatile MySingleton INSTANCE;

    @SuppressWarnings("UnusedAssignment")
    public static void initialize(
            final SomeDependency someDependency) {

        MySingleton result = INSTANCE;

        if (result != null) {
            throw new IllegalStateException("The singleton has already "
                    + "been initialized.");
        }

        synchronized (MySingleton.class) {
            result = INSTANCE;

            if (result == null) {
                INSTANCE = result = new MySingleton(someDependency);
            } 
        }
    }

    public static MySingleton get() {
        MySingleton  result = INSTANCE;

        if (result == null) {
            throw new IllegalStateException("The singleton has not been "
                    + "initialized. You must call initialize(...) before "
                    + "calling get()");
        }

       return result;
    }

    ...
}

내가 생각 한대로 initialize 메소드에서 항상 "result"를 반환 할 수 있습니다.
Michael Andrews

-2

물론 싱글 톤은 "반 패턴 (anti-pattern)"입니다 (가변 상태의 정적 정의를 가정).

불변 값 객체의 고정 세트를 원한다면 열거 형이 좋습니다. 대규모의 개방형 값 집합의 경우 일반적으로 Map구현을 기반으로하는 일부 형식의 리포지토리를 사용할 수 있습니다 . 물론 정적을 다룰 때 스레딩에주의하십시오 (충분히 광범위하게 동기화하거나 ConcurrentMap다른 스레드가 당신을 때리지 않았는지 또는 어떤 형태의 미래를 사용하지 않았는지 확인).


4
반대로 사용 된 경우에만 안티 패턴 만 정의됩니다. 안티 패턴의 정의입니다. 과거에 소속되지 않은 곳을 본다고해서 장소가 없다는 의미는 아닙니다.
geowa4 2016 년

싱글 톤을 올바르게 사용하는 것은 무능한 코드를 보여주는 것입니다.
Tom Hawtin-tackline

-6

싱글 톤은 일반적으로 반 패턴 으로 간주되며 사용해서는 안됩니다. 코드를 쉽게 테스트 할 수 없습니다.

논쟁이있는 싱글 톤은 어쨌든 말이되지 않습니다-당신이 쓴다면 어떻게 될까요 :

Singleton s = SingletonHolder.getInstance(1);
Singleton t = SingletonHolder.getInstance(2); //should probably throw IllegalStateException

여러 스레드가 동시에 호출하여 둘 이상의 인스턴스가 생성 될 수 있으므로 (다른 값이 다른) 단일 인스턴스 도 스레드로부터 안전하지 않습니다 .getInstancex


상당히 논쟁의 여지가 있습니다.
AlbertoPL 2016 년

1
그렇습니다. 따라서 "일반적으로"라는 단어를 사용합니다. 나는 그들이 일반적으로 나쁜 생각으로 간주된다고 말하는 것이 공정하다고 생각합니다
oxbow_lakes

어떤 사람들은 "반 패턴 (anti-patterns)"이 패턴의 정의에 적합하다고 주장하는데, 그것은 단지 잘못된 패턴이라는 것입니다.
Tom Hawtin-tackline

나는 그들이 나쁘다는 것을 이해합니다. 분산 컴퓨팅을 수행하고 있으며 여러 작업간에 개체를 공유해야합니다. 정적 변수를 결정적으로 초기화하는 대신 논리를 Singleton으로 추상화하고 싶습니다. getInstance를 동기화 할 수 있다고 생각합니다. 이게 효과가 있을까요? 내가해야 할 일은 많은 작업에 대해 파일을 한 번로드하고 첫 번째 작업이 전송 된 후에 만로드됩니다. (데이터를 직렬화하고 싶지 않습니다.) Singleton을보다 유연하게 만들기 위해 AbstractFileReader를 getInstance 메서드에 대한 인수로 만들 것이라고 생각했습니다. 당신의 의견을 소중히 여깁니다.

"분배 된"의 의미를 오해 할 수 있다고 생각하십니까? 원하는 것을 달성하는 다른 방법이 있습니다 : 의존성 주입을 고려 했습니까? 아니면 JNDI?
oxbow_lakes 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.