Jika Anda menyatakan tidak membatalkan dengan pernyataan pernyataan dalam kode produksi? [Tutup]

32

Saya telah melihat pertanyaan ini tetapi memiliki beberapa pertanyaan lagi tentang penggunaan assertkata kunci. Saya berdebat dengan beberapa coders lain tentang penggunaan assert. Untuk kasus penggunaan ini, ada metode yang dapat mengembalikan nol jika prasyarat tertentu dipenuhi. Kode yang saya tulis memanggil metode, lalu menyatakan itu tidak mengembalikan nol, dan terus menggunakan objek yang dikembalikan.

Contoh:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

Sekarang bayangkan saya menggunakannya seperti ini:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

Saya diberitahu bahwa saya harus menghapus assert, bahwa mereka tidak boleh digunakan dalam kode produksi, hanya digunakan dalam pengujian. Benarkah?

Big_Bad_E
sumber
19
Secara default, pernyataan dinonaktifkan. Anda harus mengaktifkannya secara eksplisit saat runtime melalui -eaatau setara.
jarmod
Pertanyaan terkait tentang Rekayasa Perangkat Lunak: softwareengineering.stackexchange.com/q/137158/187318 (meskipun sudah cukup tua, jadi jawaban yang diterima di sana masih merekomendasikan perpustakaan eksternal, yang tidak lagi diperlukan sejak diperkenalkan Objects.requireNonNulldi Jawa 8).
Hulk
Judul pertanyaan ini terlalu luas, tetapi pertanyaan yang dinyatakan dalam badan jauh lebih sempit, dan per jawaban sepenuhnya ditangani oleh fungsi pembantu.
smci
Agar lebih jelas, Anda bertanya apakah kode klien harus menerapkan pemeriksaan / pembatalan non-null pada objek. (Itu berbeda dari menanyakan apakah kode perpustakaan itu sendiri harus menjamin objek tidak boleh nol, atau menyatakan bahwa ada).
smci
1
Kemungkinan duplikat: Kapan pernyataan harus tetap dalam kode produksi?
Peter Mortensen

Jawaban:

19

Gunakan Objects.requireNonNull(Object)untuk itu.

Periksa bahwa referensi objek yang ditentukan bukan nol. Metode ini dirancang terutama untuk melakukan validasi parameter dalam metode dan konstruktor, [...]

Dalam kasus Anda ini akan menjadi:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object);
    // Do stuff using object, which would throw a NPE if object is null.
}

Fungsi ini dibuat untuk tujuan yang Anda sebutkan, yaitu secara eksplisit menandai apa yang tidak boleh nol; juga dalam produksi. Manfaat besar adalah Anda memastikan Anda menemukan nilai nol tepat di tempat mereka seharusnya tidak terjadi di tempat pertama. Anda akan memiliki lebih sedikit masalah debugging yang disebabkan oleh nilai-nilai nol yang dilewatkan di suatu tempat di mana mereka seharusnya tidak.

Manfaat lain adalah fleksibilitas tambahan mengenai cek kosong berbeda dengan assert. Sementara assertkata kunci untuk memeriksa nilai boolean, Objects.requireNonNull(Object)adalah fungsi dan dengan demikian dapat tertanam dalam kode yang jauh lebih fleksibel dan mudah dibaca. Misalnya:

Foo foo = Objects.requireNonNull(service.fetchFoo());

// You cannot write it in on line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// You cannot write it in on line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

Perlu diingat bahwa Objects.requireNonNull(Object)ini hanya untuk memeriksa nol di mana assertdigeneralisasi. assertmemiliki tujuan yang sedikit berbeda dalam hal itu, yaitu pengujian terutama. Itu harus diaktifkan, sehingga Anda dapat mengaktifkannya untuk menguji dan menonaktifkannya untuk produksi. Bagaimanapun, jangan menggunakannya untuk kode produksi. Itu bisa memperlambat aplikasi dengan validasi yang tidak perlu dan rumit yang dimaksudkan untuk pengujian. Gunakan itu untuk memisahkan cek hanya pengujian dari cek yang juga dimaksudkan untuk produksi.

Lihat dokumentasi resmi untuk perincian tentang assert.

akuzminykh
sumber
14
Siapa dan kapan dinyatakan menyatakan sebagai warisan? Ada tautan / referensi? Saya tidak bisa membayangkan pengembang waras mendefinisikan "warisan" alat yang memungkinkan validasi tanpa jejak yang dapat dengan mudah diaktifkan dalam pengujian dan dinonaktifkan dalam produksi.
Dmitry Pisklov
Mengingat bahwa Anda dapat memutuskan pada saat runtime apakah menegaskan beroperasi, tetapi Anda tidak dapat menentukan pada saat runtime apakah NPE dilemparkan oleh requireNonNull, apa yang Anda maksud dengan 'Anda memiliki lebih banyak kontrol dengan itu daripada dengan menegaskan'?
Pete Kirkham
@DmitryPisklov Anda benar, saya sudah mengeditnya. Ini adalah alat yang hebat untuk pengujian.
akuzminykh
2
@PeteKirkham Anda benar, kontrol adalah kata yang salah untuk itu. Saya lebih banyak berbicara tentang fleksibilitas dalam sintaksis. Selain itu: di sini dapat ditemukan alasan mengapa Anda ingin kode untuk melempar NPE.
akuzminykh
1
"jangan gunakan itu untuk kode produksi. Itu bisa memperlambat aplikasi" Itu bisa, tetapi bisa sepadan. Java memiliki pemeriksaan runtime bawaan untuk memastikan Anda tidak pernah overrun array. Ini diaktifkan bahkan pada penyebaran produksi, meskipun ada kemungkinan penalti kinerja. Kami bahkan tidak diberi pilihan tentang itu. Tidak selalu salah untuk memeriksa runtime dalam kode produksi.
Max Barraclough
22

Yang paling penting untuk diingat tentang pernyataan adalah bahwa mereka dapat dinonaktifkan, jadi jangan pernah berasumsi mereka akan dieksekusi.

Untuk kompatibilitas mundur, JVM menonaktifkan validasi pernyataan secara default. Mereka harus diaktifkan secara eksplisit menggunakan argumen baris perintah -enableassertions, atau steno -ea:

java -ea com.whatever.assertion.Assertion

Jadi, bukan praktik yang baik untuk mengandalkan mereka.

Karena pernyataan tidak diaktifkan secara default, Anda tidak pernah dapat menganggapnya akan dieksekusi ketika digunakan dalam kode. Jadi, Anda harus selalu memeriksa nilai nol dan mengosongkan Opsional, hindari menggunakan pernyataan untuk memeriksa input ke metode publik dan alih-alih gunakan pengecualian yang tidak dicentang ... Secara umum lakukan semua pemeriksaan seolah-olah pernyataan itu tidak ada di sana.

jeprubio
sumber
Jelas praktik yang buruk untuk mengandalkan mereka, tetapi apakah itu praktik yang buruk untuk menggunakannya secara umum?
Big_Bad_E
3
@Big_Bad_E Pernyataan adalah alat debug yang ada untuk membuat program gagal sedini dan sesering mungkin. Ini adalah tugas dari rangkaian uji untuk memastikan bahwa tidak ada cara suatu program dapat gagal terlepas dari pernyataan tersebut . Idenya adalah, setelah test suite (memiliki cakupan 100%, bukan?!?) Dijalankan tanpa kegagalan, itu menyimpan untuk menghapus pernyataan karena mereka tidak dapat memicu anyways. Tentu ada sedikit idealisme dalam alasan ini, tetapi itulah idenya.
cmaster - mengembalikan monica
11

Tentunya apa yang dikatakan kepada Anda adalah kebohongan yang terang-terangan. Inilah sebabnya.

Pernyataan dinonaktifkan secara default jika Anda hanya menjalankan jvm mandiri. Ketika mereka dinonaktifkan, mereka tidak memiliki jejak, maka mereka tidak akan mempengaruhi aplikasi produksi Anda. Namun, mereka mungkin adalah teman terbaik Anda ketika mengembangkan dan menguji kode Anda, dan sebagian besar pelari kerangka pengujian mengaktifkan pernyataan (JUnit melakukannya), jadi kode pernyataan Anda dijalankan ketika Anda menjalankan tes unit Anda, membantu Anda mendeteksi potensi bug sebelumnya (misalnya Anda dapat menambahkan pernyataan untuk beberapa pemeriksaan batas logika bisnis, dan itu akan membantu mendeteksi beberapa kode yang menggunakan nilai yang tidak pantas).

Yang mengatakan, seperti jawaban lain menyarankan, untuk alasan yang tepat (mereka tidak selalu diaktifkan) Anda tidak dapat mengandalkan pernyataan untuk melakukan beberapa pemeriksaan penting, atau (terutama!) Mempertahankan keadaan apa pun.

Untuk contoh yang menarik tentang bagaimana Anda dapat menggunakan menegaskan, lihat di sini - di akhir file ada metode singleThreadedAccess()yang dipanggil dari pernyataan tegas pada baris 201 dan ada di sana untuk menangkap potensi akses multithreaded dalam tes.

Dmitry Pisklov
sumber
4

Jawaban lain sudah mencakup ini dengan cukup baik, tetapi ada opsi lain.

Misalnya, Spring memiliki metode statis:

org.springframework.util.Assert.notNull(obj)

Ada perpustakaan lain dengan Assert.something()metode mereka sendiri juga. Cukup sederhana juga untuk menulis sendiri.

Namun, ingatlah pengecualian apa yang Anda berikan jika ini adalah layanan web. Metode sebelumnya yang disebutkan, misalnya, melempar IllegalArgumentExceptionyang secara default di Spring mengembalikan 500.

Dalam hal layanan web, ini seringkali bukan kesalahan server internal, dan seharusnya bukan 500, melainkan 400, yang merupakan permintaan yang buruk.

Christopher Schneider
sumber
1
Jika metode "Penegasan" tidak segera menghentikan proses, melempar beberapa jenis pengecualian, maka itu bukan pernyataan. Intinya assertadalah, untuk mendapatkan crash langsung dengan file inti yang dihasilkan sehingga Anda dapat melakukan post-mortem dengan debugger favorit Anda tepat di tempat di mana kondisi itu dilanggar.
cmaster - mengembalikan monica
Pemberitahuan tidak segera menghentikan proses. Mereka melempar Kesalahan, yang mungkin ditangkap. Pengecualian yang tidak ditangani akan membuat crash pengujian Anda serta kesalahannya. Anda bisa berdebat semantik jika Anda mau. Jika Anda menginginkannya, tulis pernyataan kustom yang menampilkan Kesalahan, bukan pengecualian. Faktanya adalah, dalam sebagian besar kasus, tindakan yang tepat adalah membuang pengecualian daripada kesalahan.
Christopher Schneider
Ah, Java memang mendefinisikan assertuntuk melempar. Menarik. Versi C / C ++ tidak melakukan hal seperti itu. Segera menimbulkan sinyal bahwa a) membunuh proses, dan b) menciptakan dump inti. Ini karena suatu alasan: Sangat sederhana untuk men-debug kegagalan pernyataan seperti itu karena Anda masih memiliki seluruh informasi tentang tumpukan panggilan yang tersedia. Menentukan assertuntuk melempar pengecualian sebagai gantinya, yang kemudian dapat ditangkap (tidak) dengan sengaja secara terprogram, mengalahkan tujuannya, imho.
cmaster - mengembalikan monica
Sama seperti ada beberapa (atau banyak) hal aneh di C dan C ++, ada beberapa di Jawa. Salah satunya adalah Throwable. Mereka selalu bisa ditangkap. Apakah itu hal yang baik atau tidak, saya tidak tahu. Saya kira itu tergantung. Banyak program Java adalah layanan web, dan itu tidak diinginkan untuk crash karena hampir semua alasan, jadi semuanya tertangkap dan dicatat. Salah satu hal yang hebat adalah jejak tumpukan, dan itu biasanya cukup untuk mendiagnosis alasan pengecualian atau kesalahan dengan sendirinya.
Christopher Schneider
3

Gunakan menegaskan secara bebas setiap kali melakukannya membantu menangkap kesalahan pemrograman yaitu bug.

Jangan gunakan pernyataan untuk menangkap sesuatu yang mungkin terjadi secara logis, yaitu input yang diformat dengan buruk. Gunakan hanya menegaskan ketika kesalahan tidak dapat dipulihkan.

Jangan letakkan logika produksi apa pun dalam kode yang berjalan saat pernyataan diperiksa. Jika perangkat lunak Anda ditulis dengan baik, ini sepele, tetapi jika tidak maka Anda mungkin memiliki efek samping yang halus dan perilaku keseluruhan yang berbeda dengan pernyataan yang diaktifkan dan dinonaktifkan.

Jika perusahaan Anda memiliki "kode pengujian" dan "kode produksi" melakukan hal yang sama tetapi sebagai basis kode yang berbeda (atau tahapan penyuntingan yang berbeda), keluarlah dari sana dan jangan pernah kembali. Mencoba memperbaiki tingkat ketidakmampuan itu mungkin hanya buang-buang waktu saja. Jika perusahaan Anda tidak menempatkan pernyataan tegas di luar kode tes, mohon beri tahu mereka bahwa pernyataan dinonaktifkan di build produksi dan bahwa jika tidak, memperbaiki kesalahan itu sekarang menjadi prioritas pertama Anda.

Nilai penegasan tepat untuk digunakan di dalam logika bisnis dan bukan hanya rangkaian uji. Ini membuatnya mudah untuk menghasilkan banyak tes tingkat tinggi yang tidak harus secara eksplisit menguji banyak hal untuk melewati potongan besar kode Anda dan memicu semua pernyataan ini. Dalam beberapa proyek saya, tes-tes tipikal bahkan tidak benar-benar menegaskan apa-apa, mereka hanya memerintahkan perhitungan terjadi berdasarkan input spesifik dan ini menyebabkan ratusan pernyataan diperiksa dan masalah ditemukan bahkan dalam potongan-potongan logika kecil di lubuk hati.

Kafein
sumber
2

Anda dapat menggunakan menegaskan kapan saja. Debat yang datang adalah kapan harus digunakan. Misalnya dalam panduan :

  • Jangan gunakan pernyataan untuk memeriksa argumen dalam metode publik.
  • Jangan menggunakan pernyataan untuk melakukan pekerjaan apa pun yang diperlukan aplikasi Anda untuk operasi yang benar.
Gatusko
sumber
4
Aturan yang bagus. Tetapi jawabannya akan lebih baik dengan beberapa alasan ditambahkan.
cmaster - mengembalikan monica