Validasi dengan Java murni

Ide tentang properti yang dibatasi di Java bukanlah hal baru. Sejak JDK 1.1, legiun pengembang JavaBeans telah menggunakan kerangka kerja yang kuat yang ditemukan dalam java.beanspaket untuk menerapkan aturan validasi data. Sayangnya, sebagian besar dari kita - yang tidak berada dalam bisnis pembuatan "komponen perangkat lunak yang dapat digunakan kembali yang dapat dimanipulasi secara visual dalam alat pembuat" - cukup sering mencoba untuk menemukan kembali roda dengan meninggalkan pendekatan inti Java untuk mendukung berbagai kepemilikan solusi. Eksternalisasi aturan validasi data adalah area di mana Anda dapat melihat kreativitas paling banyak. Pendekatan berbasis XML yang menarik baru-baru ini diusulkan dalam seri "Validasi dengan Java dan XML Schema" di JavaWorld. Sambil mengagumi teknologi XML, saya percaya bahwa Java memiliki semua yang dibutuhkan untuk menyelesaikan masalah dengan cara yang paling elegan. Untuk menunjukkan kepada Anda, saya mengundang Anda untuk menemukan kembali beberapa permata asli yang ditemukan dalam java.beanspaket. Mereka akan membantu Anda membangun beberapa kelas yang berguna dan mempelajari beberapa trik menarik.

Logika di balik properti yang dibatasi di Java cukup sederhana. Sebelum menerima nilai data baru, objek (pemilik properti terbatas) memastikannya diterima oleh semua pihak berkepentingan yang dapat memveto. "Permintaan persetujuan" seperti itu dikirimkan ke setiap yang terdaftar java.beans.VetoableChangeListenerdalam bentuk java.beans.PropertyChangeEventobjek. Jika satu atau lebih veto telah dikeluarkan, nilai data yang diajukan ditolak. Hak veto diajukan oleh a java.beans.PropertyVetoException. Secara umum, kompleksitas aturan yang diberlakukan oleh pendengar tersebut tidak memiliki batasan dan hanya bergantung pada persyaratan proyek dan kreativitas pengembang. Tujuan dari latihan ini adalah untuk mempelajari cara menangani kriteria validasi data paling umum yang dapat Anda terapkan ke sebagian besar objek.

Pertama, mari buat kelas yang disebut BusinessObjectdengan properti numerik sederhana. Selanjutnya, saya akan menunjukkan bagaimana Anda dapat memodifikasi properti menjadi properti terbatas untuk melihat perbedaannya dari yang sederhana.

kelas publik BusinessObject {private int numericValue; public void setNumericValue (int newNumericValue) {numericValue = newNumericValue; } public int getNumericValue () {return numericValue; }}

Harap dicatat bahwa satu-satunya properti kelas ini akan diam-diam menerima nilai apa pun dari jenis yang tepat. Untuk membuatnya dibatasi, acara yang dapat diveto perlu diproduksi dan didistribusikan ke konsumen. Sebuah kelas utilitas yang disebut java.beans.VetoableChangeSupportdirancang khusus untuk memenuhi rangkaian tanggung jawab ini. Kelas baru yang dipanggil ConstrainedObjectakan mengelola semua pengaturan dengan kelas utilitas ini, sehingga menjadi orang tua yang baik dan penuh kasih untuk BusinessObject. Berbicara tentang konsumen, kita perlu membangun kelas khusus lain yang disebutValidatoryang menghosting algoritme validasi data generik. Ini akan menjadi satu-satunya konsumen dari acara yang dapat diveto dalam kerangka kerja kami. Pendekatan yang disederhanakan ini menyimpang dari aturan game JavaBeans (periksa Spesifikasi API JavaBeans untuk mengetahui detailnya), yang mengharuskan penyajian setiap properti yang dibatasi sebagai properti terikat dan memberikan dukungan untuk menangani beberapa listener. Penyimpangan ini dapat diterima, karena Anda tidak sedang membuat JavaBean di sini, tetapi masih layak untuk disebutkan.

import java.beans. *; / ** * Tanggung jawab ConstrainedObject adalah untuk mendelegasikan tanggung jawab validasi entri data * generik ke {@link Validator} dan menyediakan * subkelas dengan antarmuka transparan padanya. * / kelas publik ConstrainedObject {private VetoableChangeSupport vetoableSupport = VetoableChangeSupport baru (ini); / ** * Membuat objek baru dengan validator properti generik * / public ConstrainedObject () {vetoableSupport.addVetoableChangeListener (new Validator ()); } / ** * Metode ini akan digunakan oleh subclass untuk memvalidasi nilai baru di * properti terbatas dari tipe int. Ini dapat dengan mudah dibebani * untuk menangani jenis primitif lainnya juga. * @param propertyName Nama program dari properti yang akan berubah. * @param oldValue Nilai lama properti.* @param newValue Nilai baru properti. * / protected void validate (String propertyName, int oldValue, int newValue) melempar PropertyVetoException {vetoableSupport.fireVetoableChange (propertyName, new Integer (oldValue), new Integer (newValue)); }}

Demi menjaga kode tetap kompak, ConstrainedObjectmenyediakan metode yang inthanya memvalidasi properti. Anda juga dapat dengan mudah membebani metode ini untuk jenis lainnya. Jika Anda melakukannya, pastikan untuk menggabungkan semua tipe primitif dalam pembungkusnya masing-masing karena PropertyChangeEventmemberikan nilai lama dan baru sebagai tipe referensi. Sekarang Anda siap untuk menerbitkan revisi kedua dari BusinessObject:

import java.beans. *; / ** * Kelas contoh ini sebenarnya menggunakan layanan validasi data yang ditentukan dalam kerangka kerja ini. * / kelas publik BusinessObject extends ConstrainedObject {private int numericValue; public void setNumericValue (int newNumericValue) melempar PropertyVetoException {// validate usulan value validate ("numericValue", numericValue, newNumericValue); // nilai baru disetujui, karena tidak ada pengecualian yang dilemparkan numericValue = newNumericValue; } public int getNumericValue () {return numericValue; }}

Seperti yang Anda lihat, metode penyetel sekarang terlihat sedikit lebih rumit dari sebelumnya. Ini adalah harga kecil yang harus dibayar karena memiliki properti terbatas. Sekarang Anda harus membangun Validator, yang merupakan bagian paling menarik dari latihan ini. Di sinilah Anda belajar cara mengeksternalisasi aturan validasi data menggunakan Java murni.

import java.beans. *; public class Validator mengimplementasikan VetoableChangeListener {public void vetoableChange (PropertyChangeEvent evt) melempar PropertyVetoException {// lakukan validasi di sini}}

Kerangka kode itu hanyalah titik awal, tetapi ini menunjukkan apa yang tersedia di awal proses validasi. Anehnya, ini memiliki banyak informasi. Dari PropertyChangeEventAnda dapat mempelajari tentang sumber objek properti yang dibatasi, nama properti itu sendiri, dan nilai yang ada dan yang diusulkan. Dengan informasi tersebut yang berguna, Anda dapat menggunakan kekuatan introspeksi untuk menanyakan informasi tambahan properti. Informasi apa yang harus Anda cari? Aturan validasi, tentu saja! Kelas java.beans.Introspectordirancang hanya untuk tujuan mengumpulkan informasi tambahan tentang kelas sasaran. Informasi tersebut diberikan dalam bentuk java.beans.BeanInfoobjek, yang dalam contoh ini, Anda dapat meminta dengan panggilan sederhana seperti ini:

BeanInfo info = Introspector.getBeanInfo (evt.getSource (). GetClass ()); 

The Introspectordapat mengumpulkan BeanInfodalam dua cara. Pertama mencoba untuk mendapatkan informasi eksplisit tentang kelas target dengan mencari kelas dengan nama yang sama kecuali BeanInfosufiksnya. Dalam hal ini, ini akan mencari kelas bernama BusinessObjectBeanInfo. Jika karena alasan apa pun kelas seperti itu tidak tersedia, maka Introspectoryang terbaik adalah membangun BeanInfoobjek secara dinamis dengan menggunakan refleksi tingkat rendah.

Meskipun subjek yang menarik, introspeksi implisit jelas bukan apa yang akan Anda andalkan dalam pencarian Anda untuk aturan validasi. Satu hal hebat tentang BeanInfokelas ini adalah hubungannya dengan kelas target, yang dapat didefinisikan sebagai "kopling lepas yang kokoh". Tidak adanya referensi dari kelas target membuat kopling menjadi sangat longgar. Jika aturan validasi berubah, Anda hanya perlu memperbarui dan mengkompilasi ulang BeanInfokelas tanpa mempengaruhi kelas target. Aturan penamaan yang didefinisikan dengan jelas yang didokumentasikan dalam API Java inti membuat kompilasi menjadi kokoh. Di sinilah pendekatan aturan-eksternalisasi dengan BeanInfomengalahkan rekan-rekan yang diterapkan kepemilikannya secara langsung.

Sebelum Anda mulai membangun BeanInfokelas eksplisit , saya harus menyebutkan bahwa Anda dapat memuatnya dengan berbagai deskriptor tentang kelas itu sendiri, atau metode dan kejadiannya, bukan hanya properti. Berkat pembuat Java, Anda tidak perlu memberikan semua informasi itu. The Introspectormengambil apa yang tersedia secara eksplisit dan kemudian melakukan sihir analisis yang tersirat. Oleh karena itu, Anda dapat berkonsentrasi pada deskriptor properti saja.

Untuk mendeskripsikan aturan validasi data properti, Anda menggunakan java.beans.PropertyDescriptorkelas. Melihat dokumentasi, Anda akan menemukan bahwa kelas ini memiliki fasilitas untuk menyediakan setiap informasi yang dapat dibayangkan tentang properti, termasuk aturan validasi data! Anda menggunakan setValuemetode untuk menentukan aturan sebagai sekumpulan atribut bernama. Berikut adalah cara membatasi batas untuk properti numerik numericValue:

PropertyDescriptor _numericValue = new PropertyDescriptor ("numericValue", targetClass, "getNumericValue", "setNumericValue"); _numericValue.setValue ("maxVal", Integer baru (100)); _numericValue.setValue ("minVal", Integer baru (-100));

Satu-satunya kelemahan di sini adalah Anda tidak dapat mengandalkan kekuatan compiler Java untuk memeriksa ejaan nama atribut. Menggunakan sekumpulan konstanta yang telah ditentukan dapat dengan mudah memperbaikinya. Kelas Validatoradalah kandidat yang baik untuk menampung konstanta seperti itu:

public static final String MAX_VALUE = "maxValue"; public static final String MIN_VALUE = "minValue";

Jadi sekarang, Anda dapat menulis ulang aturan yang sama dengan lebih aman. Dengan semua ini dalam pikiran, mari buat revisi akhir BeanInfokelas:

import java.beans. *; kelas publik BusinessObjectBeanInfo meluas SimpleBeanInfo {Kelas targetClass = BusinessObject.class; public PropertyDescriptor [] getPropertyDescriptors () {coba {PropertyDescriptor numericValue = new PropertyDescriptor ("numericValue", targetClass, "getNumericValue", "setNumericValue"); numericValue.setValue (Validator.MAX_VALUE, Integer baru (100)); numericValue.setValue (Validator.MIN_VALUE, Integer baru (-100)); PropertyDescriptor [] pds = new PropertyDescriptor [] {numericValue}; pds kembali; } catch (IntrospectionException ex) {ex.printStackTrace (); kembali nol; }}}

Terakhir, Anda menyelesaikannya Validatorsehingga dapat mengambil dan menganalisis informasi. Ini akan menggunakan metode praktis untuk mengambil PropertyDescriptordari nama BeanInfoprogram properti oleh properti. Jika properti tidak ditemukan karena alasan apa pun, metode ini akan memberi tahu pemanggil dengan pengecualian yang cukup jelas.

import java.beans. *; / ** * Kelas ini mengimplementasikan mekanisme validasi data generik yang didasarkan pada aturan * eksternal yang ditentukan di kelas BeanInfo. * / public class Validator mengimplementasikan VetoableChangeListener {public static final String MAX_VALUE = "maxValue"; public static final String MIN_VALUE = "minValue"; / ** * Satu-satunya metode yang diperlukan oleh antarmuka {@link VetoableChangeListener}. * / public void vetoableChange (PropertyChangeEvent evt) melempar PropertyVetoException {coba {// di sini kami berharap mendapatkan informasi tambahan yang didefinisikan secara eksplisit // tentang sumber-objek dari properti terbatas BeanInfo info = Introspector.getBeanInfo (evt.getSource (). getClass ()); // temukan deskriptor properti dengan nama properti yang diberikan PropertyDescriptor deskriptor = getDescriptor (evt.getPropertyName (), info);Integer max = (Integer) deskriptor.getValue (MAX_VALUE); // periksa apakah nilai baru lebih besar dari yang diperbolehkan if (max! = null && max.compareTo (evt.getNewValue ()) 0) {// komplain! melempar PropertyVetoException baru ("Value" + evt.getNewValue () + "kurang dari minimum yang diizinkan" + min, evt); }} catch (IntrospectionException ex) {ex.printStackTrace (); }} / ** * Metode utilitas ini mencoba mengambil PropertyDescriptor dari objek BeanInfo dengan nama * properti yang diberikan. * @param menamai nama program dari properti * @param info objek info kacang yang akan dicari untuk deskriptor properti. * @throws IllegalArgumentException jika properti dengan nama yang diberikan tidak ada dalam objek * BeanInfo yang diberikan. * / private PropertyDescriptor getDescriptor (Nama string,Info BeanInfo) menampilkan IllegalArgumentException {PropertyDescriptor [] pds = info.getPropertyDescriptors (); untuk (int i = 0; i
   
    

Percaya atau tidak, itu dia! Anda memiliki semua bagian di tempatnya sekarang dan siap untuk menguji validasi data Anda. Pengemudi sederhana ini melakukan hal itu: