programing

Java에서 인수를 사용하는 싱글턴

projobs 2022. 9. 25. 10:47
반응형

Java에서 인수를 사용하는 싱글턴

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;
    }
}

감사합니다!


편집:싱글톤을 사용하고 싶다는 욕구로 논란이 일었던 것 같습니다.저의 동기를 설명하겠습니다.누군가 더 좋은 아이디어를 제안해 주셨으면 합니다.그리드 컴퓨팅 프레임워크를 사용하여 태스크를 병렬로 실행하고 있습니다.일반적으로 다음과 같은 것이 있습니다.

// 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).
    }
}

보시다시피 여기서도 첫 번째 파일 경로가 전달된 후 다른 파일 경로를 전달해도 아무런 의미가 없다는 문제가 있습니다.그래서 답변에 게재되어 있던 가게의 아이디어가 마음에 듭니다.어쨌든 파일을 로드하는 로직을 실행 메서드에 포함시키는 것보다 이 로직을 싱글톤 클래스로 추상화하고 싶었습니다.또 다른 예를 들지는 않겠습니다만, 이해해주셨으면 합니다.제가 하고 싶은 일을 보다 우아하게 할 수 있는 방법을 알려주세요.다시 한 번 감사드립니다!

나는 내 요점을 분명히 할 것이다: 매개변수를 가진 싱글톤은 싱글톤이 아니다.

싱글톤이란, 정의상, 1회만 인스턴스화 하고 싶은 오브젝트입니다.파라미터를 컨스트럭터에 공급하려고 할 경우 싱글톤의 포인트는 무엇입니까?

두 가지 옵션이 있습니다.싱글톤을 일부 데이터로 초기화하려면 다음과 같이 인스턴스화데이터와 함께 로드할 수 있습니다.

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

어떤 경우에도 인스턴스화에는 항상 파라미터가 없습니다.그렇지 않으면 너의 싱글톤은 싱글톤이 아니다.

다양한 파라미터를 가진 오브젝트를 인스턴스화하고 재사용하기 위해서는 공장 같은 것이 필요하다고 생각합니다.이것은 동기화된 것을 사용하여 구현될 수 있습니다.HashMap ★★★★★★★★★★★★★★★★★」ConcurrentHashMapInteger독신, 독신이다.

단, 일반 비싱글톤 클래스를 사용해야 하는 경우가 있습니다(예를 들어 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;
        }
    }
}

더 , 는 「」를 하고 있습니다.enums는 고정 수 정적 변형만 허용하지만 매개변수화된 싱글톤으로 간주(또는 사용)할 수도 있다.

그러나 분산 솔루션이1 필요한 경우 몇 가지 측면 캐싱 솔루션을 고려해 보십시오.예를 들어 EHCache, Teracotta 등입니다.

1 여러 대의 컴퓨터에서 여러 대의 VM에 걸쳐 있다는 점에서요.

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

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)이 노래의 노래입니다.

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

    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();
}

로거 작성/검색 방법에 대해 아무도 언급하지 않아 놀랐습니다.예를 들어, 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)

몇 가지 수준의 지시 사항이 있지만, 핵심 부분은 작동 방식에 대한 모든 것을 알려주는 방법 아래에 있습니다.해시 테이블을 사용하여 기존 로거를 저장하고 키는 이름에서 파생됩니다.지정된 이름에 대한 로거가 존재하지 않는 경우 로거를 팩토리를 사용하여 작성한 후 해시 테이블에 추가합니다.

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      } 
...

"파라미터가 있는 싱글톤은 싱글톤이 아닙니다"라는 문장은 완전히 정확하지 않습니다.우리는 이것을 코드의 관점에서가 아니라 어플리케이션의 관점에서 분석할 필요가 있다.

싱글톤 클래스를 구축하여 한 번의 응용 프로그램 실행으로 객체의 단일 인스턴스를 만듭니다.파라미터가 있는 컨스트럭터를 사용하면 어플리케이션을 실행할 때마다 싱글톤 객체의 속성을 변경할 수 있는 유연성을 코드에 구축할 수 있습니다.이것은 싱글톤 패턴의 위반이 아닙니다.코드 관점에서 보면 위반으로 보입니다.

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

getters 및 setters를 사용하여 변수를 설정하고 기본 생성자를 비공개로 만듭니다.다음으로 다음을 사용합니다.

Singleton.getInstance().setX(value);

Bill Pugh의 Initialization on Demand holder idiod를 사용한 Singleton 패턴 수정.이는 특수한 언어 구조(휘발성 또는 동기화)의 오버헤드가 없는 스레드 세이프입니다.

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);
    }
}

콘텍스트로서 기능하는 싱글톤클래스를 작성하는 경우는 컨피규레이션파일을 가지고 instance 파일()에서 파라미터를 읽어내는 것이 좋습니다.

프로그램 실행 중에 싱글톤클래스에 공급되는 파라미터가 동적으로 취득되는 경우 싱글톤클래스에 다른 인스턴스를 저장하는 스태틱해시맵을 사용하면 파라미터별로 인스턴스가1개만 생성됩니다.

당신이 하려는 일을 어떻게 달성해야 하는지 이해할 수 없는 이유는 아마도 당신이 하려고 하는 일이 정말로 말이 안 되기 때문일 것이다.하고 getInstance(x)항상 같은 오브젝트를 반환할 수 있습니까?「 」에 콜을 걸었을 때, 어떤 ?getInstance(2) 다음에 또 한 번.getInstance(5)

하는 , 쓸 필요가 은 에서 .그 값을 설정하기만 하면 됩니다.getInstance()에 대한 참조는 다른 값을 것을 물론 싱글톤에 대한 다른 모든 언급은 이제 다른 내부 가치를 가지고 있다는 것을 알고 있습니다.

네가 원한다면getInstance(2) ★★★★★★★★★★★★★★★★★」getInstance(5)다른 객체를 반환하려면 싱글톤 패턴을 사용하는 것이 아니라 공장 패턴을 사용합니다.

이 예에서는 싱글톤을 사용하지 않습니다.다음 작업을 수행할 경우(Singleton.getInstance가 실제로는 스태틱한 것으로 가정함)에 주의해 주십시오.

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

obj2.3.x의 이 아니라 3.x의 입니다.이 작업을 수행해야 하는 경우 일반 클래스로 만드십시오.되어 있는 는, 「」의 .enum과도한 오브젝트 생성에 문제가 있는 경우(보통은 그렇지 않습니다), 캐싱 값을 고려할 수 있습니다(메모리 누수의 위험 없이 캐시를 구축하는 방법은 명백하므로 소스 확인 또는 도움을 받을 수 있습니다).

싱글톤은 매우 쉽게 남용될 수 있으므로 이 기사를 읽는 것이 좋습니다.

싱글톤이 안티패턴인 또 다른 이유는 권장사항에 따라 프라이빗컨스트럭터를 사용하여 기술할 경우 특정 유닛테스트에서 사용하도록 서브클래스와 설정이 매우 어렵다는 점입니다.예를 들어 레거시 코드를 유지하는 데 필요합니다.

이것은 그다지 독신적인 것은 아니지만, 당신의 문제를 해결할 수 있을 것입니다.

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;
  }
}

이런 식으로 하면 안 될까?

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;
    }
}

답변으로 올리기는 겁나지만, 왜 아무도 이것에 대해 생각하지 않는지 이해할 수 없습니다.아마 이 답변도 이미 나온 것일지도 모릅니다.그냥 이해가 안 돼요.

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()매번 같은 인스턴스가 반환됩니다. 저는가 있습니다.만약 이것이 크게 잘못되었다면 삭제하겠습니다, 저는 단지 이 주제에 관심이 있을 뿐입니다.

이 문제를 "state와 함께 singleton을 만드는 방법"으로 간주할 경우 상태를 생성자 매개 변수로 전달할 필요가 없습니다.싱글톤 인스턴스를 받은 후 상태를 초기화하거나 set 메서드를 사용하는 게시물에 동의합니다.

또 다른 질문은: 싱글톤과 주(州)가 함께 하는 것이 좋은가?

일부에서 주장하는 것과 달리, 여기 생성자에 매개 변수가 있는 싱글톤이 있습니다.

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
     */
}

싱글톤 패턴은 다음과 같습니다.

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

이 예에서 존중받는 것들입니다.

속성을 직접 설정하지 그래요?파라미터가 있는 컨스트럭터를 가진 싱글톤을 어떻게 구할 수 있는지 보여주는 전형적인 케이스이지만 상황에 따라서는 유용할 수 있습니다.예를 들어 상속의 경우 싱글톤이 일부 슈퍼클래스 속성을 설정하도록 강제합니다.

파라미터를 한 번만 초기화하여 덮어쓰지 않도록 하려면 체크 앤 슬로우 예외를 구현하여 누군가 파라미터를 다시 초기화하려고 하면 됩니다.예:

public class Service {

private String host = null;
private String port = null;

private Service() {
}

private static class ServiceSingletonHolder {

    private static final Service INSTANCE = new Service();
}

public static Service getInstance() {
    return ServiceSingletonHolder.INSTANCE;
}

public void initilize(String host, String port) {
    if (this.host != null && host != null) {
        throw new IllegalArgumentException("host can not be overwritten");
    }

    if (this.port != null && port != null) {
        throw new IllegalArgumentException("port can not be overwritten");
    }

    this.host = host;
    this.port = port;
}
}

이게 흔한 문제인 것 같아요.싱글톤의 "초기화"를 싱글톤의 "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;
    }

    ...
}

싱글톤은 물론 '반패턴'입니다(변수 상태의 스태틱 정의를 전제로 하고 있습니다).

고정 세트의 불변값 개체를 원하는 경우 enum을 사용하는 것이 좋습니다.오픈엔드의 대규모 값 집합의 경우, 어떤 형식의 저장소를 사용할 수 있습니다(일반적으로,Map실행.물론, 통계학을 다룰 때는 스레드에 주의해야 합니다(충분히 광범위하게 동기화하거나ConcurrentMap다른 실타래가 당신을 이기지 않았는지 확인하거나 어떤 형태로든 미래를 이용하거나 둘 중 하나죠.

싱글톤은 일반적으로 안티패턴으로 간주되므로 사용하지 마십시오.코드를 테스트하기 쉽게 만들지 않습니다.

논쟁을 벌이는 독신자는 어쨌든 말이 안 된다. 만약 다음과 같이 쓴다면 어떻게 될까?

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

싱글톤은 여러 스레드가 동시에 콜을 발신할 수 있기 때문에 스레드 세이프도 아닙니다.getInstance 인스턴스가 값을 가 생성됨).x를 참조해 주세요.

언급URL : https://stackoverflow.com/questions/1050991/singleton-with-arguments-in-java

반응형