Praktek yang baik atau buruk? Menginisialisasi objek dalam pengambil

167

Saya punya kebiasaan aneh, sepertinya ... setidaknya menurut rekan kerja saya. Kami telah mengerjakan proyek kecil bersama. Cara saya menulis kelas adalah (contoh sederhana):

[Serializable()]
public class Foo
{
    public Foo()
    { }

    private Bar _bar;

    public Bar Bar
    {
        get
        {
            if (_bar == null)
                _bar = new Bar();

            return _bar;
        }
        set { _bar = value; }
    }
}

Jadi, pada dasarnya, saya hanya menginisialisasi bidang apa pun ketika pengambil dipanggil dan bidang tersebut masih nol. Saya pikir ini akan mengurangi kelebihan dengan tidak menginisialisasi properti yang tidak digunakan di mana pun.

ETA: Alasan saya melakukan ini adalah karena kelas saya memiliki beberapa properti yang mengembalikan instance dari kelas lain, yang pada gilirannya juga memiliki properti dengan kelas yang lebih banyak, dan seterusnya. Memanggil konstruktor untuk kelas atas selanjutnya akan memanggil semua konstruktor untuk semua kelas ini, ketika mereka tidak selalu semua dibutuhkan.

Apakah ada keberatan terhadap praktik ini, selain dari preferensi pribadi?

UPDATE: Saya telah mempertimbangkan banyak pendapat yang berbeda sehubungan dengan pertanyaan ini dan saya akan berdiri dengan jawaban yang diterima Namun, saya sekarang memiliki pemahaman yang jauh lebih baik tentang konsep dan saya dapat memutuskan kapan akan menggunakannya dan kapan tidak.

Cons:

  • Masalah keamanan utas
  • Tidak mematuhi permintaan "setter" ketika nilai yang diberikan adalah nol
  • Optimalisasi mikro
  • Penanganan pengecualian harus dilakukan di konstruktor
  • Perlu memeriksa null dalam kode kelas

Pro:

  • Optimalisasi mikro
  • Properti tidak pernah mengembalikan nol
  • Tunda atau hindari memuat benda "berat"

Sebagian besar kontra tidak berlaku untuk perpustakaan saya saat ini, namun saya harus menguji untuk melihat apakah "optimasi mikro" benar-benar mengoptimalkan apa pun.

PEMBAHARUAN TERAKHIR:

Oke, saya mengubah jawaban saya. Pertanyaan awal saya adalah apakah ini kebiasaan yang baik atau tidak. Dan sekarang saya yakin tidak. Mungkin saya masih akan menggunakannya di beberapa bagian kode saya saat ini, tetapi tidak tanpa syarat dan pasti tidak setiap saat. Jadi saya akan kehilangan kebiasaan dan memikirkannya sebelum menggunakannya. Terimakasih semuanya!

John Willemse
sumber
14
Ini adalah pola lazy-load, tidak persis memberi Anda manfaat yang luar biasa di sini tapi itu masih bagus.
Machinarius
28
Instansiasi malas masuk akal jika Anda memiliki dampak kinerja yang terukur, atau jika anggota tersebut jarang digunakan dan memakan jumlah memori terlalu tinggi, atau jika dibutuhkan waktu lama untuk instantiasi mereka dan hanya ingin melakukannya atas permintaan. Bagaimanapun, pastikan untuk memperhitungkan masalah keamanan utas (kode Anda saat ini tidak ) dan pertimbangkan untuk menggunakan kelas <T> Malas yang disediakan .
Chris Sinclair
10
Saya pikir pertanyaan ini lebih cocok di codereview.stackexchange.com
Eduardo Brites
7
@ PLB itu bukan pola tunggal.
Colin Mackay
30
Saya terkejut bahwa tidak ada yang menyebutkan bug serius dengan kode ini. Anda memiliki properti publik yang dapat saya atur dari luar. Jika saya atur ke NULL, Anda akan selalu membuat objek baru dan mengabaikan akses setter saya. Ini bisa menjadi bug yang sangat serius. Untuk properti pribadi, ini bisa okie. Secara pribadi, saya tidak suka melakukan optimasi prematur seperti itu. Menambahkan kerumitan tanpa manfaat tambahan.
SolutionYogi

Jawaban:

170

Apa yang Anda miliki di sini adalah - naif - implementasi "inisialisasi malas".

Jawaban singkat:

Menggunakan inisialisasi malas tanpa syarat bukanlah ide yang baik. Ini memiliki tempat tetapi harus mempertimbangkan dampak dari solusi ini.

Latar belakang dan penjelasan:

Implementasi beton:
Pertama-tama mari kita lihat sampel beton Anda dan mengapa saya menganggap penerapannya naif:

  1. Itu melanggar Principle of Least Surprise (POLS) . Ketika nilai diberikan ke properti, diharapkan nilai ini dikembalikan. Dalam implementasi Anda ini tidak berlaku untuk null:

    foo.Bar = null;
    Assert.Null(foo.Bar); // This will fail
  2. Ini memperkenalkan beberapa masalah threading: Dua penelepon foo.Barpada utas yang berbeda berpotensi mendapatkan dua contoh berbeda Bardan salah satunya tanpa koneksi ke Fooinstance. Setiap perubahan yang dilakukan pada Barinstance tersebut hilang secara diam-diam.
    Ini adalah kasus pelanggaran POLS. Ketika hanya nilai yang disimpan dari suatu properti yang diakses, diharapkan aman-utas. Meskipun Anda dapat berargumen bahwa kelas tersebut tidak aman untuk thread - termasuk pengambil properti Anda - Anda harus mendokumentasikan ini dengan benar karena itu bukan kasus normal. Lebih jauh, pengenalan masalah ini tidak perlu karena akan segera kita lihat.

Secara umum:
Sekarang saatnya untuk melihat inisialisasi malas secara umum:
Inisialisasi malas biasanya digunakan untuk menunda pembangunan objek yang membutuhkan waktu lama untuk dibangun atau yang mengambil banyak memori setelah sepenuhnya dibangun.
Itu adalah alasan yang sangat valid untuk menggunakan inisialisasi malas.

Namun, properti seperti itu biasanya tidak memiliki setter, yang menghilangkan masalah pertama yang disebutkan di atas.
Selanjutnya, implementasi thread-safe akan digunakan - seperti Lazy<T>- untuk menghindari masalah kedua.

Bahkan ketika mempertimbangkan dua poin ini dalam penerapan properti malas, poin-poin berikut adalah masalah umum dari pola ini:

  1. Konstruksi objek bisa gagal, menghasilkan pengecualian dari pengambil properti. Ini merupakan pelanggaran lain terhadap POLS dan karenanya harus dihindari. Bahkan bagian tentang properti di "Pedoman Desain untuk Mengembangkan Perpustakaan Kelas" secara eksplisit menyatakan bahwa pengambil properti tidak boleh melempar pengecualian:

    Hindari melemparkan pengecualian dari pengambil properti.

    Pengambil properti harus operasi sederhana tanpa prasyarat apa pun. Jika seorang pengambil dapat memberikan pengecualian, pertimbangkan untuk mendesain ulang properti sebagai metode.

  2. Optimalisasi otomatis oleh kompiler terluka, yaitu prediksi inlining dan cabang. Silakan lihat jawaban Bill K untuk penjelasan terperinci.

Kesimpulan dari poin-poin ini adalah sebagai berikut:
Untuk setiap properti yang diimplementasikan dengan malas, Anda harus mempertimbangkan poin-poin ini.
Itu berarti, bahwa itu adalah keputusan per kasus dan tidak dapat diambil sebagai praktik umum terbaik.

Pola ini memiliki tempatnya, tetapi ini bukan praktik terbaik umum ketika menerapkan kelas. Seharusnya tidak digunakan tanpa syarat , karena alasan yang disebutkan di atas.


Pada bagian ini saya ingin membahas beberapa poin yang diajukan orang lain sebagai argumen untuk menggunakan inisialisasi malas tanpa syarat:

  1. Serialisasi:
    EricJ menyatakan dalam satu komentar:

    Objek yang mungkin bersambung tidak akan meminta kontruktornya dipanggil ketika deserialized (tergantung pada serializer, tetapi banyak yang umum berperilaku seperti ini). Menempatkan kode inisialisasi dalam konstruktor berarti Anda harus memberikan dukungan tambahan untuk deserialisasi. Pola ini menghindari pengkodean khusus itu.

    Ada beberapa masalah dengan argumen ini:

    1. Sebagian besar objek tidak akan diserialisasi. Menambahkan semacam dukungan untuk itu ketika tidak diperlukan melanggar YAGNI .
    2. Ketika sebuah kelas perlu mendukung serialisasi, ada cara untuk mengaktifkannya tanpa solusi yang tidak ada hubungannya dengan serialisasi sekilas.
  2. Optimalisasi mikro: Argumen utama Anda adalah bahwa Anda ingin membuat objek hanya ketika seseorang benar-benar mengaksesnya. Jadi, Anda sebenarnya berbicara tentang mengoptimalkan penggunaan memori.
    Saya tidak setuju dengan argumen ini karena alasan berikut:

    1. Dalam kebanyakan kasus, beberapa objek dalam memori tidak memiliki dampak apa pun pada apa pun. Komputer modern memiliki memori yang cukup. Tanpa kasus masalah aktual yang dikonfirmasi oleh profiler, ini adalah optimasi pra-matang dan ada alasan bagus untuk menolaknya.
    2. Saya mengakui fakta bahwa kadang-kadang optimasi semacam ini dibenarkan. Tetapi bahkan dalam kasus ini inisialisasi malas sepertinya bukan solusi yang tepat. Ada dua alasan yang menentangnya:

      1. Inisialisasi malas berpotensi merusak kinerja. Mungkin hanya sedikit, tetapi seperti jawaban Bill menunjukkan, dampaknya lebih besar daripada yang mungkin dipikirkan sekilas. Jadi pendekatan ini pada dasarnya memperdagangkan kinerja versus memori.
      2. Jika Anda memiliki desain di mana itu adalah kasus penggunaan umum untuk menggunakan hanya bagian dari kelas, ini mengisyaratkan masalah dengan desain itu sendiri: Kelas tersebut kemungkinan besar memiliki lebih dari satu tanggung jawab. Solusinya adalah dengan membagi kelas menjadi beberapa kelas yang lebih fokus.
Daniel Hilgarth
sumber
4
@ JohnWillemse: Itu masalah arsitektur Anda. Anda harus memperbaiki kelas Anda dengan cara yang lebih kecil dan lebih fokus. Jangan membuat satu kelas untuk 5 hal / tugas yang berbeda. Buat 5 kelas saja.
Daniel Hilgarth
26
@ JohnWillemse Mungkin menganggap ini kasus optimasi prematur kalau begitu. Kecuali jika Anda memiliki hambatan kinerja / memori yang terukur , saya akan merekomendasikan menentangnya karena meningkatkan kompleksitas dan memperkenalkan masalah threading.
Chris Sinclair
2
+1, ini bukan pilihan desain yang bagus untuk 95% kelas. Inisialisasi malas memiliki kelebihannya, tetapi tidak boleh digeneralisasi untuk SEMUA properti. Ini menambah kompleksitas, kesulitan membaca kode, masalah keamanan utas ... tanpa optimisasi yang nyata dalam 99% kasus. Juga, seperti yang dikatakan SolutionYogi sebagai komentar, kode OP buggy, yang membuktikan bahwa pola ini tidak sepele untuk diterapkan dan harus dihindari kecuali inisialisasi malas benar-benar diperlukan.
ken2k
2
@DanielHilgarth Terima kasih untuk semua cara untuk menuliskan (hampir) semuanya salah dengan menggunakan pola ini tanpa syarat. Kerja bagus!
Alex
1
@DanielHilgarth Ya dan tidak. Pelanggaran adalah masalah di sini jadi ya. Tetapi juga 'tidak' karena POLS secara ketat adalah prinsip bahwa Anda mungkin tidak akan terkejut dengan kode tersebut . Jika Foo tidak diekspos di luar program Anda, itu risiko yang dapat Anda ambil atau tidak. Dalam hal ini, saya hampir dapat menjamin bahwa Anda pada akhirnya akan terkejut , karena Anda tidak mengontrol cara properti diakses. Risikonya menjadi bug, dan argumen Anda tentang nullkasus ini menjadi lebih kuat. :-)
atlaste
49

Ini adalah pilihan desain yang bagus. Sangat disarankan untuk kode perpustakaan atau kelas inti.

Ini disebut oleh beberapa "inisialisasi malas" atau "inisialisasi tertunda" dan umumnya dianggap oleh semua sebagai pilihan desain yang baik.

Pertama, jika Anda menginisialisasi dalam deklarasi variabel tingkat kelas atau konstruktor, maka ketika objek Anda dibangun, Anda memiliki overhead untuk menciptakan sumber daya yang mungkin tidak pernah digunakan.

Kedua, sumber daya hanya akan dibuat jika diperlukan.

Ketiga, Anda menghindari sampah mengumpulkan benda yang tidak digunakan.

Terakhir, lebih mudah untuk menangani pengecualian inisialisasi yang mungkin terjadi di properti kemudian pengecualian yang terjadi selama inisialisasi variabel tingkat kelas atau konstruktor.

Ada pengecualian untuk aturan ini.

Mengenai argumen kinerja pemeriksaan tambahan untuk inisialisasi di properti "dapatkan", itu tidak signifikan. Menginisialisasi dan membuang objek adalah hit kinerja yang lebih signifikan daripada pemeriksaan pointer nol sederhana dengan lompatan.

Pedoman Desain untuk Mengembangkan Perpustakaan Kelas di http://msdn.microsoft.com/en-US/library/vstudio/ms229042.aspx

Mengenai Lazy<T>

Kelas generik Lazy<T>diciptakan persis seperti yang diinginkan poster, lihat Inisialisasi Lazy di http://msdn.microsoft.com/en-us/library/dd997286(v=vs.100).aspx . Jika Anda memiliki versi .NET yang lebih lama, Anda harus menggunakan pola kode yang diilustrasikan dalam pertanyaan. Pola kode ini telah menjadi sangat umum sehingga Microsoft ingin memasukkan kelas dalam pustaka .NET terbaru untuk membuatnya lebih mudah untuk menerapkan pola tersebut. Selain itu, jika implementasi Anda membutuhkan keamanan utas, maka Anda harus menambahkannya.

Tipe Data Primitif dan Kelas Sederhana

Obvioulsy, Anda tidak akan menggunakan inisialisasi malas untuk tipe data primitif atau penggunaan kelas sederhana seperti List<string>.

Sebelum Mengomentari tentang Malas

Lazy<T> diperkenalkan di .NET 4.0, jadi tolong jangan tambahkan komentar lain tentang kelas ini.

Sebelum Mengomentari tentang Optimalisasi Mikro

Saat Anda sedang membangun perpustakaan, Anda harus mempertimbangkan semua optimasi. Misalnya, dalam kelas .NET Anda akan melihat array bit yang digunakan untuk variabel kelas Boolean di seluruh kode untuk mengurangi konsumsi memori dan fragmentasi memori, hanya untuk menyebut dua "optimasi mikro".

Mengenai Antarmuka Pengguna

Anda tidak akan menggunakan inisialisasi malas untuk kelas yang langsung digunakan oleh antarmuka pengguna. Minggu lalu saya menghabiskan sebagian besar hari menghapus pemuatan malas dari delapan koleksi yang digunakan dalam model tampilan untuk kotak kombo. Saya punya LookupManageryang menangani pemuatan malas dan caching koleksi yang dibutuhkan oleh elemen antarmuka pengguna.

"Setter"

Saya tidak pernah menggunakan properti-set ("setter") untuk properti malas yang dimuat. Karena itu, Anda tidak akan pernah mengizinkan foo.Bar = null;. Jika Anda perlu mengatur Barmaka saya akan membuat metode yang disebut SetBar(Bar value)dan tidak menggunakan inisialisasi malas

Koleksi

Properti pengumpulan kelas selalu diinisialisasi ketika dideklarasikan karena mereka tidak boleh nol.

Kelas Kompleks

Biarkan saya ulangi secara berbeda, Anda menggunakan inisialisasi malas untuk kelas kompleks. Yang biasanya, kelas yang dirancang dengan buruk.

akhirnya

Saya tidak pernah mengatakan untuk melakukan ini untuk semua kelas atau dalam semua kasus. Itu kebiasaan buruk.

AMissico
sumber
6
Jika Anda dapat menghubungi foo.Bar berkali-kali di utas yang berbeda tanpa pengaturan nilai intervening tetapi mendapatkan nilai yang berbeda, maka Anda memiliki kelas yang buruk.
Lie Ryan
25
Saya pikir ini adalah aturan praktis yang buruk tanpa banyak pertimbangan. Kecuali Bar adalah sumber daya yang dikuasai, ini adalah optimasi mikro yang tidak perlu. Dan dalam hal Bar adalah sumber daya yang intensif, ada thread safe Lazy <T> yang dibangun ke dalam .net.
Andrew Hanlon
10
"Lebih mudah untuk menangani pengecualian inisialisasi yang mungkin terjadi di properti kemudian pengecualian yang terjadi selama inisialisasi variabel tingkat kelas atau konstruktor." - oke, ini konyol. Jika suatu objek tidak dapat diinisialisasi karena suatu alasan, saya ingin tahu ASAP. Yaitu segera ketika itu dibangun. Ada beberapa argumen bagus untuk menggunakan inisialisasi malas, tetapi saya rasa itu bukan ide yang baik untuk menggunakannya secara luas .
millimoose
20
Saya sangat khawatir bahwa pengembang lain akan melihat jawaban ini dan berpikir ini memang praktik yang baik (oh boy). Ini adalah praktik yang sangat sangat buruk jika Anda menggunakannya tanpa syarat. Selain apa yang telah dikatakan, Anda membuat hidup semua orang jauh lebih sulit (untuk pengembang klien dan untuk pengembang pemeliharaan) untuk keuntungan sangat sedikit (jika ada untungnya sama sekali). Anda harus mendengar dari pro: Donald Knuth, dalam seri The Art of Computer Programming, terkenal mengatakan "optimasi prematur adalah akar dari semua kejahatan." Apa yang Anda lakukan tidak hanya jahat, itu jahat!
Alex
4
Ada begitu banyak indikator yang Anda pilih jawaban salah (dan keputusan pemrograman salah). Anda memiliki lebih banyak kontra daripada pro dalam daftar Anda. Anda memiliki lebih banyak orang yang mendukungnya daripada untuk itu. Anggota yang lebih berpengalaman dari situs ini (@BillK dan @DanielHilgarth) yang diposting dalam pertanyaan ini menentangnya. Rekan kerja Anda sudah mengatakan itu salah. Serius, ini salah! Jika saya menangkap salah satu pengembang tim saya (saya adalah pemimpin tim) melakukan ini, dia akan mengambil batas waktu 5 menit dan kemudian diberi kuliah mengapa dia tidak boleh melakukan ini.
Alex
17

Apakah Anda mempertimbangkan menerapkan pola tersebut menggunakan Lazy<T>?

Selain pembuatan objek malas yang mudah, Anda mendapatkan keamanan utas saat objek diinisialisasi:

Seperti yang dikatakan orang lain, Anda malas memuat objek jika benar-benar berat sumber daya atau perlu waktu untuk memuatnya selama waktu konstruksi objek.

Fidemraizer Matías
sumber
Terima kasih, saya memahaminya sekarang dan saya pasti akan melihat ke dalam Lazy<T>sekarang, dan menahan diri dari menggunakan cara yang biasa saya lakukan.
John Willemse
1
Anda tidak mendapatkan keamanan utas sulap ... Anda masih harus memikirkannya. Dari MSDN:Making the Lazy<T> object thread safe does not protect the lazily initialized object. If multiple threads can access the lazily initialized object, you must make its properties and methods safe for multithreaded access.
Eric J.
@EricJ. Tentu saja Anda hanya mendapatkan keamanan utas saat menginisialisasi objek, tetapi nanti Anda harus berurusan dengan sinkronisasi seperti objek lainnya.
Matías Fidemraizer
9

Saya pikir itu tergantung pada apa yang Anda inisialisasi. Saya mungkin tidak akan melakukannya untuk daftar karena biaya konstruksinya cukup kecil, sehingga bisa masuk konstruktor. Tetapi jika itu adalah daftar pra-populasi maka saya mungkin tidak akan sampai itu diperlukan untuk pertama kalinya.

Pada dasarnya, jika biaya konstruksi melebihi biaya melakukan pemeriksaan bersyarat pada setiap akses, maka malas membuatnya. Jika tidak, lakukan di konstruktor.

Colin Mackay
sumber
Terima kasih! Itu masuk akal.
John Willemse
9

Kelemahan yang bisa saya lihat adalah jika Anda ingin bertanya apakah Bars adalah null, itu tidak akan pernah terjadi, dan Anda akan membuat daftar di sana.

Luis Tellez
sumber
Saya pikir itu bukan kelemahan.
Peter Porfy
Mengapa itu downside? Cukup periksa dengan yang sebaliknya terhadap nol. if (! Foo.Bars.Any ())
s.meijer
6
@PeterPorfy: Itu melanggar POLS . Anda memasukkan null, tetapi jangan mendapatkannya kembali. Biasanya, Anda menganggap Anda mendapatkan kembali nilai yang sama dengan yang Anda masukkan ke properti.
Daniel Hilgarth
@DanielHilgarth Terima kasih lagi. Itu adalah argumen yang sangat valid yang belum saya pertimbangkan sebelumnya.
John Willemse
6
@ AMissico: Ini bukan konsep yang dibuat-buat. Dengan cara yang sama seperti menekan tombol di sebelah pintu depan diharapkan membunyikan bel pintu, hal-hal yang terlihat seperti properti diharapkan berperilaku seperti properti. Membuka pintu perangkap di bawah kaki Anda adalah perilaku yang mengejutkan, terutama jika tombolnya tidak diberi label.
Bryan Boettcher
8

Instansiasi / inisialisasi malas adalah pola yang sangat layak. Perlu diingat, bahwa sebagai aturan umum, konsumen API Anda tidak mengharapkan getter dan setter untuk mengambil waktu yang dapat dilihat dari POV pengguna akhir (atau gagal).

Tormod
sumber
1
Saya setuju, dan saya telah sedikit mengedit pertanyaan saya. Saya berharap rangkaian penuh konstruktor yang mendasari untuk mengambil lebih banyak waktu daripada membuat instance kelas hanya ketika mereka diperlukan.
John Willemse
8

Saya hanya akan memberikan komentar pada jawaban Daniel tetapi saya jujur ​​tidak berpikir itu cukup jauh.

Meskipun ini adalah pola yang sangat baik untuk digunakan dalam situasi tertentu (misalnya, ketika objek diinisialisasi dari database), itu kebiasaan yang mengerikan untuk masuk.

Salah satu hal terbaik tentang suatu objek adalah ia menawarkan lingkungan yang aman dan tepercaya. Kasus terbaik adalah jika Anda membuat sebanyak mungkin bidang "Final", mengisinya dengan konstruktor. Ini membuat kelas Anda cukup anti peluru. Mengizinkan ladang untuk diubah melalui setter sedikit kurang begitu, tetapi tidak mengerikan. Misalnya:

SafeClass kelas
{
    Nama string = "";
    Usia bilangan bulat = 0;

    public void setName (String newName)
    {
        menegaskan (newName! = null)
        name = newName;
    } // ikuti pola ini untuk usia
    ...
    public String toString () {
        String s = "Kelas Aman memiliki nama:" + nama + "dan usia:" + usia
    }
}

Dengan pola Anda, metode toString akan terlihat seperti ini:

    if (name == null)
        melempar IllegalStateException baru ("SafeClass masuk ke keadaan ilegal! nama adalah null")
    if (age == null)
        buang IllegalStateException baru ("SafeClass masuk ke keadaan ilegal! usia nol")

    public String toString () {
        String s = "Kelas Aman memiliki nama:" + nama + "dan usia:" + usia
    }

Tidak hanya ini, tetapi Anda perlu cek nol di mana-mana Anda mungkin menggunakan objek itu di kelas Anda (Di luar kelas Anda aman karena cek nol di pengambil, tetapi Anda harus sebagian besar menggunakan anggota kelas Anda di dalam kelas)

Juga kelas Anda terus-menerus dalam keadaan tidak menentu - misalnya jika Anda memutuskan untuk membuat kelas itu menjadi kelas hibernasi dengan menambahkan beberapa penjelasan, bagaimana Anda melakukannya?

Jika Anda mengambil keputusan berdasarkan beberapa mikro-optomisasi tanpa persyaratan dan pengujian, itu hampir pasti merupakan keputusan yang salah. Bahkan, ada peluang yang sangat bagus bahwa pola Anda sebenarnya memperlambat sistem bahkan di bawah keadaan yang paling ideal karena pernyataan if dapat menyebabkan kegagalan prediksi cabang pada CPU yang akan memperlambat banyak hal berkali-kali lipat hanya menetapkan nilai dalam konstruktor kecuali objek yang Anda buat cukup kompleks atau berasal dari sumber data jarak jauh.

Untuk contoh masalah prediksi brance (yang Anda alami berulang kali, jarang sekali), lihat jawaban pertama untuk pertanyaan luar biasa ini: Mengapa lebih cepat memproses array yang diurutkan daripada array yang tidak disortir?

Bill K.
sumber
Terima kasih atas masukan Anda. Dalam kasus saya, tidak ada kelas yang memiliki metode yang mungkin perlu memeriksa null, jadi itu bukan masalah. Saya akan mempertimbangkan keberatan Anda yang lain.
John Willemse
Saya tidak begitu mengerti itu. Ini menyiratkan bahwa Anda tidak menggunakan anggota Anda di kelas tempat mereka disimpan - bahwa Anda hanya menggunakan kelas sebagai struktur data. Jika itu masalahnya Anda mungkin ingin membaca javaworld.com/javaworld/jw-01-2004/jw-0102-toolbox.html yang memiliki deskripsi yang baik tentang bagaimana kode Anda dapat ditingkatkan dengan menghindari keadaan objek yang memanipulasi secara eksternal. Jika Anda memanipulasinya secara internal, bagaimana Anda melakukannya tanpa nol memeriksa semuanya berulang kali?
Bill K
Sebagian dari jawaban ini bagus, tetapi sebagian sepertinya dibuat-buat. Biasanya saat menggunakan pola ini, toString()mau menelepon getName(), tidak pakai namelangsung.
Izkata
@ Bill Ya, kelas adalah struktur data yang sangat besar. Semua pekerjaan dilakukan di kelas statis. Saya akan memeriksa artikel di tautan. Terima kasih!
John Willemse
1
@izkata Sebenarnya di dalam kelas tampaknya menjadi undian karena cuaca Anda menggunakan rajin atau tidak, sebagian besar tempat saya pernah bekerja menggunakan anggota secara langsung. Selain itu, jika Anda selalu menggunakan pengambil, metode if () bahkan lebih berbahaya karena kegagalan prediksi cabang akan lebih sering terjadi DAN karena percabangan runtime kemungkinan akan lebih banyak kesulitan dalam menjalankan pengambil. Namun, itu semua bisa diperdebatkan dengan penyataan john bahwa mereka adalah struktur data dan kelas statis, itulah hal yang paling saya khawatirkan.
Bill K
4

Izinkan saya menambahkan satu poin lagi ke banyak poin bagus yang dibuat oleh orang lain ...

Debugger akan ( secara default ) mengevaluasi properti ketika melangkah melalui kode, yang berpotensi dapat membuat instantiate Barlebih cepat daripada biasanya terjadi dengan hanya mengeksekusi kode. Dengan kata lain, tindakan debugging semata-mata mengubah pelaksanaan program.

Ini mungkin atau mungkin bukan masalah (tergantung pada efek samping), tetapi sesuatu yang harus diperhatikan.

Branko Dimitrijevic
sumber
2

Apakah Anda yakin Foo harus menjadi instantiating apa pun?

Bagi saya sepertinya bau (walaupun tidak selalu salah ) untuk membiarkan Foo instantiate apapun. Kecuali jika Foo memiliki tujuan untuk menjadi sebuah pabrik, Foo tidak boleh membuat institusinya sendiri, tetapi malah menyuntikkannya ke konstruktornya .

Namun jika tujuan keberadaan Foo adalah untuk membuat instance dari tipe Bar, maka saya tidak melihat ada yang salah dengan melakukannya dengan malas.

KaptajnKold
sumber
4
@BenjaminGruenbaum Tidak, tidak juga. Dan dengan hormat, bahkan jika itu benar, apa yang ingin Anda sampaikan?
KaptajnKold