Cara memastikan instance unik kelas?

14

Saya mencari cara yang berbeda untuk memastikan bahwa setiap instance dari kelas yang diberikan adalah instance yang dapat diidentifikasi secara unik.

Sebagai contoh, saya memiliki Namekelas dengan bidang name. Setelah saya memiliki Nameobjek dengan nameinisialisasi ke John Smith, saya tidak ingin dapat instantiate Nameobjek yang berbeda juga dengan nama sebagai John Smith, atau jika instantiasi terjadi saya ingin referensi ke objek asli untuk dilewatkan kembali bukan dari objek baru.

Saya sadar bahwa salah satu cara untuk melakukan ini adalah memiliki pabrik statis yang menampung Mapsemua objek Nama saat ini dan pabrik memeriksa bahwa sebuah objek dengan John Smith sebagai namanya belum ada sebelum meneruskan referensi ke Nameobyek.

Cara lain yang bisa saya pikirkan dari atas kepala saya adalah memiliki Peta statis di Namekelas dan ketika konstruktor disebut melempar pengecualian jika nilai yang diteruskan namesudah digunakan di objek lain, namun saya sadar melemparkan pengecualian dalam konstruktor umumnya merupakan ide yang buruk .

Apakah ada cara lain untuk mencapai ini?

Kacang
sumber
1
Anda menginginkan seorang singleton
5
Anda sebaiknya pergi dengan yang pertama: - pabrik statis
Rohit Jain
16
@MarcB op tidak ingin singleton. dia mungkin memiliki banyak instance dari kelas yang sama, tetapi instance ini harus dapat diidentifikasi.
1
@ MarcB Saya sadar akan pola singleton tapi saya pikir hanya memastikan satu instance kelas saja? Saya ingin beberapa contoh, nilai yang berbeda. Maaf jika pertanyaannya tidak menjelaskan. sunting: Hanya melihat komentar pertama sebelum memposting.
Kacang
2
I'm aware that one way of doing this is to have a static factory that holds a Map...Jadi mengapa Anda tidak ingin melakukannya dengan cara ini?
FrustratedWithFormsDesigner

Jawaban:

13

Sebenarnya Anda sudah menjawab pertanyaan Anda. Cara pertama Anda harus lebih efektif di sini. Menggunakan static factoryselalu lebih disukai daripada di constructormana pun Anda pikir Anda bisa. Jadi, Anda dapat menghindari penggunaan Constructordalam kasus ini, jika tidak, Anda akan memiliki throw some exceptioninstance dengan nama yang diberikan.

Jadi, Anda dapat membuat metode pabrik statis: - getInstanceWithName(name)yang akan mendapatkan instance yang sudah tersedia dengan nama itu, dan jika tidak ada, itu akan membuat instance baru, dan menjadikan constructorpribadi Anda , karena sebagian besar harus dilakukan ketika berhadapan dengan static factories.

Juga, untuk itu Anda perlu mempertahankan statis Listatau Mapsemua contoh unik yang dibuat, di Factorykelas Anda .

EDIT : -

Anda tentu harus melalui - Java Efektif - Item # 1: Pertimbangkan pabrik Statis daripada Konstruktor . Anda tidak bisa mendapatkan penjelasan yang lebih baik daripada buku itu.

Rohit Jain
sumber
1
Itu sesuatu yang sudah dipikirkan OP.
BGurung
+1 untuk tautan, yang tampaknya secara jelas mengatasi beberapa masalah OP.
Robert Harvey
@RobertHarvey. Ya buku itu adalah sumber terbaik untuk sebagian besar topik seperti ini. Dan itulah barang pertama sebenarnya. :)
Rohit Jain
+1 untuk Java yang Efektif, saya punya buku, duduk di tas saya sekarang sebenarnya :) Namun saya hanya ingin tahu cara-cara lain untuk mencapai keunikan selain dari pabrik statis / metode statis dan pengecualian dalam konstruktor. Jika tidak ada maka saya akan menerima ini.
Kacang
@Kacang. Tentu. Itu adalah sumber terbaik untuk menggali informasi lebih lanjut.
Rohit Jain
5

Sebutan Java Efektif tampaknya menambah banyak kredibilitas sehingga jawaban ini didasarkan pada:

  • Efektif Java Item 8: Patuhi kontrak umum ketika mengesampingkan sama dengan
  • Efektif Java Item 9: Selalu menimpa kode hash saat Anda menimpa sama dengan
  • Efektif Java Item 15: Minimalkan mutabilitas

Saya akan mengambil langkah mundur dan mempertanyakan mengapa Anda peduli jika ada lebih dari satu contoh objek nama ini.

Saya jarang perlu melakukan semacam ini pengumpulan objek. Ini adalah dugaan saya bahwa OP melakukan ini sehingga mereka dapat dengan mudah membandingkan Nameobjek mereka ==. Atau gunakan Namebenda - benda di dalam HashMapatau serupa dengan kunci.

Jika demikian, ini adalah sesuatu yang dapat diselesaikan melalui implementasi yang tepatequals() .

Seperti itu:

public final class Name {
  private final String name;

  public Name(String name) {
    if (name == null) {
      name = ""; //or exception if you like
    }
    this.name = name;
  }

  public String getName() {
    return name;
  }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof Name)) {
      return false;
    }
    Name other = (Name) o;
    return other.name.equals(name);
  }

  @Override
  public int hashCode() {
    return name.hashCode();
  }
}

Setelah selesai, yang berikut ini benar:

Name a = new Name("weston");
Name b = new Name("weston");
assert(a.equals(b)); //same but:
assert(a!=b); //not the same instance
//test out the Name instances in a hashmap:
HashMap<Name,Object> map = new HashMap<Name,Object>();
Object detailsIn = new Object();
map.put(a,detailsIn);
Object detailsOut = map.get(b);
assert(detailsIn==detailsOut); //the map returned the same details object
//even though we put with `a` and got with `b` thanks to our correct equals implementation

Saya menduga tujuan Anda, tetapi dengan cara ini Anda dapat menggunakan Namekelas dalam peta hash dll, dan mereka tidak harus menjadi contoh yang sama persis.

Weston
sumber
Tolong contoh.
Robert Harvey
@RobertHarvey contoh implementasi yang tepat?
barat
Sepertinya Anda memberikannya. Tapi saya tidak jelas bagaimana ini berbeda dari hanya memeriksa properti Nama untuk kesetaraan dalam metode pabrik statis.
Robert Harvey
1
@RobertHarvey Saya sudah mencoba menjelaskan lebih banyak, saya menawarkan alternatif lengkap yang tidak memerlukan pabrik statis sama sekali dan saya menantang titik awal OPs bahwa mereka tidak ingin memiliki lebih dari satu kali contoh per nama.
barat
Alasan yang sah untuk memerlukan objek unik adalah jika Anda ingin blok yang disinkronkan untuk menggunakan objek sebagai monitor. Dua objek yang .equals () satu sama lain tidak akan berfungsi.
Leigh Caldwell
3
  • Buat Nameantarmuka
  • Buat antarmuka NameFactorydengan metodeName getByName(String)
  • Buat implementasi NameFactorydengan Map<String,WeakReference<Name>>isinya
  • synchronizedi peta dengan nama di dalam getByNamemetode sebelum membuat instance baruName
  • Secara opsional, gunakan implementasi privat statis Nameantarmuka di dalam implementasi Anda atasNameFactory

Pendekatan ini akan membuat Anda memastikan bahwa:

  • Hanya satu contoh Nameada kapan saja,
  • Tidak ada kebocoran memori "Lingerer" di kelas Anda, ketika tinggal Namelebih lama dari yang mereka butuhkan,
  • Desainnya tetap dapat diuji dengan benda-benda yang diejek, karena Anda menggunakan antarmuka.
dasblinkenlight
sumber
Anda tidak memiliki kebocoran memori dengan metode pabrik, jika Anda throwketika nama yang identik ada alih-alih mengembalikan objek, atau jika Anda mengembalikan nullobjek.
Robert Harvey
@RobertHarvey Maksud saya kebocoran yang tidak benar-benar bocor, tetapi benda yang sah (yang disebut "orang-orang yang masih hidup"). Peta statis sederhana akan mencegah objek nilainya agar tidak dirilis, sementara peta referensi lemah akan menyimpannya dalam memori hanya selama referensi hidup lainnya ada.
dasblinkenlight
Ah, aku mengerti maksudmu. Ini mungkin salah satu contoh langka di mana a destructorakan berguna.
Robert Harvey
1
Terlalu rumit Hal ini dapat dilakukan seperti jawaban @weston, menimpa sama dengan dan meminta pabrik menyimpan koleksi nama.
Tulains Córdova
@ user1598390 Seseorang harus membuat sesederhana mungkin, tetapi tidak lebih sederhana. "solusi" weston tidak memecahkan masalah OP (tidak ada peta), dan sekumpulan nama membuat kebocoran memori lebih lama (ya, ada kebocoran memori di Jawa).
dasblinkenlight
1

Anda harus menjadikan konstruktor sebagai pribadi dan membuat metode seperti getNameInstance(String), jika objek dengan nama yang sama sudah ada (berdasarkan pada kelas statis 'dapat cepat misalnya), Anda mengembalikan referensi itu, kalau tidak Anda membuat objek baru menggunakan konstruktor pribadi Anda dan menambahkannya ke hashtable

HericDenis
sumber
Anda telah mengulangi apa yang saya katakan di paragraf ke-3 pertanyaan. Saya mencari cara alternatif.
Kacang
Maaf, saya pikir kami menjawab hampir bersamaan karena saya memeriksa jawaban sebelum posting saya. Sebenarnya, saya mencoba menjawabnya di StackOverflown dan kemudian dimigrasi.
HericDenis
1

Coba ikuti. Anda harus melacak setiap objek yang Anda buat. Untuk tujuan ini saya menggunakan Daftar. Dan menjadikan konstruktor kelas privat, sehingga pemeriksaan awal dapat diterapkan sebelum membuat instance

class UniqueName
    {
        private string Name;
        public int ID;
        private static int Count=0;
        static List<UniqueName> lt=new List<UniqueName>();

        private UniqueName(string Name)
        {
            this.Name = Name;
            ID = ++Count;
        }

        public static UniqueName GetUniqueueInstance(string Name)
        {
            foreach (UniqueName un in lt)
            {
                if ( string.Compare( un.Name,Name,StringComparison.InvariantCultureIgnoreCase)==0)
                    return un;
            }

            UniqueName temp=new UniqueName(Name);
            lt.Add(temp);
            return temp;
        }
    }
Akshay
sumber
Ini bukan Java? Sudahkah Anda menulis kode semu? Atau, dalam bahasa apa pun? Silakan tentukan secara eksplisit.
Rohit Jain
Sepertinya C #. Akshay, Anda harus mempelajari koleksi lain selain List. Ini akan cocok untuk Dictionary<string,UniqueName>.
barat