Java'da Bağımsız Değişkenlere Sahip Singleton


142

Wikipedia'daki Singleton makalesini okuyordum ve bu örnekle karşılaştım:

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

Bu Singleton'un davranış tarzını gerçekten sevmeme rağmen, bunu yapıcıya argümanları dahil etmek için nasıl uyarlayacağımı göremiyorum. Java ile bunu yapmanın tercih edilen yolu nedir? Böyle bir şey yapmam gerekir mi?

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

Teşekkürler!


Edit: Sanırım Singleton kullanma arzumla bir tartışma fırtınası başlattım. Motivasyonumu açıklayayım ve umarım birisi daha iyi bir fikir önerebilir. Paralel görevleri yürütmek için bir ızgara bilgi işlem çerçevesi kullanıyorum. Genel olarak, böyle bir şey var:

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

Ne olur ki, tüm görevlere sadece verilerime bir referans göndermeme rağmen, görevler seri hale getirildiğinde, veriler tekrar tekrar kopyalanır. Yapmak istediğim, nesneyi tüm görevler arasında paylaşmak. Doğal olarak, sınıfı şöyle değiştirebilirim:

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

Gördüğünüz gibi, burada bile, farklı bir dosya yolundan geçmenin, ilk dosya geçildikten sonra hiçbir şey ifade etmediği sorunu var. Bu yüzden cevaplarda yayınlanan bir mağaza fikrini seviyorum . Her neyse, dosyayı run yöntemine yüklemek için mantığı eklemek yerine, bu mantığı bir Singleton sınıfına soyutlamak istedim. Başka bir örnek vermeyeceğim, ama umarım bu fikri anlarsınız. Lütfen yapmaya çalıştığım şeyi başarmanın daha zarif bir yolu için fikirlerinizi duymama izin verin. Tekrar teşekkürler!


1
Fabrika deseni istediğiniz şeydir. İdeal olarak, ızgara görevleri her şeyden tamamen bağımsız olmalı ve yürütmek ve sonuçlarını döndürmek için ihtiyaç duydukları tüm verileri gönderilmelidir. Ancak, bu her zaman en uygun çözüm değildir, bu nedenle verilerin bir dosyaya serileştirilmesi o kadar da kötü bir fikir değildir. Bence tek tek şey biraz kırmızı ringa balığı; bir singleton istemezsin.
oxbow_lakes

2
Böyle bir bagajla birlikte gelen Singleton terimini kullanmanız oldukça talihsiz bir durumdur. Bu model için uygun terim aslında Stajyerliktir. Staj, soyut değerlerin yalnızca bir örnekle temsil edilmesini sağlayan bir yöntemdir. Dize stajı en yaygın kullanım şeklidir: en.wikipedia.org/wiki/String_intern_pool.
notnoop

Terracotta'ya bir göz atmak isteyebilirsiniz. Küme genelinde nesne kimliğini korur. Zaten kümede bulunan verilere bir başvuru gönderdiğinizde, yeniden serileştirilmez.
Taylor Gautier

21
Singleton paterninin kullanılmasının gerekip gerekmediği konusunu bir kenara bırakarak, hemen hemen her cevabın bir argüman sağlama amacının değerle ayırt edilen "çoklu tektonların" yaratılmasına izin vermek olduğunu varsayarım. adı geçen parametrenin. Ancak bir başka olası amaç, singleton sınıfının benzersiz örneğinin ihtiyaç duyacağı türünün tek nesnesi olan harici bir nesneye erişim sağlamaktır . Bu nedenle, bu erişim için sağlanan bir parametreyi "birden çok tekli örnek" oluşturmayı amaçlayan bir parametreden ayırmamız gerekir.
Carl

2
"Parametreli singleton" için başka bir senaryo: ilk yaklaşan istek (thread) ile gelen bilgilere dayanarak benzersiz değişmez singletonunu oluşturacak bir web uygulamasıdır. İsteğin alanı bazı singletonların davranışlarını belirleyebilir
fustaki

Yanıtlar:


171

Demek istediğim çok netleştireceğim: parametreleri olan bir singleton bir singleton değil .

Tekil, tanım gereği, bir kereden fazla başlatılmasını istediğiniz bir nesnedir. Parametreleri yapıcıya beslemeye çalışıyorsanız, singleton'un amacı nedir?

İki seçeneğiniz var. Singleton'unuzun bazı verilerle başlatılmasını istiyorsanız, örneklemeden sonra verileri aşağıdaki gibi yükleyebilirsiniz :

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

Singletonunuzun gerçekleştirdiği işlem yineleniyorsa ve her seferinde farklı parametrelerle, parametreleri yürütülen ana yönteme de aktarabilirsiniz:

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

Her durumda, örnekleme her zaman parametresiz olacaktır. Aksi takdirde singletonunuz singleton değildir.


1
+1 Kodlama yaparken muhtemelen böyle yapardım. C #, ben sadece özellikleri olsa kullanmak istiyorum. Java, muhtemelen böyle.
Zack

131
üzgünüm, bu doğru değil. delik uygulama çalışma zamanı için aynı kalan dinamik olarak oluşturulmuş parametreleri iletmeniz gereken durumlar vardır. böylece singleton içinde bir sabit kullanamazsınız ama oluşturulduğunda bu sabiti geçmeniz gerekir. bir kez geçtikten sonra delik süresi için aynı sabit. yapıcı içinde belirli bir sabite ihtiyacınız varsa bir ayarlayıcı işi yapmaz.
masi

1
@masi, yazarın dediği gibi - bu bir singleton değil. Sabit dinamiği geçmeniz gerekiyorsa, farklı sabitleri olan bu tür sınıflar oluşturmanız gerekebilir. Yani, singleton'da bir anlamı yok.
Dmitry Zaytsev

53
Bir uygulamanın tüm ömrü boyunca yalnızca bir sınıf örneğine ihtiyacınız varsa, ancak bu örneğe başlatma zamanında bir değer sağlamanız gerekiyorsa, bu neden artık tekil bir öğe değildir?
Oscar

4
"Kurucuya parametreleri beslemeye çalışıyorsanız singleton'ın amacı nedir?" - Birisi de şöyle diyebilir: "Tüm uygulamanızı tek bir örnek yaparsanız, komut satırı argümanlarının anlamı nedir?" Ve yanıt çok mantıklı olmasıdır. Şimdi bunun tek bir sınıftan oldukça farklı olduğunu söyleyebiliriz, ancak sınıf aslında ana yöntemden args [] alan Ana sınıfsa, o zaman aynı şeydir. Sadece ayakta durabilecek son argüman, bunun oldukça istisnai bir durum olduğudur.
Düşler Uzay Başkanı

41

Sanırım çeşitli parametreleri olan nesnelerin örneklenmesi ve yeniden kullanılması için fabrika gibi bir şeye ihtiyacınız var. Senkronize edilerek HashMapveya ConcurrentHashMapbir parametre ( Integerörnek olarak) 'singleton' parametrelenebilir sınıfınızla eşleştirilerek uygulanabilir .

Bunun yerine normal, tekil olmayan sınıfları kullanmanız gereken noktaya gelseniz de (örneğin 10.000 farklı parametreli tektona ihtiyaç duymak).

İşte böyle bir mağaza için bir örnek:

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

Daha da ileriye itmek için, enumJava'lar sadece sabit sayıdaki statik varyantlara izin vermesine rağmen parametreli tektonlar olarak da düşünülebilir (veya kullanılabilir).

Ancak, dağıtılmış 1 bir çözüme ihtiyacınız varsa , bazı yanal önbellekleme çözümünü düşünün. Örneğin: EHCache, Terracotta, vb.

1 muhtemelen birden çok bilgisayara birden fazla sanal makineyi yaymak anlamında.


Evet, tam da ihtiyacım olan bu. Çok teşekkür ederim! Örneğimde argümanları ele alma şeklimin çok anlamlı olmadığını kabul ediyorum, ama bunu düşünmedim. Benim açıklamamın oxbow_lakes 'cevabının yorumlarına bakın.

1
Bu bir singleton DEĞİL ; şimdi bunlardan birden fazlasına sahipsiniz. LOL
oxbow_lakes

@Scott: Yuval'ın önerdiği gibi bir şey öneririm. Biraz daha mantıklı ve 'gerçek' bir singletonunuz var. edit
Zack

Umarım kimse bana koddaki isimleri düzenlemeyi düşünmez; Bunun yeni başlayanlar için gerçekten kafa karıştırıcı olduğunu hayal edebiliyorum.
Kabul

Evet, onlara Multitron diyebiliriz ve yine de OP'nin IMHO'da istediği aynı hedefe ulaşabiliriz.
akarnokd

22

Anlamanın alınmasını ayırmak için yapılandırılabilir bir başlatma yöntemi ekleyebilirsiniz.

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

}

Ardından Singleton.init(123), örneğin uygulama başlangıcınızda yapılandırmak için bir kez arayabilirsiniz .


13

Bazı parametrelerin zorunlu olduğunu göstermek istiyorsanız Oluşturucu desenini de kullanabilirsiniz.

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

    }


}

Ardından aşağıdaki gibi / instantiate / parametrize edebilirsiniz:

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

6

" Parametreli bir singleton bir singleton değil " ifadesi tamamen doğru değil . Bunu kod perspektifi yerine uygulama perspektifinden analiz etmeliyiz.

Bir uygulama çalışmasında bir nesnenin tek bir örneğini oluşturmak için singleton sınıfı oluşturuyoruz. Parametreli bir kurucuya sahip olarak, uygulamanızı her çalıştırdığınızda singleton nesnenizin bazı özelliklerini değiştirmek için kodunuzda esneklik oluşturabilirsiniz. Bu Singleton modelinin ihlali değildir. Bunu kod perspektifinden görürseniz bir ihlal gibi görünüyor.

Tasarım Kalıpları, iyi kod yazmamızı engellememek için esnek ve genişletilebilir kod yazmamıza yardımcı olmak için vardır.


12
Bu OP sorusuna bir cevap değil, bu bir yorum olmalı.
Thierry J.

5

Değişkeni ayarlamak ve varsayılan yapıcıyı özel yapmak için alıcıları ve ayarlayıcıları kullanın. Sonra kullan:

Singleton.getInstance().setX(value);

1
Bunun neden aşağı oy verildiğini anlamayın. Bu geçerli bir cevap tbh. : /
Zack

13
Çünkü bu saçma bir cevap. Örneğin, ilk yönetici için ilk kullanıcı adının ve parolanın yapıcı bağımsız değişkenleri olduğu bir sistem düşünün. Şimdi, bunu bir singleton yaparsam ve dediğin gibi yaparsam, yönetici için getters ve setters alırım, ki bu tam olarak istediğin şey değil. Bu nedenle, seçeneğiniz bazı durumlarda geçerli olsa da, soru olan genel duruma gerçekten cevap vermez. (evet, ben açıklanan sistemde çalışıyorum ve atama "Burada bir singleton deseni kullanmak" diyor gerçeği olmasaydı hayır, bir Singleton deseni kullanmak olmazdı)
Jasper

5

Hiç kimse bir günlükçünün nasıl oluşturulduğunu / alındığını söylemedi. Örneğin, aşağıda Log4J günlükçünün nasıl alındığı gösterilmektedir .

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

Bazı dolaylı seviyeler vardır, ancak kilit kısım, nasıl çalıştığı hakkında her şeyi anlatan yöntemin altındadır . Çıkış günlükçülerini saklamak için bir karma tablo kullanır ve anahtar addan türetilir. Günlükçü bir verme adı için mevcut değilse, günlükçiyi oluşturmak için bir fabrika kullanır ve bunu karma tablosuna ekler.

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'un talep sahibi deyiminde başlatılmasını kullanan Singleton deseninin değiştirilmesi . Bu, özel dil yapılarının (yani geçici veya senkronize) ek yükü olmadan iş parçacığı için güvenlidir:

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

Ben buna iyi bir fikir olacağını düşünüyorum finally { RInterfaceHL.rloopHandler = null; }içinde getInstancebiz dikkatli olmazsak bu statik referans bellek sızıntısına neden olabilir, çünkü. Sizin durumunuzda bu bir sorun gibi görünmüyor, ancak geçirilen nesnenin büyük olduğu ve sadece RInterfaceHLbazı değerleri almak için ctor tarafından kullanılan ve buna başvurmamak için bir senaryo hayal edebiliyorum .
TWiStErRob

Fikir: return SingletonHolder.INSTANCEiyi çalışır getInstance. Burada enkapsülasyona ihtiyaç olduğunu düşünmüyorum, çünkü dış sınıf iç sınıfın iç kısımlarını zaten biliyor, sıkıca bağlılar: rloopHandleraramadan önce init ihtiyaçlarını biliyor . Özel kurucunun da bir etkisi yoktur, çünkü iç sınıfın özel eşyaları dış sınıf tarafından kullanılabilir.
TWiStErRob

1
Bağlantı koptu. En.wikipedia.org/wiki/Initialization-on-demand_holder_idiom'dan bahsediyor muydunuz ?
Jorge Lavín

3

Yapmaya çalıştığınız şeyi nasıl gerçekleştireceğinizi anlayamamanızın nedeni, muhtemelen yapmaya çalıştığınız şeyin gerçekten mantıklı olmamasıdır. Aramak istiyorsungetInstance(x)Farklı argümanlarla , ama her zaman aynı nesneyi mi döndürüyorsunuz? Aradığınızda getInstance(2)ve sonra istediğiniz davranış nedir getInstance(5)?

Aynı nesneyi ancak dahili değerinin farklı olmasını istiyorsanız, bu hala tek bir yoldur, o zaman yapıcıyı hiç önemsemeniz gerekmez; sadece getInstance()nesnenin çıkış yolunda değeri girersiniz. Elbette, singleton'a yapılan tüm diğer referanslarınızın artık farklı bir dahili değere sahip olduğunu anlıyorsunuz.

İsterseniz getInstance(2)ve getInstance(5)diğer taraftan, farklı nesneleri döndürmek için, size Fabrika deseni kullanıyorsanız, Singleton deseni kullanmıyoruz.


3

Örneğinizde bir singleton kullanmıyorsunuz. Aşağıdakileri yaparsanız (Singleton.getInstance öğesinin gerçekten statik olduğunu varsayarak) dikkat edin:

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

Daha sonra obj2.x'in değerleri 4 değil, 3'tür. Bunu yapmanız gerekiyorsa, bunu düz bir sınıf yapın. Değerlerin sayısı küçük ve sabitse, birenum . Aşırı nesne oluşturma ile ilgili sorun yaşıyorsanız (genellikle durum böyle değildir), önbellek değerlerini düşünebilirsiniz (ve bellek sızıntısı tehlikesi olmadan önbelleklerin nasıl oluşturulacağı açık olduğu için kaynakları kontrol edebilir veya bu konuda yardım alabilirsiniz).

Tek tektonlar çok kolay kullanılabileceğinden bu makaleyi de okumak isteyebilirsiniz .


3

Singleton'ların bir anti-desen olmasının bir başka nedeni, önerilere göre yazılırsa, özel kurucu ile, alt sınıflara ayırmak ve belirli birim testlerinde kullanmak için yapılandırmak çok zor olmasıdır. Örneğin, eski kodun korunmasında gerekli olur.


3

Bağlam görevi gören bir Singleton sınıfı oluşturmak istiyorsanız, iyi bir yol bir yapılandırma dosyasına sahip olmak ve örnek () içindeki dosyadan parametreleri okumaktır.

Singleton sınıfını besleyen parametreler programınızın çalışması sırasında dinamik olarak alınıyorsa, her bir parametre için yalnızca bir örnek oluşturulduğundan emin olmak için Singleton sınıfınızda farklı örnekleri depolayan statik bir HashMap kullanın.


1

Bu bir singleton değil, sorununuzu çözebilecek bir şey olabilir.

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

Sorunu "durum ile singleton nasıl yapılır" olarak alırsak, durumu yapıcı parametresi olarak geçirmek gerekli değildir. Singleton örneğini aldıktan sonra durumları başlatan veya set yöntemini kullanan gönderileri kabul ediyorum.

Başka bir soru şudur: Devletle singletona sahip olmak iyi mi?


1

Böyle bir şey yapamaz mıydık:

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

Bazılarının iddia edebileceklerine rağmen, burada yapıcıda parametreleri olan bir singleton var

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

Singleton deseni şunları söylüyor:

  • singleton sınıfının yalnızca bir örneğinin var olduğundan emin olun
  • söz konusu örneğe genel erişim sağlar.

bu örnekle saygı duyulan

Neden mülkü doğrudan ayarlamıyorsunuz? Parametre ile yapıcıya sahip bir singleton'u nasıl alabileceğimizi göstermek için ders kitabı örneği, ancak bazı durumlarda yararlı olabilir. Örneğin, miras durumlarında singleton'u bazı üst sınıf özellikleri ayarlamaya zorlamak.


0

Bunu bir cevap olarak göndermekten korkuyorum, ama neden kimse bunu düşünmediğini anlamıyorum, belki de bu cevap zaten verildi, ben anlamadım.

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

Yana getInstance()getiriler aynı Örneğini her, ben bu işe yarayabilir. Bu çok yanlışsa, onu sileceğim, sadece bu konuyla ilgileniyorum.


-1

Bunun ortak bir sorun olduğunu düşünüyorum. Singletonun "başlangıcını", singletonun "get" sinden ayırmak işe yarayabilir (bu örnekte, çift kontrollu kilitleme varyasyonu kullanılır).

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

    ...
}

Sanırım her zaman başlatma yöntemi de "sonuç" dönebilir.
Michael Andrews

-2

Singleton, elbette, bir "anti-desen" dir (değişken durumlu bir statik tanımını varsayarak).

Sabit bir değişmez değer nesneleri kümesi istiyorsanız, numaralandırmalar gitmek için yoldur. Büyük, muhtemelen açık uçlu bir değerler kümesi için, genellikle bir Mapuygulamaya dayalı olarak bazı formlarda bir Depo kullanabilirsiniz . Tabii ki, statikle uğraşırken diş açmaya dikkat edin (ya yeterince geniş senkronize edin ya ConcurrentMapda başka bir iş parçacığının sizi dövmediğini kontrol etmek ya da bir çeşit gelecek kullanın).


4
Sadece bir anti-desen yanlış kullanılırsa, bu bir anti-desen tanımıdır. Onları geçmişte ait olmadıkları yerde görmüş olmanız, onların yeri olmadığı anlamına gelmez.
geowa4

Tek birtonun doğru kullanımı, yetersiz kod göstermektir.
Tom Hawtin - tackline

-6

Singletonlar genellikle anti-desen olarak kabul edilir ve kullanılmamalıdır. Kodun test edilmesini kolaylaştırmazlar.

Tartışmalı bir singleton zaten bir anlam ifade etmiyor - sen yazsaydın ne olurdu:

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

Birden çok iş parçacığı birden getInstancefazla eşgörünüm oluşturulmasına (muhtemelen farklı değerlerle x) eş zamanlı çağrılar yapabileceğinden , singletonunuz da iş parçacığı için güvenli değildir .


Bu oldukça tartışmalı.
AlbertoPL

1
Evet tartışmalıdır; dolayısıyla "genel" kelimesini kullanıyorum. Genellikle kötü bir fikir olarak kabul edildiğini söylemek adil
oxbow_lakes

Tartışmalı - bazı insanlar "anti-patern" denilen şeyin patern tanımına uyduğunu iddia ediyorlar, sadece kötü paternler olduklarını iddia ediyorlar.
Tom Hawtin - tackline

Kötü olduklarını anlıyorum. Dağıtılmış bilgi işlem yapıyorum ve bir nesneyi birden çok görev arasında paylaşmam gerekiyor. Statik bir değişkeni deterministik olarak başlatmak yerine, mantığı bir Singleton'a soyutlamak istiyorum. GetInstance'ı senkronize edebileceğimi hayal ediyorum. Bu işe yarar mı? Yapmam gereken, birçok görev için bir kez ve sadece ilk görev gönderildikten sonra bir dosya yüklemek. (Verilerimin serileştirilmesini istemiyorum.) Ben AbstractFileReader SingleI daha esnek hale getirmek için getInstance yöntemi için bir argüman yapmak düşündüm. Girişinize değer veriyorum.

Bence “dağıtılmış” ne demek yanlış anlayabilirsiniz? İstediğinizi başarmanın başka yolları da var: bağımlılık enjeksiyonunu düşündünüz mü? Veya JNDI?
oxbow_lakes
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.