Singleton dengan Argumen di Jawa


142

Saya sedang membaca artikel Singleton di Wikipedia dan saya menemukan contoh ini:

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

Walaupun saya sangat menyukai cara Singleton ini berperilaku, saya tidak dapat melihat bagaimana mengadaptasinya untuk memasukkan argumen ke konstruktor. Apa cara yang disukai untuk melakukan ini di Jawa? Apakah saya harus melakukan sesuatu seperti ini?

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

Terima kasih!


Sunting: Saya pikir saya telah memulai badai kontroversi dengan keinginan saya untuk menggunakan Singleton. Biarkan saya menjelaskan motivasi saya dan semoga seseorang dapat menyarankan ide yang lebih baik. Saya menggunakan kerangka kerja komputasi grid untuk menjalankan tugas secara paralel. Secara umum, saya memiliki sesuatu seperti ini:

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

Apa yang terjadi adalah bahwa meskipun saya hanya meneruskan referensi ke data saya ke semua tugas, ketika tugas-tugas bersambung, data akan disalin berulang-ulang. Yang ingin saya lakukan adalah membagikan objek di antara semua tugas. Secara alami, saya dapat memodifikasi kelas seperti:

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

Seperti yang Anda lihat, bahkan di sini saya memiliki masalah bahwa melewati jalur file yang berbeda tidak ada artinya setelah yang pertama dilewati. Inilah sebabnya saya menyukai ide untuk toko yang diposting dalam jawaban. Bagaimanapun, daripada memasukkan logika untuk memuat file dalam metode run, saya ingin abstrak logika ini menjadi kelas Singleton. Saya tidak akan memberikan contoh lain, tetapi saya harap Anda mendapatkan idenya. Tolong biarkan saya mendengar ide Anda untuk cara yang lebih elegan untuk mencapai apa yang saya coba lakukan. Terima kasih lagi!


1
Pola pabrik adalah apa yang Anda inginkan. Idealnya, tugas kisi harus sepenuhnya independen dari hal lain dan dikirim semua data yang mereka butuhkan untuk mengeksekusi dan mengembalikan hasilnya. Namun, ini tidak selalu merupakan solusi yang paling layak, jadi membuat serialisasi data ke file bukanlah ide yang buruk. Saya pikir seluruh benda tunggal sedikit herring merah; Anda tidak ingin singleton.
oxbow_lakes

2
Sangat disayangkan bahwa Anda menggunakan istilah Singleton yang dilengkapi dengan barang bawaan seperti itu. Istilah yang tepat untuk pola ini adalah Interning sebenarnya. Interning adalah metode untuk memastikan bahwa nilai-nilai abstrak diwakili oleh satu contoh saja. Pemagangan string adalah penggunaan yang paling umum: en.wikipedia.org/wiki/String_intern_pool.
notnoop

Anda mungkin ingin melihat Terracotta. Itu mempertahankan identitas objek di cluster. Ketika Anda mengirim referensi ke data yang sudah ada di cluster itu tidak mendapatkan serialisasi ulang.
Taylor Gautier

21
Mengesampingkan masalah apakah pola tunggal harus digunakan, saya perhatikan bahwa hampir setiap jawaban di sini tampaknya mengasumsikan bahwa tujuan memberikan argumen adalah untuk memungkinkan "beberapa lajang" dibuat yang dibedakan oleh nilainya. dari parameter tersebut. Tetapi tujuan lain yang mungkin adalah untuk menyediakan akses ke objek eksternal yang merupakan satu - satunya objek dari jenisnya yang dibutuhkan oleh instance unik kelas tunggal . Jadi kita perlu membedakan parameter yang disediakan untuk akses seperti itu dari parameter yang dimaksudkan untuk membuat "banyak kejadian tunggal."
Carl

2
Skenario lain untuk "singleton with parameter": aplikasi web yang akan membangun singleton abadi unik berdasarkan informasi yang datang dengan permintaan (utas) pertama yang akan datang. Domain permintaan dapat menentukan perilaku beberapa orang lajang misalnya
fustaki

Jawaban:


171

Saya akan menjelaskan maksud saya: singleton dengan parameter bukan singleton .

Singleton, menurut definisi, adalah objek yang Anda inginkan tidak lebih dari satu kali. Jika Anda mencoba memberi makan parameter ke konstruktor, apa gunanya singleton?

Anda memiliki dua opsi. Jika Anda ingin singleton diinisialisasi dengan beberapa data, Anda dapat memuatnya dengan data setelah instantiation , seperti:

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

Jika operasi yang dilakukan singleton Anda berulang, dan dengan parameter yang berbeda setiap kali, Anda mungkin juga meneruskan parameter ke metode utama yang dijalankan:

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

Bagaimanapun, instantiasi akan selalu menjadi parameter-kurang. Kalau tidak, lajang Anda bukan lajang.


1
+1 Ini adalah bagaimana saya mungkin akan melakukannya saat coding. Dalam C #, saya hanya menggunakan properti. Jawa, mungkin seperti ini.
Zack

131
maaf itu tidak benar. ada situasi di mana Anda harus meneruskan parameter yang dibuat secara dinamis yang tetap sama untuk runtime aplikasi lubang. jadi Anda tidak bisa menggunakan konstanta di dalam singleton tetapi harus melewati konstanta itu ketika dibuat. setelah melewati sekali itu konstanta yang sama untuk waktu lubang. seorang penyetel tidak akan melakukan pekerjaan jika Anda memerlukan konstanta spesifik dalam konstruktor.
masi

1
@ mas, seperti kata penulis - ini bukan singleton. Jika Anda harus lulus dengan dinamik konstan, Anda mungkin perlu membuat banyak kelas dengan konstanta berbeda. Jadi, tidak ada gunanya singleton.
Dmitry Zaytsev

53
Jika Anda hanya membutuhkan satu instance kelas untuk seumur hidup aplikasi, tetapi Anda perlu memberikan nilai tersebut pada waktu peluncuran, mengapa ini bukan lagi singleton?
Oscar

4
"Jika kamu mencoba untuk memberi makan parameter ke konstruktor, apa gunanya singleton?" - Orang juga bisa mengatakan: "Jika Anda membuat seluruh aplikasi Anda satu contoh, apa gunanya argumen baris perintah?", Dan jawabannya adalah bahwa itu sangat masuk akal. Orang sekarang dapat mengatakan bahwa ini cukup berbeda dari kelas singleton, kecuali jika kelas tersebut sebenarnya kelas Utama yang menerima args [] dari metode utama - maka itu bahkan hal yang sama. Argumen terakhir, yang mungkin hanya bertahan, adalah bahwa ini adalah situasi yang cukup luar biasa.
Dreamspace President

41

Saya pikir Anda memerlukan sesuatu seperti pabrik untuk memiliki objek dengan berbagai parameter instantiated dan digunakan kembali. Ini dapat diimplementasikan dengan menggunakan sinkronisasi HashMapatau ConcurrentHashMapmemetakan parameter ( Integercontohnya) ke kelas parameterabel 'tunggal' Anda.

Meskipun Anda mungkin sampai pada titik di mana Anda harus menggunakan kelas non-singleton reguler (sebagai gantinya membutuhkan 10.000 singleton dengan parametrized berbeda).

Berikut adalah contoh untuk toko tersebut:

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

Untuk mendorongnya lebih jauh, Java enumjuga dapat dianggap (atau digunakan sebagai) lajang parametrized, meskipun hanya memperbolehkan varian statis nomor tetap.

Namun, jika Anda membutuhkan solusi 1 terdistribusi , pertimbangkan beberapa solusi caching lateral. Misalnya: EHCache, Terracotta, dll.

1 dalam arti menjangkau beberapa VM di beberapa komputer.


Ya, inilah tepatnya yang saya butuhkan. Terima kasih banyak! Saya setuju bahwa cara saya menangani argumen dalam contoh saya tidak masuk akal, tetapi saya tidak memikirkan hal ini. Lihat penjelasan saya di komentar jawaban oxbow_lakes.

1
Ini BUKAN singleton; Anda sekarang memiliki lebih dari satu. LOL
oxbow_lakes

@Scott: Saya menyarankan sesuatu seperti yang Yuval sarankan di bawah. Itu membuat sedikit lebih masuk akal dan Anda memiliki 'benar' tunggal. sunting
Zack

Saya harap tidak ada yang keberatan saya mengedit nama-nama dalam kode; Saya bisa membayangkan ini benar-benar membingungkan bagi pemula.
Kembalikan

Ya, kita bisa memanggil mereka Multitron dan masih mencapai tujuan yang sama yang diinginkan OP di tempat pertama IMHO.
akarnokd

22

Anda dapat menambahkan metode inisialisasi yang dapat dikonfigurasi untuk memisahkan instantiation dari mendapatkan.

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

}

Kemudian Anda dapat menelepon Singleton.init(123)sekali untuk mengkonfigurasinya, misalnya di startup aplikasi Anda.


13

Anda juga dapat menggunakan pola Builder jika Anda ingin menunjukkan bahwa beberapa parameter wajib.

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

    }


}

Kemudian Anda dapat membuat / membuat instance / parametrized sebagai berikut:

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

6

Pernyataan " singleton dengan parameter bukan singleton " tidak sepenuhnya benar . Kita perlu menganalisis ini dari perspektif aplikasi daripada dari perspektif kode.

Kami membangun kelas tunggal untuk membuat satu instance objek dalam satu aplikasi dijalankan. Dengan memiliki konstruktor dengan parameter, Anda dapat membangun fleksibilitas ke dalam kode Anda untuk mengubah beberapa atribut objek tunggal Anda setiap kali Anda menjalankan aplikasi Anda. Ini bukan pelanggaran pola Singleton. Sepertinya ini pelanggaran jika Anda melihatnya dari perspektif kode.

Pola Desain ada untuk membantu kita menulis kode yang fleksibel dan dapat diperpanjang, bukan untuk menghalangi kita menulis kode yang baik.


12
Ini bukan jawaban untuk pertanyaan OP, ini harus menjadi komentar.
Thierry J.

5

Gunakan getter dan setter untuk mengatur variabel dan menjadikan konstruktor default pribadi. Kemudian gunakan:

Singleton.getInstance().setX(value);

1
Jangan mengerti mengapa ini tidak dipilih .. Ini adalah jawaban yang valid tbh. : /
Zack

13
Karena itu adalah jawaban sampah. Misalnya, bayangkan sebuah sistem di mana nama pengguna dan kata sandi awal untuk admin awal adalah argumen konstruktor. Sekarang, jika saya menjadikan ini singleton dan melakukan apa yang Anda katakan, saya mendapatkan getter dan setter untuk admin, yang tidak sesuai dengan yang Anda inginkan. Jadi, meskipun opsi Anda mungkin valid dalam beberapa kasus, itu tidak benar-benar menjawab kasus umum yang menjadi pertanyaan. (ya, saya sedang mengerjakan sistem yang saya jelaskan dan tidak, saya tidak akan menggunakan pola singleton jika bukan karena fakta bahwa tugas tersebut mengatakan "gunakan pola singleton di sini")
Jasper

5

Terkejut bahwa tidak ada yang menyebutkan bagaimana logger dibuat / diambil. Sebagai contoh, di bawah ini menunjukkan bagaimana Log4J logger diambil.

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

Ada beberapa tingkatan tipuan, tetapi kuncinya adalah di bawah metode yang cukup banyak menceritakan segalanya tentang cara kerjanya. Ini menggunakan tabel hash untuk menyimpan logger yang ada dan kunci berasal dari nama. Jika logger tidak ada untuk memberi nama, logger menggunakan pabrik untuk membuat logger dan kemudian menambahkannya ke tabel hash.

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

Modifikasi pola Singleton yang menggunakan inisialisasi Bill Pugh pada idiom pemegang permintaan . Ini adalah utas aman tanpa overhead konstruksi bahasa khusus (yaitu volatile atau disinkronkan):

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

Saya pikir itu akan menjadi ide yang baik untuk finally { RInterfaceHL.rloopHandler = null; }masuk getInstance, karena referensi statis dapat menyebabkan kebocoran memori jika kita tidak hati-hati. Dalam kasus Anda sepertinya itu bukan masalah, tapi saya bisa membayangkan skenario di mana objek yang lewat besar dan hanya digunakan oleh RInterfaceHLctor untuk mendapatkan beberapa nilai, dan tidak menyimpan referensi untuk itu.
TWiStErRob

Ide: return SingletonHolder.INSTANCEakan bekerja dengan baik di getInstance. Saya tidak berpikir ada kebutuhan untuk enkapsulasi di sini, karena kelas luar sudah tahu jeroan kelas dalam, mereka erat digabungkan: ia tahu rloopHandlerperlu init sebelum memanggil. Juga konstruktor pribadi tidak memiliki efek, karena barang pribadi kelas dalam hanya tersedia untuk kelas luar.
TWiStErRob


3

Alasan mengapa Anda tidak bisa memahami bagaimana mencapai apa yang Anda coba lakukan mungkin karena apa yang Anda coba lakukan tidak benar-benar masuk akal. Anda ingin menelepon getInstance(x)dengan argumen yang berbeda, tetapi selalu mengembalikan objek yang sama? Perilaku apa yang Anda inginkan saat menelepon getInstance(2)dan kemudian getInstance(5)?

Jika Anda ingin objek yang sama tetapi untuk nilai internal yang berbeda, yang merupakan satu-satunya cara itu masih tunggal, maka Anda tidak perlu peduli sama sekali dengan konstruktor; Anda cukup menetapkan nilai pada getInstance()pada jalan keluar objek. Tentu saja, Anda mengerti bahwa semua referensi Anda yang lain tentang singleton sekarang memiliki nilai internal yang berbeda.

Jika Anda ingin getInstance(2)dan getInstance(5)mengembalikan objek yang berbeda, di sisi lain, Anda tidak menggunakan pola Singleton, Anda menggunakan pola Factory.


3

Dalam contoh Anda, Anda tidak menggunakan singleton. Perhatikan bahwa jika Anda melakukan hal berikut (dengan asumsi bahwa Singleton.getInstance sebenarnya statis):

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

Maka nilai-nilai obj2.x adalah 3, bukan 4. Jika Anda perlu melakukan ini, jadikan ini kelas biasa. Jika jumlah nilai kecil dan tetap, Anda dapat mempertimbangkan menggunakan enum. Jika Anda mengalami masalah dengan pembuatan objek berlebihan (yang biasanya tidak terjadi), maka Anda dapat mempertimbangkan nilai caching (dan memeriksa sumber atau mendapatkan bantuan dengan itu, karena jelas cara membuat cache tanpa bahaya kebocoran memori).

Anda juga mungkin ingin membaca artikel ini karena lajang dapat dengan mudah digunakan secara berlebihan.


3

Alasan lain mengapa Singletons anti-pola adalah bahwa jika ditulis sesuai rekomendasi, dengan konstruktor pribadi, mereka sangat sulit untuk subkelas dan terkonfigurasi untuk digunakan dalam unit test tertentu. Akan diperlukan dalam memelihara kode warisan, misalnya.


3

Jika Anda ingin membuat kelas Singleton yang berfungsi sebagai Konteks, cara yang baik adalah memiliki file konfigurasi dan membaca parameter dari file di dalam instance ().

Jika parameter yang memberi makan kelas Singleton didapat secara dinamis selama menjalankan program Anda, cukup gunakan HashMap statis yang menyimpan instance berbeda di kelas Singleton Anda untuk memastikan bahwa untuk setiap parameter, hanya satu instance dibuat.


1

Ini bukan singleton, tetapi mungkin sesuatu yang bisa memperbaiki masalah Anda.

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

Jika kita menganggap masalah sebagai "cara membuat singleton dengan status", maka tidak perlu meneruskan status sebagai parameter konstruktor. Saya setuju dengan posting yang menginisialisasi negara atau menggunakan metode set setelah mendapatkan instance singleton.

Pertanyaan lain adalah: apakah baik memiliki singleton dengan negara?


1

Tidak bisakah kita melakukan sesuatu seperti ini:

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

Terlepas dari apa yang beberapa orang mungkin nyatakan, ini adalah singleton dengan parameter dalam konstruktor

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

Pola tunggal mengatakan:

  • memastikan bahwa hanya satu instance dari kelas singleton yang pernah ada
  • memberikan akses global ke instance itu.

yang dihormati dengan contoh ini.

Mengapa tidak langsung mengatur properti? Ini kasus buku teks untuk menunjukkan bagaimana kita bisa mendapatkan singleton yang memiliki konstruktor dengan parameter tetapi bisa berguna dalam beberapa situasi. Misalnya dalam kasus warisan memaksa singleton untuk mengatur beberapa properti superclass.


0

Saya takut memposting ini sebagai jawaban, tapi saya tidak mengerti mengapa tidak ada yang berpikir tentang ini, mungkin jawaban ini juga sudah diberikan, saya hanya tidak memahaminya.

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

Karena getInstance()mengembalikan Instance yang sama setiap kali, saya pikir ini bisa berhasil. Jika ini salah pada banyak hal yang akan saya hapus, saya hanya tertarik dengan topik ini.


-1

Saya pikir ini adalah masalah umum. Memisahkan "inisialisasi" singleton dari "get" singleton mungkin berhasil (contoh ini menggunakan variasi penguncian ganda diperiksa).

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

    ...
}

Selalu bisa mengembalikan "hasil" dalam metode inisialisasi juga saya kira.
Michael Andrews

-2

Singleton, tentu saja, merupakan "anti-pola" (dengan asumsi definisi statis dengan keadaan variabel).

Jika Anda ingin satu set objek nilai tetap yang tidak dapat diubah, maka enum adalah cara untuk melakukannya. Untuk sekumpulan nilai besar, mungkin ujung terbuka, Anda bisa menggunakan Repositori dari beberapa bentuk - biasanya berdasarkan Mapimplementasi. Tentu saja, ketika Anda berurusan dengan statika, berhati-hatilah dengan threading (baik menyinkronkan cukup luas atau menggunakan ConcurrentMapbaik memeriksa bahwa utas lain belum mengalahkan Anda atau menggunakan beberapa bentuk futures).


4
Hanya anti-pola jika digunakan secara tidak benar, meskipun itu adalah definisi anti-pola. Hanya karena Anda telah melihat mereka di tempat yang bukan milik mereka di masa lalu tidak berarti bahwa mereka tidak memiliki tempat.
geowa4

Penggunaan singleton yang benar adalah untuk menunjukkan kode yang tidak kompeten.
Tom Hawtin - tackline

-6

Lajang umumnya dianggap anti-pola dan tidak boleh digunakan. Mereka tidak membuat kode mudah untuk diuji.

Lajang dengan argumen tidak masuk akal - apa yang akan terjadi jika Anda menulis:

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

Singleton Anda juga tidak aman utas karena beberapa utas dapat membuat panggilan simultan untuk getInstancemenghasilkan lebih dari satu instance yang dibuat (mungkin dengan nilai yang berbeda dari x).


Itu cukup bisa diperdebatkan.
AlbertoPL

1
Ya itu masih bisa diperdebatkan; karenanya saya menggunakan kata "umumnya". Saya pikir itu adil untuk mengatakan bahwa mereka umumnya dianggap ide yang buruk
oxbow_lakes

Dapat diperdebatkan - beberapa orang mengklaim bahwa apa yang disebut "anti-pola" sesuai dengan definisi pola, hanya saja mereka adalah pola yang buruk.
Tom Hawtin - tackline

Saya mengerti bahwa mereka jahat. Saya melakukan komputasi terdistribusi dan perlu berbagi objek di antara banyak tugas. Daripada secara deterministik menginisialisasi variabel statis, saya ingin abstrak logika menjadi Singleton. Saya membayangkan saya bisa membuat sinkronisasi InstInstance. Apakah ini akan berhasil? Yang perlu saya lakukan adalah memuat file sekali untuk banyak tugas, dan hanya setelah tugas pertama telah dikirim. (Saya tidak ingin data saya serial.) Saya pikir saya akan membuat argumen AbstractFileReader ke metode getInstance untuk membuat Singleton lebih fleksibel. Saya menghargai masukan Anda.

Saya pikir Anda mungkin salah mengerti apa artinya "didistribusikan"? Ada cara lain untuk mencapai apa yang Anda inginkan: sudahkah Anda mempertimbangkan suntikan ketergantungan? Atau JNDI?
oxbow_lakes
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.