Saya hampir tidak perlu membahas mengapa menggunakan kembali kode bermanfaat. Penggunaan kembali kode biasanya membuat pengembangan program lebih cepat dan mengurangi bug. Setelah sepotong kode dienkapsulasi dan digunakan kembali, perlu untuk memeriksa sepotong kode yang sangat kecil untuk memastikan kebenaran program. Jika Anda hanya perlu membuka dan menutup koneksi basis data di satu tempat di seluruh aplikasi, jauh lebih mudah untuk memastikan bahwa koneksi normal. Tapi saya yakin Anda sudah tahu semua ini.
Ada dua jenis kode penggunaan kembali, yang saya sebut jenis penggunaan kembali:
Tipe pertama adalah penggunaan kembali fungsional, yang merupakan jenis penggunaan kembali yang paling umum. Ini juga semacam penguasaan bagi sebagian besar pengembang. Artinya, gunakan kembali satu set instruksi selanjutnya untuk melakukan operasi.
Tipe kedua adalah penggunaan kembali konteks, yaitu, fungsi yang berbeda atau kode operasi dienkapsulasi antara konteks yang sama, dan konteks yang sama dienkapsulasi sebagai kode penggunaan kembali (konteks di sini mengacu pada serangkaian instruksi operasi yang sama). Meskipun menjadi lebih populer dalam pembalikan kontrol, itu tidak umum. Selain itu, penggunaan kembali konteks tidak dijelaskan secara eksplisit, sehingga tidak digunakan oleh sistem seperti penggunaan kembali fungsional. Saya harap Anda akan berubah setelah membaca artikel ini.
Fungsi menggunakan kembali
Penggunaan kembali fungsional adalah jenis penggunaan kembali yang paling umum. Ini adalah penggunaan kembali serangkaian instruksi yang menjalankan semacam operasi. Dua metode berikut adalah membaca data dari database:
daftar publik readAllUsers () {koneksi koneksi = null; String sql = "pilih * dari pengguna"; Daftar pengguna = arraylist baru (); coba {connection = openconnection (); Pernyataan Persiapan = Connection.PrepareStatement (SQL); Hasil Hasil = Pernyataan.ExecuteQuery (); while (result.next ()) {// menggunakan kembali kode pengguna pengguna = pengguna baru (); user.setname (result.getString ("name")); user.setemail (result.getString ("email")); Users.add (pengguna); // Akhiri kode penggunaan kembali} result.close (); pernyataan.close (); Pengguna yang kembali; } catch (sqlexception e) {// abaikan untuk saat ini} akhirnya {// abaikan untuk saat ini}} daftar publik readUsersOfstatus (status string) {koneksi koneksi = null; String sql = "pilih * dari pengguna di mana status =?"; Daftar pengguna = arraylist baru (); coba {connection = openconnection (); Pernyataan Persiapan = Connection.PrepareStatement (SQL); pernyataan.setstring (1, status); Hasil Hasil = Pernyataan.ExecuteQuery (); while (result.next ()) {// menggunakan kembali kode pengguna pengguna = pengguna baru (); user.setname (result.getString ("name")); user.setemail (result.getString ("email")); Users.add (pengguna); // Akhiri kode penggunaan kembali} result.close (); pernyataan.close (); Pengguna yang kembali; } catch (sqlexception e) {// abaikan untuk saat ini} akhirnya {// abaikan untuk sekarang}}Untuk pengembang yang berpengalaman, dimungkinkan untuk segera menemukan kode yang dapat digunakan kembali. Tempat di mana "kode penggunaan kembali" dikomentari dalam kode di atas adalah sama, sehingga penggunaan kembali dapat dienkapsulasi. Ini adalah operasi yang membaca catatan pengguna ke dalam instance pengguna. Baris kode ini dapat dienkapsulasi ke dalam metode mereka sendiri, misalnya:
// Encapsulate Operasi yang sama ke dalam metode ReadUser ReadUser Pengguna Privat (hasil Hasil) melempar SQLException {user user = User baru (); user.setname (result.getString ("name")); user.setemail (result.getString ("email")); Users.add (pengguna); Pengguna Kembali; }Sekarang, hubungi metode readUser () dalam dua metode di atas (contoh berikut hanya menunjukkan metode pertama):
daftar publik readAllUsers () {koneksi koneksi = null; String sql = "pilih * dari pengguna"; Daftar pengguna = arraylist baru (); coba {connection = openconnection (); Pernyataan Persiapan = Connection.PrepareStatement (SQL); Hasil Hasil = Pernyataan.ExecuteQuery (); while (result.next ()) {user.add (readUser (result))} result.close (); pernyataan.close (); Pengguna yang kembali; } catch (sqlexception e) {// abaikan untuk saat ini} akhirnya {// abaikan untuk sekarang}}Metode ReadUser () juga dapat disembunyikan di kelasnya sendiri menggunakan Modifier Private.
Di atas adalah tentang penggunaan kembali fungsi. Penggunaan kembali fungsional adalah merangkum serangkaian instruksi yang melakukan operasi spesifik melalui metode atau kelas untuk mencapai tujuan penggunaan kembali.
Operasi yang diparameterisasi
Terkadang Anda ingin menggunakan kembali satu set operasi, tetapi operasi ini tidak persis sama di mana pun Anda menggunakannya. Misalnya, ReadAllUsers () dan ReadUsersOfStatus () Metode baik membuka koneksi, menyiapkan pernyataan, menjalankannya, dan mengulangi set hasil. Satu -satunya perbedaan adalah bahwa readUsersOfstatus () membutuhkan parameter untuk ditetapkan pada persiapan disiapkan. Kami dapat merangkum semua operasi ke dalam metode readUserList (). Seperti yang ditunjukkan di bawah ini:
Private List ReadUserList (string SQL, string [] parameter) {koneksi koneksi = null; Daftar pengguna = arraylist baru (); coba {connection = openconnection (); Pernyataan Persiapan = Connection.PrepareStatement (SQL); untuk (int i = 0; i <parameter.length; i ++) {pernyataan.setstring (i, parameter [i]); } ResultSet result = pernyataan.exeCuteQuery (); while (result.next ()) {user.add (readUser (result))} result.close (); pernyataan.close (); Pengguna yang kembali; } catch (sqlexception e) {// abaikan untuk saat ini} akhirnya {// abaikan untuk sekarang}} Sekarang kami memanggil metode readUserList(...) dari readAllUsers() dan readUsersOfStatus() dan memberikan parameter operasi yang berbeda:
Daftar Publik ReadAllUsers () {return readUserList ("Select * From Users", New String [] {});} Daftar Publik ReadUsersWithStatus (Status String) {return readUserList ("Pilih * dari pengguna", string baru [] {status});}Saya yakin Anda dapat menemukan cara lain yang lebih baik untuk menerapkan fungsi reuse dan parameterisasi mereka untuk membuatnya lebih mudah digunakan.
Konteks penggunaan kembali
Konteks penggunaan kembali sedikit berbeda dari penggunaan kembali fitur. Konteks penggunaan kembali adalah penggunaan kembali serangkaian instruksi, dan berbagai operasi selalu dilakukan antara instruksi ini. Dengan kata lain, penggunaan kembali pernyataan sebelum dan sesudah berbagai perilaku. Oleh karena itu konteks penggunaan kembali sering mengarah pada inversi kelas gaya kontrol. Konteks penggunaan kembali adalah cara yang sangat efektif untuk menggunakan kembali penanganan pengecualian, koneksi dan manajemen siklus hidup transaksi, iterasi dan shutdown aliran, dan banyak konteks operasional umum lainnya.
Berikut adalah dua metode yang dilakukan dengan InputStream:
public void printStream (inputStream inputStream) melempar ioException {if (inputStream == null) return; IOException Exception = null; coba {int karakter = inputStream.read (); while (karakter! = -1) {System.out.print ((char) karakter); // karakter yang berbeda = inputStream.read (); }} akhirnya {coba {inputStream.close (); } catch (ioException e) {if (exception == null) lempar e; }}} public String readStream (inputStream inputStream) melempar ioException {stringBuffer buffer = stringBuffer baru (); // berbeda if (inputStream == null) return; IOException Exception = null; coba {int karakter = inputStream.read (); while (karakter! = -1) {buffer.append ((char) karakter); // karakter yang berbeda = inputStream.read (); } return buffer.toString (); // berbeda} akhirnya {coba {inputStream.close (); } catch (ioException e) {if (exception == null) lempar e; }}}Kedua metode ini berbeda dari operasi aliran. Tetapi konteks di sekitar operasi ini adalah sama. Kode konteks mengulangi dan menutup inputstream. Selain perbedaan dalam menggunakan tanda komentar, kode di atas adalah kode konteksnya.
Seperti yang ditunjukkan di atas, konteksnya melibatkan penanganan pengecualian dan memastikan bahwa aliran ditutup dengan benar setelah iterasi. Menulis penanganan kesalahan seperti itu dan kode rilis sumber daya berulang kali rumit dan rentan kesalahan. Penanganan kesalahan dan penanganan koneksi yang benar lebih kompleks dalam transaksi JDBC. Jelas lebih mudah untuk menulis kode sekali dan menggunakannya kembali di mana saja.
Untungnya, metode merangkum konteksnya sederhana. Buat kelas konteks dan masukkan konteks publik ke dalamnya. Dalam penggunaan konteks, instruksi operasi yang berbeda diabstraksi ke antarmuka operasi, dan kemudian setiap operasi dienkapsulasi di kelas yang mengimplementasikan antarmuka operasi (disebut kelas operasi di sini). Anda hanya perlu memasukkan instance dari kelas operasi ke dalam konteks. Ini dapat dilakukan dengan memberikan instance dari kelas operasi sebagai parameter untuk konstruktor objek konteks, atau dengan memberikan instance dari kelas operasi sebagai parameter ke metode eksekusi spesifik konteks.
Berikut ini menunjukkan cara memisahkan contoh di atas menjadi konteks dan antarmuka operasional. StreamProcessor (Antarmuka Operasi) dilewatkan sebagai parameter ke metode ProcessStream () dari StreamProcessorContext.
// Stream Processing Plugin Interface Public interface StreamProcessor { public void process(int input);}// Stream Processing context class public class StreamProcessorContext{ // Instantiate the StreamProcessor operation interface and serve as a parameter public void processStream(InputStream inputStream, StreamProcessor processor) throws IOException { if(inputStream == null) return; IOException Exception = null; coba {int karakter = inputStream.read (); while (karakter! = -1) {processor.process (karakter); karakter = inputStream.read (); }} akhirnya {coba {inputStream.close (); } catch (ioException e) {if (exception == null) lempar e; Lemparkan pengecualian; }}}}Sekarang Anda dapat menggunakan kelas StreamProcessorContext untuk mencetak konten aliran seperti contoh berikut:
FileInputStream inputStream = FileInputStream baru ("myfile"); // Contoh operasi melalui implementasi subkelas anonim dari streamProcessor antarmuka baru streamProcessorContext (). ProcessStream (inputStream, New StreamProcessor () {public void Proses (int input) {System.out.outstream () {char)} {char) {} {{{System.out.out.out.out.out.out.processor (() {int input) {System.out.out.out.out.out.out.out.out.out.out.out.out.out.)Atau baca konten aliran input seperti ini dan tambahkan ke urutan karakter:
kelas publik streamToStringReader mengimplementasikan streamProcessor {private stringBuffer buffer = new StringBuffer (); Public StringBuffer getBuffer () {return this.buffer; } public void Process (Input int) {this.buffer.append ((char) input); }} FileInputStream inputStream = FileInputStream baru ("myFile"); streamToStringReader reader = streamTostringReader baru (); New StreamProcessorContext (). ProcessStream (inputStream, reader); // lakukan sesuatu dengan input dari stream.reader.getbuffer ();Seperti yang Anda lihat, lakukan apa pun dengan stream dengan memasukkan implementasi antarmuka streamProcessor yang berbeda. Setelah StreamProcessorContext diimplementasikan sepenuhnya, Anda tidak akan pernah memiliki masalah dengan aliran yang tidak tertutup.
Konteks penggunaan kembali sangat kuat dan dapat digunakan di banyak lingkungan lain di luar pemrosesan aliran. Kasus penggunaan yang jelas adalah untuk menangani koneksi dan transaksi basis data dengan benar ( open - process - commit()/rollback() - close() ). Kasus penggunaan lainnya adalah pemrosesan saluran NIO dan sinkronisasi utas di bagian kritis ( lock() - access shared resource - unlock() ). Ini juga dapat mengonversi pengecualian yang diperiksa dari API menjadi pengecualian yang tidak dicentang.
Ketika Anda mencari kode yang cocok untuk penggunaan kembali konteks dalam proyek Anda, cari mode operasi berikut:
Saat Anda menemukan pola seperti itu, operasi reguler sebelum dan sesudah dapat mencapai penggunaan kembali konteks.
Konteks sebagai metode template
Terkadang Anda ingin memiliki beberapa titik plugin dalam konteks. Jika konteksnya terdiri dari banyak langkah yang lebih kecil dan Anda ingin setiap langkah konteks dapat disesuaikan, Anda dapat menerapkan konteks sebagai metode templat. Metode template adalah pola desain GOF. Pada dasarnya, metode templat membagi algoritma atau protokol menjadi serangkaian langkah. Metode template biasanya diimplementasikan sebagai kelas dasar tunggal dan menyediakan metode untuk setiap langkah dalam algoritma atau protokol. Untuk menyesuaikan langkah apa pun, cukup buat kelas yang memperluas kelas dasar metode template dan angkanya metode langkah yang ingin Anda sesuaikan.
Contoh berikut adalah JDBCContext yang diimplementasikan sebagai metode templat. Subkelas dapat mengesampingkan pembukaan dan penutupan koneksi untuk memberikan perilaku khusus. Metode ProcessRecord (hasil hasil) harus selalu diganti karena abstrak. Metode ini menyediakan operasi yang tidak dalam konteks dan berbeda dalam kasus yang berbeda menggunakan JDBCContext. Contoh ini bukan JDBCContext yang sempurna. Ini hanya digunakan untuk menunjukkan cara menggunakan metode template saat menerapkan konteks.
kelas abstrak publik jdbccontext {DataSource DataSource = null; // Konstruktor tanpa parameter dapat digunakan untuk subkelas tanpa DataSource untuk mendapatkan koneksi public jdbccontext () {} public jdbccontext (dataSource dataSource) {this.dataSource = dataSource; } OpenConnection koneksi yang dilindungi () melempar sqlexception {return datasource.getConnection (); } protected void closeConnection (koneksi koneksi) melempar sqlexception {connection.close (); } // ProcessRecord (Hasil Hasil) Metode Prosesor ProsesRecord (Hasil Hasil) Melemparkan SQLException; public void execute (string sql, objek [] parameter) melempar sqlexception {koneksi koneksi = null; Pernyataan Persiapan = NULL; Hasil Hasil = NULL; coba {connection = openconnection (); Pernyataan = Connection.PrepareStatement (SQL); untuk (int i = 0; i <parameter.length; i ++) {pernyataan.setObject (i, parameter [i]); } result = pernyataan.executeQuery (); while (result.next ()) {ProcessRecord (hasil); }} akhirnya {if (result! = null) {coba {result.close (); } catch (sqlexception e) { / * abaikan * /}} if (pernyataan! = null) {coba {pernyataan.close (); } catch (sqlexception e) { / * abaikan * /}} if (pernyataan! = null) {coba {pernyataan.close (); } catch (sqlexception e) { / * abaikan * /}} if (koneksi! = null) {closeConnection (koneksi); }}}}Ini adalah subkelas yang memperluas JDBCContext untuk membaca daftar pengguna:
Public Class ReadUsers memperluas JDBCContext {List Users = new ArrayList (); Public ReadUsers (DataSource DataSource) {super (DataSource); } daftar publik getUsers () {return this.users; } protected void ProcessRecord (hasil hasil) {pengguna pengguna = pengguna baru (); user.setname (result.getString ("name")); user.setemail (result.getString ("email")); Users.add (pengguna); }}Inilah cara menggunakan kelas ReadUsers:
ReadUsers ReadUsers = ReadUsers baru (DataSource); ReadUsers.Execute ("Select * From Users", Objek Baru [0]); Daftar Pengguna = ReadUsers.getUsers (); Jika kelas ReadUsers perlu mendapatkan koneksi dari kumpulan koneksi dan melepaskannya kembali ke kumpulan koneksi setelah digunakan, Anda dapat memasukkan koneksi dengan mengesampingkan metode openConnection() dan closeConnection(Connection connection) .
Perhatikan cara menulis ulang kode operasi insert melalui metode ini. Subkelas JDBCContext mengesampingkan metode ProcessRecord untuk memberikan pemrosesan catatan khusus. Dalam contoh StreamContext, kode operasi dienkapsulasi dalam objek terpisah dan disediakan sebagai parameter metode. Objek yang mengimplementasikan streamprocessor antarmuka operasi dilewatkan sebagai parameter ke metode ProcessStreamContext kelas processStream(...) .
Anda dapat menggunakan kedua teknik saat mengimplementasikan konteksnya. Kelas JDBCContext dapat melewati Objek ConnectionOpener dan ConnectionCloser yang mengimplementasikan antarmuka operasi sebagai parameter ke metode pelaksanaan, atau sebagai parameter konstruktor. Secara pribadi, saya lebih suka menggunakan objek operasi terpisah dan antarmuka operasi karena dua alasan. Pertama, membuatnya lebih mudah untuk menguji unit kode operasi saja; Kedua, itu membuat kode operasi dapat digunakan kembali dalam berbagai konteks. Tentu saja, kode operasi juga dapat digunakan di beberapa tempat dalam kode, tetapi ini hanya keuntungan. Lagi pula, di sini kita hanya mencoba menggunakan kembali konteksnya, bukan menggunakan kembali operasi.
Kesimpulan
Sekarang Anda telah melihat dua cara berbeda untuk menggunakan kembali kode. Penggunaan kembali fitur klasik dan penggunaan kembali konteks yang kurang umum. Semoga konteks penggunaan kembali akan sama umumnya dengan penggunaan kembali fitur. Konteks penggunaan kembali adalah cara yang sangat berguna untuk melakukan kode abstrak dari detail API yang mendasari (seperti JDBC, IO, atau NIO API, dll.). Terutama jika API berisi sumber daya yang perlu dikelola (on dan tutup, dapatkan dan kembali, dll.).
Persistence/ORM API, Mr.Persister menggunakan penggunaan kembali konteks untuk mencapai koneksi otomatis dan manajemen siklus hidup transaksi. Dengan cara ini pengguna tidak perlu khawatir tentang membuka atau menutup koneksi dengan benar, atau melakukan atau mengembalikan transaksi. Mr.Persister memberikan konteks di mana pengguna dapat memasukkan operasi mereka. Konteks -konteks ini bertanggung jawab untuk membuka, menutup, melakukan, dan membalas.
Kerangka kerja musim semi yang populer berisi banyak penggunaan kembali konteks. Misalnya, springs jdbc abstraksi. Pengembang musim semi menggunakannya sebagai "pembalikan kontrol." Ini bukan satu -satunya jenis inversi kontrol yang digunakan oleh kerangka kerja pegas. Fitur inti dari Spring adalah pabrik injeksi ketergantungan atau "konteks aplikasi". Injeksi ketergantungan adalah jenis inversi kontrol lain.
Di atas adalah fungsi dan konteks penggunaan kembali kode Java yang diperkenalkan editor kepada Anda. Saya harap ini akan membantu Anda. Jika Anda memiliki pertanyaan, silakan tinggalkan saya pesan dan editor akan membalas Anda tepat waktu. Terima kasih banyak atas dukungan Anda ke situs web Wulin.com!