Anda di halaman 1dari 365

3/12/2020 www.it-ebooks.

info

Halaman 1

www.it-ebooks.info

Halaman 2

Kode Bersih

https://translate.googleusercontent.com/translate_f 1/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 3

Robert C. Martin Series


Misi dari seri ini adalah untuk meningkatkan seni pengerjaan perangkat lunak.
Buku-buku dalam seri ini bersifat teknis, pragmatis, dan substansial. Penulis adalah
pengrajin dan profesional yang sangat berpengalaman yang berdedikasi untuk menulis tentang apa
sebenarnya bekerja dalam praktik, sebagai lawan dari apa yang mungkin berhasil dalam teori. Kamu akan membaca
tentang apa yang telah dilakukan penulis, bukan apa yang menurutnya harus Anda lakukan. Jika buku itu
tentang pemrograman, akan ada banyak kode. Jika buku itu tentang mengelola, di sana
akan banyak studi kasus dari proyek nyata.

Ini adalah buku-buku yang akan dimiliki semua praktisi serius di rak buku mereka.
Ini adalah buku-buku yang akan diingat untuk membuat perbedaan dan untuk membimbing
profesional untuk menjadi pengrajin sejati.

Mengelola Proyek Agile


Sanjiv Augustine

Estimasi dan Perencanaan Agile


Mike Cohn

Bekerja Efektif dengan Kode Legacy


Michael C. Feathers

Agile Java ™: Membuat Kode dengan Pengembangan Berbasis Tes


Jeff Langr

Prinsip, Pola, dan Praktek Agile dalam C #


Robert C. Martin dan Micah Martin

Pengembangan Perangkat Lunak Agile: Prinsip, Pola, dan Praktek


Robert C. Martin
Kode Bersih: Buku Pegangan Pengerjaan Perangkat Lunak Agile
Robert C. Martin

Programmer UML Untuk Java ™


Robert C. Martin

Cocok untuk Mengembangkan Perangkat Lunak: Kerangka untuk Tes Terpadu


Rick Mugridge dan Ward Cunningham

Pengembangan Perangkat Lunak Agile dengan SCRUM


Ken Schwaber dan Mike Beedle

Rekayasa Perangkat Lunak Ekstrim: Pendekatan Langsung


Daniel H. Steinberg dan Daniel W. Palmer

Untuk informasi lebih lanjut, kunjungi informit.com/martinseries

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 2/365
3/12/2020 www.it-ebooks.info

Halaman 4

Kode Bersih
Buku Pegangan Agile
Pengerjaan Perangkat Lunak

Obyek Mentor:
Robert C. Martin
Michael C. Feathers Timothy R. Ottinger
Jeffrey J. Langr Brett L. Schuchert
James W. Grenning Kevin Dean Wampler
Object Mentor Inc.

Menulis kode bersih adalah hal yang harus Anda lakukan untuk menyebut diri Anda seorang profesional.
Tidak ada alasan yang masuk akal untuk melakukan sesuatu yang kurang dari yang terbaik.

Upper Saddle River, NJ • Boston • Indianapolis • San Francisco


New York • Toronto • Montreal • London • Munich • Paris • Madrid
Capetown • Sydney • Tokyo • Singapura • Kota Meksiko

www.it-ebooks.info

Halaman 5

Banyak sebutan yang digunakan oleh produsen dan penjual untuk membedakan produk mereka diklaim sebagai
merek dagang. Di mana sebutan itu muncul dalam buku ini, dan penerbit mengetahui adanya klaim merek dagang,
sebutan telah dicetak dengan huruf kapital awal atau di semua ibukota.

Penulis dan penerbit telah berhati-hati dalam persiapan buku ini, tetapi tidak menyatakan atau
garansi tersirat dalam bentuk apa pun dan tidak bertanggung jawab atas kesalahan atau kelalaian. Tidak ada tanggung jawab yang ditanggung
untuk kerusakan insidental atau konsekuensial sehubungan dengan atau timbul dari penggunaan informasi atau
program yang terkandung di sini.

Penerbit menawarkan diskon yang sangat baik untuk buku ini ketika dipesan dalam jumlah banyak untuk pembelian dalam jumlah besar atau

https://translate.googleusercontent.com/translate_f 3/365
3/12/2020 www.it-ebooks.info
penjualan khusus, yang mungkin termasuk versi elektronik dan / atau sampul khusus dan konten khusus untuk Anda
bisnis, tujuan pelatihan, fokus pemasaran, dan minat merek. Untuk informasi lebih lanjut silahkan hubungi:
Penjualan Korporat dan Pemerintah AS
(800) 382-3419
corpsales@pearsontechgroup.com

Untuk penjualan di luar Amerika Serikat, silakan hubungi:


Penjualan internasional
international@pearsoned.com

Termasuk referensi dan indeks bibliografi.


ISBN 0-13-235088-2 (pbk.: Kertas alk)
1. Pengembangan perangkat lunak tangkas. 2. Perangkat lunak komputer — Keandalan. I. Judul.
QA76.76.D47M3652 2008
005.1 — dc22 2008024750
Hak Cipta © 2009 Pearson Education, Inc.
Seluruh hak cipta. Dicetak di Amerika Serikat. Publikasi ini dilindungi oleh hak cipta,
dan izin harus diperoleh dari penerbit sebelum reproduksi dilarang, disimpan di a
sistem pengambilan, atau transmisi dalam bentuk apa pun atau dengan cara apa pun, elektronik, mekanik, fotokopi,
merekam, atau juga. Untuk informasi mengenai izin, tulis ke:

Pearson Education, Inc


Departemen Hak dan Kontrak
501 Boylston Street, Suite 900
Boston, MA 02116
Faks: (617) 671-3447
ISBN-13: 978-0-13-235088-4
ISBN-10: 0-13-235088-2
Teks dicetak di Amerika Serikat pada kertas daur ulang di Courier di Stoughton, Massachusetts.
Pencetakan pertama Juli 2008

www.it-ebooks.info

Halaman 6

Untuk Ann Marie: Cinta abadi dalam hidupku.

https://translate.googleusercontent.com/translate_f 4/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 7

halaman ini sengaja dibiarkan kosong

https://translate.googleusercontent.com/translate_f 5/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 8

Isi
Kata Pengantar ..................................................... ............................................... xix

Pengantar ................................................. ......................................... xxv

Di Sampul ................................................... ........................................ xxix

Bab 1: Kode Bersih ............................................. ........................... 1


Akan Ada Kode .................................................. ................................. 2
Kode yang salah ................................................ ................................................ 3
Total Biaya Memiliki Mess ........................................... ............. 4
Grand Redesign in the Sky ............................................ .............. 5
Sikap................................................. .............................................. 5
The Primal Conundrum ............................................... ....................... 6
Seni Kode Bersih? ............................................ .......................... 6
Apa itu Kode Bersih? ............................................. ............................. 7
Sekolah Pemikiran ................................................... ............................... 12
Kami Adalah Penulis ............................................... ..................................... 13
Aturan Pramuka .................................................. ............................... 14
Prekuel dan Prinsip ............................................... ......................... 15
Kesimpulan ................................................. ........................................... 15
Daftar Pustaka ................................................. ........................................ 15

Bab 2: Nama Yang Berarti ................................................. .......... 17


Pengantar ................................................. ........................................ 17
Gunakan Nama yang Mengungkap Tujuan ................................................. ............ 18
Hindari Disinformasi ................................................ .......................... 19
Buat Perbedaan Yang Berarti ............................................... ............ 20
Gunakan Nama yang Dapat Diucapkan ............................................... ................... 21
Gunakan Nama yang Dapat Dicari ............................................... ......................... 22

vii

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 6/365
3/12/2020 www.it-ebooks.info
Halaman 9

viii Isi

Hindari Penyandian ................................................ ...................................... 23


Notasi Hongaria ................................................ .......................... 23
Awalan Anggota .................................................... ............................... 24
Antarmuka dan Implementasi ............................................... ........ 24
Hindari Pemetaan Mental ............................................... ........................ 25
Nama Kelas .................................................... ......................................... 25
Nama Metode .................................................... ..................................... 25
Don't Be Cute ................................................. ......................................... 26
Pilih Satu Kata per Konsep ............................................. .................. 26
Jangan Pun .................................................. ............................................... 26
Gunakan Nama Domain Solusi .............................................. ................ 27
Gunakan Masalah Nama Domain .................................................. ................ 27
Tambahkan Konteks yang Berarti ............................................... ..................... 27
Jangan Tambahkan Konteks Sombong ............................................ ............... 29
Kata-kata Akhir .................................................... .......................................... 30

Bab 3: Fungsi .............................................. ........................... 31


Kecil! .................................................. .................................................. 34
Blok dan Indentasi ............................................... ......................... 35
Lakukan Satu Hal ............................................... ........................................ 35
Bagian dalam Fungsi ............................................... ................. 36
Satu Tingkat Abstraksi per Fungsi ............................................ .36
Membaca Kode dari Atas ke Bawah: Aturan Stepdown .................. 37
Beralih Pernyataan ................................................ ............................... 37
Gunakan Nama Deskriptif ............................................... ......................... 39
Argumen Fungsi ................................................ ............................ 40
Bentuk Monadik Umum ............................................... .................. 41
Tandai Argumen ................................................ ................................ 41
Fungsi Dyadic ................................................ .............................. 42
Triad ................................................. ............................................... 42
Objek Argumen ................................................ ............................. 43
Daftar Argumen ................................................ ................................. 43
Kata Kerja dan Kata Kunci ............................................... .......................... 43
Tidak Memiliki Efek Samping .................................................. ............................. 44
Argumen Keluaran .................................................... ............................ 45
Pemisahan Query Perintah ............................................... .............. 45

www.it-ebooks.info

Halaman 10

Isi ix

Memilih Pengecualian untuk Mengembalikan Kode Kesalahan ................................... 46


Ekstrak Blok Coba / Tangkap ............................................. .................... 46
Penanganan Kesalahan Adalah Satu Hal ............................................. ............... 47
Magnet Ketergantungan Error.java ............................................ .47
Jangan Ulangi Diri Anda ............................................. ............................ 48
Pemrograman Terstruktur ................................................ ................... 48
Bagaimana Anda Menulis Fungsinya Seperti Ini? .......................................... 49
https://translate.googleusercontent.com/translate_f 7/365
3/12/2020 www.it-ebooks.info
Kesimpulan ................................................. ........................................... 49
SetupTeardownIncluder ................................................. .................... 50
Daftar Pustaka ................................................. ........................................ 52

Bab 4: Komentar .................................................. ......................... 53


Komentar Jangan Menebus Kode Buruk ....................................... 55
Jelaskan Dirimu dalam Kode .............................................. ...................... 55
Komentar Baik ................................................ ...................................... 55
Komentar Hukum ................................................ ............................... 55
Komentar Informatif ................................................ ..................... 56
Penjelasan Tujuan ............................................... ......................... 56
Klarifikasi................................................. ..................................... 57
Peringatan Konsekuensi ............................................... ................. 58
Komentar TODO ................................................ ............................. 58
Amplifikasi ................................................. ................................... 59
Javadocs dalam API Publik .............................................. ...................... 59
Komentar buruk ................................................ .................................... 59
Bergumam ................................................. ........................................ 59
Komentar Redundan ................................................ ...................... 60
Komentar yang Menyesatkan ................................................ ...................... 63
Komentar yang Dimandatkan ................................................ ........................ 63
Komentar Jurnal ................................................ ............................ 63
Komentar Kebisingan ................................................ .............................. 64
Suara Menakutkan ................................................ .......................................... 66
Jangan Gunakan Komentar Saat Anda Dapat Menggunakan a
Fungsi atau Variabel .................................................. ......................... 67
Penanda Posisi ................................................ ............................... 67
Komentar Penutupan Brace ............................................... .................. 67
Atribusi dan Bylines ............................................... .................... 68

www.it-ebooks.info

Halaman 11

x Isi

Kode yang Diomentari .................................................. ........................ 68


Komentar HTML ................................................ ............................ 69
Informasi Nonlokal ................................................ ....................... 69
Terlalu banyak informasi ............................................... ...................... 70
Koneksi Tak Terlihat ................................................ ....................... 70
Tajuk Fungsi ................................................ .............................. 70
Javadocs dalam Kode Nonpublik .............................................. .............. 71
Contoh................................................. ........................................... 71
Daftar Pustaka ................................................. ........................................ 74

Bab 5: Memformat .............................................. ........................ 75


Tujuan Memformat .................................................. .................. 76
Pemformatan Vertikal ................................................ ............................. 76
Metafora Koran ............................................... ................. 77
Keterbukaan Vertikal Antar Konsep .............................................. 78
Kepadatan Vertikal ................................................ ................................ 79
Jarak Vertikal ................................................ .............................. 80
Pemesanan Vertikal ................................................ .............................. 84
Pemformatan Horizontal ................................................ ........................ 85
Keterbukaan dan Kepadatan Horizontal .............................................. ...... 86
Alignment Horizontal ................................................ ....................... 87
Lekukan................................................. ....................................... 88
https://translate.googleusercontent.com/translate_f 8/365
3/12/2020 www.it-ebooks.info
Lingkup Dummy ................................................ ................................. 90
Aturan Tim ................................................ ........................................... 90
Aturan Pemformatan Paman Bob .............................................. .............. 90

Bab 6: Objek dan Struktur Data .................................... 93


Abstraksi Data ................................................ .................................. 93
Data / Objek Anti-Simetri ............................................ .................. 95
Hukum Demeter .................................................. .............................. 97
Kecelakaan Kereta ................................................ .................................... 98
Hibrida ................................................. ............................................ 99
Menyembunyikan Struktur ................................................ ............................... 99
Objek Transfer Data ................................................... ........................ 100
Rekaman Aktif ................................................ ................................. 101
Kesimpulan ................................................. ......................................... 101
Daftar Pustaka ................................................. .......................................... 101

www.it-ebooks.info

Halaman 12

Isi xi

Bab 7: Penanganan Kesalahan ............................................. .............. 103


Gunakan Pengecualian Daripada Mengembalikan Kode ................................... 104
Tulis Pernyataan Coba-Tangkap-Akhirnya Anda Pertama ....................... 105
Gunakan Pengecualian yang Tidak Dicentang ............................................... ................ 106
Berikan Konteks dengan Pengecualian .............................................. ....... 107
Tentukan Kelas Pengecualian dalam Persyaratan Kebutuhan Penelepon .................. 107
Tentukan Aliran Normal .............................................. ...................... 109
Jangan Mengembalikan Null ............................................. ................................. 110
Jangan Lewati Null ............................................. ..................................... 111
Kesimpulan ................................................. ......................................... 112
Daftar Pustaka ................................................. .......................................... 112

Bab 8: Batas .............................................. ...................... 113


Menggunakan Kode Pihak Ketiga ............................................. ....................... 114
Menjelajahi dan Mempelajari Batas .................................................. .116
Belajar log4j ................................................ ................................. 116
Tes Belajar Lebih Baik Daripada Bebas ............................................ ... 118
Menggunakan Kode yang Belum Ada ........................................... ..... 118
Batas Bersih ................................................ .............................. 120
Daftar Pustaka ................................................. .......................................... 120

Bab 9: Tes Unit ............................................. .......................... 121


Tiga Hukum TDD ............................................. ...................... 122
Menjaga Tes Bersih ............................................... ........................... 123
Pengujian Mengaktifkan -ilities ............................................. ...................... 124
Tes Bersih .................................................... ......................................... 124
Bahasa Pengujian Domain-Khusus ............................................. ... 127
Standar Ganda ................................................... .............................. 127
Satu Penegasan per Tes .................................................. ............................. 130
Konsep tunggal per Tes .................................................. .................... 131
PERTAMA ..................................................... ............................................ 132
Kesimpulan ................................................. ......................................... 133
Daftar Pustaka ................................................. .......................................... 133

Bab 10: Kelas .............................................. ............................ 135


Organisasi Kelas ................................................ ............................ 136

https://translate.googleusercontent.com/translate_f 9/365
3/12/2020 www.it-ebooks.info
Enkapsulasi ................................................. ................................ 136

www.it-ebooks.info

Halaman 13

xii Isi

Kelas Harus Kecil! .................................................. ................ 136


Prinsip Tanggung Jawab Tunggal .............................................. .138
Kohesi................................................. ........................................ 140
Mempertahankan Hasil Kohesi di Banyak Kelas Kecil .................. 141
Pengorganisasian untuk Perubahan ................................................... ...................... 147
Mengisolasi dari Perubahan ............................................... ..................... 149
Daftar Pustaka ................................................. ...................................... 151

Bab 11: Sistem .................................................. .......................... 153


Bagaimana Anda Akan Membangun Kota? .................................................. ........ 154
Pisahkan Membangun Sistem dari Penggunaannya .............................. 154
Pemisahan Utama ............................................... .......................... 155
Pabrik ................................................. ........................................ 155
Injeksi Ketergantungan ................................................ ..................... 157
Meningkatkan Scaling Up ................................................ .......................................... 157
Kekhawatiran Lintas Sektor .................................................. ................... 160
Proxy Java ................................................ ........................................ 161
Kerangka Kerja AOP Pure Java .............................................. ............... 163
Aspek Aspek J ................................................ ................................. 166
Test Drive Arsitektur Sistem ............................................. ..... 166
Optimalkan Pengambilan Keputusan ............................................... ................ 167
Gunakan Standar dengan Bijak, Ketika Mereka Menambahkan Nilai yang Dapat Dilihat ......... 168
Sistem Membutuhkan Bahasa-Bahasa yang Tertentu Domain ..................................... 168
Kesimpulan ................................................. ......................................... 169
Daftar Pustaka ................................................. .......................................... 169

Bab 12: Munculnya .............................................. .................... 171


Bersihkan melalui Desain Muncul ............................................. ... 171
Aturan Desain Sederhana 1: Menjalankan Semua Pengujian ........................................ 172
Aturan Desain Sederhana 2–4: Refactoring .......................................... ..172
Tidak Ada Duplikasi ................................................ ................................... 173
Ekspresif ................................................. .............................................. 175
Kelas dan Metode Minimal .............................................. ........... 176
Kesimpulan ................................................. ......................................... 176
Daftar Pustaka ................................................. .......................................... 176

Bab 13: Konkurensi .............................................. ................ 177


Mengapa konkurensi? .................................................. ......................... 178
Mitos dan Kesalahpahaman ............................................... .............. 179

www.it-ebooks.info

Halaman 14

https://translate.googleusercontent.com/translate_f 10/365
3/12/2020 www.it-ebooks.info

Isi xiii

Tantangan ................................................. ......................................... 180


Prinsip Pertahanan Konkurensi ............................................... ....... 180
Prinsip Tanggung Jawab Tunggal ............................................... ....... 181
Konsekuensi: Batasi Cakupan Data ........................................... ..... 181
Konsekuensi: Gunakan Salinan Data ............................................ ........... 181
Konsekuensi: Utas Sebisa Mungkin Independen ............ 182
Ketahui Perpustakaan Anda ............................................... ............................ 182
Koleksi Thread-Safe .................................................. ................... 182
Ketahui Model Eksekusi Anda .............................................. ............ 183
Konsumen-Konsumen ............................................... ......................... 184
Pembaca-Penulis ............................................... ............................... 184
Filsuf Bersantap ................................................ ....................... 184
Waspadai Ketergantungan Antara Metode yang Disinkronkan ................ 185
Jaga agar Bagian yang Disinkronkan Kecil .............................................. .... 185
Menulis Kode Shut-Down Benar Sulit ..................................... 186
Menguji Kode Berurutan ............................................... ...................... 186
Perlakukan Kegagalan Palsu sebagai Masalah Calon Threading ................. 187
Dapatkan Kode Nonthreaded Anda Bekerja Dahulu .................................... 187
Buat Kode Berurutan Anda Dapat Dibatasi ............................................ 187
Jadikan Kode Berurutan Anda Dapat Diubah ............................................. ... 187
Jalankan dengan Lebih Banyak Thread Daripada Prosesor ....................................... 188
Jalankan pada Berbagai Platform .................................................. .............. 188
Instrumentasi Kode Anda untuk Mencoba dan Memaksa Kegagalan ............................ 188
Kode Tangan ................................................... .................................... 189
Otomatis ................................................. ..................................... 189
Kesimpulan ................................................. ......................................... 190
Daftar Pustaka ................................................. .......................................... 191

Bab 14: Penyempurnaan Berturut-turut ............................................ 193


Implementasi Args ................................................ ........................ 194
Bagaimana Saya Melakukan Ini? .................................................. ..................... 200
Args: The Rough Draft ............................................. ........................ 201
Jadi saya berhenti ............................................... .................................... 212
Tentang Inkrementalisme ................................................ ......................... 212
Argumen String ................................................ .............................. 214
Kesimpulan ................................................. ........................................ 250

www.it-ebooks.info

Halaman 15

xiv Isi

Bab 15: JUnit Internal ............................................. ............. 251


Kerangka Kerja JUnit ............................................... ........................ 252
Kesimpulan ................................................. ......................................... 265

Bab 16: Tanggal Refactoring Serial ......................................... 267


Pertama, Jadikan Bekerja ............................................. .............................. 268
Kemudian Lakukan dengan Benar .................................................. ............................. 270
Kesimpulan ................................................. ......................................... 284
Daftar Pustaka ................................................. .......................................... 284

https://translate.googleusercontent.com/translate_f 11/365
3/12/2020 www.it-ebooks.info
Bab 17: Bau.................................................
Komentar dan Heuristik ............................................ .285
......................................... 286
C1: Informasi Tidak Pantas .............................................. ......... 286
C2: Komentar Usang .................................................. ..................... 286
C3: Komentar Redundan .................................................. ................. 286
C4: Komentar yang ditulis dengan buruk ............................................. ............. 287
C5: Kode Keluar-Komentar ............................................ ................. 287
Lingkungan ................................................. ..................................... 287
E1: Build Membutuhkan Lebih Dari Satu Langkah ........................................ 287
E2: Tes Membutuhkan Lebih Dari Satu Langkah .......................................... 287
Fungsi ..................................................... ........................................... 288
F1: Terlalu Banyak Argumen ............................................. ................... 288
F2: Argumen Keluaran .................................................. ...................... 288
F3: Tandai Argumen .............................................. .......................... 288
F4: Fungsi Mati .............................................. ........................... 288
Umum ................................................. .............................................. 288
G1: Beberapa Bahasa dalam Satu File Sumber ...................................... 288
G2: Perilaku Jelas Tidak Diimplementasikan .......................................... 288
G3: Perilaku yang Tidak Benar di Batas ..................................... 289
G4: Override Safeties .................................................. ................... 289
G5: Duplikasi ............................................... ............................... 289
G6: Kode di Level Abstraksi Salah ........................................ 290
G7: Kelas Dasar Tergantung pada Derivatifnya ....................... 291
G8: Terlalu Banyak Informasi ............................................. ................ 291
G9: Kode Mati .............................................. ................................. 292
G10: Pemisahan Vertikal .............................................. .................. 292
G11: Inkonsistensi ............................................... .......................... 292
G12: Kekacauan ............................................... ..................................... 293

www.it-ebooks.info

Halaman 16

Isi xv

G13: Kopling Buatan .............................................. ................... 293


G14: Kecemburuan Fitur .............................................. ............................ 293
G15: Argumen Pemilih .............................................. .................. 294
G16: Maksud Tersembunyi .................................................. ....................... 295
G17: Tanggung Jawab yang salah tempat .............................................. ......... 295
G18: Statis Tidak Pantas .................................................. ................. 296
G19: Gunakan Variabel Penjelasan ............................................. ....... 296
G20: Nama Fungsi Harus Mengatakan Apa Yang Mereka Lakukan .......................... 297
G21: Memahami Algoritma ............................................. ........ 297
G22: Jadikan Ketergantungan Logis Fisik ................................... 298
G23: Memilih Polimorfisme untuk If / Else atau Switch / Case .................... 299
G24: Ikuti Konvensi Standar ............................................. ... 299
G25: Ganti Angka Ajaib dengan Konstanta Bernama .................. 300
G26: Jadilah Precise .............................................. ................................ 301
G27: Struktur atas Konvensi ............................................. ........ 301
G28: Meringkas Kondisional .................................................. ....... 301
G29: Hindari Persyaratan Negatif ................................................. .... 302
G30: Fungsi Seharusnya Melakukan Satu Hal ........................................... 302
G31: Kopling Temporal Tersembunyi ............................................. ..... 302
G32: Jangan Sewenang-wenang ........................................... ...................... 303
G33: Meringkas Ketentuan Batas ........................................ 304
G34: Fungsi Hanya Harus Turun
Satu Tingkat Abstraksi .............................................. .................. 304
G35: Simpan Data yang Dapat Dikonfigurasi di Level Tinggi ................................ 306
G36: Hindari Navigasi Transitif ............................................. ...... 306

https://translate.googleusercontent.com/translate_f 12/365
3/12/2020 www.it-ebooks.info
Jawa
J1:.................................................
Hindari Daftar Impor Panjang..................................................
dengan Menggunakan Wildcard ..307
............................ 307
J2: Jangan Mewarisi Konstanta ........................................... ................. 307
J3: Konstanta versus Enums ............................................. .............. 308
Nama ................................................. ................................................ 309
N1: Pilih Nama Deskriptif ............................................. ......... 309
N2: Pilih Nama di Tingkat Abstraksi yang Tepat .......... 311
N3: Gunakan Nomenklatur Standar Bila Mungkin ........................... 311
N4: Nama Tidak Jelas .................................................. ................. 312
N5: Gunakan Nama Panjang untuk Lingkup Panjang .......................................... 0,312
N6: Hindari Penyandian .............................................. ........................ 312
N7: Nama Harus Menjelaskan Efek Samping. ..................................... 313

www.it-ebooks.info

Halaman 17

xvi Isi

Tes ..................................................... .................................................. 0,313


T1: Tes Tidak Cukup .................................................. ......................... 313
T2: Gunakan Alat Cakupan! .................................................. ............. 313
T3: Jangan Lewati Tes Sepele .......................................... .................. 313
T4: Tes yang Diabaikan Adalah Pertanyaan tentang Ambiguitas .................. 313
T5: Uji Kondisi Batas ............................................. ........... 314
T6: Menguji Bug Hampir Seluruhnya ............................................ ........ 314
T7: Pola Kegagalan Mengungkap ........................................... 0,314
T8: Pola Cakupan Tes Dapat Diungkap ............................... 314
T9: Tes Harus Cepat ............................................ ..................... 314
Kesimpulan ................................................. ......................................... 314
Daftar Pustaka ................................................. .......................................... 315

Lampiran A: Concurrency II ............................................. ............ 317


Contoh Klien / Server .................................................. ........................ 317
Server ................................................ ...................................... 317
Menambahkan Threading ................................................ ........................... 319
Pengamatan Server .................................................... ....................... 319
Kesimpulan................................................. ..................................... 321
Jalur Eksekusi yang Mungkin .................................................. ................ 321
Jumlah Jalur ................................................... .............................. 322
Menggali lebih dalam ................................................ .............................. 323
Kesimpulan................................................. ..................................... 326
Mengenal Perpustakaan Anda ............................................... ....................... 326
Kerangka Pelaksana ................................................ ...................... 326
Solusi Nonblocking ................................................ ................... 327
Kelas Tidak-Aman-Aman .................................................. .................... 328
Ketergantungan Antara Metode
Dapat Melanggar Kode Serentak .................................................. ............. 329
Toleransi Kegagalan ............................................... .......................... 330
Penguncian Berbasis Klien .................................................. ....................... 330
Penguncian Berbasis Server .................................................. ...................... 332
Meningkatkan Throughput ................................................ ..................... 333
Perhitungan Single-Thread untuk Throughput .......................................... 334
Perhitungan Multithread untuk Throughput .............................................. 335
Jalan buntu ................................................. ............................................ 335
Pengecualian Saling ................................................ ........................... 336
Kunci & Tunggu ................................................... .................................... 337

https://translate.googleusercontent.com/translate_f 13/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 18

Isi xvii

Tidak Ada Preemption ................................................ ................................ 337


Edaran Tunggu ................................................ .................................. 337
Melanggar Pengecualian Saling ............................................... ............. 337
Breaking Lock & Tunggu .............................................. ...................... 338
Melanggar Preemption ................................................ ...................... 338
Breaking Circular Wait ............................................... .................... 338
Menguji Kode Multithreaded ............................................... .............. 339
Alat Bantu untuk Menguji Kode Berbasis Thread ................................ 342
Kesimpulan ................................................. ......................................... 342
Tutorial: Contoh Kode Lengkap ............................................. ............. 343
Klien / Server Tidak Dibaca .................................................. ............... 343
Klien / Server Menggunakan Threads ............................................. ............. 346

Lampiran B: org.jfree.date.SerialDate .......................................... 349

Lampiran C: Referensi Silang Heuristik ........................... 409

Epilog ..................................................... ............................................... 411

Indeks ................................................. .................................................. ... 413

www.it-ebooks.info

Halaman 19

https://translate.googleusercontent.com/translate_f 14/365
3/12/2020 www.it-ebooks.info

halaman ini sengaja dibiarkan kosong

www.it-ebooks.info

Halaman 20

Kata pengantar
Salah satu permen favorit kami di Denmark adalah Ga-Jol, yang memiliki uap licorice yang kuat adalah a
pelengkap sempurna untuk cuaca lembab dan sering dingin kami. Bagian dari pesona Ga-Jol untuk
as. Danes adalah ucapan bijak atau jenaka yang dicetak pada tutup setiap kotak atas. Saya membeli dua
pak kelezatan pagi ini dan menemukan bahwa itu membosankan melihat Denmark tua ini:
Terlebih dahulu aku tersenyum dan tidak tahu apa-apa .
"Kejujuran dalam hal-hal kecil bukanlah hal yang kecil." Itu pertanda baik yang konsisten dengan apa yang saya

https://translate.googleusercontent.com/translate_f 15/365
3/12/2020 www.it-ebooks.info
sudah ingin katakan di sini. Hal-hal kecil penting. Ini adalah buku tentang keprihatinan sederhana
yang nilainya jauh dari kecil.
Tuhan ada dalam perinciannya , kata arsitek Ludwig mies van der Rohe. Kutipan ini mengingatkan
argumen kontemporer tentang peran arsitektur dalam pengembangan perangkat lunak, dan par-
khususnya di dunia Agile. Bob dan saya kadang-kadang menemukan diri kami bergairah
dialog ini. Dan ya, mies van der Rohe memperhatikan utilitas dan bentuk abadi
bangunan yang mendasari arsitektur hebat. Di sisi lain, ia juga secara pribadi memilih
setiap gagang pintu untuk setiap rumah yang dirancangnya. Mengapa? Karena hal-hal kecil penting.
Dalam "debat" berkelanjutan kami tentang TDD, Bob dan saya telah menemukan bahwa kami sepakat bahwa
arsitektur ware memiliki tempat penting dalam pengembangan, meskipun kami mungkin memiliki perbedaan
visi tentang apa artinya itu. Pertengkaran semacam itu relatif tidak penting, namun,
karena kita dapat menerima begitu saja bahwa profesional yang bertanggung jawab memberikan beberapa waktu untuk berpikir-
ing dan perencanaan pada permulaan proyek. Gagasan akhir 1990-an desain hanya didorong oleh
tes dan kodenya sudah lama hilang. Namun perhatian terhadap detail bahkan lebih kritis
fondasi profesionalisme daripada visi besar mana pun. Pertama, melalui latihan di Internet
kecil sehingga profesional memperoleh kemahiran dan kepercayaan untuk praktik dalam skala besar. Kedua, itu
Sedikit konstruksi ceroboh, dari pintu yang tidak tertutup rapat atau sedikit
ubin bengkok di lantai, atau bahkan meja yang berantakan, benar-benar menghilangkan pesona
keseluruhan lebih besar. Itulah kode bersihnya.
Namun, arsitektur hanyalah satu metafora untuk pengembangan perangkat lunak, dan khususnya untuk
bagian dari perangkat lunak yang memberikan produk awal dalam arti yang sama dengan arsitek
memberikan bangunan yang masih asli. Pada hari-hari Scrum dan Agile ini, fokusnya cepat
membawa produk ke pasar. Kami ingin pabrik berjalan dengan kecepatan tinggi untuk menghasilkan perangkat lunak.
Ini adalah pabrik manusia: pemikir, pencerap perasaan yang bekerja dari suatu produk
log atau cerita pengguna untuk membuat produk . Metafora manufaktur tampak sangat kuat
berpikir. Aspek produksi manufaktur mobil Jepang, dari jalur perakitan
dunia, banyak menginspirasi Scrum.

xix

www.it-ebooks.info

Halaman 21

xx Kata pengantar

Namun, bahkan dalam industri otomotif, sebagian besar pekerjaannya tidak terletak di bidang manufaktur tetapi di
pemeliharaan — atau penghindarannya. Dalam perangkat lunak, 80% atau lebih dari apa yang kita lakukan disebut aneh
"Pemeliharaan": tindakan perbaikan. Daripada merangkul fokus khas Barat pada pro
Karena perangkat lunak yang baik, kita harus berpikir lebih seperti tukang rumah di gedung
industri, atau mekanik otomatis di bidang otomotif. Apa yang dimiliki manajemen Jepang
katakan tentang itu ?
Pada sekitar tahun 1951, pendekatan kualitas yang disebut Total Productive Maintenance (TPM) datang
di kancah Jepang. Fokusnya adalah pada pemeliharaan dan bukan pada produksi. Salah satunya
pilar utama TPM adalah serangkaian prinsip 5S. 5S adalah seperangkat disiplin — dan
di sini saya menggunakan istilah "disiplin" secara instruktif. Prinsip-prinsip 5S ini sebenarnya ada di
Tions of Lean — kata kunci lain di kancah Barat, dan semakin menonjol
kata kunci di lingkaran perangkat lunak. Prinsip-prinsip ini bukan pilihan. Sebagaimana Paman Bob menceritakan
Hal terpentingnya, praktik perangkat lunak yang baik membutuhkan disiplin seperti itu: fokus, kehadiran pikiran,
dan berpikir. Tidak selalu hanya tentang melakukan, tentang mendorong peralatan pabrik untuk meningkatkan
kurangi pada kecepatan optimal. Filosofi 5S terdiri dari konsep-konsep ini:

• Seiri , atau organisasi (pikirkan "sort" dalam bahasa Inggris). Mengetahui di mana segala sesuatu — menggunakan
pendekatan seperti penamaan yang cocok — sangat penting. Anda pikir pengidentifikasi nama tidak
penting? Baca terus di bab-bab berikut.
• Seiton , atau kerapian (pikirkan "sistematis" dalam bahasa Inggris). Ada pepatah Amerika kuno:
Tempat untuk semuanya, dan segala sesuatu di tempatnya . Sepotong kode harus ada di mana
Anda berharap menemukannya — dan, jika tidak, Anda harus mempertimbangkan kembali untuk mendapatkannya di sana.
• Seiso , atau membersihkan (pikirkan "bersinar" dalam bahasa Inggris): Jaga tempat kerja bebas dari gantung
kabel, minyak, sisa, dan limbah. Apa yang penulis katakan di sini tentang sampah Anda
kode dengan komentar dan baris kode berkomentar yang menangkap sejarah atau keinginan
masa depan? Singkirkan mereka.
• Seiketsu , atau standardisasi: Grup setuju tentang cara menjaga kebersihan tempat kerja.
Apakah menurut Anda buku ini mengatakan sesuatu tentang memiliki gaya dan rangkaian pengkodean yang konsisten
praktik dalam kelompok? Dari mana standar itu berasal? Baca terus.
• Shutsuke , atau disiplin (disiplin diri ). Ini berarti memiliki disiplin untuk mengikuti
praktik dan untuk sering merefleksikan pekerjaan seseorang dan bersedia berubah.

Jika Anda menerima tantangan — ya, tantangan — membaca dan menerapkan buku ini,
Anda akan memahami dan menghargai poin terakhir. Di sini, kami akhirnya berkendara ke

https://translate.googleusercontent.com/translate_f 16/365
3/12/2020 www.it-ebooks.info
akar profesionalisme yang bertanggung jawab dalam profesi yang harus peduli dengan kehidupan
siklus suatu produk. Karena kami memelihara mobil dan mesin lain di bawah TPM,
pemeliharaan bawah — menunggu bug muncul — adalah pengecualian. Sebaliknya, kita naik a
level: periksa mesin setiap hari dan perbaiki bagian aus sebelum pecah, atau lakukan
setara dengan pergantian 10.000 mil pepatah minyak untuk mencegah keausan. Dalam kode,
refactor tanpa ampun. Anda dapat meningkatkan satu tingkat lebih jauh lagi, karena gerakan TPM meningkatkan
lebih dari 50 tahun yang lalu: membangun mesin yang lebih mudah dirawat. Mak-
Membaca kode Anda sama pentingnya dengan membuatnya dapat dieksekusi. Praktek utama,
diperkenalkan di kalangan TPM sekitar tahun 1960, adalah untuk fokus pada memperkenalkan seluruh mesin baru atau

www.it-ebooks.info

Halaman 22

Kata pengantar xxi

mengganti yang lama. Ketika Fred Brooks memperingatkan kita, kita mungkin harus kembali melakukan soft-soft
ware potongan dari awal setiap tujuh tahun atau lebih untuk menyapu merayap cruft. Mungkin
kita harus memperbarui konstanta waktu Brooks ke urutan minggu, hari atau jam, bukan
tahun. Di situlah letak detailnya.
Ada kekuatan besar dalam detail, namun ada sesuatu yang rendah hati dan mendalam tentang ini
pendekatan terhadap kehidupan, seperti yang mungkin kita harapkan secara stereotip dari pendekatan apa pun yang mengklaim Japa-
akar nese. Tapi ini bukan hanya pandangan Timur tentang kehidupan; Orang Inggris dan Amerika
dom penuh dengan peringatan seperti itu. Kutipan Seiton dari atas mengalir dari pena
seorang menteri Ohio yang secara harfiah memandang kerapian "sebagai obat untuk setiap tingkat kejahatan."
Bagaimana dengan Seiso? Kebersihan di sebelah kesalehan . Secantik rumah itu, berantakan
meja merampas kemegahannya. Bagaimana dengan Shutsuke dalam masalah kecil ini? Dia yang setia
dalam sedikit itu banyak yang setia . Bagaimana kalau ingin kembali faktor pada waktu yang bertanggung jawab,
memperkuat posisi seseorang untuk keputusan "besar" berikutnya, daripada menundanya? SEBUAH
menjahit tepat waktu menghemat sembilan . Burung awal menangkap cacing. Jangan menunda sampai besok
apa yang bisa kamu lakukan hari ini. (Begitulah arti asli dari frasa “yang bertanggung jawab terakhir
moment ”di Lean hingga jatuh ke tangan konsultan perangkat lunak . ) Bagaimana dengan kalibrat-
ing tempat usaha kecil, individu dalam keseluruhan besar? Oak perkasa dari biji kecil
tumbuh. Atau bagaimana mengintegrasikan pekerjaan pencegahan sederhana ke dalam kehidupan sehari-hari? Satu ons
pencegahan bernilai satu pon penyembuhan. Satu apel sehari dapat menghindarkan dari penyakit. Kode bersih
menghormati akar kebijaksanaan yang mendalam di bawah budaya kita yang lebih luas, atau budaya kita seperti dulu,
atau harus, dan bisa dengan perhatian terhadap detail.
Bahkan dalam literatur arsitektur besar kita menemukan gergaji yang mengingatkan kembali pada dukungan ini.
rincian yang diajukan. Pikirkan gagang pintu mies van der Rohe. Itu seiri . Itu penuh perhatian
ke setiap nama variabel. Anda harus memberi nama variabel menggunakan perawatan yang sama dengan yang Anda
nama anak pertama.
Seperti yang diketahui oleh setiap pemilik rumah, perawatan dan perbaikan yang terus menerus tidak pernah berakhir.
Arsitek Christopher Alexander - bapak pola dan bahasa pola - pandangan
setiap tindakan desain itu sendiri sebagai tindakan perbaikan kecil dan lokal. Dan dia memandang pengerjaan
struktur halus untuk menjadi ruang lingkup arsitek; bentuk-bentuk yang lebih besar dapat diserahkan pada pola
dan aplikasi mereka oleh penduduk. Desain terus berlangsung tidak hanya karena kami menambahkan yang baru
ruangan untuk rumah, tetapi karena kami memperhatikan pengecatan ulang, mengganti karpet usang, atau peningkatan-
di wastafel dapur. Kebanyakan seni menggemakan sentimen analog. Dalam pencarian kami untuk orang lain yang
menganggap rumah Allah sebagai bagian dari perincian, kita mendapati diri kita berada dalam perusahaan yang baik dari
Penulis Perancis abad ke-19 Gustav Flaubert. Penyair Prancis Paul Valery menasihati kita bahwa a
puisi tidak pernah dilakukan dan terus dikerjakan ulang, dan berhenti mengerjakannya adalah pengabaian.
Keasyikan dengan detail seperti itu biasa terjadi pada semua upaya keunggulan. Jadi mungkin disana
sedikit baru di sini, tetapi dalam membaca buku ini Anda akan ditantang untuk mengambil pelajaran yang baik
Pline bahwa Anda lama menyerah pada apatis atau keinginan untuk spontanitas dan adil
"Merespons perubahan."
Sayangnya, kami biasanya tidak melihat keprihatinan seperti itu sebagai landasan utama seni
pemrograman. Kami meninggalkan kode kami lebih awal, bukan karena itu dilakukan, tetapi karena nilai kami
sistem lebih berfokus pada penampilan luar daripada pada substansi dari apa yang kami berikan.

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 17/365
3/12/2020 www.it-ebooks.info

Halaman 23

xxii Kata pengantar

Ketidakpedulian ini pada akhirnya membuat kita rugi: Satu sen yang buruk selalu muncul . Penelitian, baik dalam
industri maupun dalam dunia akademis, merendahkan diri ke stasiun rendah menjaga kode bersih. Kembali
di hari-hari saya bekerja di organisasi Bell Labs Software Produksi Penelitian ( Produc-
tion , memang!) kami memiliki beberapa temuan back-of-the-envelope yang menyarankan agar konsisten
gaya indentasi adalah salah satu indikator yang paling signifikan secara statistik dari kepadatan bug yang rendah.
Kami ingin itu arsitektur atau bahasa pemrograman atau gagasan tinggi lainnya
harus menjadi penyebab kualitas; sebagai orang yang seharusnya profesionalisme berutang kepada
penguasaan alat dan metode desain yang tinggi, kami merasa terhina oleh nilai bahwa pabrik-
mesin lantai, coders, menambahkan melalui aplikasi lekukan sederhana yang konsisten
gaya. Mengutip buku saya sendiri 17 tahun yang lalu, gaya seperti itu membedakan keunggulan dari
kompetensi belaka. Pandangan dunia Jepang memahami nilai penting dari kehidupan sehari-hari
pekerja dan, lebih dari itu, sistem pembangunan yang berhutang kepada yang sederhana, setiap hari
tindakan para pekerja itu. Kualitas adalah hasil dari jutaan tindakan perawatan tanpa pamrih — bukan hanya dari
metode hebat apa pun yang turun dari surga. Bahwa tindakan ini sederhana tidak berarti
bahwa mereka sederhana, dan itu tidak berarti bahwa mereka mudah. Meskipun demikian mereka adalah
jalinan kebesaran dan, lebih lagi, keindahan, dalam setiap usaha manusia. Mengabaikan mereka tidak
belum sepenuhnya manusia.
Tentu saja, saya masih seorang penganjur pemikiran pada ruang lingkup yang lebih luas, dan khususnya dari
nilai pendekatan arsitektur yang berakar pada pengetahuan domain yang mendalam dan kegunaan perangkat lunak.
Buku ini bukan tentang itu — atau, paling tidak, tidak jelas tentang itu. Buku ini lebih subtil
pesan yang kedalamannya tidak boleh dihargai terlalu rendah. Cocok dengan gergaji saat ini
dari orang-orang yang benar-benar berbasis kode seperti Peter Sommerlad, Kevlin Henney dan Giovanni
Asproni. "Kode adalah desain" dan "Kode sederhana" adalah mantra mereka. Padahal kita harus
ingatlah bahwa antarmuka adalah program, dan strukturnya memiliki banyak
untuk mengatakan tentang struktur program kami, sangat penting untuk terus mengadopsi sikap rendah hati
bahwa desain hidup dalam kode. Dan sementara pengerjaan ulang dalam metafora manufaktur mengarah ke
biaya, pengerjaan ulang desain mengarah pada nilai. Kita harus melihat kode kita sebagai artikulasi yang indah
upaya mulia desain — desain sebagai proses, bukan titik akhir statis. Ada dalam kode itu
metrik arsitektur kopling dan kohesi dimainkan. Jika Anda mendengarkan Larry Constan-
Untuk mendeskripsikan kopling dan kohesi, ia berbicara dalam hal kode — bukan konsep abstrak yang tinggi
kecuali yang mungkin ditemukan di UML. Richard Gabriel menasihati kami dalam esainya, “Abstraksi
Descant ”bahwa abstraksi itu jahat. Kode adalah anti-kejahatan, dan kode yang bersih mungkin ilahi.
Kembali ke kotak kecil saya Ga-Jol, saya pikir penting untuk dicatat bahwa Denmark
kebijaksanaan menasihati kita tidak hanya untuk memperhatikan hal-hal kecil, tetapi juga jujur dalam hal kecil
sesuatu. Ini berarti bersikap jujur pada kode, jujur kepada rekan kerja tentang keadaan kita
kode dan, yang terpenting, jujur dengan diri kita sendiri tentang kode kita. Apakah kita melakukan yang terbaik untuk itu?
"Meninggalkan perkemahan lebih bersih daripada yang kita temukan"? Apakah kita mem-re-faktor kode kita sebelum check-
masuk? Ini bukan kekhawatiran pinggiran tetapi kekhawatiran yang terletak tepat di tengah
Nilai lincah. Merupakan praktik yang direkomendasikan dalam Scrum bahwa re-factoring menjadi bagian dari
kecuali "Done." Baik arsitektur maupun kode bersih tidak menuntut kesempurnaan, hanya kejujuran
dan melakukan yang terbaik yang kami bisa. Berbuat salah adalah manusia; untuk memaafkan, ilahi. Di Scrum, kami membuat
hal yang terlihat. Kami mengangkut cucian kotor kami. Kami jujur tentang keadaan kode kami karena

www.it-ebooks.info

Halaman 24

Kata pengantar xxiii

kode tidak pernah sempurna. Kita menjadi lebih manusiawi, lebih layak bagi yang ilahi, dan lebih dekat
untuk kebesaran itu dalam detail.
Dalam profesi kita, kita sangat membutuhkan semua bantuan yang bisa kita dapatkan. Jika lantai toko bersih

https://translate.googleusercontent.com/translate_f 18/365
3/12/2020 www.it-ebooks.info
mengurangi kecelakaan,
mereka. Adapun buku ini,dan
ituperalatan toko yang
adalah aplikasi terorganisir
pragmatis terbaikmeningkatkan produktivitas,
dari prinsip Lean maka saya
untuk perangkat siap
lunak I untuk itu
pernah lihat di media cetak. Saya berharap tidak kurang dari kelompok kecil yang praktis ini, indi- vidu
video yang telah berjuang bersama selama bertahun-tahun tidak hanya untuk menjadi lebih baik, tetapi juga untuk hadiah
pengetahuan mereka untuk industri dalam pekerjaan seperti sekarang Anda temukan di tangan Anda. Itu meninggalkan
dunia sedikit lebih baik daripada yang saya temukan sebelum Paman Bob mengirimi saya naskah itu.
Setelah menyelesaikan latihan ini dengan wawasan yang tinggi, saya pergi untuk membersihkan meja saya.

James O. Coplien
Mørdrup, Denmark

www.it-ebooks.info

Halaman 25

halaman ini sengaja dibiarkan kosong

https://translate.googleusercontent.com/translate_f 19/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 26

pengantar

ocus Shift

(c) 2008 F
Direproduksi dengan izin dari Thom Holwerda.
http://www.osnews.com/story/19266/WTFs_m

https://translate.googleusercontent.com/translate_f 20/365
3/12/2020 www.it-ebooks.info

Pintu mana yang mewakili kode Anda? Pintu mana yang mewakili tim Anda atau perusahaan Anda?
Kenapa kita ada di ruangan itu? Apakah ini hanya tinjauan kode normal atau kami telah menemukan aliran
masalah mengerikan tak lama setelah ditayangkan? Apakah kita sedang debugging dalam panik, meneliti kode
yang menurut kami berhasil? Apakah pelanggan pergi berbondong-bondong dan manajer bernafas

xxv

www.it-ebooks.info

Halaman 27

xxvi pengantar

leher kita? Bagaimana kita bisa memastikan bahwa kita berakhir di belakang pintu kanan ketika keadaan berjalan
sulit? Jawabannya adalah: keahlian .
Ada dua bagian untuk mempelajari keahlian: pengetahuan dan pekerjaan. Anda harus mendapatkan
pengetahuan tentang prinsip, pola, praktik, dan heuristik yang diketahui oleh pengrajin, dan
Anda juga harus menggiling pengetahuan itu ke jari, mata, dan usus Anda dengan bekerja keras dan
berlatih.
Saya bisa mengajari Anda fisika mengendarai sepeda. Memang, matematika klasik adalah
relatif mudah. Gravitasi, gesekan, momentum sudut, pusat massa, dan sebagainya
sebagainya, dapat ditunjukkan dengan kurang dari satu halaman penuh dengan persamaan. Mengingat rumus-rumus itu I
bisa membuktikan kepada Anda bahwa mengendarai sepeda itu praktis dan memberi Anda semua pengetahuan Anda
diperlukan untuk membuatnya bekerja. Dan Anda masih akan jatuh saat pertama kali naik sepeda itu.
Pengkodean tidak berbeda. Kita bisa menuliskan semua prinsip bersih “enak”
kode dan kemudian percaya Anda untuk melakukan pekerjaan (dengan kata lain, biarkan Anda jatuh ketika Anda melanjutkan
sepeda), tetapi kemudian guru seperti apa yang membuat kita, dan siswa seperti apa
apakah itu akan membuatmu?
Tidak. Itu bukan cara buku ini akan bekerja.
Belajar menulis kode yang bersih adalah kerja keras . Ini membutuhkan lebih dari sekedar pengetahuan
prinsip dan pola. Anda harus berkeringat karenanya. Anda harus berlatih sendiri, dan menonton
dirimu gagal. Anda harus melihat orang lain berlatih dan gagal. Anda harus melihat mereka tersandung dan
menelusuri kembali langkah mereka. Anda harus melihat mereka menderita atas keputusan dan melihat harga yang mereka bayar
membuat keputusan itu dengan cara yang salah.
Bersiaplah untuk bekerja keras saat membaca buku ini. Ini bukan buku "merasa baik" itu
Anda dapat membaca di pesawat terbang dan selesai sebelum mendarat. Buku ini akan membuat Anda bekerja, dan
bekerja keras . Pekerjaan apa yang akan Anda lakukan? Anda akan membaca kode — banyak kode.
Dan Anda akan ditantang untuk berpikir tentang apa yang benar tentang kode itu dan apa yang salah
dengan itu. Anda akan diminta untuk mengikuti saat kami memisahkan modul dan mengembalikannya
bersama lagi. Ini akan membutuhkan waktu dan usaha; tapi kami pikir itu akan sia-sia.
Kami telah membagi buku ini menjadi tiga bagian. Beberapa bab pertama menjelaskan prinsip-prinsip tersebut.
ciples, pola, dan praktik penulisan kode bersih. Ada sedikit kode di dalamnya
bab, dan mereka akan sulit dibaca. Mereka akan mempersiapkan Anda untuk bagian kedua
datang. Jika Anda meletakkan buku setelah membaca bagian pertama, semoga sukses untuk Anda!
Bagian kedua dari buku ini adalah kerja keras. Ini terdiri dari beberapa studi kasus
kompleksitas yang semakin meningkat. Setiap studi kasus adalah latihan membersihkan beberapa kode — dari
mengubah kode yang memiliki beberapa masalah menjadi kode yang memiliki lebih sedikit masalah. Detail dalam
bagian ini sangat intens . Anda harus bolak-balik antara narasi dan
daftar kode. Anda harus menganalisis dan memahami kode yang sedang kami kerjakan dan
berjalan melalui alasan kami untuk membuat setiap perubahan yang kami buat. Sisihkan waktu
karena ini akan memakan waktu berhari-hari .
Bagian ketiga dari buku ini adalah imbalannya. Ini adalah satu bab berisi daftar
ristik dan aroma berkumpul saat membuat studi kasus. Saat kami berjalan melewati dan
membersihkan kode dalam studi kasus, kami mendokumentasikan setiap alasan untuk tindakan kami sebagai a

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 21/365
3/12/2020 www.it-ebooks.info
Halaman 28

pengantar xxvii

heuristik atau bau. Kami mencoba memahami reaksi kami sendiri terhadap kode yang kami baca
dan berubah, dan bekerja keras untuk menangkap mengapa kami merasakan apa yang kami rasakan dan melakukan apa yang kami lakukan.
Hasilnya adalah basis pengetahuan yang menggambarkan cara kita berpikir ketika kita menulis, membaca, dan
kode bersih.
Basis pengetahuan ini memiliki nilai terbatas jika Anda tidak melakukan pekerjaan membaca dengan cermat
melalui studi kasus di bagian kedua buku ini. Dalam studi kasus tersebut kami memiliki
sepenuhnya mencatat setiap perubahan yang kami buat dengan referensi ke depan ke heuristik. Ini untuk-
referensi lingkungan muncul dalam tanda kurung seperti ini: [H22]. Ini memungkinkan Anda melihat konteks di
dimana heuristik itu diterapkan dan ditulis! Bukan heuristik itu sendiri
sangat berharga, itu adalah hubungan antara heuristik dan keputusan-keputusan terpisah kita
dibuat sambil membersihkan kode dalam studi kasus .
Untuk lebih membantu Anda dengan hubungan-hubungan itu, kami telah menempatkan referensi silang di bagian akhir
buku yang menunjukkan nomor halaman untuk setiap referensi maju. Anda dapat menggunakannya untuk melihat
di setiap tempat di mana heuristik tertentu diterapkan.
Jika Anda membaca bagian pertama dan ketiga dan melewatkan studi kasus, maka Anda akan melakukannya
telah membaca buku "merasa baik" yang lain tentang menulis perangkat lunak yang baik. Tetapi jika Anda mengambil
waktu untuk mengerjakan studi kasus, mengikuti setiap langkah kecil, setiap keputusan menit — jika
Anda menempatkan diri Anda di tempat kami, dan memaksakan diri Anda untuk berpikir di jalan yang sama yang kita
berpikir, maka Anda akan memperoleh pemahaman yang lebih kaya tentang prinsip, pola, praktik
tices, dan heuristik. Mereka tidak akan menjadi "merasa baik" lagi. Mereka akan melakukannya
tanah ke usus, jari, dan hati Anda. Mereka akan menjadi bagian dari Anda dengan cara yang sama
bahwa sepeda menjadi perpanjangan dari keinginan Anda ketika Anda sudah menguasai cara mengendarainya.

Ucapan Terima Kasih

Karya seni
Terima kasih untuk dua artis saya, Jeniffer Kohnke dan Angela Brooks. Jennifer bertanggung jawab
untuk gambar yang menakjubkan dan kreatif di awal setiap bab dan juga untuk potret
dari Kent Beck, Ward Cunningham, Bjarne Stroustrup, Ron Jeffries, Grady Booch, Dave
Thomas, Michael Feathers, dan saya sendiri.
Angela bertanggung jawab atas gambar-gambar cerdas yang menghiasi jeroan setiap bab.
Dia telah membuat beberapa foto untuk saya selama bertahun-tahun, termasuk banyak foto dalam.
tures dalam Pengembangan Perangkat Lunak Agile: Prinsip, Pola, dan Praktik . Dia juga milikku
anak sulung yang aku senang.

www.it-ebooks.info

Halaman 29

halaman ini sengaja dibiarkan kosong

https://translate.googleusercontent.com/translate_f 22/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 30

Di Sampul
Gambar di sampul M104: Galaksi Sombrero. M104 terletak di Virgo dan
hanya di bawah 30 juta tahun cahaya dari kita. Pada intinya adalah lubang hitam supermasif berbobot
masuk sekitar satu miliar massa matahari.
Apakah gambar mengingatkan Anda tentang ledakan bulan kekuatan Klingon Praxis ? saya
ingat dengan jelas adegan di Star Trek VI yang menunjukkan cincin puing-puing ekuatorial terbang
jauh dari ledakan itu. Sejak adegan itu, cincin khatulistiwa telah menjadi artefak umum
dalam ledakan film sci-fi. Itu bahkan ditambahkan ke ledakan Alderaan di edisi selanjutnya
dari film Star Wars pertama.
Apa yang menyebabkan cincin ini terbentuk di sekitar M104? Mengapa ia memiliki pusat yang sangat besar
tonjolan dan nukleus yang terang dan sekecil itu? Bagiku itu tampak seperti lubang hitam tengah
kehilangan kesejukannya dan meniup lubang 30.000 tahun cahaya di tengah galaksi. Celakalah menimpa siapa pun
peradaban yang mungkin berada di jalur gangguan kosmik itu.
Lubang hitam supermasif menelan seluruh bintang untuk makan siang, mengubah ukuran yang cukup besar.
tion massa mereka menjadi energi. E = MC 2 cukup leverage, tetapi ketika M adalah massa bintang:

https://translate.googleusercontent.com/translate_f 23/365
3/12/2020 www.it-ebooks.info
Mencari! Berapa
Mungkinkah banyak
ukuran pusatbintang yang jatuh
batal menjadi dengan cepat ke rahang itu sebelum monster itu kenyang?
petunjuk?
Gambar M104 pada sampul adalah a
kombinasi foto cahaya tampak yang terkenal
tograph dari Hubble (kanan), dan yang terbaru
gambar inframerah dari Spitzer yang mengorbit
observatorium (di bawah, kanan). Itu infra merah
gambar yang jelas menunjukkan kepada kita sifat cincin
galaksi. Dalam cahaya tampak kita hanya melihat
tepi depan cincin dalam siluet. Pusat
tral tonjolan mengaburkan sisa cincin.
Tetapi dalam inframerah, partikel panas masuk
cincin bersinar melalui tonjolan pusat. Itu
dua gambar digabungkan memberi kita pandangan kita sudah
tidak terlihat sebelumnya dan menyiratkan bahwa sudah lama itu
adalah aktivitas yang mengamuk.

Gambar sampul: © Spitzer Space Telescope

xxix

www.it-ebooks.info

Halaman 31

halaman ini sengaja dibiarkan kosong

https://translate.googleusercontent.com/translate_f 24/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 32

1
Kode Bersih

Anda membaca buku ini karena dua alasan. Pertama, Anda seorang programmer. Kedua, kamu mau
untuk menjadi programmer yang lebih baik. Baik. Kami membutuhkan programmer yang lebih baik.

www.it-ebooks.info

Halaman 33

https://translate.googleusercontent.com/translate_f 25/365
3/12/2020 www.it-ebooks.info

2 Bab 1: Kode Bersih

Ini adalah buku tentang pemrograman yang bagus. Itu diisi dengan kode. Kita akan melihat
kode dari setiap arah yang berbeda. Kami akan melihat ke bawah dari atas, ke atas dari atas
bawah, dan melewatinya dari dalam ke luar. Pada saat kita selesai, kita akan tahu a
banyak tentang kode. Terlebih lagi, kami akan dapat membedakan antara kode yang baik dan yang buruk
kode. Kami akan tahu cara menulis kode yang baik. Dan kita akan tahu bagaimana mengubah kode buruk menjadi
kode yang baik.

Akan Ada Kode


Orang mungkin berpendapat bahwa sebuah buku tentang kode entah bagaimana ketinggalan zaman — kode itu tidak
lagi masalah; bahwa kita harus memperhatikan model dan persyaratan saja.
Memang ada yang menyarankan agar kita mendekati akhir kode. Itu akan segera semua kode
dihasilkan alih-alih ditulis. Programmer itu tidak diperlukan karena bisnis
Orang-orang akan membuat program dari spesifikasi.
Omong kosong! Kami tidak akan pernah terbebas dari kode, karena kode mewakili detail dari
Persyaratan. Pada tingkat tertentu rincian itu tidak dapat diabaikan atau diabstraksikan; mereka harus
ditentukan. Dan menentukan persyaratan secara rinci sehingga mesin dapat mengeksekusi mereka adalah
pemrograman . Spesifikasi seperti itu adalah kode .
Saya berharap bahwa tingkat abstraksi bahasa kita akan terus meningkat. saya
juga berharap bahwa jumlah bahasa khusus domain akan terus bertambah. Ini
akan menjadi hal yang baik. Tetapi itu tidak akan menghilangkan kode. Memang semua spesifikasi tertulis
di level yang lebih tinggi dan bahasa khusus domain ini akan menjadi kode! Masih perlu
menjadi ketat, akurat, dan begitu formal dan terperinci sehingga mesin dapat mengerti dan
jalankan itu.
Orang-orang yang berpikir bahwa kode suatu hari akan menghilang seperti ahli matematika yang
berharap suatu hari menemukan matematika yang tidak harus formal. Mereka berharap
bahwa suatu hari kita akan menemukan cara untuk membuat mesin yang bisa melakukan apa yang kita inginkan
dari apa yang kita katakan. Mesin-mesin ini harus dapat memahami kita dengan baik sehingga mereka
dapat menerjemahkan kebutuhan yang secara samar-samar ditentukan ke dalam program yang menjalankan dengan sempurna yang secara tepat memenuhi
kebutuhan itu.
Ini tidak akan pernah terjadi. Bahkan manusia, dengan semua intuisi dan kreativitas mereka,
telah mampu menciptakan sistem yang sukses dari perasaan samar pelanggan mereka.
Memang, jika disiplin spesifikasi persyaratan telah mengajarkan kita sesuatu, itu adalah itu
persyaratan yang ditentukan dengan baik adalah seformal kode dan dapat bertindak sebagai tes yang dapat dieksekusi untuk itu
kode!
Ingat kode itu benar-benar bahasa di mana kita pada akhirnya mengekspresikan
KASIH. Kami dapat membuat bahasa yang lebih dekat dengan persyaratan. Kami dapat membuat alat
yang membantu kami mengurai dan mengumpulkan persyaratan tersebut ke dalam struktur formal. Tapi kita akan melakukannya
jangan pernah menghilangkan presisi yang diperlukan — jadi selalu ada kode.

www.it-ebooks.info

Halaman 34

Kode yang salah 3

Kode yang salah


Saya baru-baru ini membaca kata pengantar untuk Kent Beck
Pola Implementasi buku . 1 Dia berkata, “. . . ini
Buku ini didasarkan pada premis yang agak rapuh: itu
masalah kode yang baik. . . . " Sebuah rapuh premis? Saya menemukan
setuju! Saya pikir premis adalah yang paling banyak
kuat, didukung, dan kelebihan dari semua pra-
mises di kerajinan kami (dan saya pikir Kent tahu itu). Kita
tahu hal-hal kode yang baik karena kita harus melakukannya
berurusan begitu lama dengan kekurangannya.
Saya tahu satu perusahaan bahwa, di akhir 80-an,

https://translate.googleusercontent.com/translate_f 26/365
3/12/2020 www.it-ebooks.info
menulis aplikasi pembunuh . Itu sangat populer, dan banyak
profesional membeli dan menggunakannya. Tapi kemudian
siklus rilis mulai meregang. Bug tidak
diperbaiki dari satu rilis ke yang berikutnya. Memuat kali
tumbuh dan crash meningkat. Saya ingat hari saya
mematikan produk dengan frustrasi dan tidak pernah
menggunakannya lagi. Perusahaan gulung tikar
waktu yang singkat setelah itu.
Dua dekade kemudian saya bertemu dengan salah satu karyawan awal perusahaan itu dan bertanya kepadanya
Apa yang sudah terjadi. Jawabannya menegaskan ketakutan saya. Mereka telah bergegas produk ke
pasar dan telah membuat kekacauan besar dalam kode. Ketika mereka menambahkan lebih banyak fitur, the
kode semakin buruk sampai mereka tidak bisa mengelolanya lagi. Itu yang buruk
kode yang menjatuhkan perusahaan.
Pernahkah Anda secara signifikan terhambat oleh kode yang buruk? Jika Anda seorang programmer
setiap pengalaman maka Anda sudah merasakan kesulitan ini berkali-kali. Memang, kami punya nama untuk
Itu. Kami menyebutnya mengarungi . Kami mengarungi kode buruk. Kami bekerja keras melalui tumpukan kusut
semak duri dan perangkap tersembunyi. Kami berjuang untuk menemukan jalan kami, berharap untuk beberapa petunjuk, beberapa
petunjuk, tentang apa yang sedang terjadi; tetapi yang kita lihat adalah kode yang semakin tidak masuk akal.
Tentu saja Anda terhambat oleh kode yang buruk. Jadi — mengapa Anda menulisnya?
Apakah Anda mencoba untuk berpuasa? Apakah Anda terburu-buru? Mungkin begitu. Mungkin Anda merasakannya
tidak punya waktu untuk melakukan pekerjaan dengan baik; bahwa bos Anda akan marah kepada Anda jika Anda mengambil
waktu untuk membersihkan kode Anda. Mungkin Anda hanya lelah mengerjakan program ini dan
ingin semuanya berakhir. Atau mungkin Anda melihat tumpukan barang-barang lain yang telah Anda promosikan
ingin menyelesaikan dan menyadari bahwa Anda perlu membanting modul ini bersama sehingga Anda bisa
beralih ke yang berikutnya. Kita semua sudah melakukannya.
Kita semua telah melihat kekacauan yang baru saja kita buat dan kemudian memilih untuk membiarkannya
hari yang lain. Kita semua merasa lega melihat program berantakan kami bekerja dan memutuskan bahwa a

1. [Beck07].

www.it-ebooks.info

Halaman 35

4 Bab 1: Kode Bersih

kekacauan kerja lebih baik daripada tidak sama sekali. Kita semua mengatakan kita akan kembali dan membersihkannya nanti. Dari
Tentu saja, pada masa itu kita tidak tahu hukum LeBlanc: Nanti sama dengan tidak pernah .

Total Biaya Memiliki Mess


Jika Anda telah menjadi programmer selama lebih dari dua atau tiga tahun, Anda mungkin sudah
secara signifikan diperlambat oleh kode berantakan orang lain. Jika Anda seorang programmer
selama lebih dari dua atau tiga tahun, Anda mungkin diperlambat oleh kode yang berantakan.
Tingkat perlambatan bisa signifikan. Selama rentang satu atau dua tahun, tim itu
bergerak sangat cepat pada awal proyek dapat menemukan diri mereka bergerak di siput
kecepatan. Setiap perubahan yang mereka lakukan pada kode memecah dua atau tiga bagian lain dari kode. Tidak
perubahan itu sepele. Setiap penambahan atau modifikasi pada sistem mensyaratkan bahwa kusut,
tikungan, dan simpul menjadi "dimengerti" sehingga lebih banyak kusut, tikungan, dan simpul dapat ditambahkan.
Seiring waktu kekacauan itu menjadi begitu besar dan begitu dalam dan begitu tinggi, mereka tidak dapat membersihkannya. Sana
tidak mungkin sama sekali.
Ketika kekacauan terjadi, produktivitas tim terus menurun, tanpa gejala
mendekati nol. Ketika produktivitas menurun, manajemen melakukan satu-satunya hal yang mereka bisa;
mereka menambah lebih banyak staf ke proyek dengan harapan meningkatkan produktivitas. Tetapi staf baru itu
tidak berpengalaman dalam desain sistem. Mereka tidak tahu perbedaan antara perubahan
yang cocok dengan maksud desain dan perubahan yang menggagalkan niat desain. Selanjutnya,
mereka, dan semua orang di tim, berada di bawah tekanan mengerikan untuk meningkatkan produktivitas. Begitu
mereka semua membuat semakin banyak kekacauan, mendorong produktivitas semakin jauh ke nol.
(Lihat Gambar 1-1.)

https://translate.googleusercontent.com/translate_f 27/365
3/12/2020 www.it-ebooks.info

Gambar 1-1
Produktivitas vs waktu

www.it-ebooks.info

Halaman 36

Total Biaya Memiliki Mess 5

Grand Redesign in the Sky


Akhirnya tim memberontak. Mereka memberi tahu manajemen bahwa mereka tidak dapat terus berkembang
dalam basis kode najis ini. Mereka menuntut desain ulang. Manajemen tidak mau mengeluarkan
sumber daya pada desain ulang proyek yang sama sekali baru, tetapi mereka tidak dapat menyangkal bahwa
itu mengerikan. Akhirnya mereka tunduk pada tuntutan pengembang dan mengesahkan
redesign besar di langit.
Tim harimau baru dipilih. Semua orang ingin berada di tim ini karena ini adalah green-
proyek lapangan. Mereka memulai dari awal dan menciptakan sesuatu yang benar-benar indah. Tapi hanya yang terbaik
dan paling cerdas dipilih untuk tim harimau. Semua orang harus terus mempertahankan
sistem saat ini.
Sekarang kedua tim berlomba. Tim harimau harus membangun sistem baru yang berfungsi
semua yang dilakukan sistem lama. Tidak hanya itu, mereka harus mengikuti perubahan
yang terus menerus dibuat untuk sistem yang lama. Manajemen tidak akan menggantikan yang lama
sistem hingga sistem baru dapat melakukan semua yang dilakukan sistem lama.
Perlombaan ini bisa berlangsung sangat lama. Saya telah melihatnya memakan waktu 10 tahun. Dan pada saat itu
Setelah selesai, anggota asli tim harimau sudah lama hilang, dan anggota saat ini sudah
menuntut agar sistem baru dirancang ulang karena berantakan.
Jika Anda pernah mengalami satu bagian kecil saja dari kisah yang baru saja saya ceritakan, maka Anda sudah melakukannya
tahu bahwa menghabiskan waktu menjaga kode Anda tetap bersih bukan hanya hemat biaya; ini masalah
kelangsungan hidup profesional.

Sikap
Pernahkah Anda mengarungi kekacauan yang begitu parah sehingga butuh berminggu-minggu untuk melakukan apa yang seharusnya
diambil jam? Pernahkah Anda melihat apa yang seharusnya menjadi perubahan satu baris, yang dibuat sebagai gantinya
ratusan modul berbeda? Gejala-gejala ini terlalu umum.
Mengapa ini terjadi pada kode? Mengapa kode yang baik membusuk dengan begitu cepat menjadi kode yang buruk? Kita
punya banyak penjelasan untuk itu. Kami mengeluh bahwa persyaratannya berubah dengan cara itu
menggagalkan desain aslinya. Kami meratapi jadwal yang terlalu ketat untuk melakukan hal yang benar.
Kami ngobrol tentang manajer bodoh dan pelanggan tidak toleran serta tipe pemasaran yang tidak berguna
dan pembersih telepon. Tapi kesalahannya, Dilbert sayang, bukan di bintang kita, tapi di diri kita sendiri.
Kami tidak profesional.
Ini mungkin pil pahit yang harus ditelan. Bagaimana kekacauan ini bisa menjadi kesalahan kita ? Bagaimana tentang
Persyaratan? Bagaimana dengan jadwalnya? Bagaimana dengan manajer bodoh dan tidak berguna
tipe pemasaran? Bukankah mereka yang harus disalahkan?
Tidak Manajer dan pemasar melihat ke kami untuk informasi yang mereka perlu untuk membuat
janji dan komitmen; dan bahkan ketika mereka tidak memandang kita, kita seharusnya tidak malu
tentang memberi tahu mereka apa yang kita pikirkan. Pengguna mencari kami untuk memvalidasi cara persyaratan
akan masuk ke dalam sistem. Manajer proyek mencari kami untuk membantu menyelesaikan jadwal. Kita

https://translate.googleusercontent.com/translate_f 28/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 37

6 Bab 1: Kode Bersih

sangat terlibat dalam perencanaan proyek dan berbagi banyak tanggung jawab
bility untuk setiap kegagalan; terutama jika kegagalan itu ada hubungannya dengan kode buruk!
"Tapi tunggu!" kamu bilang. "Jika saya tidak melakukan apa yang dikatakan manajer saya, saya akan dipecat." Mungkin tidak.
Kebanyakan manajer menginginkan kebenaran, bahkan ketika mereka tidak bertindak seperti itu. Kebanyakan manajer menginginkan yang baik
kode, bahkan ketika mereka terobsesi dengan jadwal. Mereka dapat mempertahankan jadwal dan
persyaratan dengan hasrat; tapi itu tugas mereka. Adalah tugas Anda untuk mempertahankan kode dengan setara
gairah.
Untuk mengantar titik ini pulang, bagaimana jika Anda seorang dokter dan memiliki seorang pasien yang menuntut
bahwa Anda menghentikan semua cuci tangan konyol sebagai persiapan untuk operasi karena itu mengambil
terlalu banyak waktu? 2 Jelas bahwa pasien adalah bos; namun dokter harus menolak sama sekali
untuk memenuhi. Mengapa? Karena dokter tahu lebih banyak daripada pasien tentang risiko penyakit
kemudahan dan infeksi. Akan tidak profesional (apalagi kriminal) bagi dokter
patuhi pasien.
Demikian juga tidak profesional bagi programmer untuk tunduk pada kehendak manajer yang tidak
memahami risiko membuat kekacauan.

The Primal Conundrum


Programmer menghadapi teka-teki nilai-nilai dasar. Semua pengembang dengan lebih dari beberapa tahun
Pengalaman tahu bahwa kekacauan sebelumnya memperlambatnya. Namun semua pengembang merasakannya
tekanan untuk membuat kekacauan agar memenuhi tenggat waktu. Singkatnya, mereka tidak meluangkan waktu
untuk pergi cepat!
Para profesional sejati tahu bahwa bagian kedua dari teka-teki itu salah. Kamu tidak akan
membuat batas waktu dengan membuat kekacauan. Memang, kekacauan akan memperlambat Anda secara instan, dan
akan memaksa Anda untuk melewatkan tenggat waktu. Satu- satunya cara untuk membuat tenggat waktu — satu-satunya cara untuk
bertindak cepat — adalah menjaga kode sebersih mungkin setiap saat.

Seni Kode Bersih?


Katakanlah Anda percaya bahwa kode berantakan adalah hambatan yang signifikan. Katakanlah Anda menerima
bahwa satu-satunya cara untuk bertindak cepat adalah menjaga kode Anda tetap bersih. Maka Anda harus bertanya pada diri sendiri: "Bagaimana
saya harus menulis kode bersih? " Tidak ada gunanya mencoba menulis kode bersih jika Anda tidak tahu apa itu
berarti kode menjadi bersih!
Berita buruknya adalah bahwa menulis kode bersih sangat mirip dengan melukis gambar. Kebanyakan dari kami
tahu kapan gambar dilukis dengan baik atau buruk. Tapi bisa mengenali seni yang bagus dari
buruk bukan berarti kita tahu cara melukis. Jadi terlalu bisa mengenali kode bersih
dari kode kotor tidak berarti kita tahu cara menulis kode bersih!

2. Ketika mencuci tangan pertama kali direkomendasikan kepada dokter oleh Ignaz Semmelweis pada tahun 1847, itu ditolak atas dasar bahwa
dokter terlalu sibuk dan tidak punya waktu untuk mencuci tangan di antara kunjungan pasien.

www.it-ebooks.info

Halaman 38

Total Biaya Memiliki Mess 7

https://translate.googleusercontent.com/translate_f 29/365
3/12/2020 www.it-ebooks.info
Menulis kode yang bersih membutuhkan penggunaan berbagai teknik kecil secara disiplin
melalui rasa “kebersihan” yang diperoleh dengan susah payah. "Kode-akal" ini adalah kuncinya.
Beberapa dari kita dilahirkan dengan itu. Sebagian dari kita harus berjuang untuk mendapatkannya. Tidak hanya membiarkannya
melihat apakah kode itu baik atau buruk, tetapi juga menunjukkan kepada kita strategi untuk menerapkan
pline untuk mengubah kode buruk menjadi kode bersih.
Seorang programmer tanpa "code-sense" dapat melihat modul yang berantakan dan mengenali
berantakan tetapi tidak tahu harus berbuat apa tentang hal itu. Seorang programmer dengan "code-sense" akan terlihat
pada modul yang berantakan dan lihat opsi dan variasi. "Kode-akal" akan membantu pro-
grammer memilih variasi terbaik dan membimbingnya untuk merencanakan urutan perilaku
melestarikan transformasi untuk pergi dari sini ke sana.
Singkatnya, seorang programmer yang menulis kode bersih adalah seorang seniman yang dapat mengambil layar kosong
melalui serangkaian transformasi hingga menjadi sistem kode yang elegan.

Apa itu Kode Bersih?


Mungkin ada definisi sebanyak yang ada programmer. Jadi saya bertanya beberapa
pemrogram terkenal dan sangat berpengalaman apa yang mereka pikirkan.

Bjarne Stroustrup, penemu C ++


dan penulis Pemrograman C ++
Bahasa

Saya suka kode saya menjadi elegan dan efisien. Itu


Logika harus mudah untuk membuatnya sulit
untuk menyembunyikan bug, ketergantungan minimal
perawatan mudah, penanganan kesalahan selesai
sesuai dengan strategi yang diartikulasikan, dan
kinerja dekat dengan optimal agar tidak menggoda
orang membuat kode berantakan dengan unsinci-
mohon optimasi. Kode bersih melakukan satu hal
baik.

Bjarne menggunakan kata "elegan." Itu


cukup kata! Kamus di MacBook ® saya
memberikan definisi berikut: menyenangkan
anggun dan bergaya dalam penampilan atau cara; cerdik dan sederhana. Perhatikan
penekanan pada kata "menyenangkan." Rupanya Bjarne berpikir bahwa kode yang bersih adalah menyenangkan untuk
Baca. Membacanya seharusnya membuat Anda tersenyum seperti kotak musik yang dirancang dengan baik atau dirancang dengan baik
mobil akan.
Bjarne juga menyebutkan efisiensi— dua kali . Mungkin ini seharusnya tidak mengejutkan kita datang
dari penemu C ++; tapi saya pikir ada lebih dari keinginan belaka untuk kecepatan.
Siklus yang terbuang tidak tepat, mereka tidak menyenangkan. Dan sekarang perhatikan kata yang digunakan Bjarne

www.it-ebooks.info

Halaman 39

8 Bab 1: Kode Bersih

untuk menggambarkan konsekuensi dari ketidakmampuan itu. Dia menggunakan kata "godaan." Ada yang dalam
kebenaran di sini. Kode buruk menggoda kekacauan untuk tumbuh! Ketika orang lain mengubah kode buruk, mereka cenderung
membuatnya lebih buruk.
Pragmatis Dave Thomas dan Andy Hunt mengatakan ini dengan cara yang berbeda. Mereka menggunakan meta
jendela rusak. 3 Bangunan dengan jendela pecah sepertinya tidak ada yang peduli
Itu. Jadi orang lain berhenti peduli. Mereka memungkinkan lebih banyak jendela menjadi rusak. Akhirnya
mereka secara aktif melanggarnya. Mereka merusak fasad dengan grafiti dan membiarkan sampah mengumpul.
lect. Satu jendela yang rusak memulai proses menuju pembusukan.
Bjarne juga menyebutkan bahwa penyerahan kesalahan harus lengkap. Ini pergi ke disk
Pline memperhatikan detail. Penanganan kesalahan singkat adalah salah satu cara yang menyediakan
grammers mengabaikan detail. Kebocoran memori adalah hal lain, kondisi balapan masih merupakan hal lain.
Penamaan tidak konsisten lagi. Hasilnya adalah bahwa kode bersih menunjukkan perhatian
detail.
Bjarne menutup dengan pernyataan bahwa kode bersih melakukan satu hal dengan baik. Itu bukan kecelakaan
bahwa ada begitu banyak prinsip desain perangkat lunak yang dapat dirubah menjadi sederhana ini
peringatan. Penulis demi penulis telah mencoba mengomunikasikan pemikiran ini. Kode yang salah coba dilakukan

https://translate.googleusercontent.com/translate_f 30/365
3/12/2020 www.it-ebooks.info
terlalu
fungsi, banyak, itu telahkelas,
masing-masing mengacaukan niat dan
setiap modul ambiguitas tujuan.
memperlihatkan Kodepikiran
sikap satu bersihyang
terfokus
tetap. Setiap
sepenuhnya
tidak terganggu, dan tidak terpolusi, oleh detail di sekitarnya.

Grady Booch, penulis Object


Analisis dan Desain Berorientasi dengan
Aplikasi

Kode bersih itu sederhana dan langsung. Kode bersih


berbunyi seperti prosa yang ditulis dengan baik. Kode bersih tidak pernah
mengaburkan niat desainer melainkan penuh
abstraksi renyah dan garis lurus
kontrol.

Grady membuat beberapa poin yang sama dengan


Bjarne, tapi dia mengambil perspektif keterbacaan . saya
terutama suka pandangannya bahwa kode bersih seharusnya
membaca seperti prosa yang ditulis dengan baik. Pikirkan kembali pada a
buku yang sangat bagus yang pernah Anda baca. Ingat bagaimana kata-kata itu hilang untuk diganti
oleh gambar! Itu seperti menonton film, bukan? Lebih baik! Anda melihat karakter, Anda
mendengar suara, Anda mengalami kesedihan dan humor.
Membaca kode yang bersih tidak akan pernah seperti membaca Lord of the Rings . Namun, liter
Bukan metafora yang buruk. Seperti novel yang bagus, kode bersih harus dengan jelas memaparkan
dalam masalah yang harus dipecahkan. Itu harus membangun ketegangan itu sampai klimaks dan kemudian memberi

3. http://www.pragmaticprogrammer.com/booksellers/2004-12.html

www.it-ebooks.info

Halaman 40

Total Biaya Memiliki Mess 9

pembaca bahwa "Aha! Tentu saja!" karena masalah dan ketegangan diselesaikan dalam wahyu
solusi yang jelas.
Saya menemukan penggunaan Grady dari frasa "abstraksi renyah" menjadi oxymoron yang menarik!
Lagi pula kata "crisp" hampir merupakan sinonim untuk "concrete." Kamus MacBook saya
memegang definisi berikut tentang "garing": cepat menentukan dan soal fakta, tanpa terburu-buru
tasi atau detail yang tidak perlu. Meskipun ini tampak penjajaran makna, kata-kata
membawa pesan yang kuat. Kode kita harus benar-benar bertentangan dengan spekulatif.
Seharusnya hanya berisi apa yang diperlukan. Pembaca kita harus menganggap kita telah
menentukan.

"Besar" Dave Thomas, pendiri


OTI, ayah baptis
Strategi gerhana

Kode bersih dapat dibaca, dan ditingkatkan dengan a


pengembang selain penulis aslinya. Memiliki
tes unit dan penerimaan. Ini memiliki makna
nama. Ini memberikan satu cara daripada banyak
cara untuk melakukan satu hal. Ini memiliki ketergantungan minimal
yang didefinisikan secara eksplisit, dan
memberikan API yang jelas dan minimal. Kode seharusnya
melek karena tergantung pada bahasanya, tidak semua
informasi yang diperlukan dapat diungkapkan dengan jelas
dalam kode saja.

Big Dave berbagi keinginan Grady untuk keterbacaan-


dengan sentuhan yang penting. Dave menegaskan itu
kode bersih memudahkan orang lain untuk meningkatkannya. Ini mungkin tampak jelas, tetapi bisa-
tidak terlalu ditekankan. Lagi pula, ada perbedaan antara kode yang mudah dibaca
dan kode yang mudah diubah.
Dave mengikat kebersihan dengan ujian! Sepuluh tahun yang lalu ini akan mengangkat banyak alis.
Tetapi disiplin Pengembangan yang Didorong Uji Coba telah memberikan dampak mendalam pada kami
industri dan telah menjadi salah satu disiplin ilmu kami yang paling mendasar. Dave benar. Kode,
tanpa tes, tidak bersih. Tidak peduli seberapa elegan itu, tidak peduli seberapa mudah dibaca dan acces-
sible, jika tidak ada ujian, itu najis.

https://translate.googleusercontent.com/translate_f 31/365
3/12/2020 www.it-ebooks.info
Dave menggunakan kata minimal dua kali. Tampaknya dia menghargai kode yang kecil
daripada kode yang besar. Memang, ini telah menjadi pengulangan yang umum di seluruh literatur perangkat lunak
mulai sejak awal. Lebih kecil lebih baik.
Dave juga mengatakan kode itu harus melek . Ini adalah referensi lembut untuk melek huruf Knuth
pemrograman. 4 Hasilnya adalah bahwa kode harus dibuat sedemikian rupa untuk membuatnya
itu bisa dibaca oleh manusia.

4. [Knuth92].

www.it-ebooks.info

Halaman 41

10 Bab 1: Kode Bersih

Michael Feathers, penulis buku Working


Efektif dengan Kode Legacy

Saya bisa mendaftar semua kualitas yang saya perhatikan


kode bersih, tetapi ada satu kualitas menyeluruh
yang mengarah pada mereka semua. Selalu bersihkan kode
Sepertinya itu ditulis oleh seseorang yang peduli.
Tidak ada yang jelas yang bisa Anda lakukan
membuatnya lebih baik. Semua hal itu dipikirkan
tentang oleh penulis kode, dan jika Anda mencoba
bayangkan peningkatan, Anda dituntun kembali ke
di mana Anda berada, duduk dalam apresiasi terhadap
kode seseorang yang tersisa untuk Anda — kode ditinggalkan oleh seseorang-
orang yang sangat peduli tentang kerajinan itu.

Satu kata: peduli. Itu benar-benar topik


buku ini. Mungkin subtitle yang sesuai
akan menjadi Cara Merawat Kode .
Michael memukul kepalanya. Kode bersih adalah
kode yang telah diurus. Seseorang telah mengambil waktu untuk membuatnya sederhana dan teratur.
Mereka telah memperhatikan detail dengan tepat. Mereka peduli.

Ron Jeffries, penulis Extreme Programming


Terpasang dan Pemrograman Ekstrim
Petualangan di C #
Ron memulai pemrograman kariernya di Fortran di
Komando Udara Strategis dan memiliki kode tertulis di
hampir setiap bahasa dan hampir setiap bahasa
mesin. Membayar untuk mempertimbangkan kata-katanya dengan hati-hati.

Dalam beberapa tahun terakhir saya mulai, dan hampir berakhir, dengan Beck
aturan kode sederhana. Dalam urutan prioritas, kode sederhana:

• Menjalankan semua tes;


• Tidak mengandung duplikasi;
• Mengungkapkan semua ide desain yang ada di
sistem;
• Meminimalkan jumlah entitas seperti kelas,
metode, fungsi, dan sejenisnya.

Dari jumlah tersebut, saya lebih fokus pada duplikasi. Ketika hal yang sama dilakukan berulang-ulang,
itu pertanda bahwa ada ide di benak kita yang tidak terwakili dengan baik dalam kode. saya mencoba untuk
mencari tahu apa itu. Kemudian saya mencoba untuk mengekspresikan ide itu dengan lebih jelas.
Ekspresivitas kepada saya termasuk nama yang bermakna, dan saya cenderung mengubah
nama-nama beberapa kali sebelum saya menetap. Dengan alat pengkodean modern seperti Eclipse,
mengganti nama cukup murah, jadi tidak masalah bagiku untuk berubah. Ekspresivitas berjalan

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 32/365
3/12/2020 www.it-ebooks.info

Halaman 42

Total Biaya Memiliki Mess 11

di luar nama, namun. Saya juga melihat apakah suatu objek atau metode melakukan lebih dari satu
benda. Jika itu adalah objek, mungkin perlu dipecah menjadi dua atau lebih objek. Jika a
metode, saya akan selalu menggunakan refactoring Metode Ekstrak di atasnya, menghasilkan satu metode
yang mengatakan lebih jelas apa yang dilakukannya, dan beberapa submethod mengatakan bagaimana hal itu dilakukan.
Duplikasi dan ekspresif membawa saya sangat jauh ke apa yang saya anggap bersih
kode, dan meningkatkan kode kotor hanya dengan dua hal ini dalam pikiran dapat membuat perbedaan besar
ence. Namun, ada satu hal lain yang saya sadari lakukan, yang agak sulit untuk dilakukan
menjelaskan.
Setelah bertahun-tahun melakukan pekerjaan ini, tampaknya bagi saya bahwa semua program terdiri dari sangat
elemen serupa. Salah satu contoh adalah "menemukan barang-barang dalam koleksi." Apakah kita memiliki data-
basis catatan karyawan, atau peta hash kunci dan nilai, atau serangkaian item
baik, kita sering menemukan diri kita menginginkan barang tertentu dari koleksi itu. Ketika saya menemukan
yang terjadi, saya akan sering membungkus implementasi tertentu dalam metode yang lebih abstrak
atau kelas. Itu memberi saya beberapa keuntungan menarik.
Saya dapat mengimplementasikan fungsi sekarang dengan sesuatu yang sederhana, katakanlah peta hash, tetapi
karena sekarang semua referensi untuk pencarian itu ditutupi oleh abstraksi kecilku, aku bisa
ubah implementasi kapan saja saya mau. Saya bisa maju dengan cepat sambil menjaga
kemampuan untuk berubah nanti.
Selain itu, koleksi abstraksi sering menarik perhatian saya pada apa yang “sebenarnya”
terjadi, dan membuat saya tidak berjalan di jalur menerapkan pengumpulan sewenang-wenang
perilaku ketika yang benar-benar saya butuhkan adalah beberapa cara yang cukup sederhana untuk menemukan apa yang saya inginkan.
Mengurangi duplikasi, ekspresif tinggi, dan membangun awal abstraksi sederhana.
Itulah yang membuat kode bersih untuk saya.

Di sini, dalam beberapa paragraf pendek, Ron telah meringkas isi buku ini. Tidak
duplikasi, satu hal, ekspresif, abstraksi kecil. Semuanya ada di sana.

Ward Cunningham, penemu Wiki,


penemu Fit, coinventor dari eXtreme
Pemrograman Kekuatan motif di belakang
Pola desain. Smalltalk dan OO
pemimpin pemikiran. Ayah baptis semua
mereka yang peduli tentang kode.

Anda tahu Anda sedang mengerjakan kode bersih ketika masing-masing


rutin yang Anda baca ternyata cukup banyak apa
kamu diharapkan. Anda bisa menyebutnya kode cantik saat
kode tersebut juga membuatnya terlihat seperti bahasanya
dibuat untuk masalah ini.

Pernyataan seperti ini adalah karakteristik Ward.


Anda membacanya, menganggukkan kepala, dan kemudian pergi ke
topik selanjutnya. Kedengarannya masuk akal, begitu jelas, sehingga nyaris tidak terdaftar sebagai sesuatu
mendalam. Anda mungkin berpikir itu seperti yang Anda harapkan. Tapi mari kita lihat lebih dekat
Lihat.

www.it-ebooks.info

Halaman 43

12 Bab 1: Kode Bersih

“. . . cukup banyak apa yang Anda harapkan. " Kapan terakhir kali Anda melihat modul itu
cukup banyak apa yang Anda harapkan? Bukankah lebih mungkin bahwa modul yang Anda lihat akan
membingungkan, rumit, kusut? Bukankah penyesatan aturan? Apakah Anda tidak terbiasa memukul-mukul
tentang mencoba untuk meraih dan memegang benang pemikiran yang memuntahkan dari seluruh sistem-

https://translate.googleusercontent.com/translate_f 33/365
3/12/2020 www.it-ebooks.info
tem dan menenun jalan mereka melalui modul yang Anda baca? Kapan terakhir kali Anda
baca beberapa kode dan menganggukkan kepala Anda seperti Anda mungkin menganggukkan kepala
pada pernyataan Ward?
Ward berharap bahwa ketika Anda membaca kode bersih Anda tidak akan terkejut sama sekali. Memang kamu
bahkan tidak akan mengeluarkan banyak usaha. Anda akan membacanya, dan itu akan menjadi apa yang Anda baca
diharapkan. Itu akan jelas, sederhana, dan menarik. Setiap modul akan mengatur panggung untuk
selanjutnya. Masing-masing memberi tahu Anda bagaimana selanjutnya akan ditulis. Program yang yang bersih begitu
ditulis dengan sangat baik sehingga Anda bahkan tidak menyadarinya. Perancang membuatnya tampak ridicu-
sangat sederhana seperti semua desain luar biasa.
Dan bagaimana dengan gagasan kecantikan Ward? Kita semua mencerca kenyataan bahwa bahasa kita
gauge tidak dirancang untuk masalah kita. Tetapi pernyataan Ward menempatkan tanggung jawab pada kita.
Dia mengatakan bahwa kode yang indah membuat bahasa terlihat seperti itu dibuat untuk masalah ! Begitu
adalah tanggung jawab kami untuk membuat bahasa terlihat sederhana! Bahasa fanatik di mana-mana,
Waspadalah! Bukan bahasa yang membuat program tampak sederhana. Itu adalah programmer
yang membuat bahasa tampak sederhana!

Sekolah Pemikiran
Bagaimana dengan saya (Paman Bob)? Apa yang saya pikirkan?
kode bersih itu? Buku ini akan memberitahu Anda, dalam persembunyian
detail, apa yang saya dan rekan saya pikirkan
kode bersih. Kami akan memberi tahu Anda apa yang menurut kami dibuat
nama variabel bersih, fungsi bersih, bersih
kelas, dll. Kami akan menyajikan opini ini sebagai berikut
kecut, dan kami tidak akan meminta maaf atas langkah kami.
Bagi kami, pada saat ini dalam karir kita, mereka adalah mutlak-
kecapi. Mereka adalah sekolah pemikiran kita tentang bersih
kode.
Seniman bela diri tidak semua setuju tentang yang terbaik
seni bela diri, atau teknik terbaik dalam bela diri
seni. Seringkali menguasai seniman bela diri akan membentuk mereka
memiliki sekolah pemikiran dan mengumpulkan siswa untuk
belajar dari mereka. Jadi kita melihat Gracie Jiu Jistu ,
didirikan dan diajarkan oleh keluarga Gracie di Brasil. Kita melihat Hakkoryu Jiu Jistu , didirikan
dan diajarkan oleh Okuyama Ryuho di Tokyo. Kita melihat Jeet Kune Do , didirikan dan diajar oleh
Bruce Lee di Amerika Serikat.

www.it-ebooks.info

Halaman 44

Kami Adalah Penulis 13

Siswa dari pendekatan ini membenamkan diri dalam ajaran pendiri.


Mereka mendedikasikan diri mereka untuk mempelajari apa yang diajarkan oleh guru tertentu itu, sering kali kepada
Sion dari ajaran master lainnya. Kemudian, ketika siswa tumbuh dalam seni mereka, mereka mungkin
menjadi siswa dari master yang berbeda sehingga mereka dapat memperluas pengetahuan dan praktik mereka.
Beberapa akhirnya melanjutkan untuk memperbaiki keterampilan mereka, menemukan teknik baru dan menemukan keterampilan mereka
sekolah sendiri.
Tak satu pun dari sekolah yang berbeda ini benar-benar benar . Namun di sekolah tertentu kita
bertindak seolah-olah ajaran dan teknik yang tepat. Bagaimanapun, ada cara yang tepat untuk
kutu Hakkoryu Jiu Jitsu, atau Jeet Kune Do. Tetapi kebenaran di dalam sekolah ini tidak inval-
idate pengajaran dari sekolah yang berbeda.
Anggap buku ini sebagai deskripsi Object Mentor School of Clean Code . Itu
teknik dan ajaran di dalam adalah cara kita mempraktikkan seni kita . Kami bersedia
mengklaim bahwa jika Anda mengikuti ajaran ini, Anda akan menikmati manfaat yang telah kami nikmati,
dan Anda akan belajar menulis kode yang bersih dan profesional. Tapi jangan membuat kesalahan
berpikir bahwa kita entah bagaimana "benar" dalam arti absolut. Ada sekolah lain dan
tuan-tuan lain yang sama-sama mengklaim profesionalisme sama seperti kita. Itu akan membawamu
untuk belajar dari mereka juga.
Memang, banyak rekomendasi dalam buku ini kontroversial. Anda akan
bly tidak setuju dengan mereka semua. Anda mungkin sangat tidak setuju dengan beberapa dari mereka. Tidak apa-apa.
Kami tidak dapat mengklaim otoritas final. Di sisi lain, rekomendasi dalam buku ini adalah
hal-hal yang telah lama kita pikirkan dan pikirkan. Kami telah mempelajarinya selama beberapa dekade

https://translate.googleusercontent.com/translate_f 34/365
3/12/2020 www.it-ebooks.info
pengalaman dan coba-coba berulang. Jadi apakah Anda setuju atau tidak, itu akan menjadi
Sayang jika Anda tidak melihat, dan menghormati, sudut pandang kami.

Kami Adalah Penulis


Bidang @author dari Javadoc memberi tahu kita siapa kita. Kami adalah penulis. Dan satu hal tentang
penulis adalah bahwa mereka memiliki pembaca. Memang, penulis bertanggung jawab untuk berkomunikasi dengan baik
dengan pembaca mereka. Lain kali Anda menulis sebaris kode, ingat Anda adalah seorang penulis,
menulis untuk pembaca yang akan menilai upaya Anda.
Anda mungkin bertanya: Berapa kode yang benar-benar dibaca? Tidak sebagian besar upaya dilakukan
menulisnya?
Apakah Anda pernah memutar ulang sesi edit? Di tahun 80-an dan 90-an kami memiliki editor seperti Emacs
yang melacak setiap penekanan tombol. Anda bisa bekerja selama satu jam dan kemudian memainkan kembali keseluruhan Anda
edit sesi seperti film kecepatan tinggi. Ketika saya melakukan ini, hasilnya sangat menarik.
Mayoritas pemutaran sedang bergulir dan menavigasi ke modul lain!

Bob memasuki modul.


Dia menggulir ke bawah ke fungsi yang membutuhkan perubahan.
Dia berhenti, mempertimbangkan pilihannya.
Oh, dia menggulir ke atas modul untuk memeriksa inisialisasi variabel.
Sekarang dia menggulir ke bawah dan mulai mengetik.

www.it-ebooks.info

Halaman 45

14 Bab 1: Kode Bersih

Ups, dia menghapus apa yang diketiknya!


Dia mengetiknya lagi.
Dia menghapusnya lagi!
Dia mengetik setengah dari sesuatu yang lain tetapi kemudian menghapusnya!
Dia menggulir ke bawah ke fungsi lain yang memanggil fungsi yang dia ubah untuk melihat bagaimana fungsinya
dipanggil.
Dia menggulung kembali dan mengetik kode yang baru saja dia hapus.
Dia berhenti.
Dia menghapus kode itu lagi!
Dia muncul jendela lain dan melihat subclass. Apakah fungsi itu ditimpa?

...
Anda mengerti maksudnya. Memang, rasio waktu yang dihabiskan membaca dan menulis lebih dari 10: 1.
Kami terus membaca kode lama sebagai bagian dari upaya untuk menulis kode baru.
Karena rasio ini sangat tinggi, kami ingin pembacaan kode menjadi mudah, bahkan jika itu membuatnya
penulisan lebih sulit. Tentu saja tidak ada cara untuk menulis kode tanpa membacanya, jadi membuatnya
mudah dibaca sebenarnya membuatnya lebih mudah untuk menulis .
Tidak ada jalan keluar dari logika ini. Anda tidak dapat menulis kode jika Anda tidak dapat membaca
kode pembulatan. Kode yang Anda coba tulis hari ini akan sulit atau mudah untuk ditulis
tergantung pada seberapa sulit atau mudah kode sekitarnya dibaca. Jadi jika Anda ingin cepat,
jika Anda ingin cepat selesai, jika Anda ingin kode Anda mudah ditulis, buatlah mudah
Baca.

Aturan Pramuka
Tidak cukup menulis kode dengan baik. Kode harus tetap bersih seiring waktu. Kita semua
kode terlihat membusuk dan menurun seiring waktu. Jadi kita harus mengambil peran aktif dalam mencegah hal ini
degradasi.
The Boy Scouts of America memiliki aturan sederhana yang dapat kita terapkan pada profesi kita.

Tinggalkan perkemahan lebih bersih daripada yang Anda temukan. 5

Jika kita semua memeriksa kode kita sedikit lebih bersih daripada ketika kita memeriksanya, kodenya
tidak bisa membusuk. Pembersihan tidak harus menjadi sesuatu yang besar. Ubah satu variabel
nama untuk yang lebih baik, hancurkan satu fungsi yang sedikit terlalu besar, hilangkan satu sedikit
duplikasi, bersihkan satu komposit jika pernyataan.
Bisakah Anda bayangkan bekerja pada proyek di mana kode menjadi lebih baik seiring waktu
berlalu? Apakah Anda yakin ada pilihan lain yang profesional? Memang, tidak berkelanjutan
peningkatan bagian intrinsik dari profesionalisme?

https://translate.googleusercontent.com/translate_f 35/365
3/12/2020 www.it-ebooks.info

5. Ini diadaptasi dari pesan perpisahan Robert Stephenson Smyth Baden-Powell kepada Scouts: “Cobalah dan tinggalkan dunia ini
sedikit lebih baik daripada yang Anda temukan. . . "

www.it-ebooks.info

Halaman 46

Bibliografi 15

Prekuel dan Prinsip


Dalam banyak hal, buku ini adalah "prekuel" dari sebuah buku yang saya tulis pada tahun 2002 berjudul Agile Software
Pengembangan: Prinsip, Pola, dan Praktek (PPP). Buku PPP menyangkut dirinya sendiri
dengan prinsip-prinsip desain berorientasi objek, dan banyak praktik yang digunakan oleh para profesional
pengembang nasional. Jika Anda belum membaca PPP, maka Anda mungkin akan melanjutkannya
diceritakan oleh buku ini. Jika Anda sudah membacanya, maka Anda akan menemukan banyak sentimen
buku itu bergema dalam buku ini di tingkat kode.
Dalam buku ini Anda akan menemukan referensi sporadis untuk berbagai prinsip desain. Ini
termasuk Prinsip Tanggung Jawab Tunggal (SRP), Prinsip Tertutup Terbuka (OCP), dan
antara lain Prinsip Ketergantungan Pembalikan (DIP). Prinsip-prinsip ini dijelaskan dalam
mendalam dalam PPP.

Kesimpulan
Buku-buku tentang seni tidak menjanjikan untuk menjadikan Anda seorang seniman. Yang bisa mereka lakukan adalah memberi Anda beberapa
alat, teknik, dan proses berpikir yang digunakan seniman lain. Begitu juga buku ini bisa-
tidak berjanji untuk menjadikan Anda seorang programmer yang baik. Tidak bisa menjanjikan untuk memberi Anda "kode-akal."
Yang bisa dilakukan hanyalah menunjukkan kepada Anda proses pemikiran programmer dan trik yang baik, teknologi
niques, dan alat yang mereka gunakan.
Sama seperti buku tentang seni, buku ini akan penuh dengan detail. Akan ada banyak kode.
Anda akan melihat kode yang baik dan Anda akan melihat kode yang buruk. Anda akan melihat kode yang buruk diubah menjadi baik
kode. Anda akan melihat daftar heuristik, disiplin, dan teknik. Anda akan melihat contoh setelahnya
contoh. Setelah itu, terserah Anda.
Ingat lelucon lama tentang pemain biola konser yang tersesat dalam perjalanannya ke sebuah pertunjukan
mance Dia menghentikan seorang lelaki tua di sudut dan bertanya kepadanya bagaimana cara mencapai Carnegie Hall.
Pria tua itu memandangi pemain biola dan biola itu terselip di bawah lengannya, dan berkata:
Tice, Nak. Praktek!"

Bibliografi
[Beck07]: Pola Implementasi , Kent Beck, Addison-Wesley, 2007.

[Knuth92]: Pemrograman Literate , Donald E. Knuth, Pusat Studi Bahasa


dan Informasi, Universitas Junior Leland Stanford, 1992.

www.it-ebooks.info

Halaman 47
https://translate.googleusercontent.com/translate_f 36/365
3/12/2020 www.it-ebooks.info

halaman ini sengaja dibiarkan kosong

www.it-ebooks.info

Halaman 48

2
https://translate.googleusercontent.com/translate_f 37/365
3/12/2020 www.it-ebooks.info

Nama Yang Berarti


oleh Tim Ottinger

pengantar
Nama-nama ada di mana-mana dalam perangkat lunak. Kami menamai variabel kami, fungsi kami, argumen kami,
kelas, dan paket. Kami memberi nama file sumber kami dan direktori yang mengandungnya. Kita
beri nama file jar kami dan file perang dan file telinga. Kami nama dan nama dan nama. Karena kami melakukannya

17

www.it-ebooks.info

Halaman 49

18 Bab 2: Nama Yang Berarti

begitu banyak, lebih baik kita melakukannya dengan baik. Berikut ini adalah beberapa aturan sederhana untuk dibuat
nama baik.

Gunakan Nama yang Mengungkap Tujuan


Mudah untuk mengatakan bahwa nama harus mengungkapkan niat. Apa yang ingin kami beri kesan pada Anda adalah itu
kami serius tentang ini. Memilih nama baik membutuhkan waktu tetapi menghemat lebih banyak daripada yang dibutuhkan.
Jadi berhati-hatilah dengan nama Anda dan ubahlah ketika Anda menemukan yang lebih baik. Semua orang yang
membaca kode Anda (termasuk Anda) akan lebih bahagia jika Anda melakukannya.
Nama variabel, fungsi, atau kelas, harus menjawab semua pertanyaan besar. Itu
harus memberi tahu Anda mengapa itu ada, apa fungsinya, dan bagaimana ia digunakan. Jika suatu nama membutuhkan
ment, maka nama tidak mengungkapkan maksudnya.

int d; // waktu berlalu dalam beberapa hari

Nama d tidak mengungkapkan apa pun. Itu tidak membangkitkan rasa waktu yang berlalu, atau hari. Kita
harus memilih nama yang menentukan apa yang sedang diukur dan unit ukuran itu-
ment:

int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;

Memilih nama yang mengungkapkan niat dapat membuatnya lebih mudah untuk dipahami dan diubah
kode. Apa tujuan dari kode ini?

Daftar publik <int []> getThem () {


List <int []> list1 = new ArrayList <int []> ();

https://translate.googleusercontent.com/translate_f 38/365
3/12/2020 www.it-ebooks.info
untuk
if (x(int
[0][]==x:4)
theList)
list1.add (x);
daftar balik1;
}

Mengapa sulit mengatakan apa yang dilakukan kode ini? Tidak ada ekspresi yang rumit.
Jarak dan lekukan masuk akal. Hanya ada tiga variabel dan dua konstanta
tersebut. Bahkan tidak ada kelas mewah atau metode polimorfik, hanya daftar
array (atau begitulah tampaknya).
Masalahnya bukan kesederhanaan kode tetapi implikasi kode (untuk koin a
frase): sejauh mana konteksnya tidak eksplisit dalam kode itu sendiri. Kode tersebut berimplikasi-
itu mensyaratkan bahwa kita mengetahui jawaban atas pertanyaan seperti:

1. Hal-hal apa saja yang ada di Daftar ?


2. Apa pentingnya zeroth subscript dari suatu item dalam theList ?
3. Apa pentingnya nilai 4 ?
4. Bagaimana saya menggunakan daftar yang dikembalikan?

www.it-ebooks.info

Halaman 50

Hindari Disinformasi 19

Jawaban atas pertanyaan-pertanyaan ini tidak ada dalam sampel kode, tetapi mereka dapat melakukannya
telah . Katakanlah kita sedang bekerja dalam permainan penyapu ranjau. Kami menemukan bahwa papan adalah daftar
sel-sel yang disebut theList . Mari kita ganti namanya menjadi gameBoard .
Setiap sel di papan direpresentasikan oleh array sederhana. Kami selanjutnya menemukan bahwa ke nol
subskrip adalah lokasi nilai status dan bahwa nilai status 4 berarti "ditandai." Hanya
dengan memberikan nama konsep ini kita dapat meningkatkan kode secara signifikan:
Daftar publik <int []> getFlaggedCells () {
List <int []> flaggedCells = ArrayList baru <int []> ();
untuk sel (int []: gameBoard)
if (sel [STATUS_VALUE] == FLAGGED)
flaggedCells.add (sel);
mengembalikan sel yang ditandai;
}

Perhatikan bahwa kesederhanaan kode tidak berubah. Masih memiliki nomor yang persis sama
operator dan konstanta, dengan jumlah yang sama persis tingkat sarangnya. Tapi kodenya
telah menjadi jauh lebih eksplisit.
Kita bisa melangkah lebih jauh dan menulis kelas sederhana untuk sel daripada menggunakan array int .
Itu dapat mencakup fungsi pengungkapan niat (sebut saja isFlagged ) untuk menyembunyikan angka ajaib
bers. Ini menghasilkan versi fungsi yang baru:

Daftar publik <Cell> getFlaggedCells () {


Daftar <Cell> flaggedCells = ArrayList baru <Cell> ();
untuk (Sel seluler: gameBoard)
if (cell.isFlagged ())
flaggedCells.add (sel);
mengembalikan sel yang ditandai;
}

Dengan perubahan nama sederhana ini, tidak sulit untuk memahami apa yang terjadi. Ini adalah
kekuatan memilih nama baik.

Hindari Disinformasi
Pemrogram harus menghindari meninggalkan petunjuk palsu yang mengaburkan makna kode. Kita harus
hindari kata-kata yang maknanya berbeda dari makna yang kami maksudkan. Sebagai contoh,
hp , aix , dan sco akan menjadi nama variabel yang buruk karena mereka adalah nama-nama plat Unix
bentuk atau varian. Bahkan jika Anda mengkodekan suatu sisi miring dan hp terlihat seperti singkatan yang bagus-
tion, itu bisa disinformatif.
Tidak mengacu pada pengelompokan rekening sebagai accountList kecuali itu sebenarnya Daftar .
Daftar kata berarti sesuatu yang spesifik untuk programmer. Jika wadah memegang
akun sebenarnya bukan Daftar , itu dapat menyebabkan kesimpulan yang salah. 1 Jadi akunGroup atau
banyak akun atau hanya akun biasa akan lebih baik.

https://translate.googleusercontent.com/translate_f 39/365
3/12/2020 www.it-ebooks.info
1. Seperti yang akan kita lihat nanti, bahkan jika wadahnya adalah Daftar, mungkin lebih baik untuk tidak menyandikan jenis wadah ke dalam namanya.

www.it-ebooks.info

Halaman 51

20 Bab 2: Nama Yang Berarti

Waspadalah dalam menggunakan nama yang bervariasi dalam hal-hal kecil. Berapa lama untuk menemukan
perbedaan halus antara XYZControllerForEfficientHandlingOfStrings dalam satu modul
dan, di suatu tempat yang sedikit lebih jauh, XYZControllerForEfficientStorageOfStrings ? Itu
kata-kata memiliki bentuk yang sangat mirip.
Konsep ejaan yang serupa juga adalah informasi . Menggunakan ejaan yang tidak konsisten tidak
Informasi . Dengan lingkungan Java modern, kami menikmati penyelesaian kode otomatis. Kita
tulis beberapa karakter nama dan tekan beberapa kombinasi hotkey (jika itu) dan
dihargai dengan daftar kemungkinan penyelesaian untuk nama itu. Sangat membantu jika nama untuk
hal yang sangat mirip mengurutkan secara alfabet dan jika perbedaannya sangat jelas,
karena pengembang cenderung memilih objek dengan nama tanpa melihat Anda berlebihan
komentar atau bahkan daftar metode yang disediakan oleh kelas itu.
Contoh yang benar-benar mengerikan dari nama disinformasi adalah penggunaan huruf kecil L atau
huruf besar O sebagai nama variabel, terutama dalam kombinasi. Masalahnya, tentu saja, adalah itu
mereka terlihat hampir seluruhnya seperti konstanta satu dan nol.

int a = l;
jika (O == l)
a = O1;
lain
l = 01;

Pembaca mungkin berpikir ini alat, tetapi kami telah memeriksa kode di mana seperti itu
segalanya berlimpah. Dalam satu kasus, penulis kode menyarankan untuk menggunakan font yang berbeda
sehingga perbedaannya lebih jelas, solusi yang harus diturunkan
semua pengembang masa depan sebagai tradisi lisan atau dalam dokumen tertulis. Masalahnya ditaklukkan
dengan finalitas dan tanpa menciptakan produk kerja baru dengan penggantian nama sederhana.

Jadikan Bermakna
Perbedaan
Programmer menciptakan masalah untuk mereka-
diri ketika mereka menulis kode hanya untuk sat-
bukan kompiler atau juru bahasa. Sebagai contoh,
karena Anda tidak dapat menggunakan nama yang sama untuk merujuk
dua hal yang berbeda dalam cakupan yang sama,
Anda mungkin tergoda untuk mengubah satu nama
dengan cara yang sewenang-wenang. Kadang-kadang ini dilakukan dengan salah mengeja, mengarah ke yang mengejutkan
situasi di mana mengoreksi kesalahan ejaan menyebabkan ketidakmampuan untuk dikompilasi. 2
Tidak cukup menambahkan nomor seri atau kata-kata derau, meskipun kompilernya adalah
puas. Jika nama harus berbeda, maka mereka juga harus berarti sesuatu yang berbeda.

2. Pertimbangkan, misalnya, praktik yang benar-benar mengerikan untuk membuat variabel bernama klass hanya karena kelas nama digunakan
untuk sesuatu yang lain.

www.it-ebooks.info

Halaman 52

https://translate.googleusercontent.com/translate_f 40/365
3/12/2020 www.it-ebooks.info

Gunakan Nama yang Dapat Diucapkan 21

Penamaan seri- angka (a1, a2, .. aN) adalah kebalikan dari penamaan yang disengaja. Seperti itu
nama tidak disinformatif — mereka tidak informatif; mereka tidak memberikan petunjuk untuk
niat penulis. Mempertimbangkan:

public static void copyChars (char a1 [], char a2 []) {


untuk (int i = 0; i <a1.length; i ++) {
a2 [i] = a1 [i];
}
}

Fungsi ini dibaca jauh lebih baik ketika sumber dan tujuan digunakan untuk argumen
nama.
Kata-kata bising adalah perbedaan lain yang tidak berarti. Bayangkan Anda memiliki Produk
kelas. Jika Anda memiliki ProductInfo atau ProductData , Anda telah membuat nama
Ferent tanpa membuat mereka berarti sesuatu yang berbeda. Info dan Data adalah noise yang tidak jelas
kata-kata seperti sebuah , sebuah , dan yang .
Perhatikan bahwa tidak ada yang salah dengan menggunakan konvensi awalan seperti sebuah dan yang begitu lama
karena mereka membuat perbedaan yang berarti. Misalnya, Anda dapat menggunakan a untuk semua variabel lokal
dan yang untuk semua argumen fungsi. 3 Masalah muncul ketika Anda memutuskan untuk memanggil
dapat theZork karena Anda sudah memiliki variabel lain bernama zork .
Kata-kata bising itu berlebihan. Variabel kata tidak boleh muncul dalam variabel
nama. Tabel kata tidak boleh muncul dalam nama tabel. Bagaimana NameString lebih baik daripada
Nama ? Apakah suatu Nama akan menjadi angka floating point? Jika demikian, itu melanggar aturan sebelumnya tentang
disinformasi. Bayangkan menemukan satu kelas bernama Pelanggan dan yang lain bernama
Tujuan Pelanggan . Apa yang harus Anda pahami sebagai perbedaan? Yang mana yang akan diwakili
jalur terbaik ke riwayat pembayaran pelanggan?
Ada aplikasi yang kami tahu di mana ini diilustrasikan. kami telah mengubah nama
untuk melindungi yang bersalah, tapi inilah bentuk kesalahannya:

getActiveAccount ();
getActiveAccounts ();
getActiveAccountInfo ();

Bagaimana para programmer dalam proyek ini seharusnya tahu yang mana dari fungsi-fungsi ini untuk memanggil?
Dengan tidak adanya konvensi khusus, variabel moneyAmount tidak dapat dibedakan
dari uang , customerInfo tidak dapat dibedakan dari pelanggan , accountData tidak bisa dibedakan-
dapat dari akun , dan Pesan tidak dapat dibedakan dari pesan . Bedakan nama dalam
sedemikian rupa sehingga pembaca tahu apa yang ditawarkan perbedaan.

Gunakan Nama yang Dapat Diucapkan


Manusia pandai kata-kata. Bagian penting dari otak kita didedikasikan untuk konsep
kata-kata. Dan kata-kata, menurut definisi, dapat diucapkan. Sayang tidak menerima

3. Paman Bob biasa melakukan ini dalam C ++ tetapi telah menghentikan praktik karena IDE modern membuatnya tidak perlu.

www.it-ebooks.info

Halaman 53

22 Bab 2: Nama Yang Berarti

keuntungan dari sebagian besar otak kita yang telah berevolusi untuk berurusan dengan bahasa yang diucapkan
pengukur. Jadi, buat nama Anda bisa diucapkan.
Jika Anda tidak bisa mengucapkannya, Anda tidak bisa mendiskusikannya tanpa terdengar seperti orang idiot. "Baik,
di sini di bee cee arr tiga cee enn tee kita punya pee ess zee kyew int, lihat? ” Ini
penting karena pemrograman adalah kegiatan sosial.
Perusahaan yang saya kenal memiliki genymdhms (tanggal pembuatan, tahun, bulan, hari, jam, menit,
dan kedua) sehingga mereka berjalan berkeliling sambil berkata "gen mengapa emm dee aich emm ess". Saya punya
kebiasaan yang menjengkelkan untuk mengucapkan segala sesuatu sebagai tertulis, jadi saya mulai mengatakan "gen-yah-mudda-
hims. " Belakangan ini disebut oleh sejumlah desainer dan analis, dan kami masih melakukannya
terdengar konyol. Tapi kami bercanda, jadi itu menyenangkan. Menyenangkan atau tidak, kami menoleransi
penamaan yang buruk. Pengembang baru harus menjelaskan variabel kepada mereka, dan kemudian mereka
membicarakannya dengan kata-kata yang dibuat-buat dan bukannya menggunakan istilah bahasa Inggris yang tepat. Membandingkan

https://translate.googleusercontent.com/translate_f 41/365
3/12/2020 www.it-ebooks.info
class DtaRcrd102 {
genymdhm Date pribadi;
modymdhms Tanggal pribadi;
private final String pszqint = "102";
/ * ... * /
};

untuk

pelanggan kelas {
DateTimestamp tanggal pribadi;
modifikasi Tanggal pribadiTimestamp ;;
private final String recordId = "102";
/ * ... * /
};

Pembicaraan cerdas sekarang mungkin: “Hei, Mikey, lihat catatan ini! Gen-
cap waktu eration diatur ke tanggal besok! Bagaimana itu bisa terjadi?"

Gunakan Nama yang Dapat Dicari


Nama huruf tunggal dan konstanta numerik memiliki masalah khusus karena tidak demikian
mudah ditemukan di seluruh badan teks.
Seseorang mungkin dengan mudah menerima MAX_CLASSES_PER_STUDENT , tetapi angka 7 bisa lebih
sulit. Pencarian dapat mengubah digit sebagai bagian dari nama file, definisi konstan lainnya
dan berbagai ekspresi di mana nilainya digunakan dengan maksud berbeda. Bahkan
lebih buruk ketika konstanta adalah angka yang panjang dan seseorang mungkin telah mengubah digit,
dengan demikian menciptakan bug sekaligus menghindari pencarian programmer.
Demikian juga, nama e adalah pilihan yang buruk untuk variabel apa pun yang mungkin programmer
perlu mencari. Ini adalah huruf paling umum dalam bahasa Inggris dan kemungkinan akan muncul
di setiap bagian teks di setiap program. Dalam hal ini, nama yang lebih panjang lebih pendek
nama, dan nama yang dapat dicari mengalahkan konstanta dalam kode.
Preferensi pribadi saya adalah bahwa nama huruf tunggal HANYA dapat digunakan sebagai variabel lokal
dapat menggunakan metode singkat. Panjang nama harus sesuai dengan ukuran cakupannya

www.it-ebooks.info

Halaman 54

Hindari Penyandian 23

[N5]. Jika variabel atau konstanta dapat dilihat atau digunakan di banyak tempat dalam tubuh kode,
sangat penting untuk memberikannya nama yang ramah pencarian. Sekali lagi bandingkan

untuk (int j = 0; j <34; j ++) {


s + = (t [j] * 4) / 5;
}

untuk

int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
jumlah int = 0;
untuk (int j = 0; j <NUMBER_OF_TASKS; j ++) {
int realTaskDays = taskEstimate [j] * realDaysPerIdealDay;
int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
jumlah + = realTaskWeeks;
}

Perhatikan bahwa jumlah , di atas, bukan nama yang sangat berguna tetapi setidaknya dapat dicari. Itu
kode yang sengaja dibuat untuk fungsi yang lebih lama, tetapi pertimbangkan betapa lebih mudahnya
akan menemukan WORK_DAYS_PER_WEEK daripada menemukan semua tempat di mana 5 digunakan dan difilter
daftar hanya ke contoh dengan makna yang dimaksudkan.

Hindari Penyandian
Kami memiliki penyandian yang cukup untuk ditangani tanpa menambah beban kami. Pengkodean
ketik atau informasi ruang lingkup ke dalam nama hanya menambah beban tambahan untuk menguraikan. Itu
tampaknya tidak masuk akal untuk meminta setiap karyawan baru untuk mempelajari pengkodean lain “bahasa
gauge ”sebagai tambahan untuk mempelajari tubuh kode (biasanya cukup besar) yang mereka akan bekerja-
masuk. Ini adalah beban mental yang tidak perlu ketika mencoba menyelesaikan masalah. Nama yang dikodekan
jarang diucapkan dan mudah salah ketik.

Notasi Hongaria
Di masa lalu, ketika kami bekerja dalam bahasa yang menantang nama panjang, kami melanggar ini

https://translate.googleusercontent.com/translate_f 42/365
3/12/2020 www.it-ebooks.info
mengesampingkan karena kebutuhan, dan dengan penyesalan. Fortran memaksa penyandian dengan membuat huruf pertama a
kode untuk jenisnya. Versi awal BASIC hanya memperbolehkan satu huruf plus satu digit. Hongaria
Notation (HN) membawa ini ke tingkat yang sama sekali baru.
HN dianggap cukup penting kembali di Windows C API, ketika setiap
benda adalah bilangan bulat pegangan atau pointer panjang atau pointer kosong , atau salah satu dari beberapa implemen-
beberapa “string” (dengan kegunaan dan atribut yang berbeda). Kompiler tidak memeriksa jenis
hari-hari itu, sehingga programmer membutuhkan penopang untuk membantu mereka mengingat tipenya.
Dalam bahasa modern kami memiliki banyak sistem tipe yang lebih kaya, dan para penyusunnya ingat
dan menegakkan jenis. Terlebih lagi, ada kecenderungan menuju kelas yang lebih kecil dan lebih pendek
berfungsi agar orang biasanya bisa melihat titik deklarasi masing-masing variabel mereka
menggunakan.

www.it-ebooks.info

Halaman 55

24 Bab 2: Nama Yang Berarti

Pemrogram Java tidak perlu pengkodean tipe. Objek sangat diketik, dan diedit
lingkungan telah maju sehingga mereka mendeteksi kesalahan tipe jauh sebelum Anda dapat menjalankan
menyusun! Jadi saat ini HN dan bentuk-bentuk lain dari pengkodean tipe hanyalah hambatan.
Mereka membuatnya lebih sulit untuk mengubah nama atau tipe variabel, fungsi, atau kelas. Mereka
membuatnya lebih sulit untuk membaca kode. Dan mereka menciptakan kemungkinan bahwa sistem pengkodean
akan menyesatkan pembaca.

PhoneNumber phoneString;
// nama tidak berubah ketika jenis diubah!

Awalan Anggota
Anda juga tidak perlu awalan variabel anggota dengan m_ lagi. Kelas dan fungsi Anda
tions harus cukup kecil sehingga Anda tidak membutuhkannya. Dan Anda harus menggunakan sunting-
ing lingkungan yang menyoroti atau mewarnai anggota untuk membuat mereka berbeda.
kelas publik Bagian {
String pribadi m_dsc; // Deskripsi tekstual
membatalkan setName (Nama string) {
m_dsc = nama;
}
}
_________________________________________________

kelas publik Bagian {


Deskripsi string;
batal setDescription (Deskripsi string) {
this.description = deskripsi;
}
}

Selain itu, orang dengan cepat belajar untuk mengabaikan awalan (atau akhiran) untuk melihat yang bermakna
bagian dari nama. Semakin banyak kita membaca kode, semakin sedikit kita melihat awalan. Akhirnya
awalan menjadi kekacauan tak terlihat dan penanda kode lama.

Antarmuka dan Implementasi


Ini terkadang merupakan kasus khusus untuk pengkodean. Misalnya, Anda sedang membangun
A BSTRACT F ACTORY untuk pembuatan bentuk. Pabrik ini akan menjadi antarmuka dan akan
diimplementasikan oleh kelas konkret. Apa yang harus Anda beri nama? IShapeFactory dan
ShapeFactory ? Saya lebih suka membiarkan antarmuka tanpa hiasan. I sebelumnya , sangat umum di
gumpalan warisan hari ini, adalah gangguan di terbaik dan terlalu banyak informasi di terburuk. Bukan saya
ingin pengguna saya mengetahui bahwa saya memberi mereka antarmuka. Saya hanya ingin mereka tahu itu
itu adalah ShapeFactory . Jadi jika saya harus menyandikan antarmuka atau implementasinya, saya memilih
pelaksanaan. Menyebutnya ShapeFactoryImp , atau bahkan CShapeFactory yang mengerikan , lebih disukai
dapat meng-enkode antarmuka.

https://translate.googleusercontent.com/translate_f 43/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info

Halaman 56

Nama Metode 25

Hindari Pemetaan Mental


Pembaca tidak harus secara mental menerjemahkan nama Anda ke nama lain yang sudah mereka miliki
tahu. Masalah ini umumnya muncul dari pilihan untuk tidak menggunakan istilah domain masalah
atau istilah domain solusi.
Ini adalah masalah dengan nama variabel satu huruf. Tentu saja penghitung lingkaran mungkin
bernama i atau j atau k (walaupun tidak pernah l !) jika ruang lingkupnya sangat kecil dan tidak ada nama lain yang dapat terhubung
galah dengan itu. Ini karena nama-nama huruf tunggal untuk penghitung lingkaran adalah tradisional.
Namun, dalam sebagian besar konteks lain, nama satu huruf adalah pilihan yang buruk; itu hanya sebuah tempat
pemegang bahwa pembaca harus secara mental memetakan ke konsep yang sebenarnya. Tidak ada alasan yang lebih buruk
anak untuk menggunakan nama c daripada karena a dan b sudah diambil.
Secara umum programmer adalah orang-orang yang cukup pintar. Orang pintar terkadang suka menunjukkan
dari kecerdasan mereka dengan menunjukkan kemampuan juggling mental mereka. Lagi pula, jika Anda bisa
ingat bahwa r adalah versi url yang lebih rendah dengan host dan skema
dihapus, maka Anda harus jelas sangat pintar.
Satu perbedaan antara seorang programmer yang cerdas dan seorang programmer profesional adalah itu
profesional memahami bahwa kejelasan adalah raja . Profesional menggunakan kekuatan mereka untuk kebaikan
dan tulis kode yang bisa dimengerti orang lain.

Nama Kelas
Kelas dan objek harus memiliki nama frasa kata benda atau kata benda seperti Pelanggan , WikiPage ,
Akun , dan AddressParser . Hindari kata-kata seperti Manajer , Pemroses , Data , atau Info dalam nama
sebuah kelas. Nama kelas tidak harus berupa kata kerja.

Nama Metode
Metode harus memiliki nama kata kerja atau frasa kata kerja seperti postPayment , deletePage , atau simpan .
Pengakses, mutator, dan predikat harus diberi nama berdasarkan nilainya dan diawali dengan get ,
set , dan yang sesuai dengan standar JavaBean. 4

string name = employee.getName ();


customer.setName ("mike");
if (paycheck.isPosted ()) ...

Ketika konstruktor kelebihan beban, gunakan metode pabrik statis dengan nama itu
jelaskan argumennya. Sebagai contoh,

Complex fulcrumPoint = Complex.FromRealNumber (23.0);

umumnya lebih baik daripada


Complex fulcrumPoint = Kompleks baru (23.0);

Pertimbangkan menegakkan penggunaannya dengan menjadikan konstruktor yang sesuai bersifat pribadi.

4. http://java.sun.com/products/javabeans/docs/spec.html

www.it-ebooks.info

Halaman 57

26 Bab 2: Nama Yang Berarti

https://translate.googleusercontent.com/translate_f 44/365
3/12/2020 www.it-ebooks.info
Jangan Lucu
Jika nama terlalu pintar, mereka akan menjadi
hanya berkesan bagi orang yang berbagi
selera humor penulis, dan hanya selama
karena orang-orang ini mengingat lelucon itu. Akan
mereka tahu nama fungsinya
Yang seharusnya dilakukan oleh HolyHandGrenade ? Tentu,
itu lucu, tapi mungkin dalam hal ini
DeleteItems mungkin nama yang lebih baik.
Pilih kejelasan dari nilai hiburan.
Kelucuan dalam kode sering muncul dalam bentuk bahasa sehari-hari atau gaul. Sebagai contoh,
jangan gunakan nama mendera () berarti membunuh () . Jangan katakan sedikit lelucon yang bergantung pada budaya
eatMyShorts () berarti batal () .
Katakan apa yang kamu maksud. Berarti apa yang Anda katakan.

Pilih Satu Kata per Konsep


Pilih satu kata untuk satu konsep abstrak dan pertahankan. Misalnya, membingungkan
telah mengambil , mengambil, dan mendapatkan sebagai metode yang setara dari kelas yang berbeda. Apa kabar
ingat nama metode mana yang cocok dengan kelas yang mana? Sedihnya, kamu sering harus ingat
perusahaan, kelompok, atau individu mana yang menulis perpustakaan atau kelas untuk mengingat yang mana
istilah itu digunakan. Jika tidak, Anda menghabiskan banyak waktu menjelajah header dan
contoh kode sebelumnya.
Lingkungan pengeditan modern seperti Eclipse dan IntelliJ-memberikan petunjuk konteks-sensitif,
seperti daftar metode yang Anda dapat memanggil objek yang diberikan. Tetapi perhatikan bahwa daftar ini tidak menggunakan
sekutu memberi Anda komentar yang Anda tulis di sekitar nama fungsi dan daftar parameter Anda.
Anda beruntung jika memberikan nama parameter dari deklarasi fungsi. Fungsinya
nama harus berdiri sendiri, dan mereka harus konsisten agar Anda dapat memilih perusahaan
metode rect tanpa eksplorasi tambahan.
Demikian juga, itu membingungkan untuk memiliki pengontrol dan manajer dan driver dalam hal yang sama
basis kode. Apa perbedaan penting antara DeviceManager dan Protokol-
Pengendali ? Mengapa keduanya bukan pengendali atau keduanya bukan manajer ? Apakah mereka berdua Driver
Betulkah? Namanya mengarahkan Anda untuk mengharapkan dua objek yang memiliki tipe yang sangat berbeda juga
memiliki kelas yang berbeda.
Leksikon yang konsisten adalah keuntungan besar bagi programmer yang harus menggunakan kode Anda.

Jangan Pun
Hindari menggunakan kata yang sama untuk dua tujuan. Menggunakan istilah yang sama untuk dua gagasan berbeda
pada dasarnya adalah permainan kata-kata.

www.it-ebooks.info

Halaman 58

Tambahkan Konteks yang Berarti 27

Jika Anda mengikuti aturan "satu kata per konsep", Anda bisa berakhir dengan banyak kelas
yang memiliki, misalnya, metode add . Selama parameter mencantumkan dan mengembalikan nilai
berbagai metode add secara semantik setara, semuanya baik-baik saja.
Namun seseorang dapat memutuskan untuk menggunakan kata add untuk "konsistensi" ketika dia tidak
sebenarnya menambahkan dalam arti yang sama. Katakanlah kita memiliki banyak kelas tempat add akan membuat
nilai baru dengan menambahkan atau menggabungkan dua nilai yang ada. Sekarang katakanlah kita sedang menulis
kelas baru yang memiliki metode yang menempatkan parameter tunggal ke dalam koleksi. Haruskah kita menelepon
metode ini tambahkan ? Ini mungkin tampak konsisten karena kami memiliki banyak metode add lainnya ,
tetapi dalam kasus ini, semantiknya berbeda, jadi kita harus menggunakan nama seperti insert atau append
sebagai gantinya. Untuk memanggil metode baru add akan menjadi permainan kata-kata.
Tujuan kami, sebagai penulis, adalah membuat kode kami semudah mungkin dipahami. Kami ingin
kode kita untuk menjadi skim cepat, bukan studi intensif. Kami ingin menggunakan sampul populer
model dimana penulis bertanggung jawab untuk membuat dirinya jelas dan bukan akademis
model di mana tugas sarjana untuk menggali makna dari kertas.

Gunakan Nama Domain Solusi


https://translate.googleusercontent.com/translate_f 45/365
3/12/2020 www.it-ebooks.info

Ingat bahwa orang yang membaca kode Anda adalah pemrogram. Jadi silakan gunakan
istilah ilmu komputer (CS), nama algoritme, nama pola, istilah matematika, dan sebagainya. Itu
tidak bijaksana untuk mengambil setiap nama dari domain masalah karena kami tidak ingin
rekan kerja harus berlari bolak-balik ke pelanggan menanyakan apa arti setiap nama
ketika mereka sudah tahu konsepnya dengan nama yang berbeda.
Nama AccountVisitor sangat berarti bagi seorang programmer yang terbiasa
pola V ISITOR . Programmer apa yang tidak tahu apa itu JobQueue ? Ada
banyak hal yang sangat teknis yang harus dilakukan oleh programmer. Memilih nama teknis untuk
hal-hal itu biasanya merupakan hal yang paling tepat.

Gunakan Masalah Nama Domain


Ketika tidak ada "programmer-eese" untuk apa yang Anda lakukan, gunakan nama dari masalah
domain lem. Setidaknya programmer yang mengelola kode Anda dapat meminta pakar domain
apa artinya.
Memisahkan solusi dan konsep domain masalah adalah bagian dari tugas pro
grammer dan desainer. Kode yang lebih berkaitan dengan konsep domain masalah
seharusnya memiliki nama yang diambil dari domain masalah.

Tambahkan Konteks yang Berarti


Ada beberapa nama yang bermakna di dalam dan tentang diri mereka sendiri — kebanyakan tidak. Sebagai gantinya,
Anda perlu menempatkan nama dalam konteks untuk pembaca Anda dengan melampirkannya dengan nama baik
kelas, fungsi, atau ruang nama. Ketika semuanya gagal, maka awalan nama mungkin perlu
essary sebagai jalan terakhir.

www.it-ebooks.info

Halaman 59

28 Bab 2: Nama Yang Berarti

Bayangkan Anda memiliki variabel bernama firstName , lastName , street , houseNumber , city ,
negara , dan kode pos . Secara keseluruhan, cukup jelas bahwa mereka membentuk alamat. Tetapi bagaimana jika
Anda baru saja melihat variabel status yang digunakan sendiri dalam suatu metode? Maukah Anda secara otomatis
menyimpulkan bahwa itu bagian dari alamat?
Anda dapat menambahkan konteks dengan menggunakan awalan: addrFirstName , addrLastName , addrState , dan sebagainya
di. Setidaknya pembaca akan mengerti bahwa variabel-variabel ini adalah bagian dari struktur yang lebih besar. Dari
Tentu saja, solusi yang lebih baik adalah membuat kelas bernama Alamat . Lalu, bahkan kompiler pun tahu
bahwa variabel milik konsep yang lebih besar.
Pertimbangkan metode pada Listing 2-1. Apakah variabel-variabel tersebut membutuhkan konteks yang lebih bermakna
teks? Nama fungsi hanya menyediakan sebagian dari konteks; algoritma menyediakan sisanya.
Setelah Anda membaca fungsi, Anda melihat bahwa tiga variabel, angka , kata kerja , dan
pluralModifier , adalah bagian dari pesan “guess statistics”. Sayangnya, konteksnya harus
disimpulkan. Saat Anda pertama kali melihat metode ini, makna variabelnya tidak jelas.

Listing 2-1
Variabel dengan konteks yang tidak jelas.
private void printGuessStatistics (kandidat char, int count) {
Nomor string;
Kata kerja string;
String pluralModifier;
if (count == 0) {
number = "no";
kata kerja = "are";
pluralModifier = "s";
} lain jika (hitung == 1) {
number = "1";
kata kerja = "is";
pluralModifier = "";
} lain {
number = Integer.toString (count);
kata kerja = "are";
pluralModifier = "s";
}
String guessMessage = String.format (
"Ada% s% s% s% s", kata kerja, angka, kandidat, jamakPengubah
);
print (guessMessage);
}

Fungsi ini agak terlalu panjang dan variabel digunakan secara keseluruhan. Untuk membagi fungsi

https://translate.googleusercontent.com/translate_f 46/365
3/12/2020 www.it-ebooks.info
Kita harus membuat kelas GuessStatisticsMessage dan membuatnya
tiga variabel bidang kelas ini. Ini memberikan konteks yang jelas untuk tiga variabel. Mereka
adalah bagian pasti dari GuessStatisticsMessage . Peningkatan konteks juga memungkinkan
algoritma yang akan dibuat lebih bersih dengan memecahnya menjadi banyak fungsi yang lebih kecil. (Lihat
Listing 2-2.)

www.it-ebooks.info

Halaman 60

Jangan Tambahkan Konteks yang Tidak Ada Dendam 29

Listing 2-2
Variabel memiliki konteks.
GuessStatisticsMessage kelas publik {
nomor String pribadi;
kata kerja String pribadi;
private String pluralModifier;

public String make (kandidat char, int count) {


createPluralDependentMessageParts (count);
return String.format (
"Ada% s% s% s% s",
kata kerja, angka, kandidat, jamakModifikasi);
}

private void createPluralDependentMessageParts (int count) {


if (count == 0) {
thereAreNoLetters ();
} lain jika (hitung == 1) {
thereIsOneLetter ();
} lain {
thereAreManyLetters (hitung);
}
}

kekosongan pribadi thereAreManyLetters (jumlah int) {


number = Integer.toString (count);
kata kerja = "are";
pluralModifier = "s";
}

kekosongan pribadi thereIsOneLetter () {


number = "1";
kata kerja = "is";
pluralModifier = "";
}

kekosongan pribadi thereAreNoLetters () {


number = "no";
kata kerja = "are";
pluralModifier = "s";
}
}

Jangan Tambahkan Konteks yang Tidak Ada Dendam


Dalam aplikasi imajiner yang disebut "Gas Station Deluxe," adalah ide yang buruk untuk mengawali setiap
kelas dengan GSD . Terus terang, Anda bekerja melawan alat Anda. Anda mengetik G dan tekan tombol com.
kunci penuh dan dihargai dengan daftar sepanjang mil dari setiap kelas dalam sistem. Apakah itu
bijaksana? Mengapa mempersulit IDE untuk membantu Anda?
Demikian juga, misalkan Anda menemukan kelas MailingAddress dalam modul akuntansi GSD , dan
Anda menamainya GSDAccountAddress . Kemudian, Anda memerlukan alamat surat untuk pelanggan Anda
aplikasi kebijaksanaan. Apakah Anda menggunakan GSDAccountAddress ? Apakah itu terdengar seperti nama yang tepat? Sepuluh dari
17 karakter adalah redundan atau tidak relevan.

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 47/365
3/12/2020 www.it-ebooks.info

Halaman 61

30 Bab 2: Nama Yang Berarti

Nama yang lebih pendek umumnya lebih baik daripada yang lebih panjang, asalkan jelas. Tambahkan no
lebih banyak konteks untuk sebuah nama daripada yang diperlukan.
Nama-nama akunAddress dan customerAddress adalah nama yang bagus untuk contoh dari
Alamat kelas tetapi bisa menjadi nama yang buruk untuk kelas. Alamat adalah nama yang bagus untuk sebuah kelas. Jika saya
perlu membedakan antara alamat MAC, alamat port, dan alamat Web, saya mungkin
pertimbangkan PostalAddress , MAC , dan URI . Nama yang dihasilkan lebih tepat, yaitu
titik semua penamaan.

Kata-kata terakhir
Hal tersulit dalam memilih nama yang baik adalah bahwa hal itu membutuhkan keterampilan deskriptif yang baik dan
latar belakang budaya bersama. Ini adalah masalah pengajaran daripada teknis, bisnis, atau
masalah manajemen. Akibatnya banyak orang di bidang ini tidak belajar melakukannya dengan sangat baik.
Orang-orang juga takut mengganti nama hal-hal karena takut bahwa beberapa pengembang lain akan melakukannya
obyek. Kami tidak berbagi ketakutan itu dan mendapati bahwa kami sebenarnya bersyukur ketika nama berubah
(menjadi lebih baik). Sebagian besar waktu kita tidak benar-benar menghafal nama-nama kelas dan metode
ods. Kami menggunakan alat modern untuk menangani detail seperti itu sehingga kami dapat fokus pada apakah
kode dibaca seperti paragraf dan kalimat, atau setidaknya seperti tabel dan struktur data (
Tence tidak selalu cara terbaik untuk menampilkan data). Anda mungkin akan berakhir mengejutkan
satu ketika Anda mengganti nama, sama seperti Anda mungkin dengan perbaikan kode lainnya. Jangan biarkan itu terjadi
menghentikan Anda di trek Anda.
Ikuti beberapa aturan ini dan lihat apakah Anda tidak meningkatkan keterbacaan Anda
kode. Jika Anda mempertahankan kode orang lain, gunakan alat refactoring untuk membantu menyelesaikannya
masalah. Itu akan membayar dalam jangka pendek dan terus membayar dalam jangka panjang.

www.it-ebooks.info

Halaman 62

https://translate.googleusercontent.com/translate_f 48/365
3/12/2020 www.it-ebooks.info

3
Fungsi

Pada hari-hari awal pemrograman kami menyusun sistem rutinitas dan subrutin kami.
Kemudian, di era Fortran dan PL / 1 kami menyusun sistem program kami, subprogram,
dan fungsi. Saat ini hanya fungsi yang bertahan dari hari-hari awal. Fungsinya adalah
baris pertama organisasi dalam program apa pun. Menulisnya dengan baik adalah topik bab ini.

31

www.it-ebooks.info

Halaman 63

32 Bab 3: Fungsi

Pertimbangkan kode pada Listing 3-1. Sulit menemukan fungsi panjang di FitNesse, 1 tetapi
setelah sedikit mencari saya menemukan yang ini. Tidak hanya panjang, tetapi sudah digandakan
kode, banyak string aneh, dan banyak tipe data dan API yang aneh dan tidak jelas. Lihat bagaimana
banyak akal yang bisa Anda buat dalam tiga menit ke depan.

Listing 3-1
HtmlUtil.java (FitNesse 20070619)
public static String testableHtml (
PageData pageData,
boolean includeSuiteSetup
) melempar Pengecualian {
WikiPage wikiPage = pageData.getWikiPage ();
StringBuffer buffer = new StringBuffer ();
if (pageData.hasAttribute ("Test")) {
if (includeSuiteSetup) {
WikiPage suiteSetup =
PageCrawlerImpl.getInheritedPage (
SuiteResponder.SUITE_SETUP_NAME, wikiPage
);
if (suiteSetup! = null) {
WikiPagePath pagePath =
suiteSetup.getPageCrawler (). getFullPath (suiteSetup);
String pagePathName = PathParser.render (pagePath);
buffer.append ("! include -setup.")
.append (pagePathName)
.append ("\ n");
}

https://translate.googleusercontent.com/translate_f 49/365
3/12/2020 www.it-ebooks.info
}
Setup WikiPage =
PageCrawlerImpl.getInheritedPage ("SetUp", wikiPage);
if (setup! = null) {
WikiPagePath setupPath =
wikiPage.getPageCrawler (). getFullPath (setup);
String setupPathName = PathParser.render (setupPath);
buffer.append ("! include -setup.")
.append (setupPathName)
.append ("\ n");
}
}
buffer.append (pageData.getContent ());
if (pageData.hasAttribute ("Test")) {
Teardown WikiPage =
PageCrawlerImpl.getInheritedPage ("TearDown", wikiPage);
if (teardown! = null) {
WikiPagePath tearDownPath =
wikiPage.getPageCrawler (). getFullPath (teardown);
String tearDownPathName = PathParser.render (tearDownPath);
buffer.append ("\ n")
.append ("! termasuk -teardown.")
.append (tearDownPathName)
.append ("\ n");
}

1. Alat pengujian open-source. www.fitnese.org

www.it-ebooks.info

Halaman 64

Fungsi 33

Listing 3-1 (lanjutan)


HtmlUtil.java (FitNesse 20070619)
if (includeSuiteSetup) {
WikiPage suiteTeardown =
PageCrawlerImpl.getInheritedPage (
SuiteResponder.SUITE_TEARDOWN_NAME,
wikiPage
);
if (suiteTeardown! = null) {
WikiPagePath pagePath =
suiteTeardown.getPageCrawler (). getFullPath (suiteTeardown);
String pagePathName = PathParser.render (pagePath);
buffer.append ("! include -teardown.")
.append (pagePathName)
.append ("\ n");
}
}
}
pageData.setContent (buffer.toString ());
return pageData.getHtml ();
}

Apakah Anda mengerti fungsinya setelah tiga menit belajar? Mungkin tidak. Ada
terlalu banyak hal yang terjadi di sana pada berbagai tingkatan abstraksi. Ada yang aneh
string dan panggilan fungsi ganjil dicampur dengan bersarang ganda jika laporan dikontrol oleh
bendera.
Namun, hanya dengan beberapa ekstraksi metode sederhana, beberapa penamaan, dan sedikit
restrukturisasi, saya bisa menangkap maksud fungsi di sembilan baris Listing 3-2.
Lihat apakah Anda dapat memahami hal itu dalam 3 menit berikutnya.

Listing 3-2
HtmlUtil.java (refactored)
public String statis renderPageWithSetupsAndTeardowns (
PageData pageData, boolean isSuite
) melempar Pengecualian {
boolean isTestPage = pageData.hasAttribute ("Test");
if (isTestPage) {
WikiPage testPage = pageData.getWikiPage ();
StringBuffer newPageContent = new StringBuffer ();
includeSetupPages (testPage, newPageContent, isSuite);
newPageContent.append (pageData.getContent ());
includeTeardownPages (testPage, newPageContent, isSuite);
pageData.setContent (newPageContent.toString ());
}

return pageData.getHtml ();


}

https://translate.googleusercontent.com/translate_f 50/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 65

34 Bab 3: Fungsi

Kecuali jika Anda seorang mahasiswa FitNesse, Anda mungkin tidak memahami semua detail.
Namun, Anda mungkin mengerti bahwa fungsi ini melakukan penyertaan beberapa pengaturan dan
merobek halaman menjadi halaman uji dan kemudian merender halaman itu ke dalam HTML. Jika Anda terbiasa
dengan JUnit, 2 Anda mungkin menyadari bahwa fungsi ini milik beberapa jenis berbasis web
kerangka pengujian. Dan, tentu saja, itu benar. Meramalkan informasi itu dari Listing 3-2
itu cukup mudah, tetapi dikaburkan dengan baik pada Listing 3-1.
Jadi apa yang membuat fungsi seperti Listing 3-2 mudah dibaca dan dipahami? Bagaimana
dapatkah kita membuat suatu fungsi mengkomunikasikan maksudnya? Atribut apa yang bisa kita berikan fungsi kita
yang akan memungkinkan pembaca biasa untuk intuisi jenis program yang mereka jalani?

Kecil!
Aturan fungsi pertama adalah bahwa mereka harus kecil. Aturan fungsi kedua adalah itu
mereka harus lebih kecil dari itu . Ini bukan pernyataan yang bisa saya benarkan. Saya tidak bisa menyediakan
setiap referensi untuk penelitian yang menunjukkan bahwa fungsi yang sangat kecil lebih baik. Apa yang bisa saya katakan
Anda adalah bahwa selama hampir empat dekade saya memiliki fungsi tertulis dari semua ukuran yang berbeda. Saya sudah menulis
sepuluh beberapa kekejian 3.000 baris jahat. Saya telah menulis beberapa fungsi di 100 hingga 300
rentang garis. Dan saya sudah menulis fungsi yang panjangnya 20 hingga 30 baris. Apa pengalaman ini?
telah mengajarkan saya, melalui trial and error yang panjang, adalah bahwa fungsinya harus sangat kecil.
Pada tahun delapan puluhan kita biasa mengatakan bahwa suatu fungsi tidak boleh lebih besar dari layar penuh.
Tentu saja kami mengatakan bahwa pada saat layar VT100 adalah 24 baris dengan 80 kolom, dan
editor kami menggunakan 4 baris untuk keperluan administrasi. Saat ini dengan font cranked-down
dan monitor besar yang bagus, Anda dapat memuat 150 karakter pada satu baris dan 100 baris atau lebih pada satu
layar. Panjang garis tidak boleh 150 karakter. Fungsinya tidak boleh lebih dari 100 baris.
Fungsinya hampir tidak boleh sepanjang 20 baris.
Seberapa pendek seharusnya suatu fungsi? Pada 1999 saya pergi mengunjungi Kent Beck di rumahnya di Ore-
gon. Kami duduk dan melakukan pemrograman bersama. Pada satu titik dia menunjukkan padaku sebuah imut
sedikit program Java / Swing yang ia sebut Sparkle . Ini menghasilkan efek visual di layar
sangat mirip dengan tongkat sihir ibu baptis peri dalam film Cinderella. Seperti kamu
menggerakkan mouse, kilau akan menetes dari kursor dengan kilau yang memuaskan,
jatuh ke bagian bawah jendela melalui medan gravitasi yang disimulasikan. Ketika Kent
menunjukkan kepada saya kode, saya terkejut oleh betapa kecil semua fungsinya. Saya terbiasa
dalam program Swing yang membutuhkan ruang vertikal bermil-mil. Setiap fungsi dalam ini pro
gram hanya dua, atau tiga, atau empat baris. Masing-masing jelas secara transparan. Masing-masing diceritakan
cerita. Dan masing-masing membawa Anda ke yang berikutnya dalam urutan yang menarik. Begitulah singkatnya fungsi Anda
seharusnya! 3

2. Alat pengujian unit sumber terbuka untuk Java. www.junit.org


3. Saya bertanya pada Kent apakah dia masih memiliki salinannya, tetapi dia tidak dapat menemukannya. Saya mencari semua komputer lama saya juga, tetapi tidak berhasil.
Yang tersisa sekarang adalah ingatan saya akan program itu.

www.it-ebooks.info

Halaman 66

https://translate.googleusercontent.com/translate_f 51/365
3/12/2020 www.it-ebooks.info

Lakukan Satu Hal 35

Seberapa pendek fungsi Anda seharusnya? Mereka biasanya harus lebih pendek dari Listing 3-2!
Memang, Listing 3-2 harus benar-benar disingkat menjadi Listing 3-3.

Listing 3-3
HtmlUtil.java (re-refactored)
public String statis renderPageWithSetupsAndTeardowns (
PageData pageData, boolean isSuite) melempar Exception {
if (isTestPage (pageData))
includeSetupAndTeardownPages (pageData, isSuite);
return pageData.getHtml ();
}

Blok dan Indentasi


Ini menyiratkan bahwa blok dalam pernyataan if , pernyataan lain , sementara pernyataan, dan
seterusnya harus sepanjang satu baris. Mungkin saluran itu harus menjadi pemanggilan fungsi. Tidak hanya itu
ini menjaga fungsi penutup kecil, tetapi juga menambah nilai dokumenter karena
fungsi yang disebut di dalam blok dapat memiliki nama deskriptif yang bagus.
Ini juga menyiratkan bahwa fungsi tidak boleh cukup besar untuk menampung struktur bersarang.
Oleh karena itu, level indent suatu fungsi tidak boleh lebih besar dari satu atau dua. Ini, dari
tentu saja, membuat fungsi lebih mudah dibaca dan dimengerti.

Lakukan Satu Hal


Seharusnya sangat jelas bahwa Listing 3-1 melakukan banyak hal
lebih dari satu hal. Itu menciptakan buffer, mengambil
halaman, mencari halaman yang diwarisi, merender jalur,
menambahkan string misterius, dan menghasilkan HTML,
antara lain. Listing 3-1 sangat sibuk dilakukan
banyak hal yang berbeda. Di sisi lain, Listing 3-3
adalah melakukan satu hal sederhana. Ini termasuk pengaturan dan
teardown ke halaman pengujian.
Saran berikut ini muncul dalam satu bentuk
atau lainnya selama 30 tahun atau lebih.

F UNCTIONS HARUS MELAKUKAN SATU HAL . T HEY HARUS MELAKUKANNYA .


T HEY HARUS MELAKUKANNYA SAJA .
Masalah dengan pernyataan ini adalah sulit untuk mengetahui apa “satu hal” itu. Apakah
Listing 3-3 melakukan satu hal? Mudah untuk menyatakan bahwa ia melakukan tiga hal:

1. Menentukan apakah halaman tersebut adalah halaman pengujian.


2. Jika demikian, termasuk setup dan teardown.
3. Merender halaman dalam HTML.

www.it-ebooks.info

Halaman 67

36 Bab 3: Fungsi

Jadi yang mana? Apakah fungsinya melakukan satu hal atau tiga hal? Perhatikan bahwa ketiganya
langkah-langkah fungsi adalah satu tingkat abstraksi di bawah nama fungsi yang dinyatakan. Kita
dapat menggambarkan fungsinya dengan menggambarkannya sebagai paragraf singkat TO 4 :

UNTUK RenderPageWithSetupsAndTeardowns, kami memeriksa untuk melihat apakah halaman tersebut adalah halaman pengujian
dan jika demikian, kami menyertakan pengaturan dan teardown. Dalam kedua kasus ini, kami merender halaman
HTML.

Jika suatu fungsi hanya melakukan langkah-langkah yang satu tingkat di bawah nama yang dinyatakan
fungsi, maka fungsi melakukan satu hal. Lagi pula, alasan kami menulis fungsi adalah untuk

https://translate.googleusercontent.com/translate_f 52/365
3/12/2020 www.it-ebooks.info
mendekomposisi konsep yang lebih besar (dengan kata lain, nama fungsi) menjadi satu set langkah di
tingkat abstraksi selanjutnya.
Seharusnya sangat jelas bahwa Listing 3-1 berisi langkah-langkah di berbagai tingkatan
abstraksi. Jadi jelas melakukan lebih dari satu hal. Bahkan Listing 3-2 memiliki dua level
abstraksi, sebagaimana dibuktikan oleh kemampuan kita untuk mengecilkannya. Tapi itu akan sangat sulit untuk berarti-
menyusut Daftar 3-3. Kita bisa mengekstrak pernyataan if ke dalam sebuah fungsi bernama
includeSetupsAndTeardownsIfTestPage , tetapi itu hanya menyatakan kembali kode tanpa mengubah
tingkat abstraksi.
Jadi, cara lain untuk mengetahui bahwa suatu fungsi melakukan lebih dari "satu hal" adalah jika Anda bisa
mengekstrak fungsi lain darinya dengan nama yang bukan hanya pernyataan ulang pelaksanaannya
mentation [G34].

Bagian dalam Fungsi


Lihat Listing 4-7 di halaman 71. Perhatikan bahwa fungsi generatePrimes dibagi menjadi
bagian seperti deklarasi , inisialisasi , dan ayakan . Ini adalah gejala yang jelas
melakukan lebih dari satu hal. Fungsi yang melakukan satu hal tidak dapat dibagi secara masuk akal
bagian.

Satu Tingkat Abstraksi per Fungsi


Untuk memastikan fungsi kita melakukan "satu hal," kita perlu memastikan bahwa
pernyataan-pernyataan dalam fungsi kita semuanya pada tingkat abstraksi yang sama. Sangat mudah untuk melihat caranya
Listing 3-1 melanggar aturan ini. Ada konsep di sana yang berada pada tingkat yang sangat tinggi
abstraksi, seperti getHtml () ; orang lain yang berada pada tingkat menengah abstraksi, seperti
sebagai: String pagePathName = PathParser.render (pagePath) ; dan masih ada lagi yang
tingkat yang sangat rendah, seperti: .append ("\ n") .
Mencampur tingkat abstraksi dalam suatu fungsi selalu membingungkan. Pembaca mungkin tidak
dapat mengetahui apakah ekspresi tertentu adalah konsep penting atau detail. Lebih buruk,

4. Bahasa LOGO menggunakan kata kunci "TO" dengan cara yang sama seperti Ruby dan Python menggunakan "def." Jadi setiap fungsi dimulai dengan
kata "TO." Ini memiliki efek yang menarik pada cara fungsi dirancang.

www.it-ebooks.info

Halaman 68

Ganti Pernyataan 37

seperti jendela pecah, begitu detail dicampur dengan konsep-konsep penting, semakin banyak
detail cenderung bertambah dalam fungsi.

Membaca Kode dari Atas ke Bawah: Aturan Stepdown


Kami ingin kode dibaca seperti narasi top-down. 5 Kami ingin setiap fungsi mengikuti
diturunkan oleh orang-orang di tingkat berikutnya abstraksi sehingga kita dapat membaca program, turun
satu tingkat abstraksi sekaligus ketika kita membaca daftar fungsi. Saya menyebutnya The Step-
Aturan bawah .
Untuk mengatakan ini secara berbeda, kami ingin dapat membaca program seolah-olah itu satu set
dari TO paragraf, yang masing-masing menggambarkan tingkat saat abstraksi dan rujukan terbaik
menyusun paragraf-paragraf TO berikutnya pada level berikutnya ke bawah.

Untuk memasukkan setup dan teardown, kami memasukkan setup, kemudian kami menyertakan
tenda, dan kemudian kami menyertakan teardown.
Untuk memasukkan pengaturan, kami menyertakan pengaturan suite jika ini adalah suite, maka kami menyertakan
pengaturan reguler.
Untuk memasukkan pengaturan suite, kami mencari hierarki induk untuk halaman "SuiteSetUp"
dan tambahkan pernyataan sertakan dengan jalur halaman itu.
Untuk mencari orang tua. . .

Ternyata sangat sulit bagi programmer untuk belajar mengikuti aturan ini dan menulis
fungsi yang tetap pada satu tingkat abstraksi. Tetapi mempelajari trik ini juga sangat
penting. Ini adalah kunci untuk menjaga fungsi tetap singkat dan memastikan mereka melakukan "satu hal."
Membuat kode dibaca seperti seperangkat paragraf TO -down adalah teknik yang efektif untuk
menjaga level abstraksi konsisten.
Lihatlah Daftar 3-7 di akhir bab ini. Itu menunjukkan keseluruhan

https://translate.googleusercontent.com/translate_f 53/365
3/12/2020 www.it-ebooks.info
Fungsi testableHtml refactored sesuai dengan prinsip-prinsip yang dijelaskan di sini. Memperhatikan
bagaimana setiap fungsi memperkenalkan fungsi berikutnya, dan masing-masing fungsi tetap pada tingkat yang konsisten
abstraksi.

Ganti Pernyataan
Sulit untuk membuat pernyataan beralih kecil . 6 Bahkan pernyataan beralih dengan hanya dua kasus adalah
lebih besar dari saya ingin satu blok atau fungsi menjadi. Ini juga sulit untuk membuat saklar negara bagian
yang melakukan satu hal. Secara alami, pergantian pernyataan selalu melakukan hal-hal N. Sayangnya-
akhir-akhir ini kita tidak selalu dapat menghindari pernyataan switch , tetapi kita dapat memastikan bahwa setiap switch
pernyataan dimakamkan di kelas tingkat rendah dan tidak pernah diulang. Kami melakukan ini, tentu saja, dengan
polimorfisme.

5. [KP78], hlm. 37.


6. Dan, tentu saja, saya sertakan jika / orang lain terlibat dalam hal ini.

www.it-ebooks.info

Halaman 69

38 Bab 3: Fungsi

Pertimbangkan Listing 3-4. Ini menunjukkan hanya satu dari operasi yang mungkin bergantung pada
tipe karyawan.

Listing 3-4
Payroll.java
perhitungan uang publikPembayaran (Karyawan e)
melempar InvalidEmployeeType {
switch (e.type) {
case KOMISI:
return calculCommissionedPay (e);
kasus SETIAP:
kembalikan kalkulasiHourlyPay (e);
kasus DIGANTI:
return calculSalariedPay (e);
default:
melempar InvalidEmployeeType (e.type) baru;
}
}

Ada beberapa masalah dengan fungsi ini. Pertama, itu besar, dan saat baru
tipe karyawan ditambahkan, itu akan tumbuh. Kedua, sangat jelas melakukan lebih dari satu hal.
Ketiga, melanggar Single Prinsip Tanggung Jawab 7 (SRP) karena ada lebih dari satu
alasan untuk itu berubah. Keempat, itu melanggar Open Closed Principle 8 (OCP) karena itu
harus berubah setiap kali tipe baru ditambahkan. Tapi mungkin masalah terburuk dengan ini
fungsi adalah bahwa ada banyak fungsi lain yang memiliki jumlah yang tidak terbatas
struktur. Misalnya kita bisa punya
isPayday (Karyawan e, Tanggal tanggal),

atau

deliverPay (Karyawan e, Pembayaran uang),

atau sejumlah orang lain. Semuanya akan memiliki struktur merusak yang sama.
Solusi untuk masalah ini (lihat Listing 3-5) adalah dengan mengubur pernyataan switch di
basement dari A BSTRACT F ACTORY , 9 dan tidak pernah membiarkan siapa pun melihatnya. Pabrik akan menggunakan
beralih pernyataan untuk membuat turunan yang tepat dari Karyawan , dan berbagai
fungsi ious, seperti calculatePay , isPayday , dan deliverPay , akan dikirim poli-
secara morfis melalui antarmuka Karyawan .
Aturan umum saya untuk pernyataan switch adalah bahwa mereka dapat ditoleransi jika muncul
hanya sekali, digunakan untuk membuat objek polimorfik, dan disembunyikan di belakang warisan

7. a. http://en.wikipedia.org/wiki/Single_responsibility_principl e
b. http://www.objectmentor.com/resources/articles/srp.pdf
8. a. http://en.wikipedia.org/wiki/Open/closed_principle
b. http://www.objectmentor.com/resources/articles/ocp.pdf
9. [GOF].

https://translate.googleusercontent.com/translate_f 54/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 70

Gunakan Nama Deskriptif 39

Listing 3-5
Karyawan dan Pabrik
public abstrak class Karyawan {
boolean abstrak publik isPayday ();
public abstract Money calculPay ();
public abstrak void deliverPay (Pembayaran uang);
}
-----------------
antarmuka publik EmployeeFactory {
makeEmployee Pegawai publik (EmployeeRecord r) melempar InvalidEmployeeType;
}
-----------------
EmployeeFactoryImpl kelas publik mengimplementasikan EmployeeFactory {
makeEmployee Pegawai publik (EmployeeRecord r) melempar InvalidEmployeeType {
switch (r.type) {
case KOMISI:
mengembalikan CommissionedEmployee (r) baru;
kasus SETIAP:
kembalikan HourlyEmployee (r) baru;
kasus DIGANTI:
mengembalikan gaji karyawan baru (r);
default:
melempar InvalidEmployeeType (r.type) baru;
}
}
}

hubungan sehingga sisa sistem tidak dapat melihatnya [G23]. Tentu saja setiap
sikapnya unik, dan ada kalanya saya melanggar satu atau lebih bagian dari aturan itu.

Gunakan Nama Deskriptif


Dalam Listing 3-7, saya mengubah nama fungsi contoh kami dari testableHtml menjadi
SetupTeardownIncluder.render . Ini adalah nama yang jauh lebih baik karena lebih baik menggambarkan apa
fungsinya tidak. Saya juga memberi masing-masing metode pribadi nama yang sama-sama deskriptif
seperti isTestable atau includeSetupAndTeardownPages . Sulit untuk melebih-lebihkan nilainya
nama baik. Ingat prinsip Ward: “ Anda tahu Anda sedang mengerjakan kode bersih
ketika setiap rutin ternyata cukup banyak seperti yang Anda harapkan. "Setengah pertempuran untuk
mencapai prinsip itu adalah memilih nama baik untuk fungsi kecil yang melakukan satu hal.
Semakin kecil dan terfokus fungsi, semakin mudah untuk memilih deskriptif
nama.
Jangan takut untuk membuat nama panjang. Nama deskriptif panjang lebih baik daripada pendek
nama penuh teka-teki. Nama deskriptif panjang lebih baik daripada komentar deskriptif panjang. Menggunakan
konvensi penamaan yang memungkinkan banyak kata untuk dengan mudah dibaca dalam nama fungsi,
dan kemudian gunakan beberapa kata itu untuk memberi fungsi nama yang mengatakan apa
itu benar.

www.it-ebooks.info

Halaman 71

https://translate.googleusercontent.com/translate_f 55/365
3/12/2020 www.it-ebooks.info

40 Bab 3: Fungsi

Jangan takut untuk menghabiskan waktu memilih nama. Memang, Anda harus mencoba beberapa
ent nama dan baca kode dengan masing-masing di tempat. IDE modern seperti Eclipse atau IntelliJ make
itu sepele untuk mengubah nama. Gunakan salah satu dari IDE tersebut dan bereksperimenlah dengan nama yang berbeda
sampai Anda menemukan yang deskriptif seperti yang Anda bisa.
Memilih nama deskriptif akan memperjelas desain modul dalam pikiran Anda dan
membantu Anda meningkatkannya. Sama sekali tidak umum bahwa perburuan nama baik menghasilkan a
restrukturisasi yang baik dari kode.
Konsisten dalam nama Anda. Gunakan frasa, kata benda, dan kata kerja yang sama dalam fungsi
nama yang Anda pilih untuk modul Anda. Pertimbangkan, misalnya, nama-nama termasukSetup-
AndTeardownPages , includeSetupPages , includeSuiteSetupPage , dan includeSetupPage . Itu
ungkapan serupa dalam nama-nama itu memungkinkan urutan untuk menceritakan sebuah kisah. Memang kalau saya
menunjukkan Anda hanya urutan di atas, Anda akan bertanya pada diri sendiri: "Apa yang terjadi
includeTeardownPages , includeSuiteTeardownPage , dan includeTeardownPage ? " Bagaimana dengan itu
karena ". . . cukup banyak apa yang Anda harapkan . "

Argumen fungsi
Jumlah ideal argumen untuk suatu fungsi adalah
nol (niladik). Berikutnya datang satu (monadik), diikuti
erat oleh dua (diad). Tiga argumen (triadik)
harus dihindari jika memungkinkan. Lebih dari tiga
(polyadic) membutuhkan pembenaran yang sangat khusus — dan
maka seharusnya tidak digunakan.
Argumennya sulit. Mereka mengambil banyak
kekuatan ceptual. Itu sebabnya saya menyingkirkan hampir semua
mereka dari contoh. Pertimbangkan, misalnya,
StringBuffer dalam contoh ini. Kita bisa melakukannya
melewatinya sebagai argumen daripada membuat
ing itu variabel instan, tetapi kemudian pembaca kami
harus menafsirkannya setiap kali mereka melihat
Itu. Ketika Anda membaca kisah yang diceritakan oleh
modul, includeSetupPage () lebih mudah dipahami daripada includeSetupPageInto (newPage-
Konten) . Argumennya berada pada tingkat abstraksi yang berbeda dari nama fungsi dan
memaksa Anda untuk mengetahui detail (dengan kata lain, StringBuffer ) yang tidak terlalu penting
pada saat itu.
Argumen bahkan lebih sulit dari sudut pandang pengujian. Bayangkan kesulitannya
menulis semua kasus uji untuk memastikan bahwa semua kombinasi berbagai argumen berfungsi
tepat. Jika tidak ada argumen, ini sepele. Jika ada satu argumen, itu tidak terlalu sulit.
Dengan dua argumen, masalahnya menjadi sedikit lebih menantang. Dengan lebih dari dua argumen
Namun, menguji setiap kombinasi dari nilai-nilai yang sesuai dapat menjadi hal yang menakutkan.

www.it-ebooks.info

Halaman 72

Argumen fungsi 41

Argumen keluaran lebih sulit untuk dipahami daripada argumen masukan. Ketika kita membaca a
fungsi, kita digunakan untuk gagasan informasi akan di fungsi melalui argumen
dan keluar melalui nilai kembali. Kami biasanya tidak mengharapkan informasi keluar
melalui argumen. Jadi argumen output sering menyebabkan kita melakukan pengambilan ganda.
Satu argumen input adalah hal terbaik berikutnya tanpa argumen. SetupTeardown-
Includer.render (pageData) cukup mudah dimengerti. Jelas kita akan membuat para
data dalam objek pageData .

Bentuk Monadik Umum


Ada dua alasan yang sangat umum untuk meloloskan satu argumen ke suatu fungsi. Mungkin kamu
mengajukan pertanyaan tentang argumen itu, seperti pada file booleanExists ("MyFile") . Atau Anda mungkin
beroperasi pada argumen itu, mengubahnya menjadi sesuatu yang lain dan mengembalikannya . Untuk

https://translate.googleusercontent.com/translate_f 56/365
3/12/2020 www.it-ebooks.info
contoh, InputStream fileOpen ("MyFile") mengubah nama file String menjadi
Nilai pengembalian InputStream . Dua kegunaan ini adalah apa yang diharapkan pembaca ketika mereka melihat suatu fungsi.
tion. Anda harus memilih nama yang membuat perbedaan menjadi jelas, dan selalu menggunakan keduanya
terbentuk dalam konteks yang konsisten. (Lihat Pemisahan Permintaan Perintah di bawah.)
Bentuk yang agak kurang umum, tetapi masih sangat berguna untuk fungsi argumen tunggal,
adalah sebuah acara . Dalam formulir ini ada argumen input tetapi tidak ada argumen output. Secara keseluruhan
Program dimaksudkan untuk menginterpretasikan pemanggilan fungsi sebagai suatu peristiwa dan menggunakan argumen untuk mengubah
keadaan sistem, misalnya, membatalkan passwordAttemptFailedNtimes (upaya int) . Menggunakan
formulir ini dengan hati-hati. Harus sangat jelas bagi pembaca bahwa ini adalah suatu peristiwa. Memilih
nama dan konteks dengan cermat.
Cobalah untuk menghindari fungsi monadik yang tidak mengikuti formulir ini, misalnya, batal
includeSetupPageInto (StringBuffer pageText) . Menggunakan argumen keluaran alih-alih a
nilai balik untuk suatu transformasi membingungkan. Jika suatu fungsi akan mengubah inputnya
argumen, transformasi harus muncul sebagai nilai balik. Memang, StringBuffer
transform (StringBuffer in) lebih baik daripada void transform- (StringBuffer out) , bahkan jika
implementasi dalam kasus pertama hanya mengembalikan argumen input. Setidaknya masih mengikuti
bentuk transformasi.

Tandai Argumen
Argumen bendera jelek. Melewati boolean menjadi suatu fungsi adalah praktik yang benar-benar mengerikan. Itu
segera mempersulit tanda tangan metode, dengan keras menyatakan bahwa fungsi ini
melakukan lebih dari satu hal. Ia melakukan satu hal jika bendera itu benar dan yang lain jika bendera itu salah!
Dalam Listing 3-7, kami tidak punya pilihan karena penelepon sudah melewati bendera itu
di, dan saya ingin membatasi ruang lingkup refactoring ke fungsi dan di bawah. Tetap saja, itu
metode panggilan render (true) hanya membingungkan bagi pembaca yang buruk. Mousing atas panggilan itu
dan melihat render (boolean isSuite) sedikit membantu, tapi tidak terlalu banyak. Kita seharusnya punya
bagi fungsi menjadi dua: renderForSuite () dan renderForSingleTest () .

www.it-ebooks.info

Halaman 73

42 Bab 3: Fungsi

Fungsi Diad
Fungsi dengan dua argumen lebih sulit dipahami daripada fungsi monadik. Untuk ujian-
ple, writeField (nama) lebih mudah dipahami daripada writeField (output-Stream, name) . 10
Meskipun arti keduanya jelas, yang pertama meluncur melewati mata, dengan mudah menyimpannya
berarti. Yang kedua membutuhkan jeda singkat sampai kita belajar untuk mengabaikan parameter pertama.
Dan itu , tentu saja, pada akhirnya menghasilkan masalah karena kita seharusnya tidak pernah mengabaikannya
bagian dari kode. Bagian yang kami abaikan adalah tempat bug akan disembunyikan.
Ada kalanya, tentu saja, di mana dua argumen sesuai. Sebagai contoh,
Titik p = Titik baru (0,0); sangat masuk akal. Poin Cartesian secara alami mengambil dua
argumen. Memang, kami akan sangat terkejut melihat Point baru (0) . Namun, keduanya
KASIH dalam hal ini adalah komponen yang dipesan dari nilai tunggal! Sedangkan Output-Stream dan
nama tidak memiliki kohesi alami, atau keteraturan alami.
Bahkan fungsi diad yang jelas seperti assertEquals (diharapkan, aktual) bermasalah.
Berapa kali Anda meletakkan aktual di mana yang diharapkan seharusnya? Dua argumen
KASIH tidak memiliki pemesanan alami. Yang diharapkan, pemesanan aktual adalah konvensi itu
membutuhkan latihan untuk belajar.
Pasangan tidak jahat, dan Anda pasti harus menulisnya. Namun, Anda seharusnya
sadar bahwa mereka datang dengan biaya dan harus mengambil keuntungan dari apa mekanim mungkin
tersedia bagi Anda untuk mengubahnya menjadi monad. Misalnya, Anda dapat membuat
metode writeField anggota outputStream sehingga Anda dapat mengatakan outputStream.
writeField (nama) . Atau Anda dapat menjadikan outputStream sebagai variabel anggota saat ini
kelas sehingga Anda tidak harus lulus. Atau Anda dapat mengekstrak kelas baru seperti FieldWriter
yang mengambil outputStream dalam konstruktornya dan memiliki metode tulis .

Triad
Fungsi yang mengambil tiga argumen secara signifikan lebih sulit untuk dipahami daripada angka dua. Itu
masalah memesan, menjeda, dan mengabaikan lebih dari dua kali lipat. Saya sarankan Anda berpikir sangat
hati-hati sebelum membuat triad.

https://translate.googleusercontent.com/translate_f 57/365
3/12/2020 www.it-ebooks.info
Sebagai contoh, pertimbangkan kelebihan umum dari assertEquals yang membutuhkan tiga argumen.
KASIH: assertEquals (pesan, diharapkan, aktual) . Berapa kali Anda membaca
pesan dan pikir itu yang diharapkan ? Saya telah tersandung dan berhenti sejenak untuk hal itu
triad berkali-kali. Bahkan, setiap kali saya melihatnya, saya melakukan pengambilan ganda dan kemudian belajar untuk mengabaikannya
pesan.
Di sisi lain, ini adalah triad yang tidak terlalu berbahaya: assertEquals (1.0,
jumlah, .001) . Meskipun ini masih membutuhkan pengambilan ganda, itu salah satu yang layak diambil. Nya
selalu baik untuk diingatkan bahwa kesetaraan nilai floating point adalah hal yang relatif.

10. Saya baru saja selesai refactoring modul yang menggunakan bentuk diad. Saya bisa membuat outputStream bidang kelas dan
mengonversi semua panggilan writeField ke bentuk monadik. Hasilnya jauh lebih bersih.

www.it-ebooks.info

Halaman 74

Argumen fungsi 43

Objek Argumen
Ketika suatu fungsi tampaknya membutuhkan lebih dari dua atau tiga argumen, kemungkinan ada beberapa
argumen-argumen itu harus dibungkus ke dalam kelas mereka sendiri. Pertimbangkan, misalnya,
perbedaan antara dua deklarasi berikut:
Lingkaran makeCircle (double x, double y, radius ganda);
Lingkaran makeCircle (Titik pusat, jari-jari ganda);

Mengurangi jumlah argumen dengan membuat objek dari mereka mungkin tampak seperti
curang, tapi ternyata tidak. Ketika kelompok variabel dilewatkan bersama, cara x dan
y adalah dalam contoh di atas, mereka cenderung bagian dari sebuah konsep yang layak nama nya
sendiri.

Daftar Argumen
Terkadang kita ingin meneruskan sejumlah variabel argumen ke suatu fungsi. Pertimbangkan, untuk
contoh, metode String.format :
String.format ("% s bekerja% .2f jam.", Nama, jam);

Jika argumen variabel semua diperlakukan secara identik, seperti pada contoh di atas, maka
mereka setara dengan satu argumen dari jenis Daftar . Dengan alasan itu, String.format adalah
sebenarnya diad. Memang, pernyataan String.format seperti yang ditunjukkan di bawah ini jelas
mengandung dua unsur.
format String publik (String format, Object ... args)

Jadi semua aturan yang sama berlaku. Fungsi yang mengambil argumen variabel dapat berupa monad,
diad, atau bahkan triad. Tapi itu akan menjadi kesalahan untuk memberi mereka lebih banyak argumen daripada
bahwa.
void monad (Integer ... args);
void diad (nama String, Integer ... args);
void triad (Nama string, jumlah int, Integer ... args);

Kata Kerja dan Kata Kunci


Memilih nama baik untuk suatu fungsi dapat menjelaskan maksud dari
fungsi dan urutan dan maksud argumen. Dalam kasus monad, the
fungsi dan argumen harus membentuk pasangan kata kerja / kata benda yang sangat bagus. Sebagai contoh,
tulis (nama) sangat menggugah. Apa pun "nama" ini, itu sedang "ditulis." Sebuah
Nama yang lebih baik mungkin adalah writeField (nama) , yang memberitahu kita bahwa "nama" adalah a
"bidang."
Yang terakhir ini adalah contoh bentuk kata kunci dari nama fungsi. Menggunakan formulir ini kami
menyandikan nama argumen ke dalam nama fungsi. Misalnya, asertEquals
mungkin lebih baik ditulis sebagai assertExpectedEqualsActual (diharapkan, aktual) . Ini sangat
mengurangi masalah karena harus mengingat urutan argumen.

https://translate.googleusercontent.com/translate_f 58/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info

Halaman 75

44 Bab 3: Fungsi

Tidak Memiliki Efek Samping


Efek sampingnya adalah kebohongan. Fungsi Anda berjanji untuk melakukan satu hal, tetapi juga melakukan hal lain yang disembunyikan
sesuatu. Terkadang ia akan membuat perubahan tak terduga ke variabel kelasnya sendiri.
Kadang-kadang itu akan membuat mereka ke parameter yang dilewatkan ke dalam fungsi atau ke sistem
bals. Dalam kedua kasus itu, mereka adalah orang-orang yang keliru dan merusak, yang seringkali berakibat aneh
kopling temporal dan dependensi pesanan.
Misalnya, perhatikan fungsi yang tampaknya tidak berbahaya dalam Listing 3-6. Fungsi ini
menggunakan algoritma standar untuk mencocokkan nama pengguna dengan kata sandi . Mengembalikan nilai true jika cocok
dan salah jika terjadi kesalahan. Tetapi juga memiliki efek samping. Bisakah Anda menemukannya?

Listing 3-6
UserValidator.java
UserValidator kelas publik {
cryptographer pribadi Cryptographer;

public boolean checkPassword (String userName, String password) {


Pengguna pengguna = UserGateway.findByName (userName);
if (user! = User.NULL) {
String codedPhrase = user.getPhraseEncodedByPassword ();
String phrase = cryptographer.decrypt (codedPhrase, password);
if ("Kata Sandi Berlaku". equal (frasa)) {
Session.initialize ();
kembali benar;
}
}
return false;
}
}

Efek sampingnya adalah panggilan ke Session.initialize () , tentu saja. The checkPassword func-
tion, dengan namanya, mengatakan bahwa ia memeriksa kata sandi. Nama tidak menyiratkan bahwa itu inisial -
sesuaikan sesi. Jadi, seorang penelepon yang percaya apa yang dikatakan oleh nama fungsi menanggung risiko
menghapus data sesi yang ada ketika dia memutuskan untuk memeriksa validitas
pengguna.
Efek samping ini menciptakan kopling sementara. Artinya, checkPassword hanya bisa
dipanggil pada waktu-waktu tertentu (dengan kata lain, ketika aman untuk menginisialisasi sesi). Jika memang
dipanggil out of order, data sesi mungkin hilang secara tidak sengaja. Kopling temporal terikat
sekering, terutama ketika disembunyikan sebagai efek samping. Jika Anda harus memiliki kopling sementara,
Anda harus membuatnya jelas atas nama fungsi. Dalam hal ini kita mungkin mengganti nama
fungsi checkPasswordAndInitializeSession , meskipun itu tentu saja melanggar “Lakukan satu
benda."

www.it-ebooks.info

Halaman 76

Pemisahan Permintaan Perintah 45

https://translate.googleusercontent.com/translate_f 59/365
3/12/2020 www.it-ebooks.info
Argumen Keluaran
Argumen secara alami ditafsirkan sebagai input ke suatu fungsi. Jika Anda telah pro-
gramming selama lebih dari beberapa tahun, saya yakin Anda telah mengambil dua kali argumen
yang sebenarnya merupakan output daripada input. Sebagai contoh:
appendFooter (s);

Apakah fungsi ini append s sebagai footer untuk sesuatu? Atau apakah itu menambahkan catatan kaki
untuk s ? Apakah s input atau output? Tidak perlu waktu lama untuk melihat tanda tangan fungsi
Dan lihat:
appendFooter publik batal (laporan StringBuffer)

Ini mengklarifikasi masalah, tetapi hanya dengan mengorbankan memeriksa deklarasi fungsi.
Apa pun yang memaksa Anda untuk memeriksa tanda tangan fungsi sama dengan pengambilan ganda. Nya
istirahat kognitif dan harus dihindari.
Pada hari-hari sebelum pemrograman berorientasi objek kadang-kadang perlu
argumen keluaran. Namun, sebagian besar kebutuhan akan argumen keluaran menghilang dalam bahasa OO
Guages karena ini adalah dimaksudkan untuk bertindak sebagai output argumen. Dengan kata lain, itu akan terjadi
lebih baik untuk appendFooter dipanggil sebagai
report.appendFooter ();

Secara umum argumen hasil harus dihindari. Jika fungsi Anda harus mengubah status
sesuatu, memilikinya mengubah keadaan objek yang dimilikinya.

Pemisahan Permintaan Perintah


Fungsinya harus melakukan sesuatu atau menjawab sesuatu, tetapi tidak keduanya. Anda baik
fungsi harus mengubah keadaan suatu objek, atau harus mengembalikan beberapa informasi tentang
benda itu. Melakukan keduanya sering menimbulkan kebingungan. Pertimbangkan, misalnya, yang berikut ini
fungsi:
set boolean publik (atribut String, nilai String);

Fungsi ini mengatur nilai atribut bernama dan mengembalikan true jika itu berhasil dan
false jika tidak ada atribut seperti itu. Ini mengarah pada pernyataan aneh seperti ini:

if (set ("username", "unclebob")) ...

Bayangkan ini dari sudut pandang pembaca. Apa artinya? Apakah itu bertanya apakah
yang “ nama ” atribut sebelumnya diatur ke “ UncleBob ”? Atau bertanya apakah
“ Nama ” atribut berhasil diatur ke “ UncleBob ”? Sulit untuk menyimpulkan artinya
panggilan karena tidak jelas apakah kata " set " adalah kata kerja atau kata sifat.
Penulis dimaksudkan ditetapkan menjadi kata kerja, tetapi dalam konteks jika pernyataan itu terasa seperti
kata sifat. Jadi pernyataan itu berbunyi "Jika atribut nama pengguna sebelumnya diatur ke
unclebob "dan bukan" atur atribut username ke unclebob dan jika itu berhasil maka. . . . " Kita

www.it-ebooks.info

Halaman 77

46 Bab 3: Fungsi

bisa mencoba untuk menyelesaikan ini dengan mengganti nama fungsi yang ditetapkan ke setAndCheckIfExists , tapi itu
tidak banyak membantu keterbacaan pernyataan if . Solusi sebenarnya adalah dengan memisahkan
perintah dari kueri sehingga ambiguitas tidak dapat terjadi.
if (atributExists ("username")) {
setAttribute ("username", "unclebob");
...
}

Memilih Pengecualian untuk Mengembalikan Kode Kesalahan


Mengembalikan kode kesalahan dari fungsi perintah adalah pelanggaran halus dari permintaan perintah
pemisahan. Mempromosikan perintah yang digunakan sebagai ekspresi dalam predikat dari jika negara-
KASIH.
if (deletePage (halaman) == E_OK)

Ini tidak menderita dari kata kerja / kata sifat kebingungan tetapi tidak mengarah ke struktur bersarang sangat
mendatang. Ketika Anda mengembalikan kode kesalahan, Anda menciptakan masalah yang harus ditangani oleh penelepon
kesalahan segera.

https://translate.googleusercontent.com/translate_f 60/365
3/12/2020 www.it-ebooks.info
if (deletePage (halaman) == E_OK) {
if (registry.deleteReference (page.name) == E_OK) {
if (configKeys.deleteKey (halaman.name.makeKey ()) == E_OK) {
logger.log ("halaman dihapus");
} lain {
logger.log ("configKey not delete");
}
} lain {
logger.log ("deleteReference from registry gagal");
}
} lain {
logger.log ("delete gagal");
return E_ERROR;
}

Di sisi lain, jika Anda menggunakan pengecualian alih-alih kode kesalahan yang dikembalikan, maka kesalahan
kode pemrosesan dapat dipisahkan dari kode jalur bahagia dan dapat disederhanakan:
coba {
deletePage (halaman);
registry.deleteReference (halaman.name);
configKeys.deleteKey (halaman.name.makeKey ());
}
catch (Exception e) {
logger.log (e.getMessage ());
}

Ekstrak Blok Coba / Tangkap

Blok coba / tangkap jelek sendiri. Mereka membingungkan struktur kode dan
campur kesalahan pemrosesan dengan pemrosesan normal. Jadi lebih baik untuk mengekstrak tubuh percobaan
dan menangkap blok ke fungsi mereka sendiri.

www.it-ebooks.info

Halaman 78

Memilih Pengecualian untuk Mengembalikan Kode Kesalahan 47

public void delete (halaman Halaman) {


coba {
deletePageAndAllReferences (halaman);
}
catch (Exception e) {
logError (e);
}
}

private void deletePageAndAllReferences (halaman halaman) melempar Exception {


deletePage (halaman);
registry.deleteReference (halaman.name);
configKeys.deleteKey (halaman.name.makeKey ());
}

void logError pribadi (Pengecualian e) {


logger.log (e.getMessage ());
}

Di atas, fungsi hapus adalah tentang pemrosesan kesalahan. Mudah dimengerti


lalu abaikan. Fungsi deletePageAndAllReferences adalah semua tentang proses
sepenuhnya menghapus halaman . Penanganan kesalahan dapat diabaikan. Ini memberikan pemisahan yang bagus itu
membuat kode lebih mudah dimengerti dan dimodifikasi.

Menangani Kesalahan Adalah Satu Hal


Fungsi harus melakukan satu hal. Menyerahkan kesalahan adalah satu hal. Dengan demikian, fungsi yang menangani
kesalahan seharusnya tidak melakukan hal lain. Ini menyiratkan (seperti pada contoh di atas) bahwa jika kata kunci
coba ada dalam suatu fungsi, itu harus menjadi kata pertama dalam fungsi dan yang ada
seharusnya tidak apa-apa setelah menangkap / akhirnya menghalangi.

Magnet Ketergantungan Error.java


Mengembalikan kode kesalahan biasanya menyiratkan bahwa ada beberapa kelas atau enum di mana semua
kode kesalahan didefinisikan.
public enum Error {
BAIK,
Tidak valid,
NO_SUCH,
TERKUNCI,
OUT_OF_RESOURCES,
WAITING_FOR_EVENT;

https://translate.googleusercontent.com/translate_f 61/365
3/12/2020 www.it-ebooks.info
}
Kelas-kelas seperti ini adalah magnet ketergantungan; banyak kelas lain harus diimpor dan digunakan
mereka. Jadi, ketika Kesalahan enum berubah, semua kelas-kelas lain perlu dikompilasi ulang
dan dipindahtugaskan. 11 Ini memberikan tekanan negatif pada kelas Error . Programmer tidak mau

11. Mereka yang merasa bahwa mereka dapat pergi tanpa kompilasi ulang dan penempatan kembali telah ditemukan — dan ditangani.

www.it-ebooks.info

Halaman 79

48 Bab 3: Fungsi

untuk menambah kesalahan baru karena dengan begitu mereka harus membangun kembali dan memindahkan semuanya. Jadi mereka menggunakan kembali
kode kesalahan lama alih-alih menambahkan yang baru.
Saat Anda menggunakan pengecualian daripada kode kesalahan, maka pengecualian baru adalah turunan dari
kelas pengecualian. Mereka dapat ditambahkan tanpa memaksakan setiap kompilasi atau pemindahan. 12

Jangan Ulangi Diri Sendiri 13


Lihat kembali Daftar 3-1 dengan cermat dan Anda
akan memperhatikan bahwa ada suatu algoritma itu
diulang empat kali, satu kali untuk masing - masing
yang Setup , SuiteSetUp , teardown , dan
Kasus SuiteTearDown . Tidak mudah dikenali
duplikasi ini karena empat contoh
dicampur dengan kode lain dan tidak
diduplikasi secara seragam. Masih duplikasi
adalah masalah karena itu menggembungkan kode dan
akan membutuhkan modifikasi empat kali lipat jika algoritma harus berubah. Itu juga a
peluang empat kali lipat untuk kesalahan kelalaian.
Duplikasi ini diperbaiki dengan metode sertakan dalam Listing 3-7. Baca terus
kode itu lagi dan perhatikan bagaimana keterbacaan seluruh modul ditingkatkan oleh
pengurangan duplikasi itu.
Duplikasi mungkin merupakan akar dari semua kejahatan dalam perangkat lunak. Banyak prinsip dan praktik yang dimiliki
telah dibuat untuk tujuan mengendalikan atau menghilangkannya. Pertimbangkan, misalnya, itu
semua bentuk normal basis data Codd berfungsi untuk menghilangkan duplikasi dalam data. Pertimbangkan juga
bagaimana pemrograman berorientasi objek berfungsi untuk memusatkan kode ke kelas dasar yang akan
jika tidak menjadi berlebihan. Pemrograman terstruktur, Pemrograman Berorientasi Aspek, Komponen
nent Oriented Programming, semuanya, sebagian, strategi untuk menghilangkan duplikasi. Itu
akan muncul bahwa sejak penemuan subrutin, inovasi dalam pengembangan perangkat lunak
ment telah menjadi upaya berkelanjutan untuk menghilangkan duplikasi dari kode sumber kami.

Pemrograman Terstruktur
Beberapa programmer mengikuti aturan pemrograman terstruktur Edsger Dijkstra. 14 Dijkstra
mengatakan bahwa setiap fungsi, dan setiap blok dalam suatu fungsi, harus memiliki satu entri dan satu
keluar. Mengikuti aturan ini berarti bahwa seharusnya hanya ada satu pernyataan pengembalian dalam fungsi
tion, tidak ada istirahat atau melanjutkan pernyataan dalam satu lingkaran, dan tidak pernah, pernah, setiap goto pernyataan.

12. Ini adalah contoh dari Open Closed Principle (OCP) [PPP02].
13. Prinsip KERING. [PRAG].
14. [SP72].

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 62/365
3/12/2020 www.it-ebooks.info

Halaman 80

Kesimpulan 49

Sementara kami bersimpati dengan tujuan dan disiplin pemrograman terstruktur,


aturan-aturan itu hanya memberikan sedikit manfaat ketika fungsi sangat kecil. Hanya dalam fungsi yang lebih besar
bahwa aturan tersebut memberikan manfaat yang signifikan.
Jadi, jika Anda menjaga fungsi Anda kecil, maka beberapa kembali sesekali , istirahat , atau
pernyataan terus tidak membahayakan dan kadang-kadang bahkan bisa lebih ekspresif daripada dosa
gle-entry, aturan keluar tunggal. Di sisi lain, goto hanya masuk akal dalam fungsi besar, jadi
itu harus dihindari.

Bagaimana Anda Menulis Fungsinya Seperti Ini?


Perangkat lunak menulis seperti jenis tulisan lainnya. Saat Anda menulis makalah atau artikel,
Anda menurunkan pikiran Anda terlebih dahulu, lalu memijatnya hingga terbaca dengan baik. Draf pertama
mungkin canggung dan tidak terorganisir, jadi Anda kata-kata itu dan restrukturisasi dan sempurnakan sampai
itu membaca seperti yang Anda inginkan.
Ketika saya menulis fungsi, mereka keluar panjang dan rumit. Mereka punya banyak
indentasi dan loop bersarang. Mereka memiliki daftar argumen yang panjang. Nama-nama itu arbitrer, dan
ada kode duplikat. Tetapi saya juga memiliki serangkaian unit test yang mencakup semuanya
baris kode yang canggung.
Jadi saya memijat dan memperbaiki kode itu, membagi fungsi, mengganti nama, menghilangkan
inating duplikasi. Saya mengecilkan metode dan mengatur ulang mereka. Kadang-kadang saya keluar utuh
kelas, sambil tetap menjaga tes lulus.
Pada akhirnya, saya berakhir dengan fungsi yang mengikuti aturan yang saya tetapkan dalam bab ini.
Saya tidak menulis mereka seperti itu untuk memulai. Saya tidak berpikir siapa pun bisa.

Kesimpulan
Setiap sistem dibangun dari bahasa khusus domain yang dirancang oleh programmer untuk
menggambarkan sistem itu. Functions adalah kata kerja dari bahasa itu, dan kelas adalah kata benda.
Ini bukan suatu kemunduran terhadap gagasan lama yang mengerikan bahwa kata benda dan kata kerja membutuhkan
dokumen dokumen adalah tebakan pertama dari kelas dan fungsi suatu sistem. Sebaliknya, ini
kebenaran yang jauh lebih tua. Seni pemrograman adalah, dan selalu, seni bahasa
rancangan.
Programmer master menganggap sistem sebagai cerita yang harus diceritakan dan bukan program
dituliskan. Mereka menggunakan fasilitas bahasa pemrograman pilihan mereka untuk membangun a
bahasa yang jauh lebih kaya dan lebih ekspresif yang dapat digunakan untuk menceritakan kisah itu. Bagian dari itu
bahasa domain-spesifik adalah hirarki fungsi yang menggambarkan semua tindakan itu
terjadi dalam sistem itu. Dalam suatu tindakan rekursi yang berseni, tindakan-tindakan tersebut ditulis
menggunakan bahasa yang sangat spesifik domain yang mereka tetapkan untuk memberi tahu bagian kecil mereka sendiri
cerita.
Bab ini membahas mekanika fungsi penulisan dengan baik. Jika kamu mengikuti
aturan di sini, fungsi Anda akan pendek, nama baik, dan terorganisir dengan baik. Tapi

www.it-ebooks.info

Halaman 81

50 Bab 3: Fungsi

jangan pernah lupa bahwa tujuan Anda sebenarnya adalah untuk menceritakan kisah sistem, dan bahwa fungsinya
Anda menulis harus pas bersama-sama dalam bahasa yang jelas dan tepat untuk membantu Anda
mengatakan itu.

https://translate.googleusercontent.com/translate_f 63/365
3/12/2020 www.it-ebooks.info
SetupTeardownIncluder
Listing 3-7
SetupTeardownIncluder.java
paket fitnesse.html;

import fitnesse.responders.run.SuiteResponder;
import fitnesse.wiki. *;

SetupTeardownIncluder kelas publik {


pageData pribadi pageData;
isoite pribadi boolean;
testPage WikiPage pribadi;
private StringBuffer newPageContent;
PageCrawler pribadi PageCrawler;

render String statis publik (PageData pageData) melempar Exception {


return render (pageData, false);
}

publik membuat String statis (PageData pageData, boolean isSuite)


throws Exception {
kembalikan SetupTeardownIncluder (pageData) .render (isSuite) baru;
}

SetupTeardownIncluder pribadi (PageData pageData) {


this.pageData = pageData;
testPage = pageData.getWikiPage ();
pageCrawler = testPage.getPageCrawler ();
newPageContent = new StringBuffer ();
}

render String pribadi (boolean isSuite) melempar Exception {


this.isSuite = isSuite;
if (isTestPage ())
includeSetupAndTeardownPages ();
return pageData.getHtml ();
}

private boolean isTestPage () melempar Exception {


return pageData.hasAttribute ("Test");
}

kekosongan pribadi includeSetupAndTeardownPages () melempar Exception {


includeSetupPages ();
includePageContent ();
includeTeardownPages ();
updatePageContent ();
}

www.it-ebooks.info

Halaman 82

SetupTeardownIncluder 51

Listing 3-7 (lanjutan)


SetupTeardownIncluder.java
kekosongan pribadi includeSetupPages () melempar Exception {
jika (isSuite)
includeSuiteSetupPage ();
includeSetupPage ();
}

kekosongan pribadi includeSuiteSetupPage () melempar Exception {


termasuk (SuiteResponder.SUITE_SETUP_NAME, "-setup");
}

kekosongan pribadi includeSetupPage () melempar Exception {


termasuk ("SetUp", "-setup");
}

private void includePageContent () melempar Exception {


newPageContent.append (pageData.getContent ());
}

kekosongan pribadi includeTeardownPages () melempar Exception {


includeTeardownPage ();
jika (isSuite)
includeSuiteTeardownPage ();
}

private void includeTeardownPage () melempar Exception {


termasuk ("TearDown", "-teardown");
}

https://translate.googleusercontent.com/translate_f 64/365
3/12/2020 www.it-ebooks.info

kekosongan pribadi includeSuiteTeardownPage () melempar Exception {


termasuk (SuiteResponder.SUITE_TEARDOWN_NAME, "-teardown");
}

private void updatePageContent () melempar Exception {


pageData.setContent (newPageContent.toString ());
}

kekosongan pribadi termasuk (String pageName, String arg) melempar Exception {


WikiPage inheritedPage = findInheritedPage (pageName);
if (inheritedPage! = null) {
String pagePathName = getPathNameForPage (inheritedPage);
buildIncludeDirective (pagePathName, arg);
}
}

WikiPage pribadi findInheritedPage (String pageName) melempar Exception {


mengembalikan PageCrawlerImpl.getInheritedPage (pageName, testPage);
}

private String getPathNameForPage (halaman WikiPage) melempar Exception {


WikiPagePath pagePath = pageCrawler.getFullPath (halaman);
kembalikan PathParser.render (pagePath);
}

private void buildIncludeDirective (String pagePathName, String arg) {


newPageContent
.append ("\ n! termasuk")

www.it-ebooks.info

Halaman 83

52 Bab 3: Fungsi

Listing 3-7 (lanjutan)


SetupTeardownIncluder.java
.append (arg)
.menambahkan(" .")
.append (pagePathName)
.append ("\ n");
}
}

Bibliografi
[KP78]: Kernighan dan Plaugher, Elemen Gaya Pemrograman , 2d. ed., McGraw-
Hill, 1978.

[PPP02]: Robert C. Martin, Pengembangan Perangkat Lunak Agile: Prinsip, Pola, dan Praktek
tices , Prentice Hall, 2002.

[GOF]: Pola Desain: Elemen-elemen Perangkat Lunak Berorientasi Objek yang Dapat Digunakan Kembali , Gamma et al.,
Addison-Wesley, 1996.

[PRAG]: Programmer Pragmatis , Andrew Hunt, Dave Thomas, Addison-Wesley,


2000

[SP72]: Pemrograman Terstruktur , O.-J. Dahl, EW Dijkstra, CAR Hoare, Akademik


Pers, London, 1972.

https://translate.googleusercontent.com/translate_f 65/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 84

4
Komentar

"Jangan berkomentar kode buruk — tulis ulang."

—Brian W. Kernighan dan PJ Plaugher 1

Tidak ada yang bisa sangat membantu sebagai komentar yang ditempatkan dengan baik. Tidak ada yang dapat mengacaukan mod
ule lebih dari komentar dogmatis sembrono. Tidak ada yang bisa begitu merusak seperti yang lama
komentar kasar yang menyebarkan kebohongan dan informasi yang salah.
Komentar tidak seperti Daftar Schindler. Mereka tidak "murni baik." Memang komentar
paling tidak, merupakan kejahatan yang diperlukan. Jika bahasa pemrograman kami cukup ekspresif, atau jika

1. [KP78], hlm. 144.

53

www.it-ebooks.info

Halaman 85

https://translate.googleusercontent.com/translate_f 66/365
3/12/2020 www.it-ebooks.info

54 Bab 4: Komentar

kami memiliki bakat untuk menggunakan bahasa-bahasa tersebut secara halus untuk mengekspresikan niat kami, kami tidak akan membutuhkannya
komentar sangat banyak — mungkin tidak sama sekali.
Penggunaan komentar yang tepat adalah untuk mengkompensasi kegagalan kami untuk mengekspresikan diri
kode. Perhatikan bahwa saya menggunakan kata gagal . Aku serius. Komentar selalu gagal. Kita harus
memilikinya karena kita tidak bisa selalu mencari cara mengekspresikan diri tanpa mereka,
tetapi penggunaannya tidak menyebabkan perayaan.
Jadi ketika Anda menemukan diri Anda dalam posisi di mana Anda perlu menulis komentar, pikirkanlah
telusuri dan lihat apakah tidak ada cara untuk membalik tabel dan mengekspresikan diri
kode. Setiap kali Anda mengekspresikan diri dalam kode, Anda harus menepuk punggung Anda. Setiap
Saat Anda menulis komentar, Anda harus meringis dan merasakan kegagalan kemampuan Anda
ekspresi.
Mengapa saya begitu terpaku pada komentar? Karena mereka berbohong. Tidak selalu, dan tidak dengan sengaja,
tapi terlalu sering. Semakin lama sebuah komentar, dan semakin jauh jaraknya dari kode yang dijelaskan,
semakin besar kemungkinannya salah. Alasannya sederhana. Programmer tidak bisa realis-
tically menjaga mereka.
Kode berubah dan berkembang. Potongan-potongan itu bergerak dari sini ke sana. Potongan-potongan itu bifur-
cate dan mereproduksi dan datang bersama lagi untuk membentuk chimera. Sayangnya
KASIH tidak selalu mengikuti mereka — tidak selalu bisa mengikuti mereka. Dan terlalu sering
komentar dipisahkan dari kode yang mereka gambarkan dan menjadi uraian anak yatim piatu dari yang pernah
penurunan akurasi. Sebagai contoh, lihat apa yang terjadi pada komentar ini dan baris di dalamnya
dimaksudkan untuk menggambarkan:
Permintaan MockRequest;
private String final HTTP_DATE_REGEXP =
"[SMTWF] [az] {2} \\, \\ s [0-9] {2} \\ s [JFMASOND] [az] {2} \\ s" +
"[0-9] {4} \\ s [0-9] {2} \\: [0-9] {2} \\: [0-9] {2} \\ sGMT";
tanggapan Respons pribadi;
konteks FitNesseContext pribadi;
responden FileResponder pribadi;
Lokal pribadi saveLocale;
// Contoh: "Sel, 02 Apr 2003 22:18:49 GMT"

Variabel contoh lain yang mungkin ditambahkan kemudian diselingi antara


HTTP_DATE_REGEXP konstan dan komentar jelasnya .
Dimungkinkan untuk menegaskan bahwa pemrogram harus cukup disiplin
simpan komentar dalam kondisi perbaikan, relevansi, dan akurasi yang tinggi. Saya setuju, mereka seharusnya.
Tapi saya lebih suka energi itu digunakan untuk membuat kode begitu jelas dan ekspresif
tidak perlu komentar di tempat pertama.
Komentar yang tidak akurat jauh lebih buruk daripada tidak ada komentar sama sekali. Mereka menipu dan menyesatkan.
Mereka menetapkan harapan yang tidak akan pernah terpenuhi. Mereka menetapkan aturan lama yang tidak perlu, atau
seharusnya tidak, diikuti lebih lama lagi.
Kebenaran hanya dapat ditemukan di satu tempat: kode. Hanya kode yang benar-benar dapat memberi tahu Anda apa
itu benar. Ini adalah satu-satunya sumber informasi yang benar-benar akurat. Karena itu, meskipun komentar
terkadang diperlukan, kita akan mengeluarkan energi yang signifikan untuk menguranginya.

www.it-ebooks.info

Halaman 86

Komentar yang bagus 55

Komentar Jangan Menebus Kode Buruk


Salah satu motivasi yang lebih umum untuk menulis komentar adalah kode yang buruk. Kami menulis mod-
ule dan kita tahu itu membingungkan dan tidak terorganisir. Kami tahu ini berantakan. Jadi kami katakan kepada kami-
diri sendiri, "Ooh, lebih baik aku berkomentar!" Tidak! Anda sebaiknya membersihkannya!
Kode yang jelas dan ekspresif dengan sedikit komentar jauh lebih unggul daripada yang berantakan dan kompleks
kode dengan banyak komentar. Daripada menghabiskan waktu Anda menulis komentar itu
jelaskan kekacauan yang Anda buat, habiskan untuk membersihkan kekacauan itu.

https://translate.googleusercontent.com/translate_f 67/365
3/12/2020 www.it-ebooks.info

Jelaskan Dirimu dalam Kode


Tentu ada kalanya kode membuat kendaraan yang buruk untuk penjelasan. Sayangnya,
banyak programmer menganggap ini sebagai kode yang jarang, jika pernah, sarana yang bagus untuk
penjelasan. Ini benar-benar salah. Yang mana yang lebih Anda sukai? Ini:
// Periksa untuk melihat apakah karyawan memenuhi syarat untuk manfaat penuh
jika ((employee.flags & HOURLY_FLAG) &&
(employee.age> 65))

Atau ini?
if (employee.isEligibleForFullBenitsits ())

Hanya perlu beberapa detik pemikiran untuk menjelaskan sebagian besar maksud Anda dalam kode. Dalam berbagai
kasus itu hanyalah masalah menciptakan fungsi yang mengatakan hal yang sama dengan komentar
kamu ingin menulis

Komentar yang bagus


Beberapa komentar diperlukan atau bermanfaat. Kami akan melihat beberapa yang saya anggap layak
bit yang mereka konsumsi. Perlu diingat, bahwa satu-satunya komentar yang benar-benar bagus adalah
komentar Anda menemukan cara untuk tidak menulis.

Komentar Hukum
Terkadang standar pengodean perusahaan kami memaksa kami untuk menulis komentar tertentu untuk yang legal
alasan. Misalnya, pernyataan hak cipta dan kepengarangan diperlukan dan masuk akal
hal-hal untuk dimasukkan ke dalam komentar di awal setiap file sumber.
Di sini, misalnya, adalah tajuk komentar standar yang kami letakkan di awal
setiap file sumber di FitNesse. Saya senang mengatakan bahwa IDE kami menyembunyikan komentar ini dari act-
ing sebagai berantakan dengan secara otomatis menciutkannya.
// Hak Cipta (C) 2003.2004.2005 oleh Object Mentor, Inc. Semua hak dilindungi undang-undang.
// Dirilis berdasarkan ketentuan Lisensi Publik Publik GNU versi 2 atau lebih baru.

www.it-ebooks.info

Halaman 87

56 Bab 4: Komentar

Komentar seperti ini tidak boleh berupa kontrak atau buku tebal hukum. Jika memungkinkan, rujuk ke stan-
lisensi dard atau dokumen eksternal lainnya daripada meletakkan semua syarat dan ketentuan
dalam komentar.

Komentar informatif
Terkadang berguna untuk memberikan informasi dasar dengan komentar. Misalnya,
sider komentar ini yang menjelaskan nilai pengembalian metode abstrak:

// Mengembalikan instance Responder yang sedang diuji.


abstrak Responder responderInstance ();

Komentar seperti ini kadang-kadang bisa berguna, tetapi lebih baik menggunakan nama fungsi.
untuk menyampaikan informasi jika memungkinkan. Misalnya, dalam hal ini komentar
dapat dibuat berlebihan dengan mengganti nama fungsi: responderBeingTested .
Inilah kasus yang sedikit lebih baik:
// format cocok kk: mm: ss EEE, MMM dd, yyyy
Pattern timeMatcher = Pattern.compile (
"\\ d *: \\ d *: \\ d * \\ w *, \\ w * \\ d *, \\ d *");

Dalam hal ini komentar memberi tahu kami bahwa ekspresi reguler dimaksudkan untuk mencocokkan a
waktu dan tanggal yang diformat dengan fungsi SimpleDateFormat.format menggunakan
format string yang ditentukan. Namun, mungkin lebih baik, dan lebih jelas, jika kode ini
pindah ke kelas khusus yang mengonversi format tanggal dan waktu. Lalu komentar
kemungkinan akan berlebihan.

Penjelasan Tujuan
Terkadang komentar melampaui informasi yang berguna tentang implementasi dan

https://translate.googleusercontent.com/translate_f 68/365
3/12/2020 www.it-ebooks.info
memberikan maksud di balik suatu keputusan. Dalam kasus berikut ini kami melihat keputusan yang menarik
didokumentasikan oleh komentar. Ketika membandingkan dua objek, penulis memutuskan bahwa dia
ingin mengurutkan objek kelasnya lebih tinggi dari objek yang lain.
public int compareTo (Objek o)
{
if (o instance of WikiPagePath)
{
WikiPagePath p = (WikiPagePath) o;
String compressedName = StringUtil.join (nama, "");
String compressedArgumentName = StringUtil.join (p.names, "");
kembalikan compressedName.compareTo (compressedArgumentName);
}
return 1; // kami lebih besar karena kami adalah tipe yang tepat.
}

Inilah contoh yang lebih baik. Anda mungkin tidak setuju dengan solusi programmer untuk
masalahnya, tapi setidaknya Anda tahu apa yang dia coba lakukan.
public void testConcurrentAddWidgets () melempar Pengecualian {
WidgetBuilder widgetBuilder =
WidgetBuilder baru (Kelas baru [] {BoldWidget.class});

www.it-ebooks.info

Halaman 88

Komentar yang bagus 57

Teks string = "'' 'teks tebal' ''";


ParentWidget parent =
BoldWidget baru (MockWidgetRoot baru (), "'' 'teks tebal' ''");
AtomicBoolean failFlag = new AtomicBoolean ();
failFlag.set (false);

// Ini upaya terbaik kami untuk mendapatkan kondisi balapan


// dengan membuat banyak utas.
untuk (int i = 0; i <25000; i ++) {
WidgetBuilderThread widgetBuilderThread =
WidgetBuilderThread baru (widgetBuilder, teks, induk, failFlag);
Utas = Utas baru (widgetBuilderThread);
thread.start ();
}
assertEquals (false, failFlag.get ());
}

Klarifikasi
Terkadang hanya membantu untuk menerjemahkan arti dari beberapa argumen atau pengembalian yang tidak jelas
nilai menjadi sesuatu yang bisa dibaca. Secara umum lebih baik untuk menemukan cara untuk membuat argumen itu.
ment atau mengembalikan nilai yang jelas dalam haknya sendiri; tetapi ketika itu bagian dari perpustakaan standar, atau di
kode yang tidak dapat Anda ubah, maka komentar klarifikasi yang bermanfaat dapat bermanfaat.

public void testCompareTo () melempar Pengecualian


{
WikiPagePath a = PathParser.parse ("PageA");
WikiPagePath ab = PathParser.parse ("PageA.PageB");
WikiPagePath b = PathParser.parse ("PageB");
WikiPagePath aa = PathParser.parse ("PageA.PageA");
WikiPagePath bb = PathParser.parse ("PageB.PageB");
WikiPagePath ba = PathParser.parse ("PageB.PageA");

assertTrue (a.compareTo (a) == 0); // a == a


assertTrue (a.compareTo (b)! = 0); // a! = b
assertTrue (ab.compareTo (ab) == 0); // ab == ab
assertTrue (a.compareTo (b) == -1); // a <b
assertTrue (aa.compareTo (ab) == -1); // aa <ab
assertTrue (ba.compareTo (bb) == -1); // ba <bb
assertTrue (b.compareTo (a) == 1); // b> a
assertTrue (ab.compareTo (aa) == 1); // ab> aa
assertTrue (bb.compareTo (ba) == 1); // bb> ba
}

Tentu saja ada risiko besar bahwa komentar klarifikasi tidak benar. Pergilah
melalui contoh sebelumnya dan lihat betapa sulitnya memverifikasi apakah mereka benar. Ini
menjelaskan mengapa klarifikasi diperlukan dan mengapa berisiko. Jadi sebelum menulis
KASIH seperti ini, berhati-hatilah agar tidak ada cara yang lebih baik, dan kemudian berhati-hatilah
akurat.

https://translate.googleusercontent.com/translate_f 69/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 89

58 Bab 4: Komentar

Peringatan Konsekuensi
Terkadang berguna untuk memperingatkan
grammers tentang konsekuensi tertentu. Untuk
contoh, berikut adalah komentar yang menjelaskan
mengapa kasus uji tertentu dimatikan:

// Jangan lari kecuali kamu


// punya waktu untuk membunuh.
public void _testWithReallyBigFile ()
{
writeLinesToFile (10000000);

response.setBody (testFile);
response.readyToSend (ini);
String responseString = output.toString ();
assertSubString ("Content-Length: 1000000000", responseString);
assertTrue (bytesSent> 1000000000);
}

Saat ini, tentu saja, kami akan mematikan test case dengan menggunakan atribut @Ignore dengan
string penjelasan yang sesuai. @Ignore ("Butuh waktu terlalu lama untuk dijalankan") . Tetapi kembali pada hari-hari
sebelum JUnit 4, meletakkan garis bawah di depan nama metode adalah pertemuan umum
tion. Komentar itu, meski kurang ajar, membuat poin cukup bagus.
Berikut contoh lain yang lebih pedih:
SimpleDateFormat statis publik membuatStandardHttpDateFormat ()
{
// SimpleDateFormat bukan utas yang aman,
// jadi kita perlu membuat setiap instance secara independen.
SimpleDateFormat df = new SimpleDateFormat ("EEE, dd MMM yyyy HH: mm: ss z");
df.setTimeZone (TimeZone.getTimeZone ("GMT"));
return df;
}

Anda mungkin mengeluh bahwa ada cara yang lebih baik untuk menyelesaikan masalah ini. Saya mungkin setuju dengan itu
kamu. Tetapi komentar, seperti yang diberikan di sini, sangat masuk akal. Ini akan mencegah beberapa hal yang terlalu berlebihan
ingin programmer dari menggunakan penginisialisasi statis atas nama efisiensi.

Komentar TODO
Terkadang masuk akal untuk meninggalkan catatan "Aktivitas" dalam bentuk // TODO komentar. Dalam
berikut kasus, komentar TODO menjelaskan mengapa fungsi memiliki implementasi yang merosot
dan apa fungsi masa depan itu seharusnya.

// TODO-MdM ini tidak diperlukan


// Kami berharap ini akan hilang ketika kami melakukan model checkout
VersionInfo makeVersion () yang diproteksi melemparkan Exception
{
kembali nol;
}

www.it-ebooks.info

Halaman 90

https://translate.googleusercontent.com/translate_f 70/365
3/12/2020 www.it-ebooks.info
Komentar buruk 59

TODO adalah pekerjaan yang menurut programmer harus dilakukan, tetapi untuk beberapa alasan
tidak bisa lakukan saat ini. Mungkin pengingat untuk menghapus fitur yang sudah usang atau a
meminta orang lain untuk melihat masalah. Mungkin permintaan orang lain untuk melakukannya
pikirkan nama yang lebih baik atau pengingat untuk melakukan perubahan yang bergantung pada a
acara yang direncanakan. Apa pun TODO lainnya , itu bukan alasan untuk meninggalkan kode buruk
sistem.
Saat ini, sebagian besar IDE yang bagus memberikan gerakan dan fitur khusus untuk menemukan semua itu
Komentar TODO , jadi tidak mungkin mereka tersesat. Tetap saja, Anda tidak ingin kode Anda
dikotori dengan TODO . Jadi memindai secara teratur dan menghilangkan yang Anda
bisa.

Amplifikasi
Sebuah komentar dapat digunakan untuk memperkuat pentingnya sesuatu yang mungkin tampak
ngawur.
String listItemContent = match.group (3) .trim ();
// trim sangat penting. Ini menghilangkan awal
// spasi yang dapat menyebabkan item dikenali
// sebagai daftar lain.
ListItemWidget baru (this, listItemContent, this.level + 1);
return buildList (text.substring (match.end ()));

Javadocs dalam API Publik


Tidak ada yang sangat membantu dan memuaskan seperti API publik yang dijelaskan dengan baik. Java-
dokumen untuk pustaka Java standar adalah contohnya. Akan sulit, paling-paling, untuk menulis
Program Java tanpa mereka.
Jika Anda menulis API publik, maka Anda tentu harus menulis javadocs yang baik untuk itu.
Tetapi ingatlah sisa nasihat dalam bab ini. Javadocs bisa juga menyesatkan,
nonlokal, dan tidak jujur seperti jenis komentar lainnya.

Komentar buruk
Sebagian besar komentar termasuk dalam kategori ini. Biasanya mereka adalah kruk atau alasan untuk kode yang buruk
atau pembenaran untuk keputusan yang tidak mencukupi, berjumlah sedikit lebih banyak dari pada programmer
berbicara sendiri.

Bergumam
Memberi komentar hanya karena Anda merasa harus melakukannya atau karena proses mengharuskannya,
adalah hack. Jika Anda memutuskan untuk menulis komentar, maka habiskan waktu yang diperlukan untuk memastikannya
adalah komentar terbaik yang bisa Anda tulis.

www.it-ebooks.info

Halaman 91

60 Bab 4: Komentar

Di sini, misalnya, adalah kasus yang saya temukan di FitNesse, di mana komentar mungkin memang ada
berguna. Tetapi penulis sedang terburu-buru atau tidak begitu memperhatikan. Ibunya-
bling meninggalkan teka-teki:

kekosongan umum loadProperties ()


{
mencoba
{
String propertiesPath = propertiesLocation + "/" + PROPERTIES_FILE;
FileInputStream propertiesStream = FileInputStream baru (propertiesPath);
loadedProperties.load (propertiesStream);
}
catch (IOException e)
{
// Tidak ada file properti berarti semua default dimuat
}
}

https://translate.googleusercontent.com/translate_f 71/365
3/12/2020 www.it-ebooks.info
Apa artinya komentar di blok tangkap ? Jelas itu berarti sesuatu untuk
penulis, tetapi maknanya tidak datang dengan baik. Rupanya, jika kita mendapat
IOException , itu berarti tidak ada file properti; dan dalam hal ini semua standarnya adalah
sarat. Tapi siapa yang memuat semua default? Apakah mereka dimuat sebelum panggilan ke
loadProperties.load ? Atau apakah loadProperties.load menangkap pengecualian, memuat default,
dan kemudian meneruskan pengecualian untuk kita abaikan? Atau apakah loadProperties.load memuat semua
default sebelum mencoba memuat file? Apakah penulis mencoba menghibur dirinya sendiri
fakta bahwa dia meninggalkan blok tangkapan kosong? Atau — dan ini adalah kemungkinan yang menakutkan—
adalah penulis yang mencoba mengatakan pada dirinya sendiri untuk kembali ke sini nanti dan menulis kode yang akan
memuat default?
Satu-satunya jalan kami adalah memeriksa kode di bagian lain sistem untuk mencari tahu apa
sedang terjadi. Setiap komentar yang memaksa Anda untuk mencari di modul lain untuk arti itu
komentar gagal berkomunikasi dengan Anda dan tidak sebanding dengan bit yang dikonsumsi.

Komentar yang Berlebihan


Listing 4-1 menunjukkan fungsi sederhana dengan komentar header yang sepenuhnya berlebihan.
Komentar mungkin lebih lama untuk dibaca daripada kode itu sendiri.

Listing 4-1
waitForClose
// Metode utilitas yang kembali ketika this.closed benar. Melempar pengecualian
// jika batas waktu tercapai.
publicForVose batal disinkronkan (final timeoutMillis lama)
melempar Pengecualian
{
jika (! ditutup)
{
tunggu (timeoutMillis);
jika (! ditutup)
throw Exception baru ("MockResponseSender tidak dapat ditutup");
}
}

www.it-ebooks.info

Halaman 92

Komentar buruk 61

Apa tujuan dari komentar ini? Ini tentu saja tidak lebih informatif daripada
kode. Itu tidak membenarkan kode, atau memberikan maksud atau alasan. Itu tidak mudah dibaca daripada
Kode. Memang, itu kurang tepat daripada kode dan membujuk pembaca untuk menerima kekurangan itu
presisi sebagai pengganti pemahaman yang benar. Ini agak seperti penjual mobil bekas gladhanding
meyakinkan Anda bahwa Anda tidak perlu mencari di bawah tenda.
Sekarang pertimbangkan legiun javadocs yang tidak berguna dan berlebihan pada Listing 4-2 yang diambil dari
Kucing jantan. Komentar-komentar ini hanya berfungsi untuk mengacaukan dan mengaburkan kode. Mereka tidak melayani dokumen
tujuan mental sama sekali. Lebih buruk lagi, saya hanya menunjukkan beberapa yang pertama. Ada
banyak lagi di modul ini.

Listing 4-2
ContainerBase.java (Tomcat)
ContainerBase abstrak kelas publik
mengimplementasikan Kontainer, Siklus Hidup, Pipa,
MBeanRegistration, Serializable {

/ **
* Penundaan prosesor untuk komponen ini.
*/
protected int backgroundProcessorDelay = -1;

/ **
* Dukungan siklus hidup untuk komponen ini.
*/
siklus hidup LifecycleSupport = yang dilindungi
LifecycleSupport baru (ini);

/ **
* Acara wadah pendengar untuk Kontainer ini.
*/
protected ArrayList listeners = new ArrayList ();

/ **
* Implementasi Loader yang digunakan untuk Kontainer ini

https://translate.googleusercontent.com/translate_f 72/365
3/12/2020 www.it-ebooks.info
* terkait.
*/
protected Loader loader = null;

/ **
* Implementasi Logger dengan Kontainer ini
* terkait.
*/
logger log dilindungi = null;

/ **
* Nama logger terkait.
*/
dilindungi String logName = null;

www.it-ebooks.info

Halaman 93

62 Bab 4: Komentar

Listing 4-2 (lanjutan)


ContainerBase.java (Tomcat)
/ **
* Implementasi Manajer dengan Kontainer ini
* terkait.
*/
manajer terproteksi manajer = nol;

/ **
* Cluster yang terkait dengan Kontainer ini.
*/
dilindungi Cluster cluster = null;

/ **
* Nama yang bisa dibaca manusia dari Kontainer ini.
*/
protected String name = null;

/ **
* Kontainer induk tempat Kontainer ini adalah anak-anak.
*/
induk Kontainer terlindungi = nol;

/ **
* Loader kelas induk yang akan dikonfigurasi ketika kita menginstal a
* Pemuat.
*/
ClassLoader yang dilindungi parentClassLoader = null;

/ **
* Objek Pipeline dengan mana Container ini berada
* terkait.
*/
pipeline pipeline dilindungi = baru StandardPipeline (ini);

/ **
* Realm yang terkait dengan Kontainer ini.
*/
Realm realm dilindungi = null;

/ **
* Objek DirContext sumber daya dengan mana Kontainer ini
* terkait.
*/
sumber daya DirContext yang dilindungi = null;

https://translate.googleusercontent.com/translate_f 73/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info

Halaman 94

Komentar buruk 63

Komentar yang menyesatkan


Terkadang, dengan semua niat terbaik, seorang programmer membuat pernyataan dalam komentarnya
itu tidak cukup tepat untuk menjadi akurat. Pertimbangkan untuk sesaat yang sangat berlebihan
tetapi juga komentar yang menyesatkan yang kita lihat pada Listing 4-1.
Apakah Anda menemukan bahwa komentar itu menyesatkan? Metode tidak kembali
saat ini. ditutup menjadi benar . Ia mengembalikan jika this.closed adalah benar ; jika tidak, ia menunggu a
buta waktu habis dan kemudian melempar pengecualian jika ini. ditutup masih tidak benar .
Ini sedikit informasi yang salah, ditulis dalam komentar yang lebih sulit dibaca daripada
tubuh kode, dapat menyebabkan programmer lain dengan senang hati memanggil fungsi ini di
harapan bahwa itu akan kembali segera setelah ini. ditutup menjadi benar . Programmer yang buruk
kemudian akan menemukan dirinya dalam sesi debugging mencoba mencari tahu mengapa kodenya dieksekusi
sangat lambat.

Komentar yang diamanatkan


Sangat konyol untuk memiliki aturan yang mengatakan bahwa setiap fungsi harus memiliki javadoc, atau
setiap variabel harus memiliki komentar. Komentar seperti ini hanya mengacaukan kode,
gerbang terletak, dan meminjamkan kebingungan umum dan disorganisasi.
Misalnya, javadocs yang diperlukan untuk setiap fungsi mengarah ke kekejian seperti Daftar-
ing 4-3. Kekacauan ini tidak menambah apa-apa dan hanya berfungsi untuk mengaburkan kode dan membuat
potensial untuk kebohongan dan penyesatan.

Listing 4-3
/ **
*
* @param title Judul CD
* @param author Penulis CD
* @param trek Jumlah trek pada CD
* @param durationInMinutes Durasi CD dalam hitungan menit
*/
public void addCD (Judul string, String penulis,
int track, int durasiInMinutes) {
CD cd = CD baru ();
cd.title = judul;
cd.author = penulis;
cd.tracks = trek;
cd.duration = durasi;
cdList.add (cd);
}

Komentar Jurnal
Kadang-kadang orang menambahkan komentar ke awal modul setiap kali mereka mengeditnya. Ini
komentar terakumulasi sebagai semacam jurnal, atau log, dari setiap perubahan yang pernah ada
terbuat. Saya telah melihat beberapa modul dengan lusinan halaman dari entri jurnal yang dijalankan ini.

www.it-ebooks.info

Halaman 95

64 Bab 4: Komentar

* Perubahan (dari 11-Oktober-2001)

https://translate.googleusercontent.com/translate_f 74/365
3/12/2020 www.it-ebooks.info
* --------------------------
* 11-Oct-2001: Mengatur ulang kelas dan memindahkannya ke paket baru
* com.jrefinery.date (DG);
* 05-Nov-2001: Menambahkan metode getDescription (), dan menghapus NotableDate
* kelas (DG);
* 12-Nov-2001: IBD memerlukan metode setDescription (), sekarang NotableDate
* kelas hilang (DG); Mengubah getPreviousDayOfWeek (),
* getFollowingDayOfWeek () dan getNearestDayOfWeek () untuk memperbaiki
* bug (DG);
* 05-Des-2001: Fixed bug di kelas SpreadsheetDate (DG);
* 29-Mei-2002: Memindahkan konstanta bulan ke antarmuka terpisah
* (MonthConstants) (DG);
* 27-Agustus-2002: Memperbaiki bug dalam metode addMonths (), terima kasih kepada N ??? levka Petr (DG);
* 03-Oktober-2002: Memperbaiki kesalahan yang dilaporkan oleh Checkstyle (DG);
* 13-Mar-2003: Implemented Serializable (DG);
* 29-Mei-2003: Fixed bug dalam metode addMonths (DG);
* 04-Sep-2003: Diimplementasikan Sebanding. Memperbarui javadocs (DG) isInRange;
* 05-Jan-2005: Memperbaiki bug dalam metode addYears () (1096282) (DG);

Dulu ada alasan bagus untuk membuat dan memelihara entri log ini di awal
setiap modul. Kami tidak memiliki sistem kontrol kode sumber yang melakukannya untuk kami. Sekarang,
Namun, jurnal panjang ini hanya lebih berantakan untuk mengaburkan modul. Mereka seharusnya
sepenuhnya dihapus.

Komentar Kebisingan
Terkadang Anda melihat komentar yang tidak lain hanyalah suara bising. Mereka menyatakan kembali yang jelas dan
tidak memberikan informasi baru.

/ **
* Konstruktor default.
*/
AnnualDateRule yang dilindungi () {
}

Tidak benar Atau bagaimana dengan ini:

/ ** Hari dalam sebulan. * /


private int dayOfMonth;

Dan kemudian ada teladan redundansi ini:

/ **
* Mengembalikan hari dalam sebulan.
*
* @ kembalikan hari dalam sebulan.
*/
public int getDayOfMonth () {
return dayOfMonth;
}

www.it-ebooks.info

Halaman 96

Komentar buruk 65

Komentar-komentar ini sangat berisik sehingga kita belajar untuk mengabaikannya. Saat kami membaca kode, kode kami
mata lewati saja. Akhirnya komentar mulai berbohong sebagai kode di sekitar mereka
perubahan.
Komentar pertama pada Listing 4-4 tampaknya tepat. 2 Ini menjelaskan mengapa catch block
sedang diabaikan. Tetapi komentar kedua adalah kebisingan murni. Rupanya programmer itu
sangat frustrasi dengan menulis coba / tangkap blok dalam fungsi ini yang ia perlu curhat.

Listing 4-4
mulai Mengirim
private void startSending ()
{
mencoba
{
doSending ();
}
catch (SocketException e)
{
// normal. seseorang menghentikan permintaan itu.
}
catch (Pengecualian e)
{

https://translate.googleusercontent.com/translate_f 75/365
3/12/2020 www.it-ebooks.info
mencoba
{
response.add (ErrorResponder.makeExceptionString (e));
response.closeAll ();
}
catch (Exception e1)
{
// Beri aku istirahat!
}
}
}

Alih-alih melampiaskan komentar yang tidak berharga dan berisik, programmer harus melakukannya
mengakui bahwa frustrasinya dapat diatasi dengan memperbaiki struktur kodenya.
Dia seharusnya mengarahkan energinya untuk mengekstraksi blok coba / tangkap terakhir itu menjadi terpisah
fungsi, seperti yang ditunjukkan pada Listing 4-5.

Listing 4-5
startSending (refactored)
private void startSending ()
{
mencoba
{
doSending ();
}

2. Tren IDE saat ini untuk memeriksa ejaan dalam komentar akan menjadi balsem bagi kita yang membaca banyak kode.

www.it-ebooks.info

Halaman 97

66 Bab 4: Komentar

Listing 4-5 (lanjutan)


startSending (refactored)
catch (SocketException e)
{
// normal. seseorang menghentikan permintaan itu.
}
catch (Pengecualian e)
{
addExceptionAndCloseResponse (e);
}
}

private void addExceptionAndCloseResponse (Pengecualian e)


{
mencoba
{
response.add (ErrorResponder.makeExceptionString (e));
response.closeAll ();
}
catch (Exception e1)
{
}
}

Ganti godaan untuk membuat kebisingan dengan tekad untuk membersihkan kode Anda. Anda akan
merasa itu membuat Anda seorang programmer yang lebih baik dan lebih bahagia.

Suara Menakutkan
Javadocs juga bisa berisik. Apa tujuan lakukan Javadocs berikut (dari yang terkenal
perpustakaan open-source) melayani? Jawab: tidak ada. Itu hanya komentar berisik yang berlebihan
ditulis karena keinginan yang salah tempat untuk menyediakan dokumentasi.

/** Nama. * /
nama String pribadi;

/** Versi. * /
versi String pribadi;

/ ** The licenceName. * /
private String licenceName;

/** Versi. * /

https://translate.googleusercontent.com/translate_f 76/365
3/12/2020 www.it-ebooks.info
info String pribadi;
Baca komentar ini lagi dengan lebih cermat. Apakah Anda melihat kesalahan cut-paste? Jika penulis
tidak memperhatikan ketika komentar ditulis (atau ditempelkan), mengapa pembaca harus
diharapkan mendapat untung dari mereka?

www.it-ebooks.info

Halaman 98

Komentar buruk 67

Jangan Gunakan Komentar Saat Anda Dapat Menggunakan Fungsi atau Variabel
Pertimbangkan hamparan kode berikut:
// apakah modul dari daftar global <mod> bergantung pada
// subsistem kita bagian dari apa?
if (smodule.getDependSubsystems (). berisi (subSysMod.getSubSystem ()))

Ini dapat diulang ulang tanpa komentar sebagai


ArrayList moduleDependees = smodule.getDependSubsystems ();
String ourSubSystem = subSysMod.getSubSystem ();
if (moduleDependees.contains (ourSubSystem))

Penulis kode asli mungkin telah menulis komentar terlebih dahulu (tidak mungkin) dan kemudian
menulis kode untuk memenuhi komentar. Namun, penulis kemudian harus refactored
kode, seperti yang saya lakukan, sehingga komentar dapat dihapus.

Penanda Posisi
Terkadang programmer suka menandai posisi tertentu dalam file sumber. Sebagai contoh, saya
baru-baru ini menemukan ini dalam program yang saya cari:
// Tindakan ////////////////////////////////////

Ada saat langka ketika masuk akal untuk mengumpulkan fungsi tertentu bersama di bawah a
spanduk seperti ini. Tetapi secara umum mereka adalah kekacauan yang harus dihilangkan - terutama
kereta berisik garis miring pada akhirnya.
Pikirkan seperti ini. Sebuah spanduk mengejutkan dan jelas jika Anda tidak melihat spanduk terlalu
sering. Jadi gunakan sangat hemat, dan hanya ketika manfaatnya signifikan. Jika Anda berlebihan
spanduk, mereka akan jatuh ke latar belakang kebisingan dan diabaikan.

Komentar Penutupan Brace


Terkadang programmer akan memberikan komentar khusus pada kurung kurawal, seperti pada Listing 4-6.
Meskipun ini mungkin masuk akal untuk fungsi panjang dengan struktur bersarang yang dalam, ini berfungsi
hanya untuk mengacaukan jenis fungsi kecil dan enkapsulasi yang kita sukai. Jadi jika Anda menemukannya
diri Anda ingin menandai kurung kurawal, cobalah untuk mempersingkat fungsi Anda.

Listing 4-6
wc.java
kelas publik wc {
public static public void (String [] args) {
BufferedReader di = BufferedReader baru (InputStreamReader baru (System.in));
Garis tali;
int lineCount = 0;
int charCount = 0;
int wordCount = 0;
coba {

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 77/365
3/12/2020 www.it-ebooks.info

Halaman 99

68 Bab 4: Komentar

Listing 4-6 (lanjutan)


wc.java
while ((line = in.readLine ())! = null) {
lineCount ++;
charCount + = line.length ();
Kata string [] = line.split ("\\ W");
wordCount + = words.length;
} // sementara
System.out.println ("wordCount =" + wordCount);
System.out.println ("lineCount =" + lineCount);
System.out.println ("charCount =" + charCount);
} // coba
catch (IOException e) {
System.err.println ("Kesalahan:" + e.getMessage ());
} // tangkap
} // utama
}

Atribusi dan Bylines


/ * Ditambahkan oleh Rick * /

Sistem kontrol kode sumber sangat baik dalam mengingat siapa yang menambahkan apa, kapan.
Tidak perlu mencemari kode dengan sedikit byline. Anda mungkin berpikir bahwa
Kents akan berguna untuk membantu orang lain tahu siapa yang harus diajak bicara tentang kode. Tetapi
kenyataannya adalah bahwa mereka cenderung bertahan selama bertahun-tahun, semakin kurang dan kurang akurat
dan relevan.
Sekali lagi, sistem kontrol kode sumber adalah tempat yang lebih baik untuk jenis informasi ini.

Kode Komentar-Keluar
Beberapa praktik sama menjijikkannya dengan kode komentar. Jangan lakukan ini!
InputStreamResponse response = baru InputStreamResponse ();
response.setBody (formatter.getResultStream (), formatter.getByteCount ());
// InputStream resultsStream = formatter.getResultStream ();
// StreamReader reader = new StreamReader (resultsStream);
// response.setContent (reader.read (formatter.getByteCount ()));

Orang lain yang melihat kode komentar itu tidak akan memiliki keberanian untuk menghapusnya. Mereka akan berpikir
itu ada karena suatu alasan dan terlalu penting untuk dihapus. Jadi kode yang dikomentari mengumpulkan seperti
ampas di bagian bawah sebotol anggur yang buruk.
Pertimbangkan ini dari apache commons:
this.bytePos = writeBytes (pngIdBytes, 0);
// hdrPos = bytePos;
writeHeader ();
writeResolution ();
// dataPos = bytePos;
if (writeImageData ()) {
writeEnd ();
this.pngBytes = resizeByteArray (this.pngBytes, this.maxPos);
}

www.it-ebooks.info

Halaman 100

Komentar buruk 69

lain {
this.pngBytes = null;
}
kembalikan this.pngBytes;

Mengapa dua baris kode itu dikomentari? Apakah itu penting? Apakah mereka pergi sebagai
pengingat untuk beberapa perubahan segera? Atau apakah mereka hanya mengomel bahwa seseorang berkomentar

https://translate.googleusercontent.com/translate_f 78/365
3/12/2020 www.it-ebooks.info
tahun yang lalu dan sama sekali tidak repot-repot membersihkan.
Ada suatu masa, di tahun enam puluhan, ketika kode komentar mungkin keluar
berguna. Tapi kami sudah memiliki sistem kontrol kode sumber yang baik untuk waktu yang sangat lama sekarang. Itu
sistem akan mengingat kode untuk kita. Kami tidak perlu berkomentar lagi. Hanya
hapus kodenya. Kami tidak akan kehilangan itu. Janji.

Komentar HTML
Komentar kode sumber HTML adalah kekejian, seperti yang Anda tahu dengan membaca kodenya
di bawah. Itu membuat komentar sulit dibaca di satu tempat di mana mereka seharusnya mudah
baca — editor / IDE. Jika komentar akan diekstraksi oleh beberapa alat (seperti Javadoc) ke
muncul di halaman Web, maka itu harus menjadi tanggung jawab alat itu, dan bukan program-
mer, untuk menghiasi komentar dengan HTML yang sesuai.
/ **
* Tugas untuk menjalankan tes fit.
* Tugas ini menjalankan tes kebugaran dan menerbitkan hasilnya.
* <p />
* <pre>
* Penggunaan:
* & lt; taskdef name = & quot; eksekusi-kebugaran-tes & quot;
* classname = & quot; fitnesse.ant.ExecuteFitnesseTestsTask & quot;
* classpathref = & quot; classpath & quot; / & gt;
* ATAU
* & lt; taskdef classpathref = & quot; classpath & quot;
* sumber daya = & quot; task.properties & quot; / & gt;
* <p />
* & lt; jalankan-tes kebugaran
* suitepage = & quot; FitNesse.SuiteAcceptanceTests & quot;
* fitnesseport = & quot; 8082 & quot;
* resultsdir = & quot; $ {results.dir} & quot;
* resultshtmlpage = & quot; fit-results.html & quot;
* classpathref = & quot; classpath & quot; / & gt;
* </pre>
*/

Informasi Nonlokal
Jika Anda harus menulis komentar, maka pastikan itu menjelaskan kode yang muncul di dekat Anda. Jangan
menawarkan informasi seluruh sistem dalam konteks komentar lokal. Pertimbangkan, misalnya,
komentar javadoc di bawah ini. Selain dari kenyataan bahwa itu sangat berlebihan, ia juga menawarkan
informasi tentang port default. Namun fungsi tersebut sama sekali tidak memiliki kendali atas
apa itu default. Komentar tersebut tidak menggambarkan fungsi, tetapi beberapa lainnya, jauh
Ini adalah bagian dari sistem. Tentu saja tidak ada jaminan bahwa komentar ini akan diubah
ketika kode yang berisi default diubah.

www.it-ebooks.info

Halaman 101

70 Bab 4: Komentar

/ **
* Port di mana fitnesse akan berjalan. Default ke <b> 8082 </b>.
*
* @param fitnessePort
*/
public void setFitnessePort (int fitnessePort)
{
this.fitnessePort = fitnessePort;
}

Terlalu banyak informasi


Jangan memasukkan diskusi historis yang menarik atau deskripsi detail yang tidak relevan ke dalam Anda
komentar. Komentar di bawah ini diambil dari modul yang dirancang untuk menguji bahwa fungsi
tion dapat mengkodekan dan mendekode base64. Selain nomor RFC, seseorang membaca ini
kode tidak memerlukan informasi rahasia yang terkandung dalam komentar.

/*
RFC 2045 - Ekstensi Surat Internet Multiguna (MIME)
Bagian Satu: Format Badan Pesan Internet
bagian 6.8. Base64 Content-Transfer-Encoding
Proses pengkodean mewakili kelompok bit input 24-bit sebagai output
string dari 4 karakter yang dikodekan. Melanjutkan dari kiri ke kanan, a
Grup input 24-bit dibentuk dengan menggabungkan 3 grup input 8-bit.
24 bit ini kemudian diperlakukan sebagai 4 kelompok 6-bit gabungan, masing-masing
dari yang diterjemahkan menjadi satu digit dalam alfabet base64.
Saat menyandikan bit stream melalui encoding base64, bit stream
harus dianggap dipesan dengan bit paling signifikan terlebih dahulu.
Yaitu, bit pertama dalam aliran akan menjadi bit orde tinggi di

https://translate.googleusercontent.com/translate_f 79/365
3/12/2020 www.it-ebooks.info
byte 8-bit pertama, dan bit kedelapan akan menjadi bit orde rendah
byte 8-bit pertama, dan seterusnya.
*/

Koneksi yang tidak terlihat


Koneksi antara komentar dan kode yang dijelaskan harus jelas. Jika Anda
pergi ke kesulitan untuk menulis komentar, maka setidaknya Anda ingin pembaca dapat
lihat komentar dan kodenya dan pahami apa yang dibicarakan komentar itu.
Pertimbangkan, misalnya, komentar ini diambil dari apache commons:

/*
* mulai dengan array yang cukup besar untuk menampung semua piksel
* (plus filter byte), dan tambahan 200 byte untuk info header
*/
this.pngBytes = byte baru [((this.width + 1) * this.height * 3) + 200];

Apa itu byte filter? Apakah ini terkait dengan +1? Atau ke * 3? Kedua? Apakah piksel bita? Mengapa
200? Tujuan dari komentar adalah untuk menjelaskan kode yang tidak menjelaskan dirinya sendiri. Sangat disayangkan
ketika komentar membutuhkan penjelasannya sendiri.

Tajuk Fungsi
Fungsi pendek tidak perlu banyak deskripsi. Nama yang dipilih dengan baik untuk fungsi kecil itu
melakukan satu hal biasanya lebih baik daripada tajuk komentar.

www.it-ebooks.info

Halaman 102

Komentar buruk 71

Javadocs dalam Kode Nonpublik


Berguna javadocs untuk API publik, mereka adalah laknat untuk kode yang tidak dimaksudkan
untuk konsumsi publik. Membuat halaman javadoc untuk kelas dan fungsi di dalam a
sistem umumnya tidak berguna, dan formalitas ekstra dari javadoc komentar jumlahnya
untuk sedikit lebih dari celah dan gangguan.

Contoh
Saya menulis modul pada Listing 4-7 untuk Perendaman XP pertama . Itu dimaksudkan untuk menjadi
contoh pengkodean dan gaya komentar yang buruk. Kent Beck kemudian refactored kode ini menjadi
bentuk jauh lebih menyenangkan di depan beberapa lusin siswa yang antusias. Kemudian saya beradaptasi
contoh untuk buku saya , Pengembangan, Prinsip, Pola, dan Praktek Perangkat Lunak Agile
dan artikel pertama Pengrajin saya yang diterbitkan di majalah Pengembangan Perangkat Lunak .
Apa yang saya temukan menarik tentang modul ini adalah bahwa ada saat ketika banyak dari kita
akan menganggapnya "didokumentasikan dengan baik." Sekarang kita melihatnya sebagai kekacauan kecil. Lihat bagaimana
banyak masalah komentar yang berbeda yang dapat Anda temukan.

Listing 4-7
GeneratePrimes.java
/ **
* Kelas ini Menghasilkan bilangan prima hingga yang ditentukan pengguna
* maksimum. Algoritma yang digunakan adalah Saringan Eratosthenes.
* <p>
* Eratosthenes dari Kirene, SM 276 SM, Sirene, Libya -
* dc 194, Alexandria. Orang pertama yang menghitung
* Lingkar Bumi. Juga dikenal karena bekerja
* Kalender dengan tahun kabisat dan mengelola perpustakaan di Alexandria.
* <p>
* Algoritma ini cukup sederhana. Diberikan array bilangan bulat
* mulai dari 2. Coret semua kelipatan 2. Temukan yang berikutnya
* bilangan bulat tidak bersilang, dan mencoret semua kelipatannya.
* Ulangi sampai Anda telah melewati akar kuadrat maksimal
* nilai.
*
* @author Alphonse
* @ versi 13 Feb 2002 atp
*/
import java.util. *;

GeneratePrimes kelas publik


{
/ **
* @param maxValue adalah batas generasi.
*/
public static int [] menghasilkanPrimes (int maxValue)
{

https://translate.googleusercontent.com/translate_f 80/365
3/12/2020 www.it-ebooks.info
if (maxValue> = 2) // satu-satunya kasus yang valid
{
// deklarasi
int s = maxValue + 1; // ukuran array
boolean [] f = boolean [s] baru;
int i;

www.it-ebooks.info

Halaman 103

72 Bab 4: Komentar

Listing 4-7 (lanjutan)


GeneratePrimes.java
// inisialisasi array menjadi true.
untuk (i = 0; i <s; i ++)
f [i] = true;

// singkirkan yang bukan prima yang dikenal


f [0] = f [1] = false;

// saringan
int j;
untuk (i = 2; i <Math.sqrt (s) + 1; i ++)
{
if (f [i]) // jika saya tidak disilangkan, silangkan kelipatannya.
{
untuk (j = 2 * i; j <s; j + = i)
f [j] = false; // multiple bukan prime
}
}

// ada berapa bilangan prima di sana?


int count = 0;
untuk (i = 0; i <s; i ++)
{
jika (f [i])
hitung ++; // jumlah benjolan.
}

int [] primes = new int [count];

// pindahkan bilangan prima ke dalam hasil


untuk (i = 0, j = 0; i <s; i ++)
{
jika (f [i]) // jika prima
primes [j ++] = i;
}

mengembalikan bilangan prima; // kembalikan bilangan prima


}
else // maxValue <2
kembalikan int baru [0]; // kembalikan null array jika input buruk.
}
}

Dalam Listing 4-8 Anda dapat melihat versi refactored dari modul yang sama. Perhatikan bahwa penggunaannya
komentar secara signifikan terkendali. Hanya ada dua komentar di seluruh modul.
Kedua komentar bersifat jelas.

Listing 4-8
PrimeGenerator.java (refactored)
/ **
* Kelas ini Menghasilkan bilangan prima hingga yang ditentukan pengguna
* maksimum. Algoritma yang digunakan adalah Saringan Eratosthenes.
* Diberikan array bilangan bulat mulai dari 2:
* Temukan bilangan bulat pertama, dan coret semua

www.it-ebooks.info

Halaman 104

https://translate.googleusercontent.com/translate_f 81/365
3/12/2020 www.it-ebooks.info

Komentar buruk 73

Listing 4-8 (lanjutan)


PrimeGenerator.java (refactored)
* berlipat ganda. Ulangi sampai tidak ada lagi kelipatan
* dalam array.
*/

PrimeGenerator kelas publik


{
private static boolean [] crossedOut;
int [] hasil private static;

public static int [] menghasilkanPrimes (int maxValue)


{
if (maxValue <2)
kembalikan int baru [0];
lain
{
uncrossIntegersUpTo (maxValue);
crossOutMultiples ();
putUncrossedIntegersIntoResult ();
hasil pengembalian;
}
}

private static batal uncrossIntegersUpTo (int maxValue)


{
crossedOut = boolean baru [maxValue + 1];
untuk (int i = 2; i <crossedOut.length; i ++)
crossedOut [i] = false;
}

private static void crossOutMultiples ()


{
int limit = determIterationLimit ();
untuk (int i = 2; i <= limit; i ++)
if (notCrossed (i))
crossOutMultiplesOf (i);
}

private static int determIterationLimit ()


{
// Setiap kelipatan dalam array memiliki faktor prima itu
// kurang dari atau sama dengan akar ukuran array,
// jadi kita tidak perlu mencoret beberapa angka
// lebih besar dari root itu.
double iterationLimit = Math.sqrt (crossedOut.length);
return (int) iterationLimit;
}

private static void crossOutMultiplesOf (int i)


{
untuk (int multiple = 2 * i;
multiple <crossedOut.length;
multiple + = i)
crossedOut [multiple] = true;
}

www.it-ebooks.info

Halaman 105

74 Bab 4: Komentar

Listing 4-8 (lanjutan)


PrimeGenerator.java (refactored)
boolean statis pribadi notCrossed (int i)
{
return crossedOut [i] == false;
}

private static void putUncrossedIntegersIntoResult ()


{
result = new int [numberOfUncrossedIntegers ()];
untuk (int j = 0, i = 2; i <crossedOut.length; i ++)
if (notCrossed (i))

https://translate.googleusercontent.com/translate_f 82/365
3/12/2020 www.it-ebooks.info
hasil [j ++] = i;
}

private int numberOfUncrossedIntegers () pribadi


{
int count = 0;
untuk (int i = 2; i <crossedOut.length; i ++)
if (notCrossed (i))
hitung ++;

jumlah pengembalian;
}
}

Sangat mudah untuk berargumen bahwa komentar pertama itu berlebihan karena bunyinya sangat mirip
yang generatePrimes berfungsi sendiri. Meski begitu, saya pikir komentar berfungsi untuk memudahkan pembaca
algoritma, jadi saya cenderung meninggalkannya.
Argumen kedua hampir pasti perlu. Ini menjelaskan alasan di balik
penggunaan akar kuadrat sebagai batas loop. Saya tidak dapat menemukan nama variabel yang sederhana, juga tidak ada
struktur pengkodean berbeda yang memperjelas hal ini. Di sisi lain, penggunaan
akar kuadrat mungkin kesombongan. Apakah saya benar-benar menghemat banyak waktu dengan membatasi iterasi?
ke akar kuadrat? Bisakah perhitungan akar kuadrat membutuhkan waktu lebih lama daripada yang saya hemat?
Ini layak untuk dipikirkan. Menggunakan akar kuadrat sebagai batas iterasi memenuhi C lama
dan merakit hacker bahasa dalam diriku, tapi aku tidak yakin itu sepadan dengan waktu dan usaha
bahwa orang lain akan mengeluarkan untuk memahaminya.

Bibliografi
[KP78]: Kernighan dan Plaugher, Elemen Gaya Pemrograman , 2d. ed., McGraw-
Hill, 1978.

www.it-ebooks.info

Halaman 106

5
Memformat

https://translate.googleusercontent.com/translate_f 83/365
3/12/2020 www.it-ebooks.info

Ketika orang melihat di bawah tenda, kami ingin mereka terkesan dengan kerapian,
sistensi, dan perhatian terhadap detail yang mereka rasakan. Kami ingin mereka dikejutkan oleh
ketertiban. Kami ingin alis mereka naik saat mereka menelusuri modul. Kami ingin
mereka merasa bahwa para profesional telah bekerja. Jika sebaliknya mereka melihat arak
banyak kode yang kelihatannya ditulis oleh sekelompok pelaut yang mabuk, lalu mereka
mungkin menyimpulkan bahwa kurangnya perhatian terhadap detail yang sama meliputi setiap aspek lain dari
proyek.

75

www.it-ebooks.info

Halaman 107

76 Bab 5: Memformat

Anda harus berhati-hati bahwa kode Anda diformat dengan baik. Anda harus memilih satu set
aturan sederhana yang mengatur format kode Anda, dan kemudian Anda harus konsisten menerapkannya
aturan itu. Jika Anda mengerjakan sebuah tim, maka tim tersebut harus menyetujui satu set
aturan pemformatan dan semua anggota harus mematuhi. Ini membantu untuk memiliki alat otomatis itu
dapat menerapkan aturan pemformatan itu untuk Anda.

Tujuan Pemformatan
Pertama-tama, mari kita perjelas. Pemformatan kode penting . Terlalu penting untuk diabaikan dan
terlalu penting untuk memperlakukan secara agama. Pemformatan kode adalah tentang komunikasi, dan
komunikasi adalah urutan pertama bisnis pengembang profesional.
Mungkin Anda berpikir bahwa "membuatnya berfungsi" adalah urutan pertama bisnis untuk
pengembang profesional. Saya harap sekarang, bahwa buku ini telah melemahkan Anda tentang hal itu
ide. Fungsi yang Anda buat hari ini memiliki peluang bagus untuk berubah di yang berikutnya
rilis, tetapi keterbacaan kode Anda akan memiliki efek mendalam pada semua perubahan
itu akan pernah dibuat. Gaya pengkodean dan keterbacaan mengatur preseden yang terus berlanjut
mempengaruhi kemampuan pemeliharaan dan ekstensibilitas lama setelah kode asli diubah
tak bisa dikenali. Gaya dan disiplin Anda tetap bertahan, meskipun kode Anda tidak.
Jadi apa masalah format yang membantu kami untuk berkomunikasi terbaik?

Pemformatan Vertikal
Mari kita mulai dengan ukuran vertikal. Seberapa besar seharusnya file sumber? Di Jawa, ukuran file sangat erat
terkait dengan ukuran kelas. Kami akan berbicara tentang ukuran kelas ketika kami berbicara tentang kelas. Untuk
saat ini mari kita pertimbangkan ukuran file.
Seberapa besar sebagian besar file sumber Java? Ternyata ada sejumlah besar ukuran dan
beberapa perbedaan gaya yang luar biasa. Gambar 5-1 menunjukkan beberapa perbedaan tersebut.
Tujuh proyek berbeda digambarkan. Junit, FitNesse, testNG, Waktu dan Uang,
JDepend, Ant, dan Tomcat. Garis-garis melalui kotak menunjukkan minimum dan maksimum
panjang file ibu di setiap proyek. Kotak menunjukkan sekitar sepertiga (satu standar
penyimpangan 1 ) file. Bagian tengah kotak adalah mean. Jadi ukuran file rata-rata di
Proyek FitNesse sekitar 65 baris, dan sekitar sepertiga dari file antara 40 dan
100+ baris. File terbesar di FitNesse adalah sekitar 400 baris dan yang terkecil adalah 6 baris.
Perhatikan bahwa ini adalah skala log, jadi perbedaan kecil dalam posisi vertikal menyiratkan sangat
perbedaan besar dalam ukuran absolut.

1. Kotak menunjukkan sigma / 2 di atas dan di bawah rata-rata. Ya, saya tahu bahwa distribusi panjang file tidak normal, dan standar
deviasi dard tidak tepat secara matematis. Tapi kami tidak mencoba untuk presisi di sini. Kami hanya mencoba merasakan.

https://translate.googleusercontent.com/translate_f 84/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 108

Pemformatan Vertikal 77

Gambar 5-1
Skala LOG distribusi panjang file (tinggi kotak = sigma)

Junit, FitNesse, dan Waktu dan Uang terdiri dari file yang relatif kecil. Tidak ada
lebih dari 500 baris dan sebagian besar file tersebut kurang dari 200 baris. Tomcat dan Ant, di jendela
Sebaliknya, ada beberapa file yang panjangnya beberapa ribu baris dan hampir setengahnya selesai
200 baris.
Apa artinya itu bagi kita? Tampaknya mungkin untuk membangun sistem yang signifikan
(FitNesse hampir 50.000 baris) dari file yang biasanya 200 baris, dengan
batas atas 500. Meskipun ini seharusnya bukan aturan yang keras dan cepat, itu harus dipertimbangkan
sangat diinginkan. File kecil biasanya lebih mudah dipahami daripada file besar.

Metafora Koran
Pikirkan artikel surat kabar yang ditulis dengan baik. Anda membacanya secara vertikal. Di atas Anda mengharapkan a
informasi utama yang akan memberi tahu Anda tentang cerita itu dan memungkinkan Anda memutuskan apakah itu kisahnya
sesuatu yang ingin Anda baca. Paragraf pertama memberi Anda sinopsis dari keseluruhan cerita,
menyembunyikan semua detail sembari memberi Anda konsep luas. Saat Anda melanjutkan ke bawah-
bangsal, detailnya meningkat sampai Anda memiliki semua tanggal, nama, kutipan, klaim, dan lainnya
minutia.
Kami ingin file sumber menjadi seperti artikel surat kabar. Nama harus sederhana
tapi jelas. Nama, dengan sendirinya, harus cukup untuk memberi tahu kami apakah kami berada di
modul yang tepat atau tidak. Bagian paling atas dari file sumber harus menyediakan tingkat tinggi

www.it-ebooks.info

Halaman 109

https://translate.googleusercontent.com/translate_f 85/365
3/12/2020 www.it-ebooks.info
78 Bab 5: Memformat

konsep dan algoritma. Detail harus meningkat ketika kita bergerak ke bawah, sampai pada akhirnya
kami menemukan fungsi dan perincian tingkat terendah dalam file sumber.
Sebuah surat kabar terdiri dari banyak artikel; kebanyakan sangat kecil. Ada yang sedikit lebih besar.
Sangat sedikit yang mengandung teks sebanyak yang bisa disimpan oleh suatu halaman. Ini membuat koran bisa digunakan . Jika
surat kabar hanya satu cerita panjang yang berisi aglomerasi fakta yang tidak teratur,
tanggal, dan nama, maka kita tidak akan membacanya.

Keterbukaan Vertikal Antar Konsep


Hampir semua kode dibaca dari kiri ke kanan dan atas ke bawah. Setiap baris mewakili ekspresi atau
sebuah klausa, dan setiap kelompok garis melambangkan pemikiran yang lengkap. Pikiran itu seharusnya
dipisahkan satu sama lain dengan garis kosong.
Pertimbangkan, misalnya, Listing 5-1. Ada baris kosong yang memisahkan paket
deklarasi, impor, dan masing-masing fungsi. Aturan yang sangat sederhana ini memiliki
ditemukan efek pada tata letak visual kode. Setiap baris kosong adalah isyarat visual yang mengidentifikasi
konsep baru dan terpisah. Saat Anda memindai daftar, mata Anda tertarik ke yang pertama
baris yang mengikuti baris kosong.

Listing 5-1
BoldWidget.java
paket fitnesse.wikitext.widgets;

import java.util.regex. *;

BoldWidget kelas publik meluas ParentWidget {


public String akhir statis REGEXP = "'' '. +?' ''";
private Pola Pola akhir statis = Pattern.compile ("'' '(. +?)' ''",
Pattern.MULTILINE + Pattern.DOTALL
);

BoldWidget publik (induk ParentWidget, teks string) melempar Pengecualian {


super (induk);
Matcher match = pattern.matcher (teks);
match.find ();
addChildWidgets (match.group (1));
}

public String render () throws Exception {


StringBuffer html = new StringBuffer ("<b>");
html.append (childHtml ()). append ("</b>");
return html.toString ();
}
}

Mengambil garis-garis kosong itu, seperti dalam Listing 5-2, memiliki efek yang sangat mengaburkan pada
keterbacaan kode.

www.it-ebooks.info

Halaman 110

Pemformatan Vertikal 79

Listing 5-2
BoldWidget.java
paket fitnesse.wikitext.widgets;
import java.util.regex. *;
BoldWidget kelas publik meluas ParentWidget {
public String akhir statis REGEXP = "'' '. +?' ''";
private Pola Pola akhir statis = Pattern.compile ("'' '(. +?)' ''",
Pattern.MULTILINE + Pattern.DOTALL);
BoldWidget publik (induk ParentWidget, teks string) melempar Pengecualian {
super (induk);
Matcher match = pattern.matcher (teks);
match.find ();
addChildWidgets (match.group (1));}
public String render () throws Exception {
StringBuffer html = new StringBuffer ("<b>");
html.append (childHtml ()). append ("</b>");
return html.toString ();
}

https://translate.googleusercontent.com/translate_f 86/365
3/12/2020 www.it-ebooks.info
}

Efek ini bahkan lebih jelas ketika Anda tidak memfokuskan mata Anda. Dalam contoh pertama
pengelompokan garis yang berbeda muncul pada Anda, sedangkan contoh kedua terlihat seperti a
kekacauan. Perbedaan antara kedua daftar ini adalah sedikit keterbukaan vertikal.

Kepadatan Vertikal
Jika keterbukaan memisahkan konsep, maka kepadatan vertikal menyiratkan hubungan dekat. Jadi garis
kode yang terkait erat harus tampak padat secara vertikal. Perhatikan betapa tidak bergunanya itu
komentar pada Listing 5-3 memecah hubungan dekat dari dua variabel instan.

Listing 5-3
ReporterConfig kelas publik {

/ **
* Nama kelas dari pendengar reporter
*/
String pribadi m_className;

/ **
* Properti pendengar reporter
*/
Daftar pribadi <Property> m_properties = ArrayList baru <Property> ();

public void addProperty (Property property) {


m_properties.add (properti);
}

Listing 5-4 jauh lebih mudah dibaca. Ini cocok dengan "mata penuh," atau setidaknya itu cocok untuk saya. saya
dapat melihatnya dan melihat bahwa ini adalah kelas dengan dua variabel dan metode, tanpa harus
gerakkan kepala atau mataku banyak. Daftar sebelumnya memaksa saya untuk menggunakan lebih banyak mata dan
gerak kepala untuk mencapai tingkat pemahaman yang sama.

www.it-ebooks.info

Halaman 111

80 Bab 5: Memformat

Listing 5-4
ReporterConfig kelas publik {
String pribadi m_className;
Daftar pribadi <Property> m_properties = ArrayList baru <Property> ();

public void addProperty (Property property) {


m_properties.add (properti);
}

Jarak Vertikal
Pernahkah Anda mengejar ekor melalui kelas, melompat dari satu fungsi ke fungsi lainnya,
gulir ke atas dan ke bawah file sumber, mencoba melihat bagaimana fungsi berhubungan dan
beroperasi, hanya untuk tersesat di sarang tikus kebingungan? Apakah Anda pernah mencari rantai
pewarisan untuk definisi variabel atau fungsi? Ini membuat frustrasi karena Anda
mencoba memahami apa yang sistem lakukan, tetapi Anda menghabiskan waktu dan mental Anda
energi untuk mencoba mencari dan mengingat di mana potongan-potongan itu.
Konsep-konsep yang berkaitan erat harus dijaga secara vertikal dekat satu sama lain [G10].
Jelas aturan ini tidak berfungsi untuk konsep yang termasuk dalam file terpisah. Tapi kemudian erat
konsep terkait tidak boleh dipisahkan menjadi file yang berbeda kecuali Anda memiliki yang sangat bagus
alasan. Memang, ini adalah salah satu alasan mengapa variabel yang dilindungi harus dihindari.
Untuk konsep-konsep yang terkait sangat erat sehingga mereka termasuk dalam file sumber yang sama,
pemisahan vertikal mereka harus menjadi ukuran seberapa penting masing-masing untuk memahami
kemampuan yang lain. Kami ingin menghindari memaksa pembaca untuk berkeliling melalui sumber kami
file dan kelas.

Deklarasi Variabel. Variabel harus dinyatakan sedekat mungkin dengan penggunaannya.


ble. Karena fungsi kami sangat pendek, variabel lokal akan muncul di bagian atas masing-masing
fungsi, seperti dalam fungsi gondrong ini dari Junit4.3.1.
readPreferences static batal pribadi () {
InputStream adalah = null;
coba {
is = FileInputStream (getPreferencesFile ()) baru;

https://translate.googleusercontent.com/translate_f 87/365
3/12/2020 www.it-ebooks.info
setPreferences (Properti baru (getPreferences ()));
getPreferences (). load (is);
} catch (IOException e) {
coba {
if (is! = null)
dekat();
} catch (IOException e1) {
}
}
}

Variabel kontrol untuk loop biasanya harus dideklarasikan dalam pernyataan loop, seperti dalam hal ini
fungsi kecil yang lucu dari sumber yang sama.

www.it-ebooks.info

Halaman 112

Pemformatan Vertikal 81

countTestCases publik int () {


int count = 0;
untuk ( Tes masing-masing : tes)
hitung + = each.countTestCases ();
jumlah pengembalian;
}

Dalam kasus yang jarang terjadi, sebuah variabel dapat dideklarasikan di bagian atas blok atau tepat sebelum loop dalam a
fungsi panjang-ish. Anda bisa melihat variabel seperti itu di potongan ini dari tengah-tengah yang sangat panjang
fungsi di TestNG.
...
untuk (tes XmlTest: m_suite.getTests ()) {
TestRunner tr = m_runnerFactory.newTestRunner (ini, tes);
tr.addListener (m_textReporter);
m_testRunners.add (tr);

invoker = tr.getInvoker ();

untuk (ITestNGMethod m: tr.getBeforeSuiteMethods ()) {


beforeSuiteMethods.put (m.getMethod (), m);
}

untuk (ITestNGMethod m: tr.getAfterSuiteMethods ()) {


afterSuiteMethods.put (m.getMethod (), m);
}
}
...

Variabel instance, di sisi lain, harus dinyatakan di bagian atas kelas. Ini
seharusnya tidak meningkatkan jarak vertikal dari variabel-variabel ini, karena dirancang dengan baik
kelas, mereka digunakan oleh banyak, jika tidak semua, metode kelas.
Ada banyak perdebatan tentang di mana variabel instan harus pergi. Di C ++ kita
biasanya mempraktekkan apa yang disebut aturan gunting , yang menempatkan semua variabel instan di
bawah. Namun, konvensi umum di Jawa adalah menempatkan mereka semua di atas kelas.
Saya tidak melihat alasan untuk mengikuti kebaktian lain. Yang penting adalah untuk contoh instance
dapat dideklarasikan di satu tempat yang terkenal. Semua orang harus tahu ke mana harus pergi untuk melihat
deklarasi.
Pertimbangkan, misalnya, kasus aneh dari kelas TestSuite di JUnit 4.3.1. saya sudah
sangat melemahkan kelas ini untuk menegaskan maksudnya. Jika Anda melihat sekitar separuh daftar,
Anda akan melihat dua variabel instan dideklarasikan di sana. Akan sulit untuk menyembunyikan mereka di tempat yang lebih baik
tempat. Seseorang yang membaca kode ini harus menemukan deklarasi dengan sengaja
penyok (seperti yang saya lakukan).
TestSuite kelas publik mengimplementasikan Test {
static public Test createTest (Kelas <? extends TestCase> theClass,
Nama string) {
...
}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 88/365
3/12/2020 www.it-ebooks.info

Halaman 113

82 Bab 5: Memformat

Konstruktor statis publik <? memperpanjang TestCase>


getTestConstructor (Kelas <? extends TestCase> theClass)
melempar NoSuchMethodException {
...
}

public Test Test statis (pesan String akhir) {


...
}

private String String exceptionToString (Throwable t) {


...
}

private String fName;

vektor pribadi <Test> fTests = vektor baru <Test> (10);

TestSuite publik () {
}

TestSuite publik (Kelas akhir <? extends TestCase> theClass) {


...
}

TestSuite publik (Kelas <? memperpanjang TestCase> theClass, nama String) {


...
}
... ... ... ... ...
}

Fungsi Bergantung. Jika satu fungsi memanggil yang lain, mereka harus ditutup secara vertikal,
dan penelepon harus berada di atas callee, jika memungkinkan. Ini memberikan program yang alami
mengalir. Jika konvensi ini diikuti dengan andal, pembaca akan dapat mempercayai bahwa fungsi tersebut didefinisikan
tions akan mengikuti segera setelah penggunaannya. Pertimbangkan, misalnya, cuplikan dari FitNesse
dalam Listing 5-5. Perhatikan bagaimana fungsi paling atas memanggil orang-orang di bawahnya dan bagaimana mereka pada gilirannya
hubungi mereka di bawah ini. Ini membuatnya mudah untuk menemukan fungsi-fungsi yang disebut dan sangat meningkatkan
keterbacaan seluruh modul.

Listing 5-5
WikiPageResponder.java
WikiPageResponder kelas publik mengimplementasikan SecureResponder {
halaman WikiPage terlindungi;
PageData pageData yang dilindungi;
String pageTitle yang dilindungi;
permintaan Permintaan dilindungi;
perayap PageCrawler yang dilindungi;

respons publik makeResponse (Konteks FitNesseContext, Permintaan permintaan)


throws Exception {
String pageName = getPageNameOrDefault (request, "FrontPage");

www.it-ebooks.info

Halaman 114

Pemformatan Vertikal 83

Listing 5-5 (lanjutan)

https://translate.googleusercontent.com/translate_f 89/365
3/12/2020 www.it-ebooks.info
WikiPageResponder.java
loadPage (pageName, konteks);
if (halaman == null)
return notFoundResponse (konteks, permintaan);
lain
return makePageResponse (konteks);
}

private String getPageNameOrDefault (Permintaan permintaan, String defaultPageName)


{
String pageName = request.getResource ();
if (StringUtil.isBlank (pageName))
pageName = defaultPageName;

kembali nama halaman;


}

void loadPage yang dilindungi (Sumber daya string, konteks FitNesseContext)


throws Exception {
WikiPagePath path = PathParser.parse (sumber daya);
crawler = context.root.getPageCrawler ();
crawler.setDeadEndStrategy (VirtualEnabledPageCrawler () baru);
halaman = crawler.getPage (context.root, path);
if (halaman! = null)
pageData = page.getData ();
}

Respons pribadi notFoundResponse (Konteks FitNesseContext, Permintaan permintaan)


throws Exception {
kembalikan NotFoundResponder (). makeResponse (konteks, permintaan) baru;
}

private SimpleResponse makePageResponse (konteks FitNesseContext)


throws Exception {
pageTitle = PathParser.render (crawler.getFullPath (halaman));
String html = makeHtml (konteks);

SimpleResponse response = new SimpleResponse ();


response.setMaxAge (0);
response.setContent (html);
mengembalikan respons;
}
...

Selain itu, cuplikan ini memberikan contoh yang bagus tentang menjaga konstanta pada saat yang tepat.
tingkat makan [G35]. The "FrontPage" konstan bisa saja dimakamkan di
fungsi getPageNameOrDefault , tapi itu akan menyembunyikan yang terkenal dan diharapkan
konstan dalam fungsi tingkat rendah yang tidak tepat. Lebih baik untuk lulus konstan itu
dari tempat di mana masuk akal untuk mengetahuinya ke tempat yang benar-benar menggunakannya.

www.it-ebooks.info

Halaman 115

84 Bab 5: Memformat

Afinitas Konseptual. Bit kode tertentu ingin


dekat bit lainnya. Mereka punya tertentu
afinitas konseptual. Semakin kuat afinitas, maka
kurang jarak vertikal harus ada di antara
mereka.
Seperti yang telah kita lihat, kedekatan ini mungkin didasarkan
pada ketergantungan langsung, seperti satu fungsi panggilan-
ing yang lain, atau fungsi menggunakan variabel. Tapi
ada kemungkinan penyebab lain afinitas. Afinitas
mungkin disebabkan karena sekelompok fungsi melakukan
membentuk operasi serupa. Pertimbangkan cuplikan ini
kode dari Junit 4.3.1:
Penegasan kelas publik {
public void assertTrue statis (Pesan string, kondisi boolean) {
jika (! kondisi)
gagal (pesan);
}

public void assertTrue statis (kondisi boolean) {


assertTrue (null, condition);

https://translate.googleusercontent.com/translate_f 90/365
3/12/2020 www.it-ebooks.info
}

public void assertFalse (pesan string, kondisi boolean) {


assertTrue (message,! condition);
}

public void assertFalse (kondisi boolean) {


assertFalse (null, condition);
}
...

Fungsi-fungsi ini memiliki afinitas konseptual yang kuat karena mereka memiliki penamaan yang sama
skema dan melakukan variasi tugas dasar yang sama. Fakta bahwa mereka saling memanggil adalah
sekunder. Bahkan jika mereka tidak melakukannya, mereka masih ingin berdekatan.

Pemesanan Vertikal
Secara umum kami ingin dependensi panggilan fungsi menunjuk ke arah bawah. Itu adalah,
fungsi yang dipanggil harus di bawah fungsi yang melakukan pemanggilan. 2 Ini menciptakan a
bagus mengalir turun modul kode sumber dari level tinggi ke level rendah.
Seperti dalam artikel surat kabar, kami mengharapkan konsep yang paling penting datang lebih dulu, dan
kami berharap mereka akan diekspresikan dengan jumlah polusi terkecil. Kami mengharapkan
detail tingkat rendah untuk menjadi yang terakhir. Ini memungkinkan kita untuk membaca sekilas file sumber, mendapatkan intisari dari

2. Ini adalah kebalikan dari bahasa seperti Pascal, C, dan C ++ yang menegakkan fungsi yang harus didefinisikan, atau setidaknya dideklarasikan,
sebelum digunakan.

www.it-ebooks.info

Halaman 116

Pemformatan Horizontal 85

beberapa fungsi pertama, tanpa harus membenamkan diri dalam detail. Listing 5-5 adalah
terorganisir dengan cara ini. Mungkin contoh yang lebih baik adalah Listing 15-5 di halaman 263, dan List-
ing 3-7 pada halaman 50.

Pemformatan Horizontal
Seberapa lebar garis seharusnya? Untuk menjawab itu, mari kita lihat seberapa lebar garis dalam pro
gram. Sekali lagi, kami memeriksa tujuh proyek yang berbeda. Gambar 5-2 menunjukkan distribusi
panjang garis dari semua tujuh proyek. Keteraturannya mengesankan, terutama di sekitar
45 karakter. Memang, setiap ukuran dari 20 hingga 60 mewakili sekitar 1 persen dari total
jumlah garis. Itu 40 persen! Mungkin 30 persen lainnya kurang dari 10 karakter
lebar. Ingat ini adalah skala log, jadi tampilan linear dari drop-off di atas 80 karakter
aktor benar-benar sangat signifikan. Programmer jelas lebih suka jalur pendek.

Gambar 5-2
Distribusi lebar garis Jawa

https://translate.googleusercontent.com/translate_f 91/365
3/12/2020 www.it-ebooks.info
Ini menunjukkan bahwa kita harus berusaha untuk menjaga garis pendek kita. Batas Hollerith lama dari
80 agak sewenang-wenang, dan saya tidak menentang garis yang merambat ke 100 atau bahkan 120. Tapi
di luar itu mungkin hanya ceroboh.
Saya dulu mengikuti aturan bahwa Anda seharusnya tidak perlu menggulir ke kanan. Tapi monitor
terlalu lebar untuk saat ini, dan programmer yang lebih muda dapat mengecilkan font yang begitu kecil

www.it-ebooks.info

Halaman 117

86 Bab 5: Memformat

bahwa mereka bisa mendapatkan 200 karakter di layar. Jangan lakukan itu. Saya pribadi menetapkan batas saya
jam 120.

Keterbukaan dan Kepadatan Horisontal


Kami menggunakan ruang putih horizontal untuk mengaitkan hal-hal yang sangat terkait dan terlepas
hal-hal yang terkait lebih lemah. Pertimbangkan fungsi berikut:
private void MeasLine (String line) {
lineCount ++;
int lineSize = line.length ();
totalChars + = lineSize;
lineWidthHistogram.addLine (lineSize, lineCount);
recordWidestLine (lineSize);
}

Saya mengepung operator penugasan dengan ruang putih untuk menonjolkan mereka. Tugas
pernyataan memiliki dua elemen utama dan berbeda: sisi kiri dan sisi kanan. Itu
ruang membuat pemisahan itu jelas.
Di sisi lain, saya tidak memberi spasi antara nama fungsi dan pembukaan
kurung. Ini karena fungsi dan argumennya terkait erat. Separat-
ing mereka membuat mereka tampak terpisah bukan siam. Saya memisahkan argumen di dalamnya
fungsi memanggil tanda kurung untuk menonjolkan koma dan menunjukkan bahwa argumennya adalah
terpisah.
Kegunaan lain untuk ruang putih adalah untuk menekankan prioritas operator.
Quadratic kelas publik {
root1 ganda statis publik (ganda a, ganda b, ganda c) {
determinan ganda = determinan (a, b, c);
return (-b + Math.sqrt (determinan)) / (2 * a);
}

root2 ganda publik statis (int a, int b, int c) {


determinan ganda = determinan (a, b, c);
return (-b - Math.sqrt (determinan)) / (2 * a);
}

determinan ganda statis privat (ganda a, ganda b, ganda c) {


return b * b - 4 * a * c;
}
}

Perhatikan seberapa baik persamaan itu dibaca. Faktor-faktor tidak memiliki ruang putih di antara mereka
karena mereka diutamakan tinggi. Istilah dipisahkan oleh ruang putih karena
tion dan pengurangan adalah prioritas yang lebih rendah.
Sayangnya, sebagian besar alat untuk memformat ulang kode tidak mengetahui prioritas
operator dan memaksakan jarak yang sama di seluruh. Jadi jarak yang halus seperti itu
yang ditunjukkan di atas cenderung hilang setelah Anda memformat ulang kode.

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 92/365
3/12/2020 www.it-ebooks.info

Halaman 118

Pemformatan Horizontal 87

Alignment Horizontal
Ketika saya adalah seorang programmer bahasa assembly, 3 saya menggunakan perataan horizontal untuk menonjolkan
struktur tertentu. Ketika saya mulai coding dalam C, C ++, dan akhirnya Java, saya terus mencoba
untuk berbaris semua nama variabel dalam satu set deklarasi, atau semua nilai dalam satu set tugas
pernyataan pemerintah. Kode saya mungkin terlihat seperti ini:
FitNesseExpediter kelas publik mengimplementasikan ResponseSender
{
Socket pribadi stopkontak;
input InputStream pribadi;
output OutputStream pribadi;
Permintaan pribadi permintaan;
Respon pribadi tanggapan;
konteks FitNesseContext pribadi;
terlindungi lama requestParsingTimeLimit;
panjang pribadi meminta Kemajuan;
panjang pribadi requestParsingDeadline;
boolean pribadi hasError;

FitNesseExpediter publik (Socket s,


Konteks FitNesseContext) melempar Pengecualian
{
this.context = konteks;
soket = s;
input = s.getInputStream ();
output = s.getOutputStream ();
requestParsingTimeLimit = 10000;
}

Namun, saya telah menemukan bahwa perataan semacam ini tidak berguna. Alinyemen tampaknya
tekankan hal-hal yang salah dan arahkan mata saya dari niat yang sebenarnya. Misalnya, dalam
daftar deklarasi di atas Anda tergoda untuk membaca daftar nama variabel dengan-
melihat tipe mereka. Demikian juga, dalam daftar pernyataan tugas Anda tergoda untuk melakukannya
lihat daftar nilai tanpa melihat operator penugasan. Untuk membuat masalah
lebih buruk lagi, alat pemformatan ulang otomatis biasanya menghilangkan perataan semacam ini.
Jadi, pada akhirnya, saya tidak melakukan hal semacam ini lagi. Saat ini saya lebih suka tidak selaras
deklarasi dan penugasan, seperti yang ditunjukkan di bawah ini, karena mereka menunjukkan definisi penting
efisiensi. Jika saya memiliki daftar panjang yang perlu disejajarkan, masalahnya adalah panjang daftar , bukan
kurangnya keselarasan. Panjang daftar deklarasi di FitNesseExpediter di bawah ini
menyarankan bahwa kelas ini harus dibagi.
FitNesseExpediter kelas publik mengimplementasikan ResponseSender
{
soket pribadi;
input InputStream pribadi;
output OutputStream pribadi;
permintaan Permintaan pribadi;

3. Siapa yang saya bercanda? Saya masih seorang programmer bahasa assembly. Anda dapat mengambil anak itu dari logam, tetapi Anda tidak bisa
keluarkan logam itu dari bocah itu!

www.it-ebooks.info

Halaman 119

88 Bab 5: Memformat

tanggapan Respons pribadi;


konteks FitNesseContext pribadi;
requestParsingTimeLimit lama yang dilindungi;
kemajuan permintaan pribadi panjang;
permintaan panjang pribadiParsingDeadline;
hasError boolean pribadi;

FitNesseExpediter publik (Socket s, konteks FitNesseContext) melempar Pengecualian

https://translate.googleusercontent.com/translate_f 93/365
3/12/2020 www.it-ebooks.info
{
this.context = konteks;
socket = s;
input = s.getInputStream ();
output = s.getOutputStream ();
requestParsingTimeLimit = 10000;
}

Lekukan
File sumber adalah hierarki seperti garis besar. Ada informasi yang berkaitan dengan
file secara keseluruhan, ke masing-masing kelas dalam file, ke metode di dalam kelas,
ke blok dalam metode, dan secara rekursif ke blok dalam blok. Setiap
tingkat hierarki ini adalah ruang lingkup di mana nama dapat dideklarasikan dan di mana deklarasi
tions dan pernyataan dieksekusi ditafsirkan.
Untuk membuat hierarki cakupan ini terlihat, kami indentasi baris kode sumber dalam pro
porsi ke posisi mereka di hiearchy. Pernyataan di tingkat file, seperti kebanyakan
deklarasi kelas, tidak berlekuk sama sekali. Metode-metode dalam suatu kelas diberi indentasi satu tingkat
di sebelah kanan kelas. Implementasi dari metode tersebut diimplementasikan satu tingkat ke
hak deklarasi metode. Implementasi blok dilaksanakan satu tingkat
di sebelah kanan blok berisi mereka, dan sebagainya.
Programmer sangat bergantung pada skema indentasi ini. Mereka secara visual berbaris
sebelah kiri untuk melihat ruang lingkup mereka muncul. Ini memungkinkan mereka dengan cepat melompati cakupan,
seperti implementasi jika atau sementara pernyataan, yang tidak relevan dengan saat ini
situasi. Mereka memindai kiri untuk deklarasi metode baru, variabel baru, dan bahkan baru
kelas. Tanpa lekukan, program akan secara virtual tidak dapat dibaca oleh manusia.
Pertimbangkan program-program berikut yang identik secara sintaksis dan semantik:
FitNesseServer kelas publik mengimplementasikan SocketServer {pribadi FitNesseContext
konteks; publik FitNesseServer (konteks FitNesseContext) {this.context =
konteks; } public void serve (Socket s) {melayani (s, 10000); } kekosongan publik
melayani (Socket s, long requestTimeout) {coba {FitNesseExpediter sender = baru
FitNesseExpediter (s, konteks);
sender.setRequestParsingTimeLimit (requestTimeout); sender.start (); }
catch (Exception e) {e.printStackTrace (); }}}

-----

FitNesseServer kelas publik mengimplementasikan SocketServer {


konteks FitNesseContext pribadi;

www.it-ebooks.info

Halaman 120

Pemformatan Horizontal 89

FitNesseServer publik (konteks FitNesseContext) {


this.context = konteks;
}

public void serve (Socket s) {


melayani (s, 10000);
}

public void serve (Socket s, long requestTimeout) {


coba {
Pengirim FitNesseExpediter = FitNesseExpediter baru (s, konteks);
sender.setRequestParsingTimeLimit (requestTimeout);
sender.start ();
}
catch (Exception e) {
e.printStackTrace ();
}
}
}

Mata Anda dapat dengan cepat melihat struktur file yang berlekuk. Anda hampir bisa secara instan
tempat variabel, konstruktor, pengakses, dan metode. Hanya perlu beberapa detik untuk
bayangkan bahwa ini adalah semacam ujung depan yang sederhana untuk sebuah soket, dengan batas waktu. Yang tidak lekukan
versi, bagaimanapun, hampir tidak bisa ditembus tanpa studi intensif.

Melanggar Indentasi. Terkadang tergoda untuk melanggar aturan indentasi


jika pernyataan, loop sementara pendek , atau fungsi pendek. Setiap kali saya menyerah pada ini
godaan, saya hampir selalu kembali dan memasukkan lekukan kembali. Jadi saya menghindari

https://translate.googleusercontent.com/translate_f 94/365
3/12/2020 www.it-ebooks.info
lingkup turun ke satu baris seperti ini:
CommentWidget kelas publik memperluas TextWidget
{
public String akhir statis REGEXP = "^ # [^ \ r \ n] * (?: (?: \ r \ n) | \ n | \ r)?";

CommentWidget publik (ParentWidget parent, String text) {super (parent, text);}


render String publik () melempar Pengecualian {return ""; }
}

Saya lebih suka memperluas dan membuat indentasi cakupan, seperti ini:
CommentWidget kelas publik meluas TextWidget {
public String akhir statis REGEXP = "^ # [^ \ r \ n] * (?: (?: \ r \ n) | \ n | \ r)?";

CommentWidget publik (ParentWidget induk, teks String) {


super (induk, teks);
}

public String render () throws Exception {


kembali "";
}
}

www.it-ebooks.info

Halaman 121

90 Bab 5: Memformat

Lingkup Dummy
Kadang-kadang tubuh sementara atau untuk pernyataan adalah boneka, seperti yang ditunjukkan di bawah ini. Saya tidak suka
struktur semacam ini dan mencoba menghindarinya. Ketika saya tidak bisa menghindarinya, saya memastikan itu
tubuh boneka diindentasi dengan benar dan dikelilingi oleh kawat gigi. Saya tidak bisa memberi tahu Anda caranya
banyak kali saya telah tertipu oleh titik koma diam-diam duduk di ujung sementara loop pada
baris yang sama. Kecuali Anda membuat koma terlihat oleh indentasi pada baris itu sendiri, itu
terlalu sulit untuk dilihat.
while (dis.read (buf, 0, readBufferSize)! = -1)
;

Aturan Tim
Judul bagian ini adalah permainan
kata-kata. Setiap programmer memiliki sendiri
aturan pemformatan favorit, tetapi jika ia berfungsi
dalam sebuah tim, maka aturan tim.
Tim pengembang harus setuju
pada gaya pemformatan tunggal, dan kemudian
setiap anggota tim itu harus menggunakan
gaya itu. Kami ingin perangkat lunak memiliki
gaya yang konsisten. Kami tidak ingin itu tampaknya telah ditulis oleh sekelompok orang yang tidak setuju
individu.
Ketika saya memulai proyek FitNesse pada tahun 2002, saya duduk bersama tim untuk bekerja
keluar gaya pengkodean. Ini memakan waktu sekitar 10 menit. Kami memutuskan di mana kami akan memasang kawat gigi kami,
berapa ukuran indentasi kita, bagaimana kita memberi nama kelas, variabel, dan metode, dan
sebagainya. Kemudian kami menyandikan aturan-aturan itu ke pemformat kode IDE kami dan macet
dengan mereka sejak saat itu. Ini bukan aturan yang saya sukai; mereka adalah aturan yang diputuskan oleh
tim. Sebagai anggota tim itu saya mengikuti mereka ketika menulis kode di FitNesse
proyek.
Ingat, sistem perangkat lunak yang baik terdiri dari sekumpulan dokumen yang membaca
baik. Mereka harus memiliki gaya yang konsisten dan halus. Pembaca harus bisa
percaya bahwa gerakan pemformatan yang dilihatnya dalam satu file sumber akan berarti sama
hal dalam diri orang lain. Hal terakhir yang ingin kita lakukan adalah menambahkan lebih banyak kompleksitas ke kode sumber
menulisnya dalam tumpukan gaya individu yang berbeda.

Aturan Pemformatan Paman Bob


Aturan yang saya gunakan secara pribadi sangat sederhana dan diilustrasikan oleh kode pada Listing 5-6.
https://translate.googleusercontent.com/translate_f 95/365
3/12/2020 www.it-ebooks.info
Pertimbangkan ini contoh bagaimana kode membuat dokumen standar pengkodean terbaik.

www.it-ebooks.info

Halaman 122

Aturan Pemformatan Paman Bob 91

Listing 5-6
CodeAnalyzer.java
CodeAnalyzer kelas publik mengimplementasikan JavaFileAnalysis {
private int lineCount;
maxLineWidth pribadi int;
private int widestLineNumber;
private LineWidthHistogram lineWidthHistogram;
TotalChars pribadi int;

CodeAnalyzer publik () {
lineWidthHistogram = LineWidthHistogram baru ();
}

public static List <File> findJavaFiles (File parentDirectory) {


Daftar file <File> = ArrayList baru <File> ();
findJavaFiles (parentDirectory, file);
mengembalikan file;
}

private static void findJavaFiles (File parentDirectory, Daftar file <File>) {


untuk (File file: parentDirectory.listFiles ()) {
if (file.getName (). endsWith (". java"))
files.add (file);
lain jika (file.isDirectory ())
findJavaFiles (file, file);
}
}

public void analyFile (File javaFile) melempar Exception {


BufferedReader br = BufferedReader baru (FileReader baru (javaFile));
Garis tali;
while ((line = br.readLine ())! = null)
MeasLine (baris);
}

private void MeasLine (String line) {


lineCount ++;
int lineSize = line.length ();
totalChars + = lineSize;
lineWidthHistogram.addLine (lineSize, lineCount);
recordWidestLine (lineSize);
}

private void recordWidestLine (int lineSize) {


if (lineSize> maxLineWidth) {
maxLineWidth = lineSize;
widestLineNumber = lineCount;
}
}

public int getLineCount () {


return lineCount;
}

public int getMaxLineWidth () {


mengembalikan maxLineWidth;
}

www.it-ebooks.info

Halaman 123

https://translate.googleusercontent.com/translate_f 96/365
3/12/2020 www.it-ebooks.info

92 Bab 5: Memformat

Listing 5-6 (lanjutan)


CodeAnalyzer.java
public int getWidestLineNumber () {
kembalikan widestLineNumber;
}

LineWidthHistogram publik getLineWidthHistogram () {


return lineWidthHistogram;
}

getMeanLineWidth ganda publik () {


return (double) totalChars / lineCount;
}

public int getMedianLineWidth () {


Integer [] sortWidths = getSortedWidths ();
int kumulativeLineCount = 0;
for (int width: sortWidths) {
kumulativeLineCount + = lineCountForWidth (lebar);
if (cumulativeLineCount> lineCount / 2)
kembali lebar;
}
throw new Error ("Tidak bisa sampai di sini");
}

private int lineCountForWidth (lebar int) {


return lineWidthHistogram.getLinesforWidth (width) .size ();
}

Integer pribadi [] getSortedWidths () {


Setel <Integer> widths = lineWidthHistogram.getWidths ();
Integer [] sortWidths = (widths.toArray (Integer baru [0]));
Arrays.sort (SortWidths);
mengembalikan SortedWidths;
}
}

www.it-ebooks.info

Halaman 124

6
https://translate.googleusercontent.com/translate_f 97/365
3/12/2020 www.it-ebooks.info

Objek dan Struktur Data

Ada alasan mengapa kami merahasiakan variabel kami. Kami tidak ingin orang lain bergantung
pada mereka. Kami ingin menjaga kebebasan untuk mengubah jenis atau implementasi mereka dengan hati-hati
atau impuls. Lalu, mengapa, begitu banyak programmer secara otomatis menambahkan getter dan setter
ke objek mereka, mengekspos variabel pribadi mereka seolah-olah mereka publik?

Abstraksi data
Pertimbangkan perbedaan antara Listing 6-1 dan Listing 6-2. Keduanya mewakili data a
titik di pesawat Cartesian. Namun yang satu memaparkan implementasinya dan yang lainnya
benar-benar menyembunyikannya.
93

www.it-ebooks.info

Halaman 125

94 Bab 6: Objek dan Struktur Data

Listing 6-1
Titik konkret
Point kelas publik {
ganda publik x;
publik ganda y;
}

Listing 6-2
Poin Abstrak
Point antarmuka publik {
getX ganda ();
double getY ();
membatalkan setCartesian (double x, double y);
getR ganda ();
gandakan getTheta ();
membatalkan setPolar (r ganda, theta ganda);
}

Hal yang indah tentang Listing 6-2 adalah bahwa tidak ada cara Anda dapat mengetahui apakah
implementasi dalam koordinat persegi panjang atau kutub. Mungkin bukan keduanya! Namun demikian
antarmuka masih jelas merupakan suatu struktur data.
Tapi itu mewakili lebih dari sekedar struktur data. Metode menegakkan akses
kebijakan. Anda dapat membaca masing-masing koordinat secara independen, tetapi Anda harus mengatur
nates bersama sebagai operasi atom.
Listing 6-1, di sisi lain, sangat jelas diimplementasikan dalam koordinat persegi panjang,
dan itu memaksa kita untuk memanipulasi koordinat tersebut secara independen. Ini memperlihatkan implementasi-
tion. Memang, itu akan mengekspos implementasi bahkan jika variabelnya adalah pribadi dan kita
menggunakan getter variabel tunggal dan setter.
Menyembunyikan implementasi bukan hanya masalah menempatkan lapisan fungsi di antara keduanya

https://translate.googleusercontent.com/translate_f 98/365
3/12/2020 www.it-ebooks.info
variabel. Menyembunyikan implementasi adalah tentang abstraksi! Kelas tidak hanya sederhana
dorong variabel-variabelnya melalui getter dan setter. Melainkan menampilkan antarmuka abstrak
yang memungkinkan penggunanya memanipulasi esensi data, tanpa harus mengetahuinya
penerapan.
Pertimbangkan Listing 6-3 dan Listing 6-4. Yang pertama menggunakan istilah konkret untuk berkomunikasi
tingkat bahan bakar kendaraan, sedangkan yang kedua melakukannya dengan abstraksi persentase.
Dalam kasus konkret Anda bisa sangat yakin bahwa ini hanyalah penentu variabel. Dalam
kasus abstrak Anda tidak memiliki petunjuk sama sekali tentang bentuk data.

Listing 6-3
Kendaraan Beton
kendaraan antarmuka publik {
gandakan getFuelTankCapacityInGallons ();
gandakan getGallonsOfGasoline ();
}

www.it-ebooks.info

Halaman 126

Data / Objek Anti-Simetri 95

Listing 6-4
Kendaraan Abstrak
kendaraan antarmuka publik {
gandakan getPercentFuelRemaining ();
}

Dalam kedua kasus di atas, pilihan kedua lebih disukai. Kami tidak ingin mengekspos
detail data kami. Sebaliknya kami ingin mengekspresikan data kami secara abstrak. Ini bukan
hanya dicapai dengan menggunakan antarmuka dan / atau getter dan setter. Kebutuhan pikiran serius
untuk dimasukkan ke dalam cara terbaik untuk mewakili data yang berisi objek. Pilihan terburuk adalah
untuk menambahkan getter dan setter.

Data / Objek Anti-Simetri


Dua contoh ini menunjukkan perbedaan antara objek dan struktur data. Objek bersembunyi
data mereka di belakang abstraksi dan memaparkan fungsi yang beroperasi pada data itu. Struktur data
untuk mengekspos data mereka dan tidak memiliki fungsi yang berarti. Kembali dan baca lagi.
Perhatikan sifat kedua definisi tersebut. Mereka adalah lawan virtual. Ini
perbedaan mungkin tampak sepele, tetapi memiliki implikasi yang luas.
Pertimbangkan, misalnya, contoh bentuk prosedural dalam Listing 6-5. The Geometri
kelas beroperasi pada tiga kelas bentuk. Kelas bentuk adalah struktur data sederhana
tanpa perilaku. Semua perilaku ada di kelas Geometri .

Listing 6-5
Bentuk Prosedural
Square kelas publik {
public Point topLeft;
sisi ganda publik;
}

Rectangle kelas publik {


public Point topLeft;
tinggi ganda publik;
lebar ganda publik;
}

Lingkaran kelas publik {


pusat Point publik;
radius ganda publik;
}

Geometri kelas publik {


PI ganda publik akhir = 3.141592653589793;

area ganda publik (Bentuk objek) melempar NoSuchShapeException


{
if (bentuk instance dari Square) {
Kotak s = (Kotak) bentuk;
return s.side * s.side;
}

https://translate.googleusercontent.com/translate_f 99/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 127

96 Bab 6: Objek dan Struktur Data

Listing 6-5 (lanjutan)


Bentuk Prosedural
lain jika (bentuk instance dari Rectangle) {
Rectangle r = (Rectangle) shape;
return r.height * r.width;
}
lain jika (bentuk instance dari Lingkaran) {
Lingkaran c = (Lingkaran) bentuk;
mengembalikan PI * c.radius * c.radius;
}
melempar NoSuchShapeException baru ();
}
}

Pemrogram berorientasi objek mungkin mengerutkan hidung mereka di ini dan mengeluh itu
prosedural — dan mereka benar. Tapi ejekan itu mungkin tidak dijamin. Pertimbangkan apa?
akan terjadi jika fungsi perimeter () ditambahkan ke Geometri . Kelas bentuk akan
jangan terpengaruh! Kelas lain yang bergantung pada bentuk juga tidak akan terpengaruh!
Di sisi lain, jika saya menambahkan bentuk baru, saya harus mengubah semua fungsi dalam Geometri untuk
menghadapinya. Sekali lagi, baca itu. Perhatikan bahwa kedua kondisi itu diametris
menentang.
Sekarang perhatikan solusi berorientasi objek di Listing 6-6. Di sini metode area () adalah
polimorfik. Tidak perlu kelas Geometri . Jadi kalau saya tambahkan bentuk baru, tidak ada yang ada
fungsi terpengaruh, tetapi jika saya menambahkan fungsi baru semua bentuk harus diubah! 1

Listing 6-6
Bentuk Polimorfik
Square public class mengimplementasikan Shape {
private Point topLeft;
sisi ganda pribadi;

area ganda publik () {


sisi belakang * sisi;
}
}

Rectangle kelas publik mengimplementasikan Shape {


private Point topLeft;
ketinggian ganda pribadi;
lebar ganda pribadi;

area ganda publik () {


kembali tinggi * lebar;
}
}

1. Ada beberapa cara untuk mengetahui desainer berorientasi objek yang berpengalaman: V ISITOR , atau dual-dispatch, untuk
contoh. Tetapi teknik ini membawa biaya sendiri dan umumnya mengembalikan struktur ke program prosedural.

www.it-ebooks.info

Halaman 128

https://translate.googleusercontent.com/translate_f 100/365
3/12/2020 www.it-ebooks.info
Hukum Demeter 97

Listing 6-6 (lanjutan)


Bentuk Polimorfik
Lingkaran kelas publik mengimplementasikan Shape {
pusat Titik pribadi;
radius ganda pribadi;
PI ganda publik akhir = 3.141592653589793;

area ganda publik () {


return PI * radius * radius;
}
}

Sekali lagi, kita melihat sifat pelengkap dari kedua definisi ini; mereka virtual
berlawanan! Ini memaparkan dikotomi mendasar antara objek dan struktur data:

Kode prosedural (kode menggunakan struktur data) membuatnya mudah untuk menambahkan fungsi baru tanpa
mengubah struktur data yang ada. Kode OO, di sisi lain, membuatnya mudah untuk ditambahkan
kelas baru tanpa mengubah fungsi yang ada.

Pelengkap ini juga benar:


Kode prosedural membuatnya sulit untuk menambahkan struktur data baru karena semua fungsi harus
perubahan. Kode OO membuatnya sulit untuk menambahkan fungsi baru karena semua kelas harus berubah.

Jadi, hal-hal yang sulit untuk OO mudah untuk prosedur, dan hal-hal yang sulit
sulit untuk prosedur mudah untuk OO!
Dalam sistem kompleks mana pun akan ada saatnya kita ingin menambahkan data baru
jenis daripada fungsi baru. Untuk kasus ini objek dan OO paling tepat. Di
sisi lain, akan ada saatnya kita ingin menambahkan fungsi baru sebagai lawan
untuk tipe data. Dalam hal itu kode prosedural dan struktur data akan lebih sesuai.
Pemrogram yang matang tahu bahwa gagasan bahwa segala sesuatu adalah objek adalah mitos . Beberapa-
kali Anda benar - benar ingin struktur data sederhana dengan prosedur beroperasi pada mereka.

Hukum Demeter
Ada heuristik terkenal yang disebut Hukum Demeter 2 yang mengatakan modul tidak boleh
tahu tentang jeroan benda yang dimanipulasi. Seperti yang kita lihat di bagian terakhir, objek
sembunyikan data mereka dan tampilkan operasi. Ini berarti bahwa suatu objek tidak boleh mengekspos objeknya
struktur internal melalui pengakses karena untuk melakukannya adalah untuk mengekspos, daripada menyembunyikan, itu
struktur internal.
Lebih tepatnya, Hukum Demeter mengatakan bahwa metode f dari kelas C seharusnya hanya memanggil
metode ini:

•C
• Objek yang dibuat oleh f

2. http://en.wikipedia.org/wiki/Law_of_Demete r

www.it-ebooks.info

Halaman 129

98 Bab 6: Objek dan Struktur Data

• Suatu objek diteruskan sebagai argumen ke f


• Objek yang dipegang dalam variabel instan dari C

Metode tidak boleh memanggil metode pada objek yang dikembalikan oleh salah satu
fungsi yang diizinkan. Dengan kata lain, berbicara dengan teman, bukan dengan orang asing.
Kode 3 berikut ini tampaknya melanggar Hukum Demeter (antara lain)
karena ia memanggil fungsi getScratchDir () pada nilai balik dari getOptions () lalu
panggilan getAbsolutePath () pada nilai kembali getScratchDir () .

final String outputDir = ctxt.getOptions (). getScratchDir (). getAbsolutePath ();

Kereta Karam
Kode semacam ini sering disebut kereta karam karena terlihat seperti sekelompok kereta yang digabungkan
mobil. Rantai panggilan seperti ini umumnya dianggap gaya ceroboh dan seharusnya

https://translate.googleusercontent.com/translate_f 101/365
3/12/2020 www.it-ebooks.info
dihindari [G36]. Biasanya yang terbaik adalah membaginya sebagai berikut:
Opsi opts = ctxt.getOptions ();
File scratchDir = opts.getScratchDir ();
final String outputDir = scratchDir.getAbsolutePath ();

Apakah kedua cuplikan kode ini


tions dari Hukum Demeter? Pasti
modul yang mengandung tahu bahwa
objek ctxt berisi opsi, yang
menyimpan direktori awal, yang memiliki
jalan absolut. Itu banyak pengetahuan
untuk satu fungsi tahu. Panggilan
fungsi tahu cara menavigasi
banyak objek yang berbeda.
Apakah ini merupakan pelanggaran Demeter tergantung pada apakah ctxt , Opsi , dan
ScratchDir adalah objek atau struktur data. Jika mereka adalah objek, maka struktur internal mereka
harus disembunyikan daripada diekspos, dan pengetahuan jeroan mereka adalah biola yang jelas
hukum Demeter. Di sisi lain, jika ctxt , Opsi , dan ScratchDir adil
struktur data tanpa perilaku, maka mereka secara alami mengekspos struktur internal mereka, dan sebagainya
Demeter tidak berlaku.
Penggunaan fungsi pengaksesor membingungkan masalah ini. Jika kode tersebut ditulis sebagai
rendah, maka kita mungkin tidak akan bertanya tentang pelanggaran Demeter.

keluaran akhir StringDir = ctxt.options.scratchDir.absolutePath;

Masalah ini akan jauh lebih membingungkan jika struktur data hanya memiliki variabel publik
dan tidak ada fungsi, sedangkan objek memiliki variabel pribadi dan fungsi publik. Namun,

3. Ditemukan di suatu tempat dalam kerangka apache.

www.it-ebooks.info

Halaman 130

Hukum Demeter 99

ada kerangka kerja dan standar (misalnya, "kacang") yang menuntut bahkan data sederhana
struktur memiliki pengakses dan mutator.

Hibrida
Kebingungan ini kadang-kadang menyebabkan struktur hibrida malang yang setengah objek dan
setengah struktur data. Mereka memiliki fungsi yang melakukan hal-hal penting, dan mereka juga memiliki keduanya
variabel publik atau aksesor publik dan mutator yang, untuk semua maksud dan tujuan, dibuat
variabel swasta publik, menggoda fungsi eksternal lainnya untuk menggunakan variabel tersebut
cara program prosedural akan menggunakan struktur data. 4
Hibrida seperti itu membuat sulit untuk menambahkan fungsi baru tetapi juga membuatnya sulit untuk menambahkan data baru
struktur. Mereka adalah yang terburuk dari kedua dunia. Hindari membuatnya. Mereka menunjukkan a
desain yang kacau yang penulisnya tidak yakin — atau lebih buruk, tidak tahu — apakah mereka membutuhkannya
perlindungan dari fungsi atau tipe.

Menyembunyikan Struktur
Bagaimana jika ctxt , opsi , dan scratchDir adalah objek dengan perilaku nyata? Lalu, karena
objek seharusnya menyembunyikan struktur internal mereka, kita seharusnya tidak dapat menavigasi
melalui mereka. Lalu bagaimana kita mendapatkan jalur absolut dari direktori awal?
ctxt.getAbsolutePathOfScratchDirectoryOption ();

atau
ctx.getScratchDirectoryOption (). getAbsolutePath ()

Opsi pertama dapat menyebabkan ledakan metode pada objek ctxt. Pra- kedua
jumlah yang getScratchDirectoryOption () mengembalikan struktur data, bukan objek. Tidak juga
Opsi terasa enak.
Jika ctxt adalah objek, kita harus mengatakannya untuk melakukan sesuatu; kita seharusnya tidak menanyakannya
tentang internalnya. Jadi mengapa kita menginginkan jalur absolut dari direktori awal? Apa
apakah kita akan melakukan itu? Pertimbangkan kode ini dari (banyak baris lebih jauh ke dalam)
modul yang sama:

https://translate.googleusercontent.com/translate_f 102/365
3/12/2020 www.it-ebooks.info
String outFile = outputDir + "/" + className.replace ('.', '/') + ".Class";
FileOutputStream fout = FileOutputStream baru (outFile);
BufferedOutputStream bos = BufferedOutputStream baru (demam);

Campuran berbagai level detail [G34] [G6] agak menyusahkan. Titik, garis miring,
ekstensi file, dan objek File tidak boleh dicampur secara sembarangan, dan dicampur
dengan kode terlampir. Mengabaikan itu, bagaimanapun, kita melihat bahwa maksud mendapatkan hubungan tersebut
lute path dari direktori scratch adalah membuat file scratch dari nama yang diberikan.

4. Ini kadang-kadang disebut Fitur Envy dari [Refactoring].

www.it-ebooks.info

Halaman 131

100 Bab 6: Objek dan Struktur Data

Jadi, bagaimana jika kita memberi tahu objek ctxt untuk melakukan ini?
BufferedOutputStream bos = ctxt.createScratchFileStream (classFileName);

Itu sepertinya hal yang masuk akal untuk dilakukan objek! Ini memungkinkan ctxt untuk menyembunyikannya
internal dan mencegah fungsi saat ini dari harus melanggar Hukum Demeter oleh
menavigasi melalui objek yang seharusnya tidak diketahui.

Objek Transfer Data


Bentuk klasik dari struktur data adalah kelas dengan variabel publik dan tidak ada fungsi
tions. Ini kadang-kadang disebut objek transfer data, atau DTO. DTO adalah struktur yang sangat berguna
mendatang, terutama ketika berkomunikasi dengan database atau mem-parsing pesan dari soket,
dan seterusnya. Mereka sering menjadi yang pertama dalam serangkaian tahap terjemahan yang mengubah data mentah
dalam database menjadi objek dalam kode aplikasi.
Yang agak lebih umum adalah bentuk "kacang" yang ditunjukkan pada Listing 6-7. Kacang memiliki pribadi
variabel yang dimanipulasi oleh getter dan setter. Kuasi-enkapsulasi kacang tampaknya
membuat beberapa OO puritan merasa lebih baik tetapi biasanya tidak memberikan manfaat lain.

Listing 6-7
address.java
Alamat kelas publik {
jalan pribadi String;
private String streetExtra;
kota String pribadi;
keadaan String pribadi;
zip string pribadi;

Alamat publik (String street, String streetExtra,


Kota string, String state, String zip) {
this.street = street;
this.streetExtra = streetExtra;
this.city = kota;
this.state = state;
this.zip = zip;
}

public String getStreet () {


jalan kembali;
}

public String getStreetExtra () {


kembali streetExtra;
}

public String getCity () {


kota kembali;
}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 103/365
3/12/2020 www.it-ebooks.info

Halaman 132

Bibliografi 101

Listing 6-7 (lanjutan)


address.java
public String getState () {
negara kembali;
}

public String getZip () {


kembali zip;
}
}

Rekaman Aktif
Rekaman Aktif adalah bentuk khusus DTO. Mereka adalah struktur data dengan publik (atau
diakses) variabel; tetapi mereka biasanya memiliki metode navigasi seperti simpan dan temukan . Biasanya
Sebenarnya Rekaman Aktif ini adalah terjemahan langsung dari tabel database, atau data lainnya
sumber.
Sayangnya kami sering menemukan bahwa pengembang mencoba memperlakukan struktur data ini seolah-olah
mereka adalah objek dengan memasukkan metode aturan bisnis di dalamnya. Ini aneh karena itu
menciptakan hibrid antara struktur data dan objek.
Solusinya, tentu saja, adalah memperlakukan Rekaman Aktif sebagai struktur data dan membuat
objek terpisah yang berisi aturan bisnis dan yang menyembunyikan data internal mereka (yang
mungkin hanya contoh Rekaman Aktif).

Kesimpulan
Objek mengekspos perilaku dan menyembunyikan data. Ini membuatnya mudah untuk menambahkan objek jenis baru
tanpa mengubah perilaku yang ada. Itu juga membuatnya sulit untuk menambahkan perilaku baru ke yang sudah ada
benda. Struktur data mengekspos data dan tidak memiliki perilaku yang signifikan. Ini membuatnya mudah
menambahkan perilaku baru ke struktur data yang ada tetapi membuatnya sulit untuk menambahkan struktur data baru
untuk fungsi yang ada.
Dalam sistem apa pun kita terkadang menginginkan fleksibilitas untuk menambahkan tipe data baru, dan
jadi kami lebih suka objek untuk bagian sistem itu. Lain kali kita akan menginginkan fleksibilitas
tambahkan perilaku baru, dan pada bagian sistem itu kami lebih suka tipe dan prosedur data.
Pengembang perangkat lunak yang baik memahami masalah ini tanpa prasangka dan memilih
pendekatan yang terbaik untuk pekerjaan yang ada.

Bibliografi
[Refactoring]: Refactoring: Meningkatkan Desain Kode yang Ada , Martin Fowler et al.,
Addison-Wesley, 1999.

www.it-ebooks.info

Halaman 133

https://translate.googleusercontent.com/translate_f 104/365
3/12/2020 www.it-ebooks.info

halaman ini sengaja dibiarkan kosong

www.it-ebooks.info

Halaman 134

7
Penanganan Kesalahan
oleh Michael Feathers

https://translate.googleusercontent.com/translate_f 105/365
3/12/2020 www.it-ebooks.info

Mungkin aneh jika ada bagian tentang penanganan kesalahan dalam buku tentang kode bersih. Kesalahan
penanganan hanyalah salah satu hal yang harus kita semua lakukan saat memprogram. Masukan bisa
abnormal dan perangkat bisa gagal. Singkatnya, hal-hal bisa salah, dan ketika mereka melakukannya, kita sebagai pro-
grammers bertanggung jawab untuk memastikan bahwa kode kami melakukan apa yang perlu dilakukan.
Namun, koneksi ke kode bersih harus jelas. Banyak basis kode yang
didominasi oleh penanganan kesalahan. Ketika saya katakan mendominasi, saya tidak bermaksud bahwa kesalahan itu terjadi.
dling adalah semua yang mereka lakukan. Maksud saya hampir tidak mungkin untuk melihat apa yang dilakukan kode
karena semua penanganan kesalahan tersebar. Penanganan kesalahan penting, tetapi jika itu
mengaburkan logika, itu salah .
Dalam bab ini saya akan menguraikan sejumlah teknik dan pertimbangan yang dapat Anda gunakan
untuk menulis kode yang bersih dan tangguh — kode yang menangani kesalahan dengan anggun dan gaya.

103

www.it-ebooks.info

Halaman 135

104 Bab 7: Penanganan Kesalahan

Gunakan Pengecualian Daripada Mengembalikan Kode


Kembali di masa lalu yang jauh ada banyak bahasa yang tidak memiliki pengecualian. Pada mereka
bahasa teknik untuk penanganan dan pelaporan kesalahan terbatas. Anda juga menetapkan
flag kesalahan atau mengembalikan kode kesalahan yang bisa diperiksa oleh penelepon. Kode dalam Listing 7-1
menggambarkan pendekatan ini.

Listing 7-1
DeviceController.java
DeviceController kelas publik {
...
membatalkan publik sendShutDown () {
DeviceHandle handle = getHandle (DEV1);
// Periksa keadaan perangkat
if (handle! = DeviceHandle.INVALID) {
// Simpan status perangkat ke bidang rekaman
recoverveDeviceRecord (handle);
// Jika tidak ditangguhkan, tutup
if (record.getStatus ()! = DEVICE_SUSPENDED) {
pauseDevice (handle);
clearDeviceWorkQueue (handle);
closeDevice (pegangan);
} lain {
logger.log ("Perangkat ditangguhkan. Tidak dapat dimatikan");
}
} lain {
logger.log ("Pegangan tidak valid untuk:" + DEV1.toString ());
}
}
...
}

Masalah dengan pendekatan ini adalah bahwa mereka mengacaukan penelepon. Penelepon harus
periksa kesalahan segera setelah panggilan. Sayangnya, mudah untuk dilupakan. Untuk alasan ini
anak lebih baik untuk melemparkan pengecualian ketika Anda menemukan kesalahan. Kode panggilannya adalah
pembersih. Logikanya tidak dikaburkan oleh penanganan kesalahan.
Listing 7-2 menunjukkan kode setelah kami memilih untuk melemparkan pengecualian dalam metode itu
dapat mendeteksi kesalahan.

Listing 7-2
DeviceController.java (dengan pengecualian)

https://translate.googleusercontent.com/translate_f 106/365
3/12/2020 www.it-ebooks.info
DeviceController kelas publik {
...

membatalkan publik sendShutDown () {


coba {
tryToShutDown ();
} catch (DeviceShutDownError e) {
logger.log (e);
}
}

www.it-ebooks.info

Halaman 136

Tulis Pernyataan Coba-Tangkap-Akhirnya Anda Pertama 105

Listing 7-2 (lanjutan)


DeviceController.java (dengan pengecualian)
void pribadi tryToShutDown () melempar DeviceShutDownError {
DeviceHandle handle = getHandle (DEV1);
Catatan DeviceRecord = recoverveDeviceRecord (handle);

pauseDevice (handle);
clearDeviceWorkQueue (handle);
closeDevice (pegangan);
}

private DeviceHandle getHandle (DeviceID id) {


...
melemparkan DeviceShutDownError baru ("Pegangan tidak valid untuk:" + id.toString ());
...
}

...
}

Perhatikan seberapa bersih itu. Ini bukan hanya masalah estetika. Kode lebih baik
karena dua masalah yang kusut, algoritme untuk shutdown dan kesalahan perangkat
dling, sekarang terpisah. Anda dapat melihat masing-masing masalah tersebut dan memahaminya
secara mandiri.

Tulis Pernyataan Coba-Tangkap-Akhirnya Anda Pertama


Salah satu hal paling menarik tentang pengecualian adalah mereka menentukan ruang lingkup di dalam Anda
program. Ketika Anda mengeksekusi kode di bagian coba dari pernyataan try - catch - akhirnya , Anda
menyatakan bahwa eksekusi dapat dibatalkan pada titik mana pun dan kemudian melanjutkan pada tangkapan .
Di satu sisi, coba blok seperti transaksi. Hasil tangkapan Anda harus meninggalkan program Anda di a
keadaan konsisten, apa pun yang terjadi dalam percobaan . Untuk alasan ini adalah praktik yang baik untuk melakukannya
Mulailah dengan pernyataan try - catch - akhirnya ketika Anda menulis kode yang bisa melempar
pengecualian. Ini membantu Anda menentukan apa yang seharusnya diharapkan oleh pengguna kode itu, apa pun yang terjadi
salah dengan kode yang dieksekusi di coba .
Mari kita lihat sebuah contoh. Kita perlu menulis beberapa kode yang mengakses file dan membaca
beberapa objek serial.
Kami mulai dengan unit test yang menunjukkan bahwa kami akan mendapatkan pengecualian ketika file tidak ada:
@Test (diharapkan = StorageException.class)
public void recoverSectionShouldThrowOnInvalidFileName () {
sectionStore.retrieveSection ("tidak valid - file");
}

Tes mendorong kami untuk membuat rintisan ini:


Daftar publik <RecordedGrip> recoverveSection (String sectionName) {
// dummy return sampai kita memiliki implementasi nyata
kembalikan ArrayList baru <RecordedGrip> ();
}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 107/365
3/12/2020 www.it-ebooks.info

Halaman 137

106 Bab 7: Penanganan Kesalahan

Tes kami gagal karena tidak melempar pengecualian. Selanjutnya, kami mengubah implementasi kami
sehingga mencoba mengakses file yang tidak valid. Operasi ini melempar pengecualian:
Daftar publik <RecordedGrip> recoverveSection (String sectionName) {
coba {
Aliran FileInputStream = FileInputStream baru (sectionName)
} catch (Exception e) {
melempar StorageException baru ("kesalahan pengambilan", e);
}
kembalikan ArrayList baru <RecordedGrip> ();
}

Tes kami lulus sekarang karena kami telah menangkap pengecualian. Pada titik ini, kita dapat memperbaiki
untuk. Kita dapat mempersempit jenis pengecualian yang kita tangkap agar sesuai dengan jenis yang sebenarnya
dilempar dari konstruktor FileInputStream : FileNotFoundException :
Daftar publik <RecordedGrip> recoverveSection (String sectionName) {
coba {
Aliran FileInputStream = FileInputStream (sectionName) baru;
stream.close ();
} catch (FileNotFoundException e) {
melempar StorageException baru ("kesalahan pengambilan", e);
}
kembalikan ArrayList baru <RecordedGrip> ();
}

Sekarang kita telah mendefinisikan ruang lingkup dengan struktur try - catch , kita dapat menggunakan TDD untuk membangun
sisa dari logika yang kita butuhkan. Logika itu akan ditambahkan di antara penciptaan
FileInputStream dan tutup , dan dapat berpura-pura bahwa tidak ada yang salah.
Cobalah untuk menulis tes yang memaksa pengecualian, dan kemudian tambahkan perilaku ke pawang Anda ke satelit.
isfy tes Anda. Ini akan menyebabkan Anda untuk membangun cakupan transaksi pada blok coba terlebih dahulu dan
akan membantu Anda mempertahankan sifat transaksi dari ruang lingkup itu.

Gunakan Pengecualian yang Tidak Dicentang


Perdebatan selesai. Selama bertahun-tahun programmer Java berdebat tentang manfaat dan kewajiban
ikatan pengecualian yang diperiksa. Ketika pengecualian yang dicentang diperkenalkan di versi pertama
Jawa, mereka sepertinya ide yang bagus. Tanda tangan dari setiap metode akan mendaftar semua
pengecualian yang bisa diteruskan ke peneleponnya. Selain itu, pengecualian ini adalah bagian dari tipe tersebut
dari metode ini. Kode Anda secara harfiah tidak akan dikompilasi jika tanda tangan tidak sesuai dengan apa yang Anda miliki
kode bisa dilakukan.
Pada saat itu, kami berpikir bahwa pengecualian yang diperiksa adalah ide yang bagus; dan ya, mereka bisa
menghasilkan beberapa manfaat. Namun, jelas sekarang bahwa mereka tidak diperlukan untuk produksi
perangkat lunak yang kuat. C # tidak memiliki pengecekan pengecualian, dan meskipun ada upaya yang berani, C ++
juga tidak. Python atau Ruby juga tidak. Namun dimungkinkan untuk menulis perangkat lunak yang kuat di semua
dari bahasa-bahasa ini. Karena itu masalahnya, kita harus memutuskan — benar-benar — apakah diperiksa
pengecualian layak harganya.

www.it-ebooks.info

Halaman 138

Tentukan Kelas Pengecualian dalam Ketentuan Kebutuhan Penelepon 107

Berapa harga? Harga pengecualian diperiksa adalah Open / Closed Prinsip 1 pelanggaran.
Jika Anda melemparkan pengecualian yang diperiksa dari suatu metode dalam kode Anda dan hasil tangkapannya adalah tiga level
di atas, Anda harus menyatakan bahwa pengecualian dalam tanda tangan setiap metode antara Anda dan
hasil tangkapan . Ini berarti bahwa perubahan pada level rendah dari perangkat lunak dapat memaksa tanda tangan
perubahan pada banyak level yang lebih tinggi. Modul yang diubah harus dibangun kembali dan dipekerjakan kembali,
meskipun tidak ada yang mereka pedulikan berubah.

https://translate.googleusercontent.com/translate_f 108/365
3/12/2020 www.it-ebooks.info
Pertimbangkan hirarki panggilan sistem besar. Fungsi pada fungsi panggilan atas
di bawahnya, yang memanggil lebih banyak fungsi di bawahnya, ad infinitum. Sekarang katakanlah salah satu
fungsi tingkat terendah dimodifikasi sedemikian rupa sehingga harus mengeluarkan pengecualian. Jika itu
pengecualian diperiksa, maka tanda tangan fungsi harus menambahkan klausa pelemparan . Tapi ini
berarti bahwa setiap fungsi yang memanggil fungsi kami yang dimodifikasi juga harus dimodifikasi
tangkap pengecualian baru atau untuk menambahkan klausa lemparan yang sesuai dengan tanda tangannya. Iklan
infinitum. Hasil bersihnya adalah serangkaian perubahan yang bekerja dari tingkat terendah
dari perangkat lunak ke yang tertinggi! Enkapsulasi rusak karena semua fungsi di jalur
lemparan harus tahu tentang detail pengecualian tingkat rendah itu. Mengingat bahwa tujuan
pengecualian adalah untuk memungkinkan Anda menangani kesalahan dari jauh, itu memalukan bahwa
tions memecahkan enkapsulasi dengan cara ini.
Pengecualian yang dicek terkadang dapat berguna jika Anda menulis perpustakaan kritis: Anda
harus menangkap mereka. Tetapi dalam pengembangan aplikasi umum biaya ketergantungan lebih besar daripada itu
keuntungan-keuntungan.

Berikan Konteks dengan Pengecualian


Setiap pengecualian yang Anda lempar harus memberikan konteks yang cukup untuk menentukan sumber dan
lokasi kesalahan. Di Jawa, Anda bisa mendapatkan jejak tumpukan dari pengecualian apa pun; Namun, tumpukan
trace tidak dapat memberi tahu Anda maksud operasi yang gagal.
Buat pesan kesalahan informatif dan sampaikan bersama dengan pengecualian Anda. Men-
tion operasi yang gagal dan jenis kegagalan. Jika Anda masuk aplikasi Anda,
sampaikan informasi yang cukup untuk dapat mencatat kesalahan dalam tangkapan Anda .

Tentukan Kelas Pengecualian dalam Ketentuan Kebutuhan Penelepon


Ada banyak cara untuk mengklasifikasikan kesalahan. Kita dapat mengklasifikasikan mereka berdasarkan sumbernya: Apakah mereka
berasal dari satu komponen atau lainnya? Atau tipenya: Apakah mereka kegagalan perangkat, jaringan
kegagalan, atau kesalahan pemrograman? Namun, ketika kita mendefinisikan kelas pengecualian dalam aplikasi
kation, perhatian kita yang paling penting adalah bagaimana mereka ditangkap .

1. [Martin].

www.it-ebooks.info

Halaman 139

108 Bab 7: Penanganan Kesalahan

Mari kita lihat contoh klasifikasi pengecualian yang buruk. Ini dia coba - tangkap - akhirnya
pernyataan untuk panggilan perpustakaan pihak ketiga. Ini mencakup semua pengecualian yang dapat dilakukan panggilan
melemparkan:
ACMEPort port = ACMEPort baru (12);

coba {
port.open ();
} catch (DeviceResponseException e) {
reportPortError (e);
logger.log ("Pengecualian respons perangkat", e);
} catch (ATM1212UnlockedException e) {
reportPortError (e);
logger.log ("Buka pengecualian", e);
} catch (GMXError e) {
reportPortError (e);
logger.log ("Pengecualian respons perangkat");
} akhirnya {
...
}

Pernyataan itu mengandung banyak duplikasi, dan kita seharusnya tidak terkejut. Di sebagian besar
kecuali menangani situasi, pekerjaan yang kita lakukan relatif standar terlepas dari
penyebab sebenarnya. Kami harus merekam kesalahan dan memastikan bahwa kami dapat melanjutkan.
Dalam hal ini, karena kita tahu bahwa pekerjaan yang kita lakukan kira-kira sama
terlepas dari pengecualiannya, kami dapat menyederhanakan kode kami dengan membungkus API
yang kami panggil dan memastikan bahwa itu mengembalikan jenis pengecualian umum:
Port LocalPort = LocalPort baru (12);

https://translate.googleusercontent.com/translate_f 109/365
3/12/2020 www.it-ebooks.info
coba {
port.open ();
} catch (PortDeviceFailure e) {
reportError (e);
logger.log (e.getMessage (), e);
} akhirnya {
...
}

Kelas LocalPort kami hanyalah pembungkus sederhana yang menangkap dan menerjemahkan pengecualian
dilemparkan oleh kelas ACMEPort :
LocalPort kelas publik {
innerPort ACMEPort pribadi;

LocalPort publik (int portNumber) {


innerPort = ACMEPort baru (portNumber);
}

public void open () {


coba {
innerPort.open ();
} catch (DeviceResponseException e) {
melempar PortDeviceFailure (e) baru;
} catch (ATM1212UnlockedException e) {
melempar PortDeviceFailure (e) baru;
} catch (GMXError e) {

www.it-ebooks.info

Halaman 140

Tentukan Aliran Normal 109

melempar PortDeviceFailure (e) baru;


}
}
...
}

Pembungkus seperti yang kami definisikan untuk ACMEPort bisa sangat berguna. Padahal, pembungkusnya
API pihak ketiga adalah praktik terbaik. Saat Anda membungkus API pihak ketiga, Anda meminimalkan
dependensi atasnya: Anda dapat memilih untuk pindah ke perpustakaan lain di masa depan tanpa
banyak penalti. Membungkus juga memudahkan untuk mengejek panggilan pihak ketiga saat Anda berada
menguji kode Anda sendiri.
Satu keuntungan akhir dari pembungkus adalah Anda tidak terikat dengan API vendor tertentu
pilihan desain. Anda dapat menentukan API yang Anda rasa nyaman. Di pendahulunya
contoh, kami mendefinisikan satu jenis pengecualian untuk kegagalan perangkat port dan menemukan bahwa kami
bisa menulis kode yang jauh lebih bersih.
Seringkali kelas pengecualian tunggal baik untuk area kode tertentu. Informasi
dikirim dengan pengecualian dapat membedakan kesalahan. Gunakan kelas yang berbeda hanya jika ada
saat-saat ketika Anda ingin menangkap satu pengecualian dan membiarkan yang lain melewati.

Tentukan Aliran Normal


Jika Anda mengikuti saran di pendahulunya
bagian, Anda akan berakhir dengan jumlah yang baik
pemisahan antara logika bisnis Anda
dan penanganan kesalahan Anda. Sebagian besar dari Anda
kode akan mulai terlihat seperti bersih
algoritma tanpa hiasan. Namun,
berhenti melakukan ini mendorong deteksi kesalahan
ke tepi program Anda. Anda membungkus
API eksternal sehingga Anda dapat membuang
pengecualian sendiri, dan Anda mendefinisikan penangan di atas kode Anda sehingga Anda dapat menangani apa pun
perhitungan dibatalkan. Sebagian besar waktu ini merupakan pendekatan yang hebat, tetapi ada beberapa kali
ketika Anda mungkin tidak ingin membatalkan.
Mari kita lihat sebuah contoh. Berikut adalah beberapa kode aneh yang menjumlahkan pengeluaran dalam a
aplikasi penagihan:
coba {
Pengeluaran MealExpenses = costsReportDAO.getMeals (employee.getID ());
m_total + = costs.getTotal ();
} catch (MealExpensesNotFound e) {
m_total + = getMealPerDiem ();
}

Dalam bisnis ini, jika biaya makan, mereka menjadi bagian dari total. Jika tidak, maka

https://translate.googleusercontent.com/translate_f 110/365
3/12/2020 www.it-ebooks.info
karyawan mendapat makanan per jumlah diem untuk hari itu. Pengecualian mengacaukan logika.
Bukankah lebih baik jika kita tidak harus berurusan dengan kasus khusus? Jika tidak, kode kami
akan terlihat jauh lebih sederhana. Akan terlihat seperti ini:
Pengeluaran MealExpenses = costsReportDAO.getMeals (employee.getID ());
m_total + = costs.getTotal ();

www.it-ebooks.info

Halaman 141

110 Bab 7: Penanganan Kesalahan

Bisakah kita membuat kode sesederhana itu? Ternyata kita bisa. Kita bisa mengubah
ExpenseReportDAO sehingga selalu mengembalikan objek MealExpense . Jika tidak ada makanan
biaya, ia mengembalikan objek MealExpense yang mengembalikan per diem sebagai totalnya:

PerDiemMealExpenses kelas publik mengimplementasikan MealExpenses {


public int getTotal () {
// kembalikan default per diem
}
}

Ini disebut S PECIAL C ASE P ATTERN [Fowler]. Anda membuat kelas atau mengkonfigurasi
objek sehingga menangani kasus khusus untuk Anda. Ketika Anda melakukannya, kode klien tidak punya
untuk berurusan dengan perilaku luar biasa. Perilaku itu diringkas dalam objek kasus khusus.

Jangan Kembali Null


Saya pikir setiap diskusi tentang penanganan kesalahan harus mencakup penyebutan hal-hal yang kami
lakukan itu mengundang kesalahan. Yang pertama dalam daftar adalah mengembalikan nol . Saya tidak bisa mulai menghitung jumlahnya
aplikasi yang saya lihat di mana hampir setiap baris lainnya adalah cek untuk null . Disini adalah
beberapa kode contoh:
registerItem publik batal (Item item) {
if (item! = null) {
Registri ItemRegistry = peristentStore.getItemRegistry ();
if (registry! = null) {
Item yang ada = registry.getItem (item.getID ());
if (existing.getBillingPeriod (). hasRetailOwner ()) {
existing.register (item);
}
}
}
}

Jika Anda bekerja di basis kode dengan kode seperti ini, itu mungkin tidak terlihat terlalu buruk bagi Anda, tetapi memang begitu
buruk! Ketika kita mengembalikan nol , kita pada dasarnya menciptakan pekerjaan untuk diri kita sendiri dan membuka
masalah pada penelepon kami. Yang diperlukan hanyalah satu cek nol yang hilang untuk mengirim aplikasi
berputar di luar kendali.
Apakah Anda memperhatikan fakta bahwa tidak ada cek nol di baris kedua dari sarang itu
jika pernyataan? Apa yang akan terjadi pada saat runtime jika PersistentStore adalah nol ? Kita
akan memiliki NullPointerException saat runtime, dan baik seseorang yang menangkap
NullPointerException di tingkat atas atau tidak. Apa pun itu buruk . Apa tepatnya
harus Anda lakukan sebagai respons terhadap NullPointerException yang dilemparkan dari kedalaman aplikasi Anda
kation?
Sangat mudah untuk mengatakan bahwa masalah dengan kode di atas adalah bahwa ia tidak memiliki cek nol ,
tetapi dalam kenyataannya, masalahnya adalah terlalu banyak . Jika Anda tergoda untuk mengembalikan null dari
metode, pertimbangkan untuk melempar pengecualian atau mengembalikan objek S PECIAL C ASE sebagai gantinya. Jika
Anda memanggil metode pengembalian- nol dari API pihak ketiga, pertimbangkan untuk membungkusnya
metode dengan metode yang melempar pengecualian atau mengembalikan objek kasus khusus.

www.it-ebooks.info

Halaman 142

https://translate.googleusercontent.com/translate_f 111/365
3/12/2020 www.it-ebooks.info

Jangan Lewati Null 111

Dalam banyak kasus, objek kasus khusus adalah obat yang mudah. Bayangkan Anda memiliki kode
seperti ini:

Daftar <Employee> employee = getEmployees ();


jika (karyawan! = nol) {
untuk (Karyawan e: karyawan) {
totalPay + = e.getPay ();
}
}

Saat ini, getEmp Karyawan dapat mengembalikan nol , tetapi apakah harus? Jika kami mengubah getEmployee, maka
bahwa ia mengembalikan daftar kosong, kita dapat membersihkan kode:
Daftar <Employee> employee = getEmployees ();
untuk (Karyawan e: karyawan) {
totalPay + = e.getPay ();
}

Untungnya, Java memiliki Collections.emptyList () , dan mengembalikan daftar yang sudah ditentukan sebelumnya
yang dapat kita gunakan untuk tujuan ini:
Daftar publik <Pekerjaan> getEmp Karyawan () {
jika (.. tidak ada karyawan ..)
return Collections.emptyList ();
}

Jika Anda mengkode dengan cara ini, Anda akan meminimalkan kemungkinan NullPointerExceptions dan Anda
kode akan lebih bersih.

Jangan Lewati Null


Mengembalikan null dari metode buruk, tetapi meneruskan null ke metode lebih buruk. Kecuali kamu
bekerja dengan API yang mengharapkan Anda untuk lulus nol , Anda harus menghindari melewati nol di
kode Anda bila memungkinkan.
Mari kita lihat contoh untuk melihat alasannya. Berikut adalah metode sederhana yang menghitung met
ric untuk dua poin:
MetricsCalculator kelas publik
{
Proyeksi x ganda publik (Poin p1, Poin p2) {
return (p2.x - p1.x) * 1.5;
}
...
}

Apa yang terjadi ketika seseorang memberikan null sebagai argumen?

calculator.xProjection (null, Point baru (12, 13));

Kami akan mendapatkan NullPointerException , tentu saja.


Bagaimana kita memperbaikinya? Kami dapat membuat jenis pengecualian baru dan membuangnya:
MetricsCalculator kelas publik
{

www.it-ebooks.info

Halaman 143

112 Bab 7: Penanganan Kesalahan

Proyeksi x ganda publik (Poin p1, Poin p2) {


if (p1 == null || p2 == null) {
melempar InvalidArgumentException (
"Argumen tidak valid untuk MetricsCalculator.xProjection");
}
return (p2.x - p1.x) * 1.5;
}
}

Ini lebih baik? Mungkin sedikit lebih baik daripada pengecualian pointer nol , tapi ingat, kita
harus mendefinisikan handler untuk InvalidArgumentException . Apa yang harus dilakukan pawang? Adalah
Adakah tindakan yang baik?
Ada alternatif lain. Kita bisa menggunakan serangkaian asersi:

https://translate.googleusercontent.com/translate_f 112/365
3/12/2020 www.it-ebooks.info

MetricsCalculator kelas publik


{
Proyeksi x ganda publik (Poin p1, Poin p2) {
menegaskan p1! = null: "p1 tidak boleh nol";
menegaskan p2! = null: "p2 tidak boleh nol";
return (p2.x - p1.x) * 1.5;
}
}

Ini dokumentasi yang bagus, tetapi itu tidak menyelesaikan masalah. Jika seseorang melewati nol , kami akan
masih memiliki kesalahan runtime.
Dalam bahasa pemrograman yang paling tidak ada cara yang baik untuk berurusan dengan nol yang
dilewatkan oleh penelepon secara tidak sengaja. Karena ini masalahnya, pendekatan rasionalnya adalah melarang
melewati null secara default. Ketika Anda melakukannya, Anda dapat kode dengan pengetahuan bahwa null dalam sebuah
daftar argumen adalah indikasi masalah, dan berakhir dengan kesalahan yang jauh lebih sedikit ceroboh.

Kesimpulan
Kode bersih dapat dibaca, tetapi juga harus kuat. Ini bukan tujuan yang saling bertentangan. Kita dapat
tulis kode bersih yang kuat jika kita melihat penanganan kesalahan sebagai masalah terpisah, sesuatu yang benar
dapat dilihat secara independen dari logika utama kami. Sejauh kita mampu melakukan itu, kita bisa
alasan tentang hal itu secara mandiri, dan kita dapat membuat langkah besar dalam pemeliharaan kami
kode.

Bibliografi
[Martin]: Pengembangan Perangkat Lunak Agile: Prinsip, Pola, dan Praktik, Robert C.
Martin, Prentice Hall, 2002.

www.it-ebooks.info

Halaman 144

8
Batas
oleh James Grenning

https://translate.googleusercontent.com/translate_f 113/365
3/12/2020 www.it-ebooks.info

Kami jarang mengendalikan semua perangkat lunak dalam sistem kami. Terkadang kami membeli paket pihak ketiga-
usia atau gunakan open source. Di lain waktu kami bergantung pada tim di perusahaan kami sendiri untuk berproduksi
komponen atau subsistem bagi kami. Entah bagaimana kita harus dengan rapi mengintegrasikan kode asing ini

113

www.it-ebooks.info

Halaman 145

114 Bab 8: Batas

dengan kita sendiri. Dalam bab ini kita melihat praktik dan teknik untuk menjaga batas
perangkat lunak kami bersih.

Menggunakan Kode Pihak Ketiga


Ada ketegangan alami antara penyedia antarmuka dan pengguna antarmuka.
Penyedia paket dan kerangka kerja pihak ketiga berupaya agar penerapannya luas
dapat bekerja di banyak lingkungan dan menarik bagi khalayak luas. Pengguna, di sisi lain,
ingin antarmuka yang berfokus pada kebutuhan khusus mereka. Ketegangan ini dapat menyebabkan masalah
pada batas-batas sistem kami.
Mari kita lihat java.util.Map sebagai contoh. Seperti yang Anda lihat dengan memeriksa Gambar 8-1,
Peta memiliki antarmuka yang sangat luas dengan banyak kemampuan. Tentu saja kekuatan dan kelenturan ini
bility itu berguna, tetapi juga bisa menjadi liabilitas. Misalnya, aplikasi kita mungkin membangun a
Petakan dan bagikan. Tujuan kami mungkin agar tidak ada penerima Peta kami yang dihapus
apa pun di peta. Tetapi tepat di bagian atas daftar adalah metode clear () . Setiap pengguna
yang Map memiliki kekuatan untuk membersihkannya. Atau mungkin konvensi desain kami adalah yang khusus
jenis objek dapat disimpan dalam Peta , tetapi Peta tidak dapat diandalkan untuk membatasi jenis
benda ditempatkan di dalamnya. Setiap pengguna yang ditentukan dapat menambahkan item dari jenis apa pun ke Peta apa pun .

• hapus () batal - Peta


• berisi Kunci (kunci Objek) boolean - Peta
• berisi Nilai (Nilai objek) boolean - Peta
• entrySet () Set - Peta
• sama dengan (Objek o) boolean - Peta
• dapatkan (kunci Objek) Objek - Peta
• getClass () Kelas <? extends Object> - Object
• hashCode () int - Map
• isEmpty () boolean - Peta
• keySet () Set - Peta
• notify () void - Object
• notifyAll () void - Object
• put (kunci Objek, nilai Objek) Objek - Peta
• putAll (Map t) batal - Peta
• hapus (kunci Objek) Objek - Peta
• ukuran () int - Peta
• toString () String - Obyek
• nilai () Koleksi - Peta
• wait () void - Object
• menunggu (batas waktu lama) batal - Obyek
• tunggu (batas waktu lama, int nanos) tidak berlaku - Obyek

Gambar 8-1
Metode Peta

Jika aplikasi kita membutuhkan Peta dari Sensor s, Anda mungkin menemukan sensor diatur seperti ini:
Sensor peta = HashMap baru ();

https://translate.googleusercontent.com/translate_f 114/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 146

Menggunakan Kode Pihak Ketiga 115

Kemudian, ketika beberapa bagian lain dari kode perlu mengakses sensor, Anda melihat kode ini:
Sensor s = (Sensor) sensors.get (sensorId);

Kami tidak hanya melihatnya sekali, tetapi berulang-ulang di seluruh kode. Klien ini
kode membawa tanggung jawab untuk mendapatkan Objek dari Peta dan melemparkannya ke kanan
Tipe. Ini berfungsi, tetapi ini bukan kode bersih. Juga, kode ini tidak menceritakan kisahnya sebaik itu
bisa. Keterbacaan kode ini dapat sangat ditingkatkan dengan menggunakan obat generik, seperti yang ditunjukkan
di bawah:
Map <Sensor> sensor = HashMap baru <Sensor> ();
...
Sensor s = sensors.get (sensorId);

Namun, ini tidak menyelesaikan masalah yang Map <Sensor> menyediakan kemampuan lebih dari kita
butuh atau inginkan.
Melewati instance Map <Sensor> secara bebas di sekitar sistem berarti akan ada
ada banyak tempat untuk memperbaiki jika antarmuka ke Peta pernah berubah. Anda mungkin berpikir perubahan seperti itu
menjadi tidak mungkin, tetapi ingat bahwa itu berubah ketika dukungan generik ditambahkan di Java 5.
Memang, kami telah melihat sistem yang dihambat untuk menggunakan obat generik karena semata-mata
besarnya perubahan yang diperlukan untuk mengganti penggunaan liberal Map s.
Cara yang lebih bersih untuk menggunakan Peta mungkin terlihat seperti berikut ini. Tidak ada pengguna Sensor yang peduli
sedikit jika obat generik digunakan atau tidak. Pilihan itu telah menjadi (dan selalu harus) suatu
detail implementasi.
Sensor kelas publik {
sensor Peta pribadi = HashMap baru ();

Sensor publik getById (String id) {


return (Sensor) sensors.get (id);
}

//menggunting
}

Antarmuka pada batas ( Peta ) disembunyikan. Ia mampu berkembang dengan dampak yang sangat kecil
sisa aplikasi. Penggunaan obat generik tidak lagi menjadi masalah besar karena casting
dan manajemen tipe ditangani di dalam kelas Sensor .
Antarmuka ini juga dirancang dan dibatasi untuk memenuhi kebutuhan aplikasi. Itu
menghasilkan kode yang lebih mudah dipahami dan lebih sulit untuk disalahgunakan. Kelas Sensor dapat
menegakkan aturan desain dan bisnis.
Kami tidak menyarankan agar setiap penggunaan Peta dienkapsulasi dalam formulir ini. Sebaliknya, kita
menyarankan Anda untuk tidak melewati Peta s (atau antarmuka lainnya pada batas) di sekitar Anda
sistem. Jika Anda menggunakan antarmuka batas seperti Peta , simpan di dalam kelas, atau keluarga dekat
kelas, di mana ia digunakan. Hindari mengembalikannya dari, atau menerimanya sebagai argumen untuk,
API publik.

www.it-ebooks.info

Halaman 147

116 Bab 8: Batas


https://translate.googleusercontent.com/translate_f 115/365
3/12/2020 www.it-ebooks.info

Menjelajahi dan Mempelajari Batas


Kode pihak ketiga membantu kami mendapatkan lebih banyak fungsionalitas yang disampaikan dalam waktu yang lebih singkat. Di mana kita mulai?
kapan kita ingin menggunakan beberapa paket pihak ketiga? Bukan tugas kami untuk menguji pihak ketiga
kode, tetapi mungkin dalam kepentingan terbaik kami untuk menulis tes untuk kode pihak ketiga yang kami gunakan.
Misalkan tidak jelas bagaimana menggunakan perpustakaan pihak ketiga kami. Kita mungkin menghabiskan satu atau dua hari
(atau lebih) membaca dokumentasi dan memutuskan bagaimana kita akan menggunakannya. Lalu kita
mungkin menulis kode kami untuk menggunakan kode pihak ketiga dan melihat apakah itu sesuai dengan yang kami pikirkan. Kita
tidak akan terkejut menemukan diri kita terjebak dalam sesi debugging panjang yang berusaha
mencari tahu apakah bug yang kita alami ada dalam kode kita atau bug mereka.
Mempelajari kode pihak ketiga itu sulit. Mengintegrasikan kode pihak ketiga juga sulit.
Melakukan keduanya sekaligus sangat sulit. Bagaimana jika kita mengambil pendekatan yang berbeda? Sebagai gantinya
untuk bereksperimen dan mencoba hal-hal baru dalam kode produksi kami, kami dapat menulis beberapa
tes untuk mengeksplorasi pemahaman kita tentang kode pihak ketiga. Jim Newkirk menyebut tes semacam itu
tes belajar. 1
Dalam tes pembelajaran kami memanggil API pihak ketiga, karena kami berharap dapat menggunakannya dalam aplikasi kami.
Kami pada dasarnya melakukan eksperimen terkontrol yang memeriksa pemahaman kami tentang API itu.
Tes fokus pada apa yang kita inginkan dari API.

Belajar log4j
Katakanlah kita ingin menggunakan paket apache log4j daripada log yang dibuat khusus
ger. Kami mengunduhnya dan membuka halaman dokumentasi pengantar. Tanpa terlalu banyak
membaca kami menulis test case pertama kami, mengharapkannya untuk menulis "halo" ke konsol.
@Uji
public void testLogCreate () {
Logger Logger = Logger.getLogger ("MyLogger");
logger.info ("hello");
}

Ketika kami menjalankannya, logger menghasilkan kesalahan yang memberi tahu kami bahwa kami membutuhkan sesuatu yang disebut
Appender . Setelah membaca sedikit lebih lanjut kami menemukan bahwa ada ConsoleAppender . Jadi kami membuat
ConsoleAppender dan lihat apakah kami telah membuka kunci masuk ke konsol.

@Uji
public void testLogAddAppender () {
Logger Logger = Logger.getLogger ("MyLogger");
AppoleAppender appender = ConsoleAppender baru ();
logger.addAppender (appender);
logger.info ("hello");
}

1. [BeckTDD], hlm. 136–137.

www.it-ebooks.info

Halaman 148

Belajar log4j 117

Kali ini kami menemukan bahwa Appender tidak memiliki aliran output. Aneh — sepertinya masuk akal
punya satu. Setelah sedikit bantuan dari Google, kami mencoba yang berikut:
@Uji
public void testLogAddAppender () {
Logger Logger = Logger.getLogger ("MyLogger");
logger.removeAllAppenders ();
logger.addAppender (ConsoleAppender baru)
PatternLayout baru ("% p% t% m% n"),
ConsoleAppender.SYSTEM_OUT));
logger.info ("hello");
}

Itu berhasil; pesan log yang mencakup "halo" keluar di konsol! Sepertinya aneh
bahwa kita harus memberi tahu ConsoleAppender bahwa ia menulis ke konsol.
Cukup menarik, ketika kita menghapus argumen ConsoleAppender.SystemOut , kita
lihat bahwa "halo" masih dicetak. Tetapi ketika kita mengeluarkan PatternLayout , itu sekali lagi
dataran tentang kurangnya aliran output. Ini perilaku yang sangat aneh.

https://translate.googleusercontent.com/translate_f 116/365
3/12/2020 www.it-ebooks.info
Melihat sedikit lebih cermat pada dokumentasi, kita melihat bahwa default
Konstruktor ConsoleAppender adalah "tidak dikonfigurasi," yang tampaknya tidak terlalu jelas atau berguna.
Ini terasa seperti bug, atau setidaknya inkonsistensi, di log4j .
Sedikit lebih banyak googling, membaca, dan menguji, dan kami akhirnya berakhir dengan Listing 8-1.
Kami telah menemukan banyak hal tentang cara kerja log4j , dan kami telah menyandikannya
pengetahuan menjadi satu set tes unit sederhana.

Listing 8-1
LogTest.java
LogTest kelas publik {
Logger logger pribadi;

@Sebelum
public void initialize () {
logger = Logger.getLogger ("logger");
logger.removeAllAppenders ();
Logger.getRootLogger (). HapusAllAppenders ();
}
@Uji
publicLogger public void () {
BasicConfigurator.configure ();
logger.info ("basicLogger");
}

@Uji
addAppenderWithStream () publik batal ()
logger.addAppender (ConsoleAppender baru)
PatternLayout baru ("% p% t% m% n"),
ConsoleAppender.SYSTEM_OUT));
logger.info ("addAppenderWithStream");
}

www.it-ebooks.info

Halaman 149

118 Bab 8: Batas

Listing 8-1 (lanjutan)


LogTest.java
@Uji
addAppenderWithoutStream publik kosong () {
logger.addAppender (ConsoleAppender baru)
PatternLayout baru ("% p% t% m% n")));
logger.info ("addAppenderWithoutStream");
}
}

Sekarang kita tahu bagaimana cara mendapatkan konsol logger sederhana diinisialisasi, dan kita dapat merangkum
pengetahuan itu ke dalam kelas logger kita sendiri sehingga sisa aplikasi kita terisolasi
yang log4j batas antarmuka.

Tes Belajar Lebih Baik Daripada Gratis


Tes belajar berakhir tanpa biaya. Kami harus belajar API dan menulis
tes-tes itu adalah cara yang mudah dan terisolasi untuk mendapatkan pengetahuan itu. Tes pembelajarannya adalah
eksperimen tepat yang membantu meningkatkan pemahaman kita.
Tidak hanya tes belajar gratis, tes ini juga memberikan pengembalian investasi yang positif. Saat disana
adalah rilis baru dari paket pihak ketiga, kami menjalankan tes pembelajaran untuk melihat apakah ada
perbedaan perilaku.
Tes pembelajaran memverifikasi bahwa paket pihak ketiga yang kami gunakan berfungsi sebagaimana mestinya
mengharapkan mereka. Setelah terintegrasi, tidak ada jaminan bahwa kode pihak ketiga akan tetap ada
kompatibel dengan kebutuhan kita. Penulis asli akan memiliki tekanan untuk mengubah kode mereka
memenuhi kebutuhan baru mereka sendiri. Mereka akan memperbaiki bug dan menambah kemampuan baru. Dengan masing-masing
rilis muncul risiko baru. Jika paket pihak ketiga berubah dengan cara tertentu tidak kompatibel dengan
tes kami, kami akan segera tahu.
Apakah Anda memerlukan pembelajaran yang disediakan oleh tes pembelajaran atau tidak, batas yang bersih
harus didukung oleh serangkaian tes keluar yang menjalankan antarmuka dengan cara yang sama
kode produksi tidak. Tanpa tes batas ini untuk memudahkan migrasi, kita mungkin
tergoda untuk tetap menggunakan versi lama lebih lama dari yang seharusnya.

https://translate.googleusercontent.com/translate_f 117/365
3/12/2020 www.it-ebooks.info

Menggunakan Kode Yang Belum Ada


Ada jenis batas lain, yang memisahkan yang diketahui dari yang tidak diketahui. Sana
sering ada tempat dalam kode di mana pengetahuan kita tampaknya menurun. Terkadang
apa yang ada di sisi lain batas tidak dapat diketahui (setidaknya sekarang). Terkadang
kami memilih untuk tidak melihat lebih jauh dari batas.
Beberapa tahun yang lalu saya adalah bagian dari tim yang mengembangkan perangkat lunak untuk radio.
sistem imunisasi. Ada subsistem, "Transmitter," yang kami tahu sedikit
tentang, dan orang-orang yang bertanggung jawab untuk subsistem belum sampai pada titik mendefinisikan
antarmuka mereka. Kami tidak ingin diblokir, jadi kami memulai pekerjaan kami jauh dari
bagian kode yang tidak diketahui.

www.it-ebooks.info

Halaman 150

Menggunakan Kode Yang Belum Ada 119

Kami memiliki ide yang cukup bagus tentang di mana dunia kita berakhir dan dunia baru dimulai. Seperti yang kita
berhasil, kami kadang-kadang bertemu dengan batas ini. Meskipun kabut dan awan
ketidaktahuan mengaburkan pandangan kita di luar batas, pekerjaan kita membuat kita sadar akan apa yang kita
ingin antarmuka batas menjadi. Kami ingin memberi tahu pemancar sesuatu seperti ini:

Kunci pemancar pada frekuensi yang disediakan dan keluarkan representasi analog dari
data yang berasal dari aliran ini.

Kami tidak tahu bagaimana itu akan dilakukan karena API belum dirancang.
Jadi kami memutuskan untuk mengerjakan perinciannya nanti.
Agar tidak diblokir, kami mendefinisikan antarmuka kami sendiri. Kami menyebutnya sesuatu
catchy, seperti Transmitter . Kami memberikannya metode yang disebut mengirimkan yang mengambil frekuensi dan a
aliran data. Ini adalah antarmuka yang kami inginkan .
Satu hal yang baik tentang penulisan antarmuka yang kami inginkan adalah bahwa itu berada di bawah kami
kontrol. Ini membantu menjaga kode klien lebih mudah dibaca dan fokus pada apa yang dicoba
menyelesaikan.
Pada Gambar 8-2, Anda dapat melihat bahwa kami mengisolasi kelas CommunicationsController
dari API pemancar (yang di luar kendali kami dan tidak terdefinisi). Dengan menggunakan milik kita sendiri
aplikasi antarmuka khusus, kami menjaga kode CommunicationsController kami bersih dan
ekspresif. Setelah API pemancar ditentukan, kami menulis TransmitterAdapter ke
menjembatani kesenjangan. A DAPTER 2 merangkum interaksi dengan API dan menyediakan a
satu tempat untuk berubah ketika API berkembang.

Gambar 8-2
Memprediksi pemancar

Desain ini juga memberi kita jahitan 3 yang sangat nyaman dalam kode untuk pengujian. Menggunakan sebuah
cocok FakeTransmitter , kita dapat menguji kelas CommunicationsController . Kita juga bisa
buat tes batas setelah kami memiliki TransmitterAPI yang memastikan kami menggunakan
API dengan benar.

2. Lihat pola Adaptor di [GOF].


3. Lihat lebih lanjut tentang jahitan di [WELC].

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 118/365
3/12/2020 www.it-ebooks.info

Halaman 151

120 Bab 8: Batas

Batas Bersih
Hal-hal menarik terjadi pada batas-batas. Perubahan adalah salah satunya. Perangkat lunak yang bagus
desain mengakomodasi perubahan tanpa investasi besar dan pengerjaan ulang. Ketika kita menggunakan kode
yang di luar kendali kami, perhatian khusus harus diberikan untuk melindungi investasi kami dan menghasilkan
yakin perubahan di masa depan tidak terlalu mahal.
Kode di batas membutuhkan pemisahan yang jelas dan tes yang menentukan harapan. Kita
harus menghindari membiarkan terlalu banyak kode kita tahu tentang keterangan pihak ketiga. Lebih baik
untuk bergantung pada sesuatu yang Anda kendalikan daripada sesuatu yang tidak Anda kendalikan, jangan sampai berakhir
mengendalikan kamu.
Kami mengelola batas pihak ketiga dengan memiliki sedikit tempat dalam kode yang merujuk
mereka. Kami dapat membungkusnya seperti yang kami lakukan dengan Peta , atau kami dapat menggunakan A DAPTER untuk mengkonversi
antarmuka sempurna kami ke antarmuka yang disediakan. Apa pun cara kode kita berbicara kepada kita dengan lebih baik,
mempromosikan penggunaan yang konsisten secara internal melintasi batas, dan memiliki lebih sedikit perawatan
menunjuk saat kode pihak ketiga berubah.

Bibliografi
[BeckTDD]: Pengembangan Berbasis Tes, Kent Beck, Addison-Wesley, 2003.

[GOF]: Pola Desain: Elemen-elemen Perangkat Lunak Berorientasi Objek yang Dapat Digunakan Kembali, Gamma et al.,
Addison-Wesley, 1996.

[WELC]: Bekerja Efektif dengan Legacy Code, Addison-Wesley, 2004.

www.it-ebooks.info

Halaman 152

https://translate.googleusercontent.com/translate_f 119/365
3/12/2020 www.it-ebooks.info

9
Tes Unit

Profesi kami telah berkembang pesat dalam sepuluh tahun terakhir. Pada 1997 tidak ada yang pernah mendengar
Pengembangan Berbasis Tes. Bagi sebagian besar dari kita, unit test adalah potongan-potongan pendek
kode yang kami tulis untuk memastikan program kami "bekerja." Kami akan susah payah
tulis kelas dan metode kita, lalu kita akan membuat beberapa kode ad hoc untuk diuji
mereka. Biasanya ini akan melibatkan semacam program driver sederhana yang memungkinkan
kami berinteraksi secara manual dengan program yang telah kami tulis.
Saya ingat menulis program C ++ untuk sistem waktu-nyata yang disematkan kembali di Windows
pertengahan 90-an. Program ini adalah penghitung waktu sederhana dengan tanda tangan berikut:
void Timer :: ScheduleCommand (Command * theCommand, int milidetik)

Idenya sederhana; yang melaksanakan metode dari Command akan dieksekusi dalam baru
utas setelah jumlah milidetik yang ditentukan. Masalahnya adalah, bagaimana cara mengujinya.

121

www.it-ebooks.info

Halaman 153

122 Bab 9: Tes Unit

Saya membuat program driver sederhana yang mendengarkan keyboard. Setiap saat a
karakter diketik, itu akan menjadwalkan perintah yang akan mengetik karakter yang sama lima
detik kemudian. Lalu aku mengetuk melodi berirama pada keyboard dan menunggu itu
melodi untuk memutar ulang di layar lima detik kemudian.
"Aku. . . mau-cewek. . . adil. . . seperti-gadis-yang-marr. . . ied. . . terhormat . . . tua . . ayah."
Saya benar-benar menyanyikan melodi itu sambil mengetik "." kunci, dan kemudian saya menyanyikannya lagi sebagai
titik-titik muncul di layar.
Itu ujian saya! Begitu saya melihatnya bekerja dan menunjukkannya kepada rekan-rekan saya, saya melempar
kode uji pergi.
Seperti yang saya katakan, profesi kita telah jauh. Saat ini saya akan menulis tes yang dibuat
yakin bahwa setiap sudut dan celah kode itu berfungsi seperti yang saya harapkan. Saya akan mengisolasi saya
kode dari sistem operasi daripada hanya memanggil fungsi waktu standar. saya
akan mengejek fungsi-fungsi pengaturan waktu sehingga saya memiliki kontrol mutlak atas waktu. saya akan
jadwalkan perintah yang mengatur bendera boolean, dan kemudian saya akan melangkah maju, menonton
bendera itu dan memastikan bahwa mereka berubah dari false menjadi true sama seperti saya mengubah waktu menjadi
nilai yang benar.
Begitu saya mendapatkan serangkaian tes untuk lulus, saya akan memastikan bahwa tes itu nyaman
untuk menjalankan bagi siapa pun yang perlu bekerja dengan kode. Saya akan memastikan bahwa tes dan

https://translate.googleusercontent.com/translate_f 120/365
3/12/2020 www.it-ebooks.info
kode diperiksa bersama-sama ke dalam paket sumber yang sama.
Ya, kami telah menempuh perjalanan panjang; tapi kita harus pergi lebih jauh. Gerakan Agile dan TDD-
KASIH telah mendorong banyak programmer untuk menulis tes unit otomatis, dan lebih banyak lagi
bergabung dengan barisan mereka setiap hari. Tetapi dengan terburu-buru gila untuk menambahkan pengujian ke disiplin kita, banyak
programmer telah melewatkan beberapa poin penulisan yang lebih halus dan penting
tes yang bagus.

Tiga Hukum TDD


Sekarang semua orang tahu bahwa TDD meminta kami untuk menulis unit test terlebih dahulu, sebelum kami menulis
kode tion. Tapi aturan itu hanyalah puncak gunung es. Pertimbangkan tiga hukum berikut: 1

Hukum Pertama Anda tidak boleh menulis kode produksi sampai Anda menulis unit test yang gagal.

Hukum Kedua Anda tidak boleh menulis lebih dari satu unit test daripada cukup untuk gagal, dan tidak
tumpukan gagal.

Hukum Ketiga Anda tidak boleh menulis lebih banyak kode produksi daripada cukup untuk lulus saat ini
gagal dalam tes.

1. Profesionalisme dan Pengembangan Berbasis Tes , Robert C. Martin, Object Mentor, Perangkat Lunak IEEE, Mei / Juni 2007 (Vol. 24,
3) hlm. 32–36
http://doi.ieeecomputersociety.org/10.1109/MS.2007.85

www.it-ebooks.info

Halaman 154

Menjaga Tes Bersih 123

Ketiga undang-undang ini mengunci Anda ke dalam sebuah siklus yang panjangnya mungkin tiga puluh detik. Tes
dan kode produksi ditulis bersama, dengan tes hanya beberapa detik di depan
kode produksi.
Jika kita bekerja dengan cara ini, kita akan menulis lusinan tes setiap hari, ratusan tes setiap hari
bulan, dan ribuan tes setiap tahun. Jika kita bekerja dengan cara ini, tes tersebut akan mencakup
sekutu semua kode produksi kami. Sebagian besar dari tes tersebut, yang dapat menyaingi ukuran
kode produksi itu sendiri, dapat menyajikan masalah manajemen yang menakutkan.

Menjaga Tes Bersih


Beberapa tahun yang lalu saya diminta untuk melatih tim yang secara eksplisit memutuskan tes mereka
kode tidak boleh dipertahankan dengan standar kualitas yang sama dengan kode produksinya.
Mereka saling memberi lisensi untuk melanggar aturan dalam unit test mereka. "Cepat dan kotor" tadinya
semboyan. Variabel mereka tidak harus dinamai dengan baik, fungsi tes mereka tidak
harus pendek dan deskriptif. Kode tes mereka tidak perlu dirancang dengan baik dan
dipartisi. Selama kode tes bekerja, dan selama itu mencakup
kode duction, sudah cukup bagus.
Beberapa dari Anda yang membaca ini mungkin bersimpati dengan keputusan itu. Mungkin, lama di
dulu, Anda menulis tes seperti yang saya tulis untuk kelas Timer itu . Ini adalah langkah besar
menulis semacam tes membuang, untuk menulis serangkaian tes unit otomatis. Jadi, seperti
Tim saya sedang melatih, Anda mungkin memutuskan bahwa memiliki tes kotor lebih baik daripada tidak
tes.
Apa yang tim ini tidak sadari adalah bahwa memiliki tes kotor setara dengan, jika tidak lebih buruk
daripada, tidak memiliki tes. Masalahnya adalah bahwa tes harus berubah sebagai kode produksi
berkembang. Semakin kotor tes, semakin sulit untuk berubah. Semakin kusut kode tes,
semakin besar kemungkinan Anda akan menghabiskan lebih banyak waktu menjejalkan tes baru ke dalam suite daripada itu
diperlukan untuk menulis kode produksi baru. Saat Anda memodifikasi kode produksi, tes lama dimulai
untuk gagal, dan kekacauan dalam kode tes membuatnya sulit untuk mendapatkan tes tersebut untuk lulus lagi. Sehingga
tes menjadi dipandang sebagai kewajiban yang semakin meningkat.
Dari rilis ke rilis biaya untuk mempertahankan test suite tim saya naik. Akhirnya itu
menjadi satu-satunya keluhan terbesar di antara para pengembang. Ketika manajer bertanya mengapa
perkiraan mereka menjadi sangat besar, para pengembang menyalahkan tes. Akhirnya mereka
terpaksa membuang test suite sepenuhnya.
Tetapi, tanpa test suite mereka kehilangan kemampuan untuk memastikan bahwa perubahan kode mereka
basis bekerja seperti yang diharapkan. Tanpa test suite mereka tidak dapat memastikan bahwa perubahan menjadi satu

https://translate.googleusercontent.com/translate_f 121/365
3/12/2020 www.it-ebooks.info
bagian dari sistem mereka tidak merusak bagian lain dari sistem mereka. Jadi tingkat kecacatan mereka mulai
Bangkit. Ketika jumlah cacat yang tidak diinginkan meningkat, mereka mulai takut membuat perubahan. Mereka
berhenti membersihkan kode produksi mereka karena mereka khawatir perubahan akan berbuat lebih banyak
merugikan daripada kebaikan. Kode produksi mereka mulai membusuk. Pada akhirnya mereka dibiarkan tanpa tes,
kode produksi kusut dan penuh bug, pelanggan yang frustrasi, dan perasaan bahwa mereka
upaya pengujian telah gagal mereka.

www.it-ebooks.info

Halaman 155

124 Bab 9: Tes Unit

Di satu sisi mereka benar. Upaya menguji mereka telah gagal mereka. Tapi itu keputusan mereka
untuk memungkinkan tes menjadi berantakan itu adalah benih kegagalan itu. Seandainya mereka melakukan tes
bersih, upaya pengujian mereka tidak akan gagal. Saya bisa mengatakan ini dengan pasti karena
Saya telah berpartisipasi dalam, dan melatih, banyak tim yang telah berhasil dengan unit bersih
tes.
Moral cerita ini sederhana: Kode uji sama pentingnya dengan kode produksi. Itu
bukan warga negara kelas dua. Itu membutuhkan pemikiran, desain, dan perawatan. Itu harus dijaga tetap bersih
sebagai kode produksi.

Pengujian Mengaktifkan -ilities


Jika tes Anda tidak bersih, Anda akan kehilangannya. Dan tanpa mereka, Anda sangat kehilangan
hal yang membuat kode produksi Anda fleksibel. Ya, Anda membacanya dengan benar. Ini adalah unit test
yang menjaga kode kita fleksibel, dapat dipelihara, dan dapat digunakan kembali. Alasannya sederhana. Jika Anda memiliki
tes, Anda tidak takut membuat perubahan pada kode! Tanpa tes, setiap perubahan adalah mungkin
bug. Tidak peduli seberapa fleksibel arsitektur Anda, tidak peduli seberapa baik Anda mempartisi
desain, tanpa tes Anda akan enggan melakukan perubahan karena rasa takut bahwa Anda
akan memperkenalkan bug yang tidak terdeteksi.
Tetapi dengan tes yang ketakutan hampir menghilang. Semakin tinggi cakupan tes Anda, semakin sedikit
ketakutanmu. Anda dapat membuat perubahan dengan impunitas dekat terhadap kode yang kurang dari bintang
arsitektur dan desain yang kusut dan buram. Memang, Anda dapat meningkatkan arsitektur itu
dan desain tanpa rasa takut!
Jadi, memiliki rangkaian unit pengujian otomatis yang mencakup kode produksi adalah kuncinya
menjaga desain dan arsitektur Anda sebersih mungkin. Tes memungkinkan semua utilitas,
karena tes memungkinkan perubahan .
Jadi, jika tes Anda kotor, maka kemampuan Anda untuk mengubah kode Anda terhambat, dan Anda
mulai kehilangan kemampuan untuk memperbaiki struktur kode itu. Semakin kotor tes Anda, itu
kode Anda menjadi lebih kotor. Akhirnya Anda kehilangan tes, dan kode Anda membusuk.

Tes Bersih
Apa yang membuat tes bersih? Tiga hal. Keterbacaan, keterbacaan, dan keterbacaan. Baca-
kemampuan mungkin bahkan lebih penting dalam pengujian unit daripada dalam kode produksi. Apa
membuat tes dapat dibaca? Hal yang sama yang membuat semua kode dapat dibaca: kejelasan, kesederhanaan,
dan kepadatan ekspresi. Dalam ujian Anda ingin banyak berbicara dengan ekspresi sesedikit mungkin
bisa jadi.
Pertimbangkan kode dari FitNesse di Listing 9-1. Ketiga tes ini sulit dilakukan
mengerti dan tentu saja bisa ditingkatkan. Pertama, ada jumlah duplikat yang mengerikan
kode [G5] dalam panggilan berulang untuk addPage dan assertSubString . Lebih penting lagi, ini
kode hanya dimuat dengan detail yang mengganggu ekspresifitas tes.

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 122/365
3/12/2020 www.it-ebooks.info

Halaman 156

Tes Bersih 125

Listing 9-1
SerializedPageResponderTest.java
public void testGetPageHieratchyAsXml () melempar Pengecualian
{
crawler.addPage (root, PathParser.parse ("PageOne"));
crawler.addPage (root, PathParser.parse ("PageOne.ChildOne"));
crawler.addPage (root, PathParser.parse ("PageTwo"));

request.setResource ("root");
request.addInput ("ketik", "halaman");
Responder responder = new SerializedPageResponder ();
Respons SimpleResponse =
(SimpleResponse) responder.makeResponse (
FitNesseContext baru (root), permintaan);
String xml = response.getContent ();

assertEquals ("text / xml", response.getContentType ());


assertSubString ("<name> PageOne </name>", xml);
assertSubString ("<name> PageTwo </name>", xml);
assertSubString ("<name> ChildOne </name>", xml);
}

public void testGetPageHieratchyAsXmlDoesntContainSymbolicLinks ()


melempar Pengecualian
{
WikiPage pageOne = crawler.addPage (root, PathParser.parse ("PageOne"));
crawler.addPage (root, PathParser.parse ("PageOne.ChildOne"));
crawler.addPage (root, PathParser.parse ("PageTwo"));

Data PageData = pageOne.getData ();


Properti WikiPageProperties = data.getProperties ();
WikiPageProperty symLinks = properties.set (SymbolicPage.PROPERTY_NAME);
symLinks.set ("SymPage", "PageTwo");
pageOne.commit (data);

request.setResource ("root");
request.addInput ("ketik", "halaman");
Responder responder = new SerializedPageResponder ();
Respons SimpleResponse =
(SimpleResponse) responder.makeResponse (
FitNesseContext baru (root), permintaan);
String xml = response.getContent ();

assertEquals ("text / xml", response.getContentType ());


assertSubString ("<name> PageOne </name>", xml);
assertSubString ("<name> PageTwo </name>", xml);
assertSubString ("<name> ChildOne </name>", xml);
assertNotSubString ("SymPage", xml);
}

public void testGetDataAsHtml () melempar Pengecualian


{
crawler.addPage (root, PathParser.parse ("TestPageOne"), "halaman pengujian");

request.setResource ("TestPageOne");
request.addInput ("type", "data");

www.it-ebooks.info

Halaman 157

126 Bab 9: Tes Unit

Listing 9-1 (lanjutan)


SerializedPageResponderTest.java
Responder responder = new SerializedPageResponder ();
Respons SimpleResponse =
(SimpleResponse) responder.makeResponse (
FitNesseContext baru (root), permintaan);
String xml = response.getContent ();

https://translate.googleusercontent.com/translate_f 123/365
3/12/2020 www.it-ebooks.info
assertEquals ("text / xml", response.getContentType ());
assertSubString ("halaman pengujian", xml);
assertSubString ("<Test", xml);
}

Misalnya, lihat panggilan PathParser . Mereka mengubah string menjadi PagePath


contoh yang digunakan oleh crawler. Transformasi ini sama sekali tidak relevan dengan tes di
tangan dan hanya berfungsi untuk mengaburkan niat. Rincian seputar penciptaan
responden dan pengumpulan dan casting tanggapan juga hanya berisik. Lalu ada
dengan cara ham bahwa URL permintaan dibangun dari sumber daya dan argumen. (Saya sudah membantu
tulis kode ini, jadi saya merasa bebas mengkritiknya.)
Pada akhirnya, kode ini tidak dirancang untuk dibaca. Pembaca yang buruk dibanjiri dengan a
segerombolan detail yang harus dipahami sebelum tes masuk akal.
Sekarang pertimbangkan pengujian yang ditingkatkan pada Listing 9-2. Tes-tes ini melakukan hal yang persis sama,
tetapi mereka telah di refactored menjadi bentuk yang jauh lebih bersih dan lebih jelas.

Listing 9-2
SerializedPageResponderTest.java (refactored)
public void testGetPageHierarchyAsXml () melempar Exception {
makePages ("PageOne", "PageOne.ChildOne", "PageTwo");

submitRequest ("root", "ketik: halaman");

assertResponseIsXML ();
assertResponseContains (
"<name> PageOne </name>", "<name> PageTwo </name>", "<name> ChildOne </name>"
);
}

public void testSymbolicLinksAreNotInXmlPageHierarchy () melempar Exception {


Halaman WikiPage = makePage ("PageOne");
makePages ("PageOne.ChildOne", "PageTwo");

addLinkTo (halaman, "PageTwo", "SymPage");

submitRequest ("root", "ketik: halaman");

assertResponseIsXML ();
assertResponseContains (
"<name> PageOne </name>", "<name> PageTwo </name>", "<name> ChildOne </name>"
);
assertResponseDoesNotContain ("SymPage");
}

www.it-ebooks.info

Halaman 158

Tes Bersih 127

Listing 9-2 (lanjutan)


SerializedPageResponderTest.java (refactored)
public void testGetDataAsXml () melempar Exception {
makePageWithContent ("TestPageOne", "test page");

submitRequest ("TestPageOne", "ketik: data");

assertResponseIsXML ();
assertResponseContains ("halaman pengujian", "<Tes");
}

B uild -O perate -C HECK 2 pola dibuat jelas oleh struktur tes ini.
Masing-masing tes jelas dibagi menjadi tiga bagian. Bagian pertama membangun data uji, the
bagian kedua beroperasi pada data uji itu, dan bagian ketiga memeriksa apakah operasi menghasilkan
hasil yang diharapkan.
Perhatikan bahwa sebagian besar detail yang mengganggu telah dihilangkan. Tesnya
langsung ke titik dan hanya menggunakan tipe data dan fungsi yang benar-benar mereka butuhkan. Siapa saja
yang membaca tes-tes ini harus dapat mengetahui apa yang mereka lakukan dengan sangat cepat, tanpa menjadi
disesatkan atau kewalahan oleh detail.

Bahasa Pengujian Khusus Domain


Tes dalam Listing 9-2 menunjukkan teknik membangun bahasa khusus domain
untuk tes Anda. Daripada menggunakan API yang digunakan programmer untuk memanipulasi sistem
Selain itu, kami membangun serangkaian fungsi dan utilitas yang memanfaatkan API dan itu

https://translate.googleusercontent.com/translate_f 124/365
3/12/2020 www.it-ebooks.info
membuat
menjadi APItes lebih
khususnyaman untuk ditulis
yang digunakan olehdan
tes.lebih mudah
Mereka dibaca.
adalah Fungsi
bahasa dan utilitas
pengujian yangini
memprogram-
mer digunakan untuk membantu diri mereka sendiri untuk menulis tes mereka dan untuk membantu mereka yang harus membaca itu
tes nanti.
API pengujian ini tidak dirancang di muka; melainkan berevolusi dari referensi berkelanjutan
Mempelajari kode uji yang sudah terlalu ternoda oleh detail yang membingungkan. Sama seperti Anda melihat saya
refactor Listing 9-1 ke Listing 9-2, demikian juga pengembang yang disiplin akan melakukan refactor pengujian mereka
kode menjadi bentuk yang lebih ringkas dan ekspresif.

Standar Ganda
Di satu sisi tim yang saya sebutkan di awal bab ini memiliki hal-hal yang benar. Itu
kode dalam API pengujian memang memiliki serangkaian standar teknik yang berbeda dari
kode tion. Itu harus tetap sederhana, ringkas, dan ekspresif, tetapi tidak harus seefisien
kode produksi. Bagaimanapun, ini berjalan dalam lingkungan pengujian, bukan lingkungan produksi, dan
kedua lingkungan tersebut memiliki kebutuhan yang sangat berbeda.

2. http://fitnesse.org/FitNesse.AcceptanceTestPatterns

www.it-ebooks.info

Halaman 159

128 Bab 9: Tes Unit

Pertimbangkan pengujian pada Listing 9-3. Saya menulis tes ini sebagai bagian dari sistem kontrol lingkungan
Saya sedang membuat prototipe. Tanpa membahas detailnya, Anda dapat mengatakan bahwa tes ini memeriksa hal itu
alarm suhu rendah, pemanas, dan peniup semua dihidupkan saat suhu
masa depan "terlalu dingin."

Listing 9-3
EnvironmentControllerTest.java
@Uji
public void turnOnLoTempAlarmAtThreashold () melempar Exception {
hw.setTemp (WAY_TOO_COLD);
controller.tic ();
assertTrue (hw.heaterState ());
assertTrue (hw.blowerState ());
assertFalse (hw.coolerState ());
assertFalse (hw.hiTempAlarm ());
assertTrue (hw.loTempAlarm ());
}

Tentu saja ada banyak detail di sini. Misalnya, apa itu tic fungsi semua
tentang? Sebenarnya, saya lebih suka Anda tidak khawatir tentang itu saat membaca tes ini. Saya lebih suka kamu saja
khawatir tentang apakah Anda setuju bahwa keadaan akhir sistem konsisten dengan suhu
perature menjadi "terlalu dingin."
Perhatikan, saat Anda membaca tes, bahwa mata Anda harus bergerak maju dan mundur di antara keduanya
nama negara yang diperiksa, dan arti negara yang diperiksa. Kamu melihat
heaterState , dan kemudian mata Anda meninggalkan kiri untuk menegaskan benar . Anda melihat coolerState dan Anda
mata harus melacak ke kiri untuk menegaskan Salah . Ini membosankan dan tidak bisa diandalkan. Itu membuat tes sulit
untuk membaca.
Saya meningkatkan kemampuan membaca tes ini dengan mengubahnya menjadi Listing 9-4.

Listing 9-4
EnvironmentControllerTest.java (refactored)
@Uji
public void turnOnLoTempAlarmAtThreshold () melempar Exception {
wayTooCold ();
assertEquals ("HBchL", hw.getState ());
}

Tentu saja saya menyembunyikan detail fungsi tic dengan membuat fungsi wayTooCold . Tetapi
hal yang perlu diperhatikan adalah string aneh di assertEquals . Huruf besar berarti "hidup", huruf kecil
berarti "mati," dan huruf-hurufnya selalu dalam urutan berikut: {heater, blower, cooler,
hi-temp-alarm, lo-temp-alarm} .
Meskipun ini dekat dengan pelanggaran aturan tentang pemetaan mental, 3 tampaknya
sesuai dalam hal ini. Perhatikan, begitu Anda tahu artinya, mata Anda melayang

https://translate.googleusercontent.com/translate_f 125/365
3/12/2020 www.it-ebooks.info

3. “Hindari Pemetaan Mental” di halaman 25.

www.it-ebooks.info

Halaman 160

Tes Bersih 129

string itu dan Anda dapat dengan cepat menafsirkan hasilnya. Membaca tes menjadi hampir a
kesenangan. Lihatlah Daftar 9-5 dan lihat betapa mudahnya memahami tes-tes ini.

Listing 9-5
EnvironmentControllerTest.java (pilihan lebih besar)
@Uji
public void turnOnCoolerAndBlowerIfTooHot () melempar Exception {
terlalu panas();
assertEquals ("hBChl", hw.getState ());
}

@Uji
public void turnOnHeaterAndBlowerIfTooCold () melempar Exception {
terlalu dingin();
assertEquals ("HBchl", hw.getState ());
}

@Uji
public void turnOnHiTempAlarmAtThreshold () melempar Exception {
wayTooHot ();
assertEquals ("hBCHl", hw.getState ());
}

@Uji
public void turnOnLoTempAlarmAtThreshold () melempar Exception {
wayTooCold ();
assertEquals ("HBchL", hw.getState ());
}

Fungsi getState ditunjukkan pada Listing 9-6. Perhatikan bahwa ini sangat tidak efisien
kode. Untuk membuatnya efisien, saya mungkin harus menggunakan StringBuffer .

Listing 9-6
MockControlHardware.java
public String getState () {
Status string = "";
negara + = pemanas? "H": "h";
negara + = blower? "B": "b";
negara + = lebih dingin? "C": "c";
negara + = hiTempAlarm? "H": "h";
negara + = loTempAlarm? "L": "l";
negara kembali;
}

StringBuffer s agak jelek. Bahkan dalam kode produksi saya akan menghindari mereka jika biayanya
kecil; dan Anda bisa berpendapat bahwa biaya kode dalam Listing 9-6 sangat kecil. Namun,
aplikasi ini jelas merupakan sistem real-time tertanam, dan kemungkinan komputer dan
sumber daya memori sangat terbatas. Lingkungan pengujian , bagaimanapun, tidak mungkin
terkendala sama sekali.

www.it-ebooks.info

Halaman 161

https://translate.googleusercontent.com/translate_f 126/365
3/12/2020 www.it-ebooks.info

130 Bab 9: Tes Unit

Itulah sifat standar ganda. Ada hal-hal yang mungkin tidak pernah Anda lakukan di
lingkungan produksi yang sangat baik dalam lingkungan pengujian. Biasanya mereka terlibat
masalah memori atau efisiensi CPU. Tetapi mereka tidak pernah melibatkan masalah kebersihan.

Satu Penegasan per Tes


Ada aliran pemikiran 4 yang mengatakan bahwa setiap fungsi tes dalam tes JUnit harus memiliki satu
dan hanya satu pernyataan yang tegas. Aturan ini mungkin terlihat kejam, tetapi keuntungannya bisa dilihat
dalam Listing 9-5. Tes-tes itu sampai pada satu kesimpulan yang cepat dan mudah dimengerti.
Tetapi bagaimana dengan Listing 9-2? Tampaknya tidak masuk akal bahwa kita bisa dengan mudah entah bagaimana
menggabungkan pernyataan bahwa output adalah XML dan itu berisi substring tertentu. Bagaimana-
pernah, kita dapat memecah tes menjadi dua tes terpisah, masing-masing dengan pernyataan sendiri, sebagai
ditunjukkan pada Listing 9-7.

Listing 9-7
SerializedPageResponderTest.java (Pernyataan Tunggal)
public void testGetPageHierarchyAsXml () melempar Exception {
diberikanPages ("PageOne", "PageOne.ChildOne", "PageTwo");

whenRequestIsIssued ("root", "ketik: halaman");

thenResponseShouldBeXML ();
}

public void testGetPageHierarchyHasRightTags () melempar Exception {


diberikanPages ("PageOne", "PageOne.ChildOne", "PageTwo");

whenRequestIsIssued ("root", "ketik: halaman");

thenResponseShouldContain (
"<name> PageOne </name>", "<name> PageTwo </name>", "<name> ChildOne </name>"
);
}

Perhatikan bahwa saya telah mengubah nama fungsi untuk menggunakan yang diberikan saat umum
lalu 5 konvensi. Ini membuat tes lebih mudah dibaca. Sayangnya, memisahkan tes
seperti yang ditunjukkan menghasilkan banyak kode duplikat.
Kita dapat menghilangkan duplikasi dengan menggunakan pola dan penempatan T EMPLATE M ETHOD 6
bagian diberikan / ketika di kelas dasar, dan kemudian bagian dalam turunan yang berbeda. Atau kita bisa
buat kelas tes yang benar-benar terpisah dan letakkan bagian yang diberikan dan ketika di fungsi @Before
dan kapan bagian dalam setiap fungsi @Test . Tapi ini sepertinya terlalu banyak mekanisme untuk
masalah kecil. Pada akhirnya, saya lebih suka multiple asserts pada Listing 9-2.

4. Lihat entri blog Dave Astel: http://www.artima.com/weblogs/viewpost.jsp?thread=35578


5. [RSpec].
6. [GOF].

www.it-ebooks.info

Halaman 162

Satu Penegasan per Tes 131

Saya pikir aturan tunggal menegaskan adalah pedoman yang baik. 7 Saya biasanya mencoba membuat domain-
bahasa pengujian khusus yang mendukungnya, seperti pada Listing 9-5. Tapi saya tidak takut untuk mengatakannya
lebih dari satu menegaskan dalam suatu ujian. Saya pikir hal terbaik yang bisa kita katakan adalah jumlah
menegaskan dalam ujian harus diminimalkan.

Konsep tunggal per Tes


Mungkin aturan yang lebih baik adalah kita ingin menguji satu konsep di setiap fungsi tes. Kami tidak
ingin fungsi pengujian panjang yang menguji satu hal lain demi satu. Listing 9-8
adalah contoh dari tes semacam itu. Tes ini harus dibagi menjadi tiga tes independen
karena menguji tiga hal independen. Menggabungkan semuanya menjadi satu fungsi

https://translate.googleusercontent.com/translate_f 127/365
3/12/2020 www.it-ebooks.info
memaksa pembaca untuk mencari tahu mengapa setiap bagian ada di sana dan apa yang sedang diuji oleh itu
bagian.

Listing 9-8
/ **
* Tes lain-lain untuk metode addMonths ().
*/
public void testAddMonths () {
SerialDate d1 = SerialDate.createInstance (31, 5, 2004);

SerialDate d2 = SerialDate.addMonths (1, d1);


assertEquals (30, d2.getDayOfMonth ());
assertEquals (6, d2.getMonth ());
assertEquals (2004, d2.getYYYY ());

SerialDate d3 = SerialDate.addMonths (2, d1);


assertEquals (31, d3.getDayOfMonth ());
assertEquals (7, d3.getMonth ());
assertEquals (2004, d3.getYYYY ());

SerialDate d4 = SerialDate.addMonths (1, SerialDate.addMonths (1, d1));


assertEquals (30, d4.getDayOfMonth ());
assertEquals (7, d4.getMonth ());
assertEquals (2004, d4.getYYYY ());
}

Tiga fungsi tes mungkin harus seperti ini:

• Diberi hari terakhir dalam sebulan dengan 31 hari (seperti Mei):

1. Ketika Anda menambahkan satu bulan, sehingga hari terakhir bulan itu adalah tanggal 30
(seperti Juni), maka tanggalnya harus tanggal 30 bulan itu, bukan tanggal 31.
2. Ketika Anda menambahkan dua bulan ke tanggal itu, sehingga bulan terakhir memiliki 31 hari,
maka tanggalnya harus tanggal 31.

7. "Simpan ke kode!"

www.it-ebooks.info

Halaman 163

132 Bab 9: Tes Unit

• Diberi hari terakhir dalam sebulan dengan 30 hari di dalamnya (seperti Juni):

1. Ketika Anda menambahkan satu bulan sehingga hari terakhir bulan itu memiliki 31 hari, maka tanggal
tanggal harus tanggal 30, bukan tanggal 31.
Dinyatakan seperti ini, Anda dapat melihat bahwa ada aturan umum yang bersembunyi di tengah miscella-
tes neous. Saat Anda menambahkan bulan, tanggalnya tidak boleh lebih besar dari hari terakhir
bulan. Ini menyiratkan bahwa penambahan bulan pada tanggal 28 Februari akan menghasilkan bulan Maret
Tanggal 28 Itu tes yang hilang dan akan menjadi tes yang berguna untuk menulis.
Jadi bukan beberapa pernyataan di setiap bagian dari Listing 9-8 yang menyebabkan masalah.
Justru itu adalah fakta bahwa ada lebih dari satu konsep yang diuji. Jadi mungkin yang terbaik
aturannya adalah bahwa Anda harus meminimalkan jumlah pernyataan per konsep dan menguji hanya satu con-
kecuali per fungsi tes.

PERTAMA 8
Tes bersih mengikuti lima aturan lain yang membentuk akronim di atas:

Cepat Pengujian harus cepat. Mereka harus berlari dengan cepat. Saat tes berjalan lambat, Anda tidak akan mau
sering menjalankannya. Jika Anda tidak sering menjalankannya, Anda tidak akan menemukan masalah lebih awal
cukup untuk memperbaikinya dengan mudah. Anda tidak akan merasa bebas untuk membersihkan kode. Akhirnya kodenya
akan mulai membusuk.

Tes Independen seharusnya tidak saling bergantung. Satu tes tidak harus mengatur kondisi
untuk tes selanjutnya. Anda harus dapat menjalankan setiap tes secara independen dan menjalankan tes dalam
pesanan apa pun yang Anda suka. Ketika tes tergantung satu sama lain, maka yang pertama gagal menyebabkan kasasi.
Cade kegagalan hilir, membuat diagnosis sulit dan menyembunyikan cacat hilir.

https://translate.googleusercontent.com/translate_f 128/365
3/12/2020 www.it-ebooks.info
Berulang Pengujian harus diulang dalam lingkungan apapun. Anda harus dapat menjalankan
tes di lingkungan produksi, di lingkungan QA, dan di laptop Anda sementara
pulang ke rumah di kereta tanpa jaringan. Jika tes Anda tidak dapat diulang di lingkungan apa pun
ment, maka Anda akan selalu memiliki alasan mengapa mereka gagal. Anda juga tidak akan bisa
untuk menjalankan tes ketika lingkungan tidak tersedia.

Validasi Sendiri Pengujian harus memiliki output boolean. Entah mereka lulus atau gagal. Kamu
tidak harus membaca file log untuk mengetahui apakah tes lulus. Anda seharusnya tidak
untuk secara manual membandingkan dua file teks yang berbeda untuk melihat apakah tes lulus. Jika tes tidak
validasi diri, maka kegagalan bisa menjadi subyektif dan menjalankan tes bisa membutuhkan waktu lama
evaluasi manual.

8. Materi Pelatihan Mentor Objek.

www.it-ebooks.info

Halaman 164

Bibliografi 133

Tepat Waktu Tes harus ditulis tepat waktu. Tes unit harus ditulis adil
sebelum kode produksi yang membuat mereka lulus. Jika Anda menulis tes setelah produksi
kode, maka Anda mungkin menemukan kode produksi sulit untuk diuji. Anda mungkin memutuskan itu
kode produksi terlalu sulit untuk diuji. Anda mungkin tidak merancang kode produksi untuk dapat diuji.

Kesimpulan
Kami baru saja menggaruk permukaan topik ini. Memang, saya pikir seluruh buku bisa
menulis tentang tes bersih . Tes sama pentingnya dengan kesehatan proyek seperti halnya produksi
kode adalah. Mungkin mereka bahkan lebih penting, karena tes mempertahankan dan meningkatkan
fleksibilitas, pemeliharaan, dan penggunaan kembali kode produksi. Jadi pertahankan tes Anda
tetap bersih. Berusahalah untuk membuatnya ekspresif dan ringkas. Menciptakan API pengujian yang bertindak sebagai
bahasa khusus domain yang membantu Anda menulis tes.
Jika Anda membiarkan tes membusuk, maka kode Anda juga akan membusuk. Jaga kebersihan tes Anda.

Bibliografi
[RSpec]: RSpec: Pengembangan Berbasis Perilaku untuk Programmer Ruby ,
Aslak Hellesøy, David Chelimsky, Rak Buku Pragmatis, 2008.

[GOF]: Pola Desain: Elemen-elemen Perangkat Lunak Berorientasi Objek yang Dapat Digunakan Kembali , Gamma et al.,
Addison-Wesley, 1996.

https://translate.googleusercontent.com/translate_f 129/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 165

halaman ini sengaja dibiarkan kosong

www.it-ebooks.info

Halaman 166

https://translate.googleusercontent.com/translate_f 130/365
3/12/2020 www.it-ebooks.info

10
Kelas
dengan Jeff Langr

Sejauh ini dalam buku ini kami telah fokus pada bagaimana menulis baris dan blok kode dengan baik. Kita punya
menyelidiki komposisi fungsi yang tepat dan bagaimana mereka saling terkait. Tapi untuk semua perhatian
dengan ekspresi pernyataan kode dan fungsi-fungsi yang ada di dalamnya, kita masih
tidak memiliki kode bersih hingga kami memperhatikan tingkat organisasi kode yang lebih tinggi. Ayo
berbicara tentang kelas yang bersih.

135

www.it-ebooks.info

Halaman 167

136 Bab 10: Kelas

Organisasi Kelas
Mengikuti konvensi Java standar, sebuah kelas harus dimulai dengan daftar variabel. Pub-
konstanta statis, jika ada, harus didahulukan. Kemudian variabel statis pribadi, diikuti oleh pri-
vate instance instance. Jarang ada alasan bagus untuk memiliki variabel publik.
Fungsi publik harus mengikuti daftar variabel. Kami suka menempatkan utilitas pribadi
dipanggil oleh fungsi publik tepat setelah fungsi publik itu sendiri. Ini mengikuti stepdown
aturan dan bantu program membaca seperti artikel koran.

Enkapsulasi
Kami ingin menjaga variabel dan fungsi utilitas kami tetap pribadi, tetapi kami tidak fanatik tentang hal itu.
Terkadang kita perlu membuat variabel atau fungsi utilitas terlindungi agar bisa
diakses dengan tes. Bagi kami, aturan tes. Jika tes dalam paket yang sama perlu memanggil fungsi
atau mengakses variabel, kami akan membuatnya terlindungi atau cakupan paket. Namun, pertama-tama kita akan mencari

https://translate.googleusercontent.com/translate_f 131/365
3/12/2020 www.it-ebooks.info
cara untuk menjaga privasi. Melonggarkan enkapsulasi selalu menjadi pilihan terakhir.

Kelas Harus Kecil!


Aturan kelas yang pertama adalah bahwa mereka harus kecil. Aturan kelas yang kedua adalah mereka
harus lebih kecil dari itu. Tidak, kami tidak akan mengulangi teks yang sama persis dari
Bab fungsi . Tetapi seperti halnya fungsi, yang lebih kecil adalah aturan utama dalam hal ini
merancang kelas. Seperti halnya fungsi, pertanyaan langsung kami selalu "Seberapa kecil?"
Dengan fungsi, kami mengukur ukuran dengan menghitung garis fisik. Dengan kelas kita menggunakan a
ukuran yang berbeda. Kami menghitung tanggung jawab. 1
Listing 10-1 menguraikan kelas, SuperDashboard , yang memperlihatkan sekitar 70 metode publik.
Sebagian besar pengembang akan setuju bahwa ukurannya agak terlalu super. Beberapa pengembang mungkin merujuk
untuk SuperDashboard sebagai "kelas dewa."

Listing 10-1
Terlalu Banyak Tanggung Jawab
SuperDashboard kelas publik memperluas JFrame mengimplementasikan MetaDataUser
public String getCustomizerLanguagePath ()
kekosongan publik setSystemConfigPath (String systemConfigPath)
public String getSystemConfigDocument ()
kekosongan publik setSystemConfigDocument (String systemConfigDocument)
getGuruState boolean publik ()
getNoviceState boolean publik ()
getOpenSourceState boolean publik ()
public showObject publik (objek MetaObject)
public void showProgress (String s)

1. [RDD].

www.it-ebooks.info

Halaman 168

Kelas Harus Kecil! 137

Listing 10-1 (lanjutan)


Terlalu Banyak Tanggung Jawab
boolean publik isMetadataDirty ()
public void setIsMetadataDirty (boolean isMetadataDirty)
Komponen publik getLastFocusedComponent ()
public void setLastFocused (Komponen lastFocused)
public void setMouseSelectState (boolean isMouseSelected)
boolean publik isMouseSelected ()
LanguageManager publik getLanguageManager ()
Proyek publik getProject ()
Proyek publik getFirstProject ()
Proyek publik getLastProject ()
public String getNewProjectName ()
public void setComponentSizes (Dimensi redup)
public String getCurrentDir ()
public void setCurrentDir (String newDir)
status pembaruan publik batal (int dotPos, int markPos)
Kelas publik [] getDataBaseClasses ()
MetadataFeeder publik, getMetadataFeeder ()
public void addProject (Proyek proyek)
public boolean setCurrentProject (Proyek proyek)
public boolean removeProject (Proyek proyek)
MetaProjectHeader publik, getProgramMetadata ()
resetDashboard publik batal ()
Project loadProject publik (String fileName, String projectName)
public void setCanSaveMetadata (boolean canSave)
MetaObject publik getSelectedObject ()
membatalkan pilihan publik ()
kekosongan publik setProject (proyek proyek)
public void editorAction (String actionName, event ActionEvent)
public void setMode (mode int)
FileManager publik getFileManager ()
kekosongan publik setFileManager (FileManager fileManager)
ConfigManager publik getConfigManager ()
public void setConfigManager (ConfigManager configManager)
publik ClassLoader getClassLoader ()
public void setClassLoader (ClassLoader classLoader)
Properti publik getProps ()
public String getUserHome ()
public String getBaseDir ()
public int getMajorVersionNumber ()
public int getMinorVersionNumber ()

https://translate.googleusercontent.com/translate_f 132/365
3/12/2020 www.it-ebooks.info
public int getBuildNumber
menyisipkan () (
MetaObject publik
Target MetaObject, disisipkan MetaObject, proyek MetaProbject)
public void processMenuItems (MetaObject metaObject)
public void processMenuSeparators (MetaObject metaObject)
public void processTabPages (MetaObject metaObject)
public void processPlacement (objek MetaObject)
public void processCreateLayout (objek MetaObject)
public void updateDisplayLayer (objek MetaObject, int layerIndex)
public void propertyEditedRepaint (objek MetaObject)
public void processDeleteObject (objek MetaObject)
public boolean getAttachedToDesigner ()
public void processProjectChangedState (boolean hasProjectChanged)
public void processObjectNameChanged (objek MetaObject)
public run vject ()

www.it-ebooks.info

Halaman 169

138 Bab 10: Kelas

Listing 10-1 (lanjutan)


Terlalu Banyak Tanggung Jawab
public void setAçowDragging (boolean allowDragging)
public boolean allowDragging ()
boolean publik adalah Menyesuaikan ()
public void setTitle (Judul string)
IdeMenuBar publik getIdeMenuBar ()
public void showHelper (MetaObject metaObject, String propertyName)
// ... banyak metode non-publik mengikuti ...
}

Tetapi bagaimana jika SuperDashboard hanya berisi metode yang ditunjukkan pada Listing 10-2?

Listing 10-2
Cukup kecil?
SuperDashboard kelas publik memperluas JFrame mengimplementasikan MetaDataUser
Komponen publik getLastFocusedComponent ()
public void setLastFocused (Komponen lastFocused)
public int getMajorVersionNumber ()
public int getMinorVersionNumber ()
public int getBuildNumber ()
}

Lima metode tidak terlalu banyak, kan? Dalam hal ini karena walaupun jumlahnya kecil
metode, SuperDashboard memiliki terlalu banyak tanggung jawab .
Nama kelas harus menggambarkan tanggung jawab apa yang dipenuhinya. Bahkan, penamaan
mungkin merupakan cara pertama untuk membantu menentukan ukuran kelas. Jika kita tidak bisa mendapatkan ringkasan
nama untuk sebuah kelas, maka kemungkinan besar terlalu besar. Semakin mendua nama kelas, semakin banyak
kemungkinan memiliki tanggung jawab terlalu banyak. Misalnya, nama kelas termasuk kata-kata musang
seperti Processor atau Manager atau Super sering mengisyaratkan agregasi yang tidak menguntungkan
tanggung jawab.
Kita juga harus dapat menulis deskripsi singkat tentang kelas dalam sekitar 25 kata,
tanpa menggunakan kata-kata "jika," "dan," "atau," atau "tetapi." Bagaimana kita menggambarkan
SuperDashboard ? " SuperDashboard menyediakan akses ke komponen yang terakhir dimiliki
fokus, dan itu juga memungkinkan kita untuk melacak versi dan membangun angka. " "Dan" pertama adalah a
mengisyaratkan bahwa SuperDashboard memiliki tanggung jawab terlalu banyak.

Prinsip Tanggung Jawab Tunggal


Prinsip Tanggung Jawab Tunggal (SRP) 2 menyatakan bahwa kelas atau modul harus memiliki satu,
dan hanya satu, alasan untuk berubah . Prinsip ini memberi kita definisi tanggung jawab,
dan pedoman untuk ukuran kelas. Kelas harus memiliki satu tanggung jawab — satu alasan untuk
perubahan.

2. Anda dapat membaca lebih banyak tentang prinsip ini di [PPP].

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 133/365
3/12/2020 www.it-ebooks.info

Halaman 170

Kelas Harus Kecil! 139

Kelas SuperDashboard yang tampaknya kecil di Listing 10-2 memiliki dua alasan untuk berubah.
Pertama, ia melacak informasi versi yang tampaknya perlu diperbarui setiap kali
perangkat lunak dikirimkan. Kedua, ia mengelola komponen Java Swing (turunan dari
JFrame , representasi Swing dari jendela GUI tingkat atas). Tidak diragukan lagi kami akan mau
perbarui nomor versi jika kita mengubah kode Swing apa pun, tetapi sebaliknya tidak perlu
intinya benar: Kami dapat mengubah informasi versi berdasarkan perubahan pada kode lain di
sistem.
Mencoba mengidentifikasi tanggung jawab (alasan untuk berubah) sering membantu kita mengenali dan
buat abstraksi yang lebih baik dalam kode kita. Kita dapat dengan mudah mengekstrak ketiga SuperDashboard
metode yang menangani informasi versi ke dalam kelas terpisah bernama Version. (Lihat
Listing 10-3.) Kelas Versi adalah konstruk yang memiliki potensi tinggi untuk digunakan kembali di lain
aplikasi!

Listing 10-3
Kelas tanggung jawab tunggal
Versi kelas publik {
public int getMajorVersionNumber ()
public int getMinorVersionNumber ()
public int getBuildNumber ()
}

SRP adalah salah satu konsep yang lebih penting dalam desain OO. Itu juga salah satu yang lebih sederhana
konsep untuk dipahami dan dipatuhi. Namun anehnya, SRP sering merupakan desain kelas yang paling banyak disalahgunakan
prinsip. Kami secara teratur menghadapi kelas yang melakukan terlalu banyak hal. Mengapa?
Membuat perangkat lunak berfungsi dan membersihkan perangkat lunak adalah dua kegiatan yang sangat berbeda.
Sebagian besar dari kita memiliki ruang terbatas di kepala kita, jadi kita fokus pada mendapatkan kode kita untuk bekerja lebih banyak
dari organisasi dan kebersihan. Ini sepenuhnya tepat. Mempertahankan pemisahan
kekhawatiran sama pentingnya dalam kegiatan pemrograman kita seperti halnya dalam program kita.
Masalahnya adalah bahwa terlalu banyak dari kita berpikir bahwa kita selesai begitu program bekerja.
Kami gagal untuk beralih ke lain keprihatinan organisasi dan kebersihan. Kami pindah ke
Masalah selanjutnya daripada kembali dan memecah kelas yang kelebihan menjadi decoupled
unit dengan tanggung jawab tunggal.
Pada saat yang sama, banyak pengembang takut sejumlah besar kecil, satu tujuan
kelas membuatnya lebih sulit untuk memahami gambaran yang lebih besar. Mereka khawatir itu
mereka harus menavigasi dari satu kelas ke kelas lain untuk mencari tahu bagaimana pekerjaan yang lebih besar
ulung.
Namun, sistem dengan banyak kelas kecil tidak memiliki bagian yang bergerak selain sistem
dengan beberapa kelas besar. Ada banyak hal yang harus dipelajari dalam sistem dengan sedikit besar
kelas. Jadi pertanyaannya adalah: Apakah Anda ingin alat Anda diorganisasikan ke dalam kotak alat dengan banyak
laci kecil masing-masing berisi komponen yang terdefinisi dengan baik dan berlabel baik? Atau kamu mau
beberapa laci tempat Anda membuang semuanya?
Setiap sistem yang cukup besar akan mengandung banyak logika dan kompleksitas. Harga
Tujuan utama dalam mengelola kompleksitas tersebut adalah untuk mengaturnya sehingga pengembang tahu di mana

www.it-ebooks.info

Halaman 171

140 Bab 10: Kelas

mencari untuk menemukan hal-hal dan hanya perlu memahami kompleksitas yang terkena dampak langsung
diberikan waktu. Sebaliknya, sistem dengan kelas multiguna yang lebih besar selalu menghambat kita

https://translate.googleusercontent.com/translate_f 134/365
3/12/2020 www.it-ebooks.info
bersikeras kami mengarungi banyak hal yang tidak perlu kita ketahui sekarang.
Untuk menyatakan kembali poin-poin sebelumnya untuk penekanan: Kami ingin sistem kami terdiri dari
banyak kelas kecil, tidak sedikit yang besar. Setiap kelas kecil merangkum satu respons
Bility, memiliki satu alasan untuk berubah, dan berkolaborasi dengan beberapa orang lain untuk mencapai
perilaku sistem yang diinginkan.

Kohesi
Kelas harus memiliki sejumlah kecil variabel instan. Masing-masing metode kelas
harus memanipulasi satu atau lebih variabel tersebut. Secara umum semakin banyak variabel metode
memanipulasi metode yang lebih kohesif bagi kelasnya. Kelas di mana masing-masing variabel
digunakan oleh masing-masing metode secara maksimal kohesif.
Secara umum tidak disarankan atau tidak mungkin untuk menciptakan keterpaduan yang maksimal
kelas; di sisi lain, kami ingin kohesi menjadi tinggi. Ketika kohesi tinggi, itu
berarti bahwa metode dan variabel kelas saling tergantung dan digantung bersama sebagai a
keseluruhan logis.
Pertimbangkan implementasi Stack pada Listing 10-4. Ini adalah kelas yang sangat kohesif.
Dari tiga metode hanya ukuran () gagal menggunakan kedua variabel.

Listing 10-4
Stack.java Kelas yang kohesif.
tumpukan kelas publik {
private int topOfStack = 0;
Daftar elemen <Integer> = LinkedList baru <Integer> ();

ukuran int publik () {


kembali topOfStack;
}

public void push (int element) {


topOfStack ++;
elements.add (elemen);
}

public int pop () melempar PoppedWhenEmpty {


if (topOfStack == 0)
melempar PoppedWhenEmpty baru ();
int element = elements.get (- topOfStack);
elements.remove (topOfStack);
elemen pengembalian;
}
}

Strategi menjaga fungsi tetap kecil dan membuat daftar parameter singkat dapat
waktu menyebabkan proliferasi variabel instan yang digunakan oleh subset metode.
Ketika ini terjadi, itu hampir selalu berarti bahwa setidaknya ada satu kelas lain yang berusaha

www.it-ebooks.info

Halaman 172

Kelas Harus Kecil! 141

keluar dari kelas yang lebih besar. Anda harus mencoba memisahkan variabel dan metode menjadi dua atau
lebih banyak kelas sehingga kelas baru lebih kohesif.

Mempertahankan Hasil Kohesi di Banyak Kelas Kecil


Hanya tindakan memecah fungsi besar menjadi fungsi yang lebih kecil menyebabkan proliferasi
kelas. Pertimbangkan fungsi besar dengan banyak variabel yang dideklarasikan di dalamnya. Katakanlah kamu
ingin mengekstrak satu bagian kecil dari fungsi itu menjadi fungsi terpisah. Namun kodenya
Anda ingin mengekstrak menggunakan empat variabel yang dideklarasikan dalam fungsi. Anda harus lulus semua
empat variabel tersebut ke dalam fungsi baru sebagai argumen?
Tidak semuanya! Jika kita mempromosikan keempat variabel ke variabel instan kelas, maka
kita bisa mengekstrak kode tanpa melewati variabel apa pun . Akan mudah pecah
fungsi naik menjadi potongan-potongan kecil.
Sayangnya, ini juga berarti bahwa kelas kita kehilangan kohesi karena mereka menumpuk
semakin banyak variabel instan yang ada hanya untuk memungkinkan beberapa fungsi membagikannya.
Tapi tunggu! Jika ada beberapa fungsi yang ingin berbagi variabel tertentu, bukankah begitu
membuat mereka kelas dengan hak mereka sendiri? Tentu saja. Ketika kelas kehilangan kohesi, berpisah
mereka!
Jadi memecah suatu fungsi besar menjadi banyak fungsi yang lebih kecil sering memberi kita peluang

https://translate.googleusercontent.com/translate_f 135/365
3/12/2020 www.it-ebooks.info
Untuk membagi beberapa kelas yang lebih kecil juga. Ini memberikan program kami organisasi yang jauh lebih baik
dan struktur yang lebih transparan.
Sebagai demonstrasi dari apa yang saya maksud, mari kita gunakan contoh terhormat yang diambil dari
Buku bagus Knuth, Programming Literate. 3 Listing 10-5 menunjukkan terjemahan ke Jawa
program PrintPrimes Knuth . Agar adil bagi Knuth, ini bukan program yang ditulisnya
melainkan karena itu dihasilkan oleh alat WEB-nya. Saya menggunakannya karena ini merupakan awal yang baik
tempat untuk memecah fungsi besar menjadi banyak fungsi dan kelas yang lebih kecil.

Listing 10-5
PrintPrimes.java
paket LiteratePrimes;

PrintPrimes kelas publik {


public static public void (String [] args) {
int akhir M = 1000;
RR akhir = 50;
int akhir CC = 4;
int akhir WW = 10;
ORDMAX int akhir = 30;
int P [] = int baru [M + 1];
PAGENUMBER int;
int PAGEOFFSET;
int ROWOFFSET;
int C;

3. [Knuth92].

www.it-ebooks.info

Halaman 173

142 Bab 10: Kelas

Listing 10-5 (lanjutan)


PrintPrimes.java
int J;
int K;
boolean JPRIME;
int ORD;
int SQUARE;
int N;
int MULT [] = int baru [ORDMAX + 1];

J = 1;
K = 1;
P [1] = 2;
ORD = 2;
SQUARE = 9;

while (K <M) {
lakukan {
J = J + 2;
if (J == SQUARE) {
ORD = ORD + 1;
SQUARE = P [ORD] * P [ORD];
MULT [ORD - 1] = J;
}
N = 2;
JPRIME = true;
while (N <ORD && JPRIME) {
sementara (MULT [N] <J)
MULT [N] = MULT [N] + P [N] + P [N];
if (MULT [N] == J)
JPRIME = false;
N = N + 1;
}
} while (! JPRIME);
K = K + 1;
P [K] = J;
}
{
PAGENUMBER = 1;
PAGEOFFSET = 1;
while (PAGEOFFSET <= M) {
System.out.println ("The First" + M +
"Bilangan Prima --- Halaman" + PAGENUMBER);
System.out.println ("");
untuk (ROWOFFSET = PAGEOFFSET; ROWOFFSET <PAGEOFFSET + RR; ROWOFFSET ++) {
untuk (C = 0; C <CC; C ++)
jika (ROWOFFSET + C * RR <= M)
System.out.format ("% 10d", P [ROWOFFSET + C * RR]);

https://translate.googleusercontent.com/translate_f 136/365
3/12/2020 www.it-ebooks.info
} System.out.println ("");
System.out.println ("\ f");
PAGENUMBER = PAGENUMBER + 1;
PAGEOFFSET = PAGEOFFSET + RR * CC;
}
}
}
}

www.it-ebooks.info

Halaman 174

Kelas Harus Kecil! 143

Program ini, ditulis sebagai fungsi tunggal, berantakan. Ia memiliki struktur


mendatang, sejumlah besar variabel aneh, dan struktur berpasangan erat. Paling tidak, yang satu
fungsi besar harus dipecah menjadi beberapa fungsi yang lebih kecil.
Listing 10-6 hingga Listing 10-8 menunjukkan hasil pemisahan kode pada Listing 10-5
ke dalam kelas dan fungsi yang lebih kecil, dan memilih nama yang bermakna untuk kelas tersebut, fungsinya
tions, dan variabel.

Listing 10-6
PrimePrinter.java (refactored)
paket LiteratePrimes;

PrimePrinter kelas publik {


public static public void (String [] args) {
int akhir NUMBER_OF_PRIMES = 1000;
int [] primes = PrimeGenerator.generate (NUMBER_OF_PRIMES);

int akhir ROWS_PER_PAGE = 50;


int akhir COLUMNS_PER_PAGE = 4;
RowColumnPagePrinter tablePrinter =
RowColumnPagePrinter baru (ROWS_PER_PAGE,
COLUMNS_PER_PAGE,
"The First" + NUMBER_OF_PRIMES +
" Bilangan prima");

tablePrinter.print (primes);
}

Listing 10-7
RowColumnPagePrinter.java
paket LiteratePrimes;

impor java.io.PrintStream;

RowColumnPagePrinter kelas publik {


private int rowsPerPage;
kolom int pribadiPerPage;
nomor int pribadiPerPage;
private String pageHeader;
PrintStream pribadi printStream;

publik RowColumnPagePrinter (int rowsPerPage,


int kolomPerPage,
String pageHeader) {
this.rowsPerPage = rowsPerPage;
this.columnsPerPage = kolomPerPage;
this.pageHeader = pageHeader;
numbersPerPage = rowsPerPage * kolomPerPage;
printStream = System.out;
}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 137/365
3/12/2020 www.it-ebooks.info
Halaman 175

144 Bab 10: Kelas

Listing 10-7 (lanjutan)


RowColumnPagePrinter.java
public void print (data int []) {
int pageNumber = 1;
untuk (int firstIndexOnPage = 0;
firstIndexOnPage <data.length;
firstIndexOnPage + = numbersPerPage) {
int lastIndexOnPage =
Math.min (firstIndexOnPage + numbersPerPage - 1,
data.length - 1);
printPageHeader (pageHeader, pageNumber);
printPage (firstIndexOnPage, lastIndexOnPage, data);
printStream.println ("\ f");
pageNumber ++;
}
}

private printPage void (int firstIndexOnPage,


int lastIndexOnPage,
int [] data) {
int firstIndexOfLastRowOnPage =
firstIndexOnPage + rowsPerPage - 1;
untuk (int firstIndexInRow = firstIndexOnPage;
firstIndexInRow <= firstIndexOfLastRowOnPage;
firstIndexInRow ++) {
printRow (firstIndexInRow, lastIndexOnPage, data);
printStream.println ("");
}
}

private void printRow (int firstIndexInRow,


int lastIndexOnPage,
int [] data) {
untuk (int kolom = 0; kolom <kolomPerPage; kolom ++) {
int index = firstIndexInRow + kolom * rowsPerPage;
if (index <= lastIndexOnPage)
printStream.format ("% 10d", data [indeks]);
}
}

private void printPageHeader (String pageHeader,


int pageNumber) {
printStream.println (pageHeader + "--- Page" + pageNumber);
printStream.println ("");
}

kekosongan publik setOutput (PrintStream printStream) {


this.printStream = printStream;
}
}

www.it-ebooks.info

Halaman 176

Kelas Harus Kecil! 145

Listing 10-8
PrimeGenerator.java
paket LiteratePrimes;

import java.util.ArrayList;

PrimeGenerator kelas publik {


private static int [] primes;

https://translate.googleusercontent.com/translate_f 138/365
3/12/2020 www.it-ebooks.info
private static ArrayList <Integer> multiplesOfPrimeFactors;

int static] [] hasilkan (int n) {


primes = int baru [n];
multiplesOfPrimeFactors = ArrayList baru <Integer> ();
set2AsFirstPrime ();
checkOddNumbersForSub berikutnyaentPrimes ();
mengembalikan bilangan prima;
}

private static void set2AsFirstPrime () {


bilangan prima [0] = 2;
multiplesOfPrimeFactors.add (2);
}

private static void checkOddNumbersForSub berikutnyaentPrimes () {


int primeIndex = 1;
untuk (kandidat int = 3;
primeIndex <primes.length;
kandidat + = 2) {
if (isPrime (kandidat))
primes [primeIndex ++] = kandidat;
}
}

private static boolean isPrime (kandidat int) {


if (isLeastRelevantMultipleOfNextLargerPrimeFactor (kandidat)) {
multiplesOfPrimeFactors.add (kandidat);
return false;
}
return isNotMultipleOfAnyPreviousPrimeFactor (kandidat);
}

boolean statis pribadi


isLeastRelevantMultipleOfNextLargerPrimeFactor (kandidat int) {
int nextLargerPrimeFactor = primes [multiplesOfPrimeFactors.size ()];
int leastRelevantMultiple = nextLargerPrimeFactor * nextLargerPrimeFactor;
mengembalikan kandidat == leastRelevantMultiple;
}

boolean statis pribadi


isNotMultipleOfAnyPreviousPrimeFactor (kandidat int) {
untuk (int n = 1; n <multiplesOfPrimeFactors.size (); n ++) {
if (isMultipleOfNthPrimeFactor (kandidat, n))
return false;
}

www.it-ebooks.info

Halaman 177

146 Bab 10: Kelas

Listing 10-8 (lanjutan)


PrimeGenerator.java
kembali benar;
}

boolean statis pribadi


isMultipleOfNthPrimeFactor (kandidat int, int n) {
kembali
kandidat == smallestOddNthMultipleNotLessThanCandidate (kandidat, n);
}

int statis pribadi


smallestOddNthMultipleNotLessThanCandidate (kandidat int, int n) {
int multiple = multiplesOfPrimeFactors.get (n);
while (multiple <kandidat)
beberapa + = 2 * bilangan prima [n];
multiplesOfPrimeFactors.set (n, banyak);
kembali beberapa;
}
}

Hal pertama yang mungkin Anda perhatikan adalah programnya menjadi jauh lebih lama. Itu pergi dari a
sedikit lebih dari satu halaman hingga hampir tiga halaman panjangnya. Ada beberapa alasan untuk ini
pertumbuhan. Pertama, program refactored menggunakan nama variabel yang lebih panjang dan lebih deskriptif.
Kedua, program refactored menggunakan deklarasi fungsi dan kelas sebagai cara untuk menambahkan
komentar terhadap kode. Ketiga, kami menggunakan teknik spasi putih dan format untuk menjaga
program dapat dibaca.
Perhatikan bagaimana program ini dibagi menjadi tiga tanggung jawab utama. Utama

https://translate.googleusercontent.com/translate_f 139/365
3/12/2020 www.it-ebooks.info
Program terkandung dalam kelas PrimePrinter dengan sendirinya. Tanggung jawabnya adalah untuk menangani
lingkungan eksekusi. Ini akan berubah jika metode doa berubah. Untuk
contoh, jika program ini dikonversi ke layanan SOAP, ini adalah kelas yang akan
terpengaruh.
The RowColumnPagePrinter tahu semua tentang cara memformat daftar nomor ke
halaman dengan jumlah baris dan kolom tertentu. Jika format output diperlukan
berubah, maka ini adalah kelas yang akan terpengaruh.
Kelas PrimeGenerator tahu cara membuat daftar bilangan prima. Perhatikan itu
tidak dimaksudkan untuk dipakai sebagai objek. Kelas hanyalah ruang lingkup yang bermanfaat
variabelnya dapat dideklarasikan dan disembunyikan. Kelas ini akan berubah jika algoritma untuk
menghitung perubahan bilangan prima.
Ini bukan penulisan ulang! Kami tidak memulai dari awal dan menulis program dari awal
lagi. Memang, jika Anda melihat dua program yang berbeda, Anda akan melihat bahwa mereka menggunakan
algoritma dan mekanisme yang sama untuk menyelesaikan pekerjaan mereka.
Perubahan tersebut dibuat dengan menulis suite uji yang memverifikasi perilaku tepat dari
program pertama. Kemudian segudang perubahan kecil dibuat, satu per satu. Setelah masing-masing
perubahan program dilakukan untuk memastikan bahwa perilaku tidak berubah. Satu kecil
langkah demi langkah, program pertama dibersihkan dan diubah menjadi program kedua.

www.it-ebooks.info

Halaman 178

Mengorganisir untuk Perubahan 147

Mengorganisir untuk Perubahan


Untuk sebagian besar sistem, perubahan bersifat berkelanjutan. Setiap perubahan membuat kita berisiko terhadap risiko
sisa sistem tidak lagi berfungsi sebagaimana dimaksud. Dalam sistem yang bersih kami mengatur
kelas sehingga mengurangi risiko perubahan.
Kelas Sql di Listing 10-9 digunakan untuk menghasilkan string SQL yang terbentuk dengan benar
metadata yang sesuai. Ini adalah pekerjaan yang sedang berjalan dan, dengan demikian, belum mendukung fungsi SQL.
nasionalitas seperti pernyataan pembaruan . Ketika saatnya tiba untuk kelas Sql untuk mendukung
perbarui pernyataan, kita harus "membuka" kelas ini untuk membuat modifikasi. Masalah
dengan membuka kelas adalah bahwa hal itu menimbulkan risiko. Setiap modifikasi ke kelas memiliki
potensi melanggar kode lain di kelas. Itu harus diuji ulang sepenuhnya.

Listing 10-9
Kelas yang harus dibuka untuk perubahan
kelas publik Sql {
Sql publik (Tabel string, kolom [] kolom)
public String create ()
public String insert (Object [] bidang)
public String selectAll ()
public String findByKey (String keyColumn, String keyValue)
pilih String publik (Kolom kolom, pola String)
public String select (Kriteria kriteria)
public String readyInsert ()
private String columnList (Kolom [] kolom)
private String valuesList (bidang Obyek [], kolom Kolom [] terakhir)
private String selectWithCriteria (String criteria)
private String placeholderList (Kolom [] kolom)
}

Kelas Sql harus berubah ketika kita menambahkan tipe pernyataan baru. Itu juga harus berubah
ketika kita mengubah detail dari tipe pernyataan tunggal — misalnya, jika kita perlu memodifikasi
yang pilih fungsi untuk subselects dukungan. Dua alasan untuk berubah ini berarti bahwa
Kelas Sql melanggar SRP.
Kami dapat menemukan pelanggaran SRP ini dari sudut pandang organisasi yang sederhana. Metode
garis besar Sql menunjukkan bahwa ada metode pribadi, seperti selectWithCriteria , itu
tampaknya hanya berhubungan dengan memilih pernyataan.
Perilaku metode privat yang hanya berlaku untuk sebagian kecil dari suatu kelas dapat bermanfaat
heuristik untuk menemukan area potensial untuk perbaikan. Namun, dorongan utama untuk
Tindakan harus merupakan perubahan sistem itu sendiri. Jika kelas Sql dianggap selesai secara logis,
maka kita tidak perlu khawatir tentang memisahkan tanggung jawab. Jika kita tidak membutuhkan pembaruan
fungsi untuk masa mendatang, maka kita harus meninggalkan Sql sendirian. Tapi begitu kita
menemukan diri kita membuka kelas, kita harus mempertimbangkan memperbaiki desain kita.
Bagaimana jika kita mempertimbangkan solusi seperti itu di Listing 10-10? Setiap antarmuka publik

https://translate.googleusercontent.com/translate_f 140/365
3/12/2020 www.it-ebooks.info
metode yang didefinisikan dalam Sql sebelumnya dari Listing 10-9 di refactored ke turunannya sendiri
dari kelas Sql . Perhatikan bahwa metode pribadi, seperti valuesList , pindah langsung ke tempat mana

www.it-ebooks.info

Halaman 179

148 Bab 10: Kelas

mereka dibutuhkan. Perilaku pribadi umum diisolasi ke sepasang kelas utilitas, Di mana
dan Daftar Kolom .

Listing 10-10
Satu set kelas tertutup
abstrak public class Sql {
Sql publik (Tabel string, kolom [] kolom)
publik abstrak menghasilkan String ();
}

CreateSql kelas publik memperluas Sql {


CreateSql publik (Tabel string, kolom [] kolom)
@Override public String generate ()
}

SelectSql kelas publik memperluas Sql {


SelectSql publik (Tabel string, kolom [] kolom)
@Override public String generate ()
}

kelas publik InsertSql memperluas Sql {


publik InsertSql (Tabel string, kolom [] kolom, Obyek [] bidang)
@Override public String generate ()
private String valuesList (bidang Obyek [], kolom Kolom [] terakhir)
}

SelectWithCriteriaSql kelas publik meluas Sql {


SelectWithCriteriaSql publik (
Tabel string, kolom [] kolom, Kriteria kriteria)
@Override public String generate ()
}

SelectWithMatchSql kelas publik meluas Sql {


SelectWithMatchSql publik (
Tabel string, kolom [] kolom, kolom kolom, pola string)
@Override public String generate ()
}

kelas publik FindByKeySql meluas Sql


FindByKeySql publik (
Tabel string, kolom [] kolom, String keyColumn, String keyValue)
@Override public String generate ()
}

kelas publik PreparedInsertSql memperluas Sql {


public PreparedInsertSql (Tabel string, kolom [] kolom)
@Override public String generate () {
private String placeholderList (Kolom [] kolom)
}

kelas publik Di mana {


public Where (Kriteria string)
public String menghasilkan ()
}

www.it-ebooks.info

Halaman 180

https://translate.googleusercontent.com/translate_f 141/365
3/12/2020 www.it-ebooks.info

Mengorganisir untuk Perubahan 149

Listing 10-10 (lanjutan)


Satu set kelas tertutup
ColumnList kelas publik {
Daftar Column publik (Kolom [] kolom)
public String menghasilkan ()
}

Kode di setiap kelas menjadi sangat sederhana. Pemahaman kami dibutuhkan


waktu untuk memahami kelas apa pun berkurang hampir tidak ada. Risiko yang satu fungsi bisa
istirahat yang lain menjadi semakin kecil. Dari sudut pandang tes, itu menjadi lebih mudah
tugas untuk membuktikan semua bit logika dalam solusi ini, karena semua kelas terisolasi dari satu
lain.
Sama pentingnya, ketika saatnya menambahkan pernyataan pembaruan , tidak ada yang ada
kelas perlu diubah! Kami memberi kode logika untuk membuat pernyataan pembaruan dalam subkelas baru Sql
bernama UpdateSql . Tidak ada kode lain dalam sistem yang akan rusak karena perubahan ini.
Logika Sql kami yang direstrukturisasi mewakili yang terbaik dari semua dunia. Ini mendukung SRP. Juga
mendukung prinsip desain kelas OO kunci lainnya yang dikenal sebagai Prinsip Terbuka-Tertutup, atau
OCP: 4 Kelas harus terbuka untuk ekstensi tetapi ditutup untuk modifikasi. Kami direstrukturisasi
Kelas Sql terbuka untuk memungkinkan fungsionalitas baru melalui subclassing, tetapi kita dapat melakukan perubahan ini
sambil menjaga setiap kelas lainnya tertutup. Kami cukup membuang kelas UpdateSql kami di tempat.
Kami ingin menyusun sistem kami sehingga kami dapat sesedikit mungkin bercinta dengan kami
memperbaruinya dengan fitur baru atau diubah. Dalam sistem yang ideal, kami menggabungkan fitur-fitur baru
mendatang dengan memperluas sistem, bukan dengan membuat modifikasi pada kode yang ada.

Mengisolasi dari Perubahan


Kebutuhan akan berubah, oleh karena itu kode akan berubah. Kami belajar di OO 101 bahwa ada
kelas crete, yang berisi detail implementasi (kode), dan kelas abstrak, yang
hanya mewakili konsep. Kelas klien tergantung pada detail nyata yang berisiko kapan
detail-detail itu berubah. Kami dapat memperkenalkan antarmuka dan kelas abstrak untuk membantu mengisolasikannya
dampak dari detail tersebut.
Ketergantungan pada detail nyata menciptakan tantangan untuk menguji sistem kami. Jika kita
membangun kelas Portofolio dan itu tergantung pada TokyoStockExchange API eksternal untuk
menurunkan nilai portofolio, uji kasus kami dipengaruhi oleh volatilitas pencarian tersebut.
Sulit untuk menulis ujian ketika kami mendapat jawaban berbeda setiap lima menit!
Alih-alih merancang Portofolio sehingga secara langsung tergantung pada TokyoStockExchange ,
kami membuat antarmuka, StockExchange , yang menyatakan metode tunggal:

antarmuka publik StockExchange {


Uang currentPrice (simbol String);
}

4. [PPP].

www.it-ebooks.info

Halaman 181

150 Bab 10: Kelas

Kami merancang TokyoStockExchange untuk mengimplementasikan antarmuka ini. Kami juga memastikan bahwa
konstruktor Portofolio menggunakan referensi StockExchange sebagai argumen:
Portofolio publik {
pertukaran StockExchange pribadi;
Portofolio publik (pertukaran Bursa Saham) {
this.exchange = tukar;
}
// ...
}

Sekarang pengujian kami dapat membuat implementasi yang dapat diuji dari antarmuka StockExchange itu
mengemulasi TokyoStockExchange . Implementasi tes ini akan memperbaiki nilai saat ini untuk
simbol apa pun yang kami gunakan dalam pengujian. Jika pengujian kami menunjukkan pembelian lima saham Microsoft

https://translate.googleusercontent.com/translate_f 142/365
3/12/2020 www.it-ebooks.info
untuk portofolio kami, kami mengkode implementasi tes untuk selalu mengembalikan $ 100 per saham
Microsoft Implementasi pengujian kami dari antarmuka StockExchange berkurang menjadi sederhana
pencarian tabel. Kami kemudian dapat menulis tes yang mengharapkan $ 500 untuk nilai portofolio keseluruhan kami.
PortofolioTest kelas publik {
pertukaran FixedStockExchangeStub pribadi;
portofolio Portofolio pribadi;

@Sebelum
setup void yang dilindungi () melempar Exception {
exchange = new FixedStockExchangeStub ();
exchange.fix ("MSFT", 100);
portfolio = Portofolio baru (pertukaran);
}

@Uji
public void GivenFiveMSFTTotalShouldBe500 () melempar Pengecualian {
portfolio.add (5, "MSFT");
Assert.assertEquals (500, portfolio.value ());
}
}

Jika suatu sistem dipisahkan cukup untuk diuji dengan cara ini, itu juga akan lebih fleksibel
dan mempromosikan lebih banyak penggunaan kembali. Tidak adanya kopling berarti elemen-elemen sistem kami
lebih terisolasi satu sama lain dan dari perubahan. Isolasi ini membuatnya lebih mudah untuk memahami
tahan setiap elemen sistem.
Dengan meminimalkan pemasangan dengan cara ini, kelas kami mematuhi prinsip desain kelas lain
dikenal sebagai Prinsip Ketergantungan Pembalikan (DIP). 5 Intinya, DIP mengatakan bahwa kami
kelas harus bergantung pada abstraksi, bukan pada detail nyata.
Alih-alih bergantung pada detail implementasi dari TokyoStock-
Kelas pertukaran , kelas Portofolio kami sekarang tergantung pada antarmuka StockExchange .
The bursa efek antarmuka merupakan konsep abstrak meminta harga saat ini
simbol. Abstraksi ini mengisolasi semua detail spesifik untuk memperoleh harga seperti itu,
termasuk dari mana harga itu diperoleh.

5. [PPP].

www.it-ebooks.info

Halaman 182

Bibliografi 151

Bibliografi
[RDD]: Desain Objek: Peran, Tanggung Jawab, dan Kolaborasi , Rebecca Wirfs-
Brock et al., Addison-Wesley, 2002.

[PPP]: Pengembangan Perangkat Lunak Agile: Prinsip, Pola, dan Praktik , Robert C. Martin,
Prentice Hall, 2002.

[Knuth92]: Pemrograman Literate, Donald E. Knuth, Pusat Studi Bahasa


dan Informasi, Universitas Junior Leland Stanford, 1992.

https://translate.googleusercontent.com/translate_f 143/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 183

halaman ini sengaja dibiarkan kosong

https://translate.googleusercontent.com/translate_f 144/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 184

11
Sistem
oleh Dr. Kevin Dean Wampler

“Kompleksitas membunuh. Itu menyedot kehidupan dari pengembang,


itu membuat produk sulit untuk direncanakan, dibangun, dan diuji. "

—Ray Ozzie, CTO, Microsoft Corporation

153

www.it-ebooks.info

Halaman 185

154 Bab 11: Sistem

https://translate.googleusercontent.com/translate_f 145/365
3/12/2020 www.it-ebooks.info

Bagaimana Anda Akan Membangun Kota?


Bisakah Anda mengatur sendiri semua detailnya? Mungkin tidak. Bahkan mengelola kota yang sudah ada
terlalu banyak untuk satu orang. Namun, kota berfungsi (sebagian besar waktu). Mereka bekerja karena kota
memiliki tim orang yang mengelola bagian kota tertentu, sistem air, listrik
sistem, lalu lintas, penegakan hukum, kode bangunan, dan sebagainya. Beberapa dari mereka adalah
bertanggung jawab atas gambaran besar , sementara yang lain fokus pada detail.
Kota-kota juga berfungsi karena mereka telah mengembangkan tingkat abstraksi dan mod
keanehan yang memungkinkan individu dan "komponen" yang mereka kelola bekerja
efektif, bahkan tanpa memahami gambaran besar.
Meskipun tim perangkat lunak sering diorganisir seperti itu juga, sistem tempat mereka bekerja
Seringkali tidak memiliki pemisahan yang sama antara keprihatinan dan tingkat abstraksi. Kode bersih
membantu kami mencapai ini di tingkat abstraksi yang lebih rendah. Dalam bab ini mari kita pertimbangkan caranya
untuk tetap bersih di level abstraksi yang lebih tinggi, level sistem .

Pisahkan Membangun Sistem dari Penggunaannya


Pertama, pertimbangkan bahwa konstruksi adalah proses yang sangat berbeda dari penggunaan . Saat saya menulis ini,
ada sebuah hotel baru yang sedang dibangun yang saya lihat di luar jendela saya di Chicago. Hari ini
kotak beton telanjang dengan derek konstruksi dan lift melesat ke luar. Itu
orang-orang yang sibuk di sana semua memakai topi keras dan pakaian kerja. Dalam satu tahun atau lebih, hotel akan
jadi. Derek dan lift akan hilang. Bangunan itu akan bersih, terbungkus dalam
dinding jendela kaca dan cat yang menarik. Orang-orang yang bekerja dan tinggal di sana akan terlihat
sangat berbeda juga.

Sistem perangkat lunak harus memisahkan proses startup, ketika objek aplikasi berada
dibangun dan dependensi "kabel" bersama-sama, dari logika runtime yang dibutuhkan
selesai setelah startup.

Proses startup adalah masalah yang harus ditangani oleh aplikasi apa pun. Ini adalah con pertama
CERN bahwa kita akan memeriksa dalam bab ini. The pemisahan keprihatinan adalah salah satu yang tertua
dan teknik desain paling penting dalam kerajinan kami.
Sayangnya, sebagian besar aplikasi tidak memisahkan masalah ini. Kode untuk startup
proses adalah ad hoc dan dicampur dengan logika runtime. Berikut ini adalah contoh khas:
Layanan publik getService () {
if (service == null)
service = MyServiceImpl baru (...); // Default cukup bagus untuk kebanyakan kasus?
layanan pengembalian;
}

Ini adalah idiom INISIALISASI / EVALUASI MALAS , dan memiliki beberapa kelebihan. Kita
jangan membuat overhead konstruksi kecuali kita benar-benar menggunakan objek, dan startup kita
hasilnya bisa lebih cepat. Kami juga memastikan bahwa null tidak pernah dikembalikan.

www.it-ebooks.info

Halaman 186

Pisahkan Membangun Sistem dari Penggunaannya 155

Namun, kami sekarang memiliki ketergantungan kode-keras pada MyServiceImpl dan semuanya
constructor membutuhkan (yang telah saya hilangkan). Kami tidak dapat mengkompilasi tanpa menyelesaikannya
dependensi, bahkan jika kita tidak pernah benar-benar menggunakan objek jenis ini saat runtime!
Pengujian bisa menjadi masalah. Jika MyServiceImpl adalah objek kelas berat, kita perlu melakukannya
pastikan bahwa T EST D OUBLE 1 atau M OCK O BJECT yang sesuai akan ditugaskan ke layanan
bidang wakil sebelum metode ini dipanggil selama pengujian unit. Karena kami memiliki konstruksi
logika dicampur dengan pemrosesan runtime normal, kita harus menguji semua jalur eksekusi (untuk
contoh, tes nol dan bloknya). Memiliki kedua tanggung jawab ini berarti bahwa
Metode melakukan lebih dari satu hal, jadi kami melanggar Prinsip Tanggung Jawab Tunggal
dengan cara yang kecil.
Mungkin yang terburuk, kita tidak tahu apakah MyServiceImpl adalah objek yang tepat
kasus. Saya tersirat dalam komentar. Mengapa kelas dengan metode ini harus melakukannya?
tahu konteks global? Bisakah kita pernah benar-benar tahu objek yang tepat untuk digunakan di sini? Apakah ini adil?
mungkin untuk satu jenis menjadi benar untuk semua konteks yang mungkin?
Satu kejadian LAZY - INITIALISASI bukan masalah serius, tentu saja. Namun,

https://translate.googleusercontent.com/translate_f 146/365
3/12/2020 www.it-ebooks.info
biasanya ada banyak contoh idiom pengaturan kecil seperti ini di aplikasi. Karenanya,
strategi pengaturan global (jika ada) tersebar di aplikasi, dengan sedikit
modularitas dan duplikasi yang sering signifikan.
Jika kita rajin membangun sistem yang terbentuk dengan baik dan kuat, kita tidak boleh membiarkannya
sedikit, idiom yang nyaman menyebabkan kerusakan modularitas. Proses startup dari objek cont
struction dan wiring tidak terkecuali. Kita harus memodulasi proses ini secara terpisah
logika runtime normal dan kita harus memastikan bahwa kita memiliki strategi global yang konsisten
ingin menyelesaikan dependensi utama kami.

Pemisahan Utama
Salah satu cara untuk memisahkan konstruksi dari penggunaan adalah dengan memindahkan semua aspek konstruksi
utama , atau modul dipanggil oleh utama , dan untuk merancang seluruh sistem dengan asumsi itu semua
objek telah dibangun dan dihubungkan dengan kabel dengan tepat. (Lihat Gambar 11-1.)
Alur kontrolnya mudah diikuti. Fungsi utama membangun objek yang diperlukan
untuk sistem, lalu meneruskannya ke aplikasi, yang hanya menggunakannya. Perhatikan
arah panah ketergantungan melintasi penghalang antara utama dan aplikasi.
Mereka semua pergi satu arah, menunjuk jauh dari utama. Ini berarti bahwa aplikasi tersebut tidak memiliki
pengetahuan utama atau proses konstruksi. Itu hanya mengharapkan bahwa semuanya memiliki
telah dibangun dengan benar.

Pabrik
Terkadang, tentu saja, kita perlu membuat aplikasi bertanggung jawab ketika suatu objek mendapat
dibuat. Misalnya, dalam sistem pemrosesan pesanan, aplikasi harus membuat

1. [Mezzaros07].

www.it-ebooks.info

Halaman 187

156 Bab 11: Sistem

Gambar 11-1
Memisahkan konstruksi pada main ()

Contoh LineItem untuk ditambahkan ke Pesanan . Dalam hal ini kita dapat menggunakan A BSTRACT F ACTORY 2
pola untuk memberikan kontrol aplikasi kapan membangun LineItems , tetapi simpan detailnya
konstruksi yang terpisah dari kode aplikasi. (Lihat Gambar 11-2.)

Gambar 11-2
Konstruksi pemisahan dengan pabrik

https://translate.googleusercontent.com/translate_f 147/365
3/12/2020 www.it-ebooks.info
Sekali lagi perhatikan bahwa semua titik dependensi dari utama ke OrderProcessing
aplikasi. Ini berarti bahwa aplikasi dipisahkan dari rincian cara melakukannya
membangun LineItem . Kemampuan itu dimiliki dalam implementasi LineItemFactoryIm , yang
ada di sisi utama garis. Namun aplikasi ini sepenuhnya mengendalikan kapan
yang LineItem contoh bisa dibangun dan bahkan dapat memberikan konstruktor aplikasi-spesifik
argumen.

2. [GOF].

www.it-ebooks.info

Halaman 188

Meningkatkan 157

Injeksi Ketergantungan
Mekanisme yang kuat untuk memisahkan konstruksi dari penggunaan adalah Dependency Injection (DI),
penerapan Inversion of Control (IoC) untuk manajemen ketergantungan. 3 Pembalikan
Kontrol memindahkan tanggung jawab sekunder dari suatu objek ke objek lain yang didedikasikan
untuk tujuan tersebut, dengan demikian mendukung Prinsip Tanggung Jawab Tunggal. Dalam konteks
manajemen ketergantungan, suatu objek tidak boleh mengambil tanggung jawab untuk instantiating ketergantungan
Dency sendiri. Alih-alih, ia harus menyerahkan tanggung jawab ini ke mekanisme “otoritatif” lainnya
nism, dengan demikian membalikkan kontrol. Karena pengaturan adalah masalah global, ini otoritatif
Mekanisme biasanya berupa rutinitas "utama" atau wadah tujuan khusus .
Pencarian JNDI adalah implementasi "parsial" dari DI, di mana objek meminta direktori
server untuk memberikan "layanan" yang cocok dengan nama tertentu.
MyService myService = (MyService) (jndiContext.lookup ("NameOfMyService"));

Objek yang memohon tidak mengontrol objek apa yang sebenarnya dikembalikan (selama itu
mengimplementasikan antarmuka yang sesuai, tentu saja), tetapi objek yang memohon masih aktif
menyelesaikan ketergantungan.
Injeksi Ketergantungan Sejati melangkah lebih jauh. Kelas tidak mengambil langkah langsung ke
menyelesaikan ketergantungannya; itu sepenuhnya pasif. Sebagai gantinya, ia menyediakan metode setter atau
argumen konstruktor (atau keduanya) yang digunakan untuk menyuntikkan dependensi. Selama
proses struction, wadah DI instantiates objek yang diperlukan (biasanya sesuai permintaan)
dan menggunakan argumen konstruktor atau metode penyetel yang disediakan untuk menyatukan ketergantungan
dency. Objek dependen mana yang benar-benar digunakan ditentukan melalui konfigurasi
file atau secara terprogram dalam modul konstruksi tujuan khusus.
Kerangka Kerja Pegas menyediakan wadah DI yang paling terkenal untuk Jawa. 4 Anda mendefinisikan
objek mana yang akan disatukan dalam file konfigurasi XML, lalu Anda minta secara khusus
objek dengan nama dalam kode Java. Kami akan melihat contohnya segera.
Tapi bagaimana dengan keutamaan LAZY - INITIALISASI ? Ungkapan ini masih terkadang
berguna dengan DI. Pertama, sebagian besar wadah DI tidak akan membangun objek sampai dibutuhkan. Kedua,
banyak dari wadah ini menyediakan mekanisme untuk memohon pabrik atau untuk membangun
proxy, yang dapat digunakan untuk LAZY - EVALUASI dan optimisasi serupa . 5

Meningkatkan
Kota tumbuh dari kota, yang tumbuh dari pemukiman. Pada awalnya jalannya sempit dan
praktis tidak ada, maka mereka diaspal, kemudian melebar dari waktu ke waktu. Bangunan kecil dan

3. Lihat, misalnya, [Fowler].


4. Lihat [Musim Semi]. Ada juga kerangka kerja Spring.NET.
5. Jangan lupa bahwa instantiasi / evaluasi yang malas hanyalah sebuah optimasi dan mungkin prematur!

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 148/365
3/12/2020 www.it-ebooks.info

Halaman 189

158 Bab 11: Sistem

plot kosong diisi dengan bangunan yang lebih besar, beberapa di antaranya pada akhirnya akan diganti
dengan gedung pencakar langit.
Pada awalnya tidak ada layanan seperti listrik, air, limbah, dan Internet (terkesiap!). Ini
layanan juga ditambahkan saat populasi dan kepadatan bangunan meningkat.
Pertumbuhan ini bukan tanpa rasa sakit. Berapa kali Anda mengemudi, bemper ke bemper
melalui proyek "perbaikan" jalan dan bertanya pada diri sendiri, "Mengapa mereka tidak membangunnya lebar-lebar
cukup pertama kali !? ”
Tapi itu tidak mungkin terjadi dengan cara lain. Siapa yang bisa membenarkan biaya enam
jalur jalan raya melalui tengah kota kecil yang mengantisipasi pertumbuhan? Siapa yang mau
ingin jalan seperti itu melewati kota mereka?
Ini adalah mitos bahwa kita bisa mendapatkan sistem "benar pada kali pertama." Sebaliknya, kita harus mengimplementasikan
hanya cerita hari ini , kemudian refactor dan perluas sistem untuk mengimplementasikan cerita baru
besok. Ini adalah inti dari kelincahan iteratif dan tambahan. Tes-driven dikembangkan
ment, refactoring, dan kode bersih yang mereka hasilkan membuatnya bekerja pada level kode.
Tetapi bagaimana dengan di tingkat sistem? Bukankah arsitektur sistem memerlukan perencanaan
ning? Tentu saja, itu tidak dapat tumbuh secara bertahap dari yang sederhana menjadi kompleks , bukan?

Sistem perangkat lunak unik dibandingkan dengan sistem fisik. Arsitektur mereka dapat tumbuh
secara bertahap, jika kita mempertahankan pemisahan kekhawatiran yang tepat.

Sifat fana sistem perangkat lunak memungkinkan ini, seperti yang akan kita lihat. Marilah kita terlebih dahulu
pertimbangkan contoh tandingan arsitektur yang tidak memisahkan masalah secara memadai.
Arsitektur EJB1 dan EJB2 asli tidak memisahkan masalah secara tepat dan
dengan demikian memberlakukan hambatan yang tidak perlu untuk pertumbuhan organik. Pertimbangkan Entity Bean untuk a
kelas Bank yang persisten . Entitas kacang adalah representasi in-memory dari data relasional, di
Dengan kata lain, baris tabel.
Pertama, Anda harus mendefinisikan antarmuka lokal (sedang diproses) atau jarak jauh (terpisah JVM), yang
klien akan gunakan. Listing 11-1 menunjukkan kemungkinan antarmuka lokal:

Listing 11-1
Antarmuka lokal EJB2 untuk Bank EJB
paket com.example.banking;
impor java.util.Collections;
import javax.ejb. *;

antarmuka publik BankLocal meluas java.ejb.EJBLocalObject {


String getStreetAddr1 () melempar EJBException;
String getStreetAddr2 () melempar EJBException;
String getCity () melempar EJBException;
String getState () melempar EJBException;
String getZipCode () melempar EJBException;
membatalkan setStreetAddr1 (String street1) melempar EJBException;
membatalkan setStreetAddr2 (String street2) melempar EJBException;
membatalkan setCity (Kota string) melempar EJBException;
membatalkan setState (keadaan String) melempar EJBException;

www.it-ebooks.info

Halaman 190

Meningkatkan 159

Listing 11-1 (lanjutan)


Antarmuka lokal EJB2 untuk Bank EJB
membatalkan setZipCode (zip String) melempar EJBException;

https://translate.googleusercontent.com/translate_f 149/365
3/12/2020 www.it-ebooks.info
Koleksi getAccounts () melempar EJBException;
membatalkan setAccounts (Akun koleksi) melempar EJBException;
membatalkan addAccount (AccountDTO akunDTO) melempar EJBException;
}

Saya telah menunjukkan beberapa atribut untuk alamat Bank dan kumpulan akun itu
bank memiliki, masing-masing akan memiliki data yang ditangani oleh EJB Akun terpisah .
Listing 11-2 menunjukkan kelas implementasi yang sesuai untuk bean Bank .

Listing 11-2
Implementasi EJB2 Entity Bean yang sesuai
paket com.example.banking;
impor java.util.Collections;
import javax.ejb. *;

public abstract class Bank mengimplementasikan javax.ejb.EntityBean {


// Logika bisnis...
String abstrak publik getStreetAddr1 ();
String abstrak publik getStreetAddr2 ();
public getCity String abstrak ();
getState String publik abstrak ();
public string abstrak getZipCode ();
kekosongan abstrak publik setStreetAddr1 (String street1);
kekosongan abstrak publik setStreetAddr2 (String street2);
kekosongan abstrak publik setCity (String city);
kekosongan abstrak publik setState (String state);
kekosongan abstrak publik setZipCode (String zip);
Koleksi publik abstrak getAccounts ();
public abstrak batal setAccounts (Akun koleksi);
addAccount publik batal (AccountDTO akunDTO) {
Konteks InitialContext = InitialContext baru ();
AccountHomeLocal accountHome = context.lookup ("AccountHomeLocal");
AkunLokal akun = akunHome.create (akunDTO);
Akun koleksi = getAccounts ();
accounts.add (akun);
}
// Logika wadah EJB
public abstrak void setId (Integer id);
abstrak publik Integer getId ();
ejbCreate Integer publik (Integer id) {...}
public void ejbPostCreate (Integer id) {...}
// Sisanya harus diimplementasikan tetapi biasanya kosong:
kekosongan publik setEntityContext (EntityContext ctx) {}
public void unsetEntityContext () {}
public void ejbActivate () {}
public void ejbPassivate () {}
public void ejbLoad () {}
public void ejbStore () {}
public void ejbRemove () {}
}

www.it-ebooks.info

Halaman 191

160 Bab 11: Sistem

Saya belum menunjukkan antarmuka LocalHome yang sesuai , pada dasarnya pabrik dulu
membuat objek, atau pun metode Bank finder (kueri) yang mungkin Anda tambahkan.
Akhirnya, Anda harus menulis satu atau lebih deskriptor penerapan XML yang menentukan
detail pemetaan objek-relasional ke toko kegigihan, perilaku transaksional yang diinginkan,
kendala keamanan, dan sebagainya .
Logika bisnis sangat erat dengan "wadah" aplikasi EJB2. Kamu harus
jenis wadah subclass dan Anda harus memberikan banyak metode siklus hidup yang diperlukan
oleh wadah.
Karena pemasangan ini ke wadah kelas berat, pengujian unit terisolasi sulit dilakukan.
Hal ini diperlukan untuk mengejek wadah, yang sulit, atau membuang banyak waktu penyebaran
EJB dan tes ke server nyata. Penggunaan kembali di luar arsitektur EJB2 secara efektif tidak mungkin
sible, karena kopling yang ketat.
Akhirnya, bahkan pemrograman berorientasi objek pun dirusak. Satu kacang tidak bisa mewarisi
dari kacang lain. Perhatikan logika untuk menambahkan akun baru. Ini biasa terjadi pada kacang EJB2
untuk mendefinisikan "objek transfer data" (DTO) yang pada dasarnya adalah "struct" tanpa perilaku.
Ini biasanya mengarah pada tipe yang berlebihan yang pada dasarnya menyimpan data yang sama, dan itu membutuhkan
kode boilerplate untuk menyalin data dari satu objek ke objek lainnya.

Kekhawatiran Lintas Sektoral

https://translate.googleusercontent.com/translate_f 150/365
3/12/2020 www.it-ebooks.info
Arsitektur EJB2 mendekati pemisahan kekhawatiran yang sebenarnya di beberapa area. Untuk
contoh, transaksi yang diinginkan, keamanan, dan beberapa perilaku kegigihan adalah
dideklarasikan dalam deskriptor penyebaran, terlepas dari kode sumber.
Perhatikan bahwa kekhawatiran seperti kegigihan cenderung melintasi batas objek alami
sebuah domain. Anda ingin mempertahankan semua objek Anda secara umum menggunakan strategi yang sama, untuk ujian
ple , menggunakan DBMS 6 tertentu dibandingkan file datar, mengikuti konvensi penamaan tertentu untuk
tabel dan kolom, menggunakan semantik transaksional yang konsisten, dan sebagainya .
Pada prinsipnya, Anda dapat bernalar tentang strategi kegigihan Anda secara modular, dienkapsulasi
cara. Namun, dalam praktiknya, Anda harus menyebarkan kode yang pada dasarnya sama yang mengimplementasikan
Strategi tence di banyak objek. Kami menggunakan istilah keprihatinan lintas sektoral untuk masalah seperti
ini. Sekali lagi, kerangka kerja ketekunan mungkin modular dan logika domain kita, dalam isola-
tion, mungkin modular. Masalahnya adalah persimpangan halus dari domain ini.
Bahkan, cara arsitektur EJB menangani ketekunan, keamanan, dan transaksi ,
“Mengantisipasi” pemrograman berorientasi aspek (AOP), 7 yang merupakan pendekatan tujuan umum
untuk mengembalikan modularitas untuk masalah lintas sektoral.
Dalam AOP, konstruksi modular yang disebut aspek menentukan titik mana dalam sistem yang seharusnya
ubah perilaku mereka secara konsisten untuk mendukung masalah tertentu. Ini
spesifikasi dilakukan dengan menggunakan mekanisme deklaratif atau program yang ringkas.

6. Sistem manajemen basis data.


7. Lihat [AOSD] untuk informasi umum tentang aspek dan [AspectJ]] dan [Colyer] untuk informasi spesifik AspectJ.

www.it-ebooks.info

Halaman 192

Proksi Jawa 161

Dengan menggunakan ketekunan sebagai contoh, Anda akan mendeklarasikan objek dan atribut mana (atau
pola - pola tersebut) harus dipertahankan dan kemudian mendelegasikan tugas-tugas kegigihan kepada Anda secara terus-menerus.
kerangka kerja tence. Modifikasi perilaku yang dibuat noninvasively 8 untuk kode sasaran
oleh kerangka kerja AOP. Mari kita lihat tiga aspek atau mekanisme seperti aspek di Jawa.

Proksi Jawa
Proxy Java cocok untuk situasi sederhana, seperti panggilan metode pembungkus secara individual
benda atau kelas. Namun, proxy dinamis yang disediakan di JDK hanya berfungsi dengan baik
antarmuka. Untuk kelas proxy, Anda harus menggunakan perpustakaan manipulasi byte-code, seperti
CGLIB, ASM, atau Javassist. 9
Listing 11-3 memperlihatkan kerangka untuk proksi JDK untuk memberikan dukungan persistensi
aplikasi Bank kami , hanya mencakup metode untuk mendapatkan dan mengatur daftar akun.

Listing 11-3
Contoh Proxy JDK
// Bank.java (menekan nama paket ...)
import java.utils. *;

// Abstraksi bank.
antarmuka publik Bank {
Koleksi <Account> getAccounts ();
membatalkan setAccounts (Pengumpulan akun <Account>);
}

// BankImpl.java
import java.utils. *;

// "Plain Java Java Object" (POJO) yang mengimplementasikan abstraksi.


BankImpl kelas publik mengimplementasikan Bank {
Daftar pribadi <Account> akun;

Koleksi publik <Account> getAccounts () {


mengembalikan akun;
}
kekosongan setAccount publik (Pengumpulan akun <Account>) {
this.accounts = ArrayList baru <Account> ();
untuk (Akun akun: akun) {
this.accounts.add (akun);
}
}
}

https://translate.googleusercontent.com/translate_f 151/365
3/12/2020 www.it-ebooks.info
// BankProxyHandler.java
import java.lang.reflect. *;
import java.util. *;

8. Berarti tidak diperlukan pengeditan manual dari kode sumber target yang diperlukan.
9. Lihat [CGLIB], [ASM], dan [Javassist].

www.it-ebooks.info

Halaman 193

162 Bab 11: Sistem

Listing 11-3 (lanjutan)


Contoh Proxy JDK
// "InvocationHandler" diperlukan oleh API proxy.
BankProxyHandler kelas publik mengimplementasikan InvocationHandler {
bank Bank swasta;

BankHandler publik (Bank bank) {


this.bank = bank;
}

// Metode yang didefinisikan dalam InvocationHandler


publik memanggil objek (objek proxy, metode metode, objek [] args)
melempar Throwable {
String methodName = method.getName ();
if (methodName.equals ("getAccounts")) {
bank.setAccounts (getAccountsFromDatabase ());
return bank.getAccounts ();
} lain jika (methodName.equals ("setAccounts")) {
bank.setAccounts ((Collection <Account>) args [0]);
setAccountsToDatabase (bank.getAccounts ());
kembali nol;
} lain {
...
}
}

// Banyak detail di sini:


Koleksi terproteksi <Account> getAccountsFromDatabase () {...}
kekosongan setAccountsToDatabase yang dilindungi (Pengumpulan akun <Account>) {...}
}

// Di tempat lain...

Bank bank = (Bank) Proxy.newProxyInstance (


Bank.class.getClassLoader (),
Kelas baru [] {Bank.class},
BankProxyHandler baru (BankImpl baru ()));

Kami mendefinisikan Bank antarmuka , yang akan dibungkus oleh proxy, dan Plain-Old
Java Object (POJO), BankImpl , yang mengimplementasikan logika bisnis. (Kami akan meninjau kembali POJO
segera.)
API Proksi membutuhkan objek InvocationHandler yang dipanggil untuk mengimplementasikan apa pun
Panggilan metode bank dilakukan ke proxy. BankProxyHandler kami menggunakan refleksi Java
API untuk memetakan pemanggilan metode generik ke metode yang sesuai di BankImpl ,
dan seterusnya.
Ada banyak kode di sini dan relatif rumit, bahkan untuk kasus sederhana ini. 10
Menggunakan salah satu pustaka byte-manipulasi juga sama menantang. Kode ini "volume"

10. Untuk contoh yang lebih rinci tentang Proxy API dan contoh penggunaannya, lihat, misalnya, [Goetz].

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 152/365
3/12/2020 www.it-ebooks.info
Halaman 194

Kerangka AOP Java Murni 163

dan kompleksitas adalah dua kelemahan proxy. Mereka membuatnya sulit untuk dibuat bersih
kode! Juga, proksi tidak menyediakan mekanisme untuk menentukan eksekusi seluruh sistem
"Poin" yang menarik, yang diperlukan untuk solusi AOP sejati. 11

Kerangka AOP Java Murni


Untungnya, sebagian besar boilerplate proxy dapat ditangani secara otomatis oleh alat. Proksi
digunakan secara internal dalam beberapa kerangka kerja Java, misalnya, Spring AOP dan JBoss AOP,
untuk mengimplementasikan aspek-aspek di Jawa murni. 12 Di Musim Semi, Anda menulis logika bisnis Anda sebagai Biasa-Lama
Java Objects . POJO murni berfokus pada domain mereka. Mereka tidak memiliki ketergantungan
kerangka kerja perusahaan (atau domain lainnya). Karenanya, mereka secara konsep lebih sederhana dan
lebih mudah untuk menguji drive. Kesederhanaan relatif membuatnya lebih mudah untuk memastikan bahwa Anda
menemukan cerita pengguna yang sesuai dengan benar dan mempertahankan serta mengembangkan kode untuk
cerita masa depan.
Anda memasukkan infrastruktur aplikasi yang diperlukan, termasuk konstruksi lintas sektoral
cerns seperti ketekunan, transaksi, keamanan, caching, failover, dan sebagainya, menggunakan deklarasi
file konfigurasi atau API. Dalam banyak kasus, Anda sebenarnya menentukan Spring atau
Aspek perpustakaan JBoss, di mana kerangka kerjanya menangani mekanisme menggunakan proxy Java
atau byte-code libraries secara transparan kepada pengguna. Deklarasi ini mendorong ketergantungan
wadah injeksi (DI), yang instantiate objek utama dan kabel mereka bersama
permintaan .
Listing 11-4 menunjukkan fragmen khas dari file konfigurasi Spring V2.5, app.xml 13 :

Listing 11-4
Pegas 2.X file konfigurasi
<beans>
...
<bean id = "appDataSource"
class = "org.apache.commons.dbcp.BasicDataSource"
destroy-method = "close"
p: driverClassName = "com.mysql.jdbc.Driver"
p: url = "jdbc: mysql: // localhost: 3306 / mydb"
p: username = "me" />

<bean id = "bankDataAccessObject"
class = "com.example.banking.persistence.BankDataAccessObject"
p: dataSource-ref = "appDataSource" />

<bean id = "bank"

11. AOP terkadang bingung dengan teknik yang digunakan untuk mengimplementasikannya, seperti metode intersepsi dan "pembungkus"
proksi. Nilai sebenarnya dari sistem AOP adalah kemampuan untuk menentukan perilaku sistemik secara ringkas dan modular.
12. Lihat [Musim Semi] dan [JBoss]. “Java Murni” berarti tanpa menggunakan AspectJ.
13. Diadaptasi dari http : //www.theserverside.com/tt/articles/article.tss? L = IntrotoSpring25

www.it-ebooks.info

Halaman 195

164 Bab 11: Sistem

Listing 11-4 (lanjutan)


Pegas 2.X file konfigurasi
class = "com.example.banking.model.Bank"
p: dataAccessObject-ref = "bankDataAccessObject" />
...
</beans>

https://translate.googleusercontent.com/translate_f 153/365
3/12/2020 www.it-ebooks.info
Setiap "kacang" seperti salah satu bagian dari "boneka Rusia", dengan objek domain untuk a
Bank diproksi (dibungkus) oleh objek pengakses data (DAO), yang dengan sendirinya diproksi oleh a
Sumber data driver JDBC. (Lihat Gambar 11-3.)

Gambar 11-3
"Boneka Rusia" dari dekorator

Klien yakin itu memanggil getAccounts () pada objek Bank , tetapi sebenarnya adalah
ke bagian terluar dari serangkaian objek D ECORATOR 14 yang memperluas perilaku dasar
dari Bank POJO. Kita bisa menambahkan dekorator lain untuk transaksi, caching, dan sebagainya .
Dalam aplikasi, beberapa baris diperlukan untuk meminta wadah DI untuk tingkat atas
objek dalam sistem, sebagaimana ditentukan dalam file XML.

XmlBeanFactory bf =
XmlBeanFactory baru (ClassPathResource baru ("app.xml", getClass ()));
Bank bank = (Bank) bf.getBean ("bank");

Karena begitu sedikit baris kode Java khusus Musim Semi diperlukan, aplikasi ini hampir
sepenuhnya dipisahkan dari Spring , menghilangkan semua masalah kopling ketat dari sistem
seperti EJB2.
Meskipun XML dapat secara verbal dan sulit dibaca, 15 “kebijakan” yang ditentukan dalam konteks ini
file figuration lebih sederhana daripada proksi rumit dan logika aspek yang disembunyikan
lihat dan buat secara otomatis. Jenis arsitektur ini begitu memikat sehingga
bekerja seperti Spring menyebabkan perombakan lengkap standar EJB untuk versi 3. EJB3

14. [GOF].
15. Contoh dapat disederhanakan menggunakan mekanisme yang mengeksploitasi konvensi atas konfigurasi dan Java 5 anotasi untuk mengurangi
jumlah logika "kabel" eksplisit yang diperlukan.

www.it-ebooks.info

Halaman 196

Kerangka AOP Java Murni 165

sebagian besar mengikuti model Musim Semi yang secara deklaratif mendukung penggunaan lintas sektoral
File konfigurasi XML dan / atau penjelasan Java 5.
Listing 11-5 menunjukkan objek Bank kami ditulis ulang di EJB3 16 .

Listing 11-5
EBJ3 Bank EJB
paket com.example.banking.model;
import javax.persistence. *;
import java.util.ArrayList;
impor java.util.Collection;

@Kesatuan
@Tabel (name = "BANK")
Bank kelas publik mengimplementasikan java.io.Serializable {
@Id @GeneratedValue (strategi = GenerationType.AUTO)
id int pribadi;

@Embeddable // Objek "inlined" di baris DB Bank


Alamat kelas publik {
String streetAddr1 yang dilindungi;
String streetAddr2 yang dilindungi;
kota String yang dilindungi;
negara bagian String yang dilindungi;
zipCode String yang dilindungi;
}

@Embedded
alamat Alamat pribadi;

@OneToMany (cascade = CascadeType.ALL, fetch = FetchType.EAGER,

https://translate.googleusercontent.com/translate_f 154/365
3/12/2020 www.it-ebooks.info
mappedBy =akun
Koleksi pribadi <Account> "bank")
= ArrayList baru <Account> ();

public int getId () {


return id;
}

membatalkan publik setId (int id) {


this.id = id;
}

addAccount public void (Akun akun) {


account.setBank (ini);
accounts.add (akun);
}

Koleksi publik <Account> getAccounts () {


mengembalikan akun;
}

16. Diadaptasi dari http://www.onjava.com/pub/a/onjava/2006/05/17/standar-with-ejb3-java-persistence-api.html

www.it-ebooks.info

Halaman 197

166 Bab 11: Sistem

Listing 11-5 (lanjutan)


EBJ3 Bank EJB
kekosongan setAccount publik (Pengumpulan akun <Account>) {
this.accounts = akun;
}
}

Kode ini jauh lebih bersih daripada kode EJB2 asli. Beberapa perincian entitas adalah
masih di sini, terkandung dalam anotasi. Namun, karena tidak ada informasi yang
sisi anotasi, kode bersih, jelas, dan karenanya mudah untuk menguji drive, memelihara, dan
begitu seterusnya.
Beberapa atau semua informasi ketekunan dalam anotasi dapat dipindahkan ke XML
deskriptor penyebaran, jika diinginkan, meninggalkan POJO yang benar-benar murni. Jika pemetaan kegigihan
detail tidak akan sering berubah, banyak tim dapat memilih untuk menyimpan anotasi, tetapi dengan
jauh lebih sedikit kelemahan berbahaya dibandingkan dengan invasi EJB2.

AspekJ Aspek
Akhirnya, alat berfitur lengkap untuk memisahkan masalah melalui aspek adalah AspectJ
bahasa, 17 ekstensi Java yang menyediakan dukungan “kelas” untuk aspek-aspek seperti modular-
itu membangun. Pendekatan Java murni yang disediakan oleh Spring AOP dan JBoss AOP adalah suf
cukup untuk 80–90 persen kasus di mana aspek-aspeknya paling berguna. Namun, AspekJ
menyediakan alat yang sangat kaya dan kuat untuk memisahkan masalah. Kekurangan dari
AspectJ adalah kebutuhan untuk mengadopsi beberapa alat baru dan untuk belajar konstruksi bahasa baru dan
idiom penggunaan.
Masalah adopsi telah sebagian dimitigasi oleh "annota-
bentuk tion ”dari AspectJ, di mana penjelasan Java 5 digunakan untuk mendefinisikan aspek menggunakan Java murni
kode. Juga, Kerangka Kerja Spring memiliki sejumlah fitur yang membuat penggabungan
aspek berbasis anotasi jauh lebih mudah untuk tim dengan pengalaman AspectJ yang terbatas.
Diskusi lengkap tentang AspectJ berada di luar cakupan buku ini. Lihat [AspectJ], [Colyer],
dan [Spring] untuk informasi lebih lanjut.

Uji Drive Arsitektur Sistem


Kekuatan memisahkan keprihatinan melalui pendekatan seperti aspek tidak dapat dilebih-lebihkan. Jika
Anda dapat menulis logika domain aplikasi Anda menggunakan POJO, dipisahkan dari arsitektur apa pun
Ture keprihatinan di tingkat kode, maka dimungkinkan untuk benar-benar menguji drive arsitektur Anda. Kamu
dapat mengembangkannya dari yang sederhana menjadi canggih, sesuai kebutuhan, dengan mengadopsi teknologi baru

https://translate.googleusercontent.com/translate_f 155/365
3/12/2020 www.it-ebooks.info

17. Lihat [AspectJ] dan [Colyer].

www.it-ebooks.info

Halaman 198

Optimalkan Pengambilan Keputusan 167

permintaan. Tidak perlu melakukan Big Design Up Front 18 (BDUF). Bahkan, BDUF adalah genap
berbahaya karena menghambat adaptasi terhadap perubahan, karena resistensi psikologis terhadap penyakit.
upaya carding sebelum dan karena cara pilihan arsitektur mempengaruhi selanjutnya
memikirkan desain.
Arsitek bangunan harus melakukan BDUF karena tidak layak membuat arsitek radikal.
perubahan tektural ke struktur fisik yang besar setelah konstruksi berjalan dengan baik. 19
Meskipun perangkat lunak memiliki fisika sendiri , 20 secara ekonomis layak untuk membuat radikal
berubah, jika struktur perangkat lunak memisahkan keprihatinannya secara efektif.
Ini berarti kita dapat memulai proyek perangkat lunak dengan “sederhana secara naif” tetapi
lihat arsitektur, menyampaikan cerita pengguna yang bekerja dengan cepat, lalu menambahkan lebih banyak infrastruktur
saat kami meningkatkan. Beberapa situs web terbesar di dunia telah mencapai ketersediaan yang sangat tinggi
dan kinerja, menggunakan caching data yang canggih, keamanan, virtualisasi, dan sebagainya,
semua dilakukan secara efisien dan fleksibel karena desain yang digabungkan secara minimal sesuai
sederhana di setiap tingkat abstraksi dan ruang lingkup.
Tentu saja, ini tidak berarti bahwa kita masuk ke proyek "tanpa kemudi." Kami punya beberapa
harapan dari ruang lingkup umum, tujuan, dan jadwal untuk proyek, serta gen
struktur eral sistem yang dihasilkan. Namun, kita harus mempertahankan kemampuan untuk berubah
tentu saja dalam menanggapi keadaan yang berkembang.
Arsitektur EJB awal hanyalah salah satu dari banyak API terkenal yang over-eng
neered dan bahwa kompromi pemisahan kekhawatiran. Bahkan API yang dirancang dengan baik dapat over-
bunuh ketika mereka tidak benar-benar dibutuhkan. API yang baik sebagian besar akan hilang dari tampilan sebagian besar
pada saat itu, sehingga tim mengeluarkan sebagian besar upaya kreatifnya yang berfokus pada pengguna
ries sedang diimplementasikan. Jika tidak, maka kendala arsitektur akan menghambat efisiensi
pengiriman nilai optimal kepada pelanggan.
Untuk rekap diskusi panjang ini,

Arsitektur sistem yang optimal terdiri dari domain termodulasi yang menjadi perhatian, masing-masing
diimplementasikan dengan Plain Old Java (atau lainnya) Objects. Domain yang berbeda terintegrasi
parut bersama dengan Aspek invasif minimal atau alat seperti Aspek. Arsitektur ini bisa
menjadi test-driven, sama seperti kode.

Optimalkan Pengambilan Keputusan


Modularitas dan pemisahan masalah membuat manajemen dan keputusan terdesentralisasi
memungkinkan. Dalam sistem yang cukup besar, apakah itu kota atau proyek perangkat lunak, tidak
satu orang dapat membuat semua keputusan.

18. Tidak menjadi bingung dengan praktik yang baik dari desain di muka, BDUF adalah praktik merancang semuanya di muka sebelumnya
mengimplementasikan apa saja.
19. Masih ada sejumlah besar eksplorasi berulang dan pembahasan detail, bahkan setelah konstruksi dimulai.
20. Istilah fisika perangkat lunak pertama kali digunakan oleh [Kolence].

www.it-ebooks.info

Halaman 199

https://translate.googleusercontent.com/translate_f 156/365
3/12/2020 www.it-ebooks.info

168 Bab 11: Sistem

Kita semua tahu yang terbaik adalah memberikan tanggung jawab kepada orang-orang yang paling berkualitas. Kami sering
lupa bahwa yang terbaik adalah menunda keputusan sampai saat-saat terakhir yang memungkinkan . Ini bukan
malas atau tidak bertanggung jawab; memungkinkan kita membuat pilihan berdasarkan informasi dengan informasi terbaik.
Keputusan prematur adalah keputusan yang dibuat dengan pengetahuan suboptimal. Kami akan memilikinya
umpan balik pelanggan jauh lebih sedikit, refleksi mental pada proyek, dan pengalaman dengan kami
pilihan implementasi jika kita memutuskan terlalu cepat.

Kelincahan yang diberikan oleh sistem POJO dengan masalah termodulasi memungkinkan kita untuk membuat
mal, keputusan tepat waktu, berdasarkan pada pengetahuan terbaru. Kompleksitasnya
keputusan juga berkurang.

Gunakan Standar dengan Bijak, Ketika Mereka Menambahkan Nilai yang Dapat Menunjukkan
Konstruksi bangunan adalah keajaiban yang harus diperhatikan karena kecepatan bangunan baru
dibangun (bahkan di tengah musim dingin) dan karena desain luar biasa yang mungkin
ble dengan teknologi saat ini. Konstruksi adalah industri yang matang dengan bagian-bagian yang sangat optimal,
metode, dan standar yang telah berkembang di bawah tekanan selama berabad-abad.
Banyak tim menggunakan arsitektur EJB2 karena itu adalah standar, bahkan ketika lebih ringan-
berat dan desain yang lebih mudah sudah cukup. Saya telah melihat tim
menjadi terobsesi dengan berbagai standar yang sangat hyped dan kehilangan fokus pada implementasi
nilai bagi pelanggan mereka.

Standar memudahkan penggunaan kembali ide dan komponen, merekrut orang dengan pengalaman yang relevan
rience, merangkum ide-ide bagus, dan menyatukan komponen. Namun, proses
menciptakan standar kadang-kadang bisa memakan waktu terlalu lama bagi industri untuk menunggu, dan beberapa standar
kehilangan kontak dengan kebutuhan nyata para pengadopsi yang seharusnya mereka layani.

Sistem Memerlukan Bahasa Domain-Khusus


Konstruksi bangunan, seperti kebanyakan domain, telah mengembangkan bahasa yang kaya dengan
lary, idiom, dan pola 21 yang menyampaikan informasi penting dengan jelas dan singkat. Dalam soft-
ware, baru-baru ini ada minat baru dalam menciptakan Bahasa Khusus Domain
(DSL), 22 yang terpisah, bahasa scripting kecil atau API dalam bahasa standar itu
mengizinkan kode untuk ditulis sehingga dibaca seperti bentuk prosa terstruktur yang merupakan domain
ahli mungkin menulis.
DSL yang baik meminimalkan "kesenjangan komunikasi" antara konsep domain dan
kode yang mengimplementasikannya, sama seperti praktik tangkas mengoptimalkan komunikasi dalam a
tim dan dengan para pemangku kepentingan proyek. Jika Anda menerapkan logika domain di

21. Pekerjaan [Alexander] sangat berpengaruh pada komunitas perangkat lunak.


22. Lihat, misalnya, [DSL]. [JMock] adalah contoh yang bagus dari Java API yang membuat DSL.

www.it-ebooks.info

Halaman 200

Bibliografi 169

bahasa yang sama dengan yang digunakan oleh pakar domain, ada sedikit risiko bahwa Anda akan salah
terlambat domain menjadi implementasi.
DSL, ketika digunakan secara efektif, meningkatkan level abstraksi di atas idiom dan desain kode
pola. Mereka memungkinkan pengembang untuk mengungkapkan maksud kode pada tingkat yang sesuai
abstraksi.

Domain-Bahasa Khusus memungkinkan semua tingkat abstraksi dan semua domain di aplikasi
untuk diekspresikan sebagai POJO, dari kebijakan tingkat tinggi hingga detail tingkat rendah.

Kesimpulan

https://translate.googleusercontent.com/translate_f 157/365
3/12/2020 www.it-ebooks.info
Sistem juga harus bersih. Arsitektur invasif menguasai logika domain dan
dampak kelincahan. Ketika logika domain dikaburkan, kualitas menderita karena bug menemukannya
lebih mudah disembunyikan dan cerita menjadi lebih sulit untuk diterapkan. Jika kelincahan dikompromikan, produksi-
tivity menderita dan manfaat TDD hilang.
Pada semua tingkatan abstraksi, maksudnya harus jelas. Ini hanya akan terjadi jika Anda
tulis POJO dan Anda menggunakan mekanisme seperti aspek untuk memasukkan implementasi lain
keprihatinan secara non-invasif.
Apakah Anda sedang merancang sistem atau modul individual, jangan pernah lupa untuk menggunakan
hal paling sederhana yang mungkin bisa berhasil .

Bibliografi
[Alexander]: Christopher Alexander, Jalan Bangunan Abadi, Universitas Oxford
Pers, New York, 1979.

[AOSD]: Port Pengembangan Perangkat Lunak Berorientasi Aspek, http://aosd.net

[ASM]: Halaman Utama ASM, http : //asm.objectweb.org/

[AspectJ]: http://eclipse.org/aspectj

[CGLIB]: Perpustakaan Pembuatan Kode, http://cglib.sourceforge.net/

[Colyer]: Adrian Colyer, Andy Clement, George Hurley, Mathew Webster, Eclipse
AspectJ, Person Education, Inc., Upper Saddle River, NJ, 2005.

[DSL]: Bahasa pemrograman khusus domain, http : //en.wikipedia.org/wiki/Domain-


specific_programming_language

[Fowler]: Pembalikan Wadah Kontrol dan pola Injeksi Ketergantungan,


http://martinfowler.com/articles/injection.html

www.it-ebooks.info

Halaman 201

170 Bab 11: Sistem

[Goetz]: Brian Goetz, Teori dan Praktek Jawa: Dekorasi dengan Dynamic Proxie s,
http://www.ibm.com/developerworks/java/library/j-jtp08305.html

[Javassist]: Halaman Utama Javassist, http://www.csg.is.titech.ac.jp/~chiba/javassist/

[JBoss]: Halaman Depan JBoss, http://jboss.org

[JMock]: JMock — Pustaka Objek Mock Ringan untuk Java, http://jmock.org

[Kolence]: Kenneth W. Kolence, Fisika perangkat lunak dan ukuran kinerja komputer-
, Prosiding konferensi tahunan ACM — Volume 2 , Boston, Massachusetts,
hlm. 1024-1040, 1972.

[Musim Semi]: Kerangka Kerja Musim Semi , http://www.springframework.org

[Mezzaros07]: Pola XUnit , Gerard Mezzaros, Addison-Wesley, 2007.

[GOF]: Pola Desain: Elemen-elemen Perangkat Lunak Berorientasi Objek yang Dapat Digunakan Kembali , Gamma et al.,
Addison-Wesley, 1996.

https://translate.googleusercontent.com/translate_f 158/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 202

12
Munculnya
oleh Jeff Langr

Bersihkan melalui Desain Muncul


Bagaimana jika ada empat aturan sederhana yang bisa Anda ikuti yang akan membantu Anda menciptakan yang baik
desain saat Anda bekerja? Bagaimana jika dengan mengikuti aturan-aturan ini Anda memperoleh wawasan tentang struktur
Gambar dan desain kode Anda, membuatnya lebih mudah untuk menerapkan prinsip-prinsip seperti SRP dan DIP?
Bagaimana jika keempat aturan ini memfasilitasi munculnya desain yang bagus?
Banyak dari kita merasa bahwa empat aturan Kent Beck tentang Simple Design 1 sangat membantu
membuat perangkat lunak yang dirancang dengan baik.

1. [XPE].

171

https://translate.googleusercontent.com/translate_f 159/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 203

172 Bab 12: Kemunculan

Menurut Kent, desain adalah "sederhana" jika mengikuti aturan berikut:

• Menjalankan semua tes


• Tidak mengandung duplikasi
• Mengekspresikan maksud programmer
• Meminimalkan jumlah kelas dan metode

Aturan diberikan sesuai urutan kepentingannya.

Aturan Desain Sederhana 1: Menjalankan Semua Pengujian


Pertama dan terutama, desain harus menghasilkan sistem yang berfungsi sebagaimana dimaksud. Suatu sistem mungkin
memiliki desain yang sempurna di atas kertas, tetapi jika tidak ada cara sederhana untuk memverifikasi bahwa sistem
sekutu bekerja sebagaimana dimaksud, maka semua usaha kertas dipertanyakan.
Suatu sistem yang diuji secara komprehensif dan lulus dari semua pengujiannya sepanjang waktu adalah
sistem yang mampu. Itu pernyataan yang jelas, tetapi yang penting. Sistem yang tidak dapat diuji
tidak dapat diverifikasi. Dapat diperdebatkan, sistem yang tidak dapat diverifikasi seharusnya tidak pernah digunakan.
Untungnya, membuat sistem kami dapat diuji mendorong kami menuju desain tempat kelas kami
adalah tujuan kecil dan tunggal. Hanya saja lebih mudah untuk menguji kelas yang sesuai dengan SRP. Itu
semakin banyak tes yang kami tulis, semakin kami akan terus mendorong ke arah hal-hal yang lebih mudah untuk diuji.
Jadi memastikan sistem kami dapat diuji sepenuhnya membantu kami menciptakan desain yang lebih baik.
Kopling ketat membuatnya sulit untuk menulis tes. Jadi, sama halnya, semakin banyak tes yang kami tulis,
semakin banyak kita menggunakan prinsip-prinsip seperti DIP dan alat-alat seperti injeksi ketergantungan, antarmuka, dan
abstraksi untuk meminimalkan kopling. Desain kami semakin meningkat.
Hebatnya, mengikuti aturan sederhana dan jelas yang mengatakan kita perlu melakukan tes dan
menjalankannya secara terus-menerus memengaruhi kepatuhan sistem kami pada sasaran OO utama yang rendah
kopling dan kohesi tinggi. Tes menulis mengarah pada desain yang lebih baik.

Aturan Desain Sederhana 2–4: Refactoring


Begitu kami memiliki tes, kami diberdayakan untuk menjaga kode dan kelas kami tetap bersih. Kami melakukan ini dengan
secara bertahap refactoring kode. Untuk setiap beberapa baris kode yang kami tambahkan, kami berhenti dan merenung
pada desain baru. Apakah kita baru saja menurunkannya? Jika demikian, kami membersihkannya dan menjalankan tes kami untuk menunjukkan
menyatakan bahwa kami belum merusak apa pun. Fakta bahwa kita menjalani tes ini menghilangkan rasa takut
bahwa membersihkan kode akan merusaknya!
Selama langkah refactoring ini, kita dapat menerapkan apa saja dari seluruh tubuh pengetahuan
tentang desain perangkat lunak yang baik. Kita dapat meningkatkan kohesi, mengurangi kopling, memisahkan
cerns, memodulasi masalah sistem, mengecilkan fungsi dan kelas kami, memilih nama yang lebih baik,
dan seterusnya. Di sinilah kami menerapkan tiga aturan final desain sederhana: Hilangkan
duplikasi, memastikan ekspresif, dan meminimalkan jumlah kelas dan metode.

www.it-ebooks.info

Halaman 204

Tidak Ada Duplikasi 173

https://translate.googleusercontent.com/translate_f 160/365
3/12/2020 www.it-ebooks.info

Tidak Ada Duplikasi


Duplikasi adalah musuh utama dari sistem yang dirancang dengan baik. Ini merupakan tambahan
pekerjaan, risiko tambahan, dan kompleksitas tambahan yang tidak perlu. Manifes duplikasi
sendiri dalam berbagai bentuk. Baris-baris kode yang terlihat persis sama tentu saja adalah duplikasi.
Baris-baris kode yang serupa seringkali dapat dipijat agar terlihat lebih mirip
mereka dapat lebih mudah di-refactored. Dan duplikasi dapat ada dalam bentuk lain seperti
duplikasi implementasi. Misalnya, kami mungkin memiliki dua metode dalam koleksi
kelas:
ukuran int () {}
boolean isEmpty () {}

Kami dapat memiliki implementasi terpisah untuk setiap metode. The isEmpty metode bisa melacak
sebuah boolean, sementara ukuran bisa melacak penghitung. Atau, kita dapat menghilangkan duplikasi ini dengan mengikat
isEmpty dengan definisi ukuran :

boolean isEmpty () {
return 0 == size ();
}

Menciptakan sistem yang bersih membutuhkan kemauan untuk menghilangkan duplikasi, bahkan hanya dalam beberapa
baris kode. Sebagai contoh, pertimbangkan kode berikut:
public void scaleToOneDimension (
float diinginkanDimensi, float imageDimension) {
if (Math.abs (diinginkanDimensi - imageDimension) <errorThreshold)
kembali;
float scalingFactor = diinginkanDimensi / imageDimension;
scalingFactor = (float) (Math.floor (scalingFactor * 100) * 0.01f);

RenderedOp newImage = ImageUtilities.getScaledImage (


gambar, scalingFactor, scalingFactor);
image.dispose ();
System.gc ();
image = newImage;
}
public void rotate (int derajat) yang disinkronkan {
RenderedOp newImage = ImageUtilities.getRotatedImage (
gambar, derajat);
image.dispose ();
System.gc ();
image = newImage;
}

Untuk menjaga sistem ini bersih, kita harus menghilangkan sejumlah kecil duplikasi di antaranya
metode scaleToOneDimension dan rotate :
public void scaleToOneDimension (
float diinginkanDimensi, float imageDimension) {
if (Math.abs (diinginkanDimensi - imageDimension) <errorThreshold)
kembali;
float scalingFactor = diinginkanDimensi / imageDimension;
scalingFactor = (float) (Math.floor (scalingFactor * 100) * 0.01f);

www.it-ebooks.info

Halaman 205

174 Bab 12: Kemunculan

replaceImage (ImageUtilities.getScaledImage (
gambar, scalingFactor, scalingFactor));
}

public void rotate (int derajat) yang disinkronkan {


replaceImage (ImageUtilities.getRotatedImage (gambar, derajat));
}

void replaceImage pribadi (RenderedOp newImage) {


image.dispose ();
System.gc ();
image = newImage;
}
Ketika kami mengekstrak kesamaan pada tingkat yang sangat kecil ini, kami mulai mengenali pelanggaran SRP.
Jadi kita mungkin memindahkan metode yang baru diekstrak ke kelas lain. Itu meningkatkan visibilitasnya.
Orang lain dalam tim mungkin mengenali peluang untuk lebih jauh mengabstraksi yang baru
metode dan menggunakannya kembali dalam konteks yang berbeda. "Reuse in the small" ini dapat menyebabkan sistem com-
kerumitan menyusut secara dramatis. Memahami cara mencapai penggunaan kembali dalam kelompok kecil sangat penting
untuk mencapai penggunaan kembali dalam skala besar.

https://translate.googleusercontent.com/translate_f 161/365
3/12/2020 www.it-ebooks.info
Pola T EMPLATE M ETHOD 2 adalah teknik umum untuk menghilangkan level yang lebih tinggi
duplikasi. Sebagai contoh:
VacationPolicy kelas publik {
public void accrueUSDivisionVacation () {
// kode untuk menghitung liburan berdasarkan jam kerja hingga saat ini
// ...
// kode untuk memastikan liburan memenuhi minimum AS
// ...
// kode untuk menerapkan vaction ke catatan penggajian
// ...
}

public void accrueEUDivisionVacation () {


// kode untuk menghitung liburan berdasarkan jam kerja hingga saat ini
// ...
// kode untuk memastikan liburan memenuhi minimum UE
// ...
// kode untuk menerapkan vaction ke catatan penggajian
// ...
}
}

Kode lintas accrueUSDivisionVacation dan accrueEuropeanDivisionVacation sebagian besar


sama, dengan pengecualian menghitung minimum legal. Itu sedikit dari algoritma
perubahan berdasarkan jenis karyawan.
Kita dapat menghilangkan duplikasi yang jelas dengan menerapkan pola T EMPLATE M ETHOD .
abstrak public class VacationPolicy {
public void accrueVacation () {
calculBaseVacationHours ();

2. [GOF].

www.it-ebooks.info

Halaman 206

Ekspresif 175

alterForLegalMinimums ();
applyToPayroll ();
}

private void calculBaseVacationHours () {/ * ... * /};


abstrak lindungi alterForLegalMinimums ();
batal pribadi applyToPayroll () {/ * ... * /};
}

USVacationPolicy kelas publik memperluas VacationPolicy {


@Override protected void alterForLegalMinimums () {
// Logika khusus AS
}
}

EUVacationPolicy kelas publik memperluas VacationPolicy {


@Override protected void alterForLegalMinimums () {
// Logika khusus UE
}
}
Subkelas mengisi "lubang" dalam algoritma accrueVacation , memasok hanya bit
informasi yang tidak digandakan.

Ekspresif
Sebagian besar dari kita memiliki pengalaman mengerjakan kode berbelit-belit. Banyak dari kita memiliki
membuat beberapa kode yang berbelit-belit sendiri. Sangat mudah untuk menulis kode yang kami mengerti, karena
pada saat kami menulisnya kami sangat memahami masalah yang kami coba selesaikan.
Pemelihara kode lainnya tidak akan memiliki pemahaman yang begitu dalam.
Sebagian besar biaya proyek perangkat lunak adalah pemeliharaan jangka panjang. Untuk
meminimalkan potensi cacat saat kami memperkenalkan perubahan, sangat penting bagi kami untuk dapat melakukannya
memahami apa yang dilakukan suatu sistem. Sebagai sistem menjadi lebih kompleks, mereka mengambil lebih banyak dan
lebih banyak waktu bagi pengembang untuk memahami, dan ada peluang yang lebih besar untuk kesalahan
pemahaman. Oleh karena itu, kode harus dengan jelas menyatakan maksud dari pembuatnya. Lebih jelas
penulis dapat membuat kode, semakin sedikit waktu yang dihabiskan orang lain untuk memahaminya. Ini
akan mengurangi cacat dan mengecilkan biaya perawatan.
Anda dapat mengekspresikan diri dengan memilih nama baik. Kami ingin dapat mendengar kelas

https://translate.googleusercontent.com/translate_f 162/365
3/12/2020 www.it-ebooks.info
atau nama fungsi dan tidak heran ketika kita menemukan tanggung jawabnya.
Anda juga dapat mengekspresikan diri dengan menjaga fungsi dan kelas Anda kecil. Kecil
kelas dan fungsi biasanya mudah diberi nama, mudah ditulis, dan mudah dimengerti.
Anda juga dapat mengekspresikan diri dengan menggunakan nomenklatur standar. Pola desain, untuk
Contohnya, sebagian besar tentang komunikasi dan ekspresif. Dengan menggunakan standar
nama pola, seperti C OMMAND atau V ISITOR , atas nama kelas yang mengimplementasikan
Dengan pola-pola itu, Anda dapat mendeskripsikan desain Anda dengan ringkas kepada pengembang lain.
Unit test yang ditulis dengan baik juga ekspresif. Tujuan utama dari tes adalah untuk bertindak sebagai dokumen.
sebagai contoh. Seseorang yang membaca tes kami harus bisa mendapatkan pemahaman yang cepat
berdiri tentang apa yang dimaksud dengan kelas.

www.it-ebooks.info

Halaman 207

176 Bab 12: Kemunculan

Tetapi cara terpenting untuk menjadi ekspresif adalah dengan mencoba . Terlalu sering kita mendapatkan kode kita
bekerja dan kemudian beralih ke masalah berikutnya tanpa memberikan pemikiran yang cukup untuk membuat
kode itu mudah dibaca orang berikutnya. Ingat, kemungkinan orang berikutnya membaca
kodenya adalah Anda.
Jadi, banggakan sedikit dalam pengerjaan Anda. Luangkan sedikit waktu dengan masing-masing fungsi Anda
dan kelas. Pilih nama yang lebih baik, pisahkan fungsi besar menjadi fungsi yang lebih kecil, dan
umumnya hanya mengurus apa yang telah Anda buat. Perawatan adalah sumber daya yang berharga.

Kelas dan Metode Minimal


Bahkan konsep sama mendasarnya seperti penghapusan duplikasi, ekspresi kode, dan
SRP bisa terlalu jauh. Dalam upaya membuat kelas dan metode kita kecil, kita mungkin
buat terlalu banyak kelas dan metode kecil. Jadi aturan ini menunjukkan bahwa kita juga menjaga fungsi kita.
tion dan jumlah kelas rendah.
Hitungan kelas dan metode yang tinggi kadang-kadang merupakan hasil dari dogmatisme sia-sia. Menipu-
sider, misalnya, standar pengkodean yang bersikeras membuat antarmuka untuk masing-masing dan
setiap kelas. Atau pertimbangkan pengembang yang bersikeras bahwa bidang dan perilaku harus selalu terpisah.
terbagi dalam kelas data dan kelas perilaku. Dogma semacam itu harus dilawan dan lebih
pendekatan pragmatis diadopsi.
Tujuan kami adalah menjaga sistem kami secara keseluruhan tetap kecil sementara kami juga menjaga fungsi kami
dan kelas kecil. Ingat, bagaimanapun, bahwa aturan ini adalah prioritas terendah dari empat aturan
Desain Sederhana. Jadi, meskipun penting untuk menjaga agar kelas dan fungsi tetap rendah, ini penting
lebih penting untuk memiliki tes, menghilangkan duplikasi, dan mengekspresikan diri Anda.

Kesimpulan
Apakah ada serangkaian praktik sederhana yang dapat menggantikan pengalaman? Jelas tidak. Di sisi lain
tangan, praktik yang dijelaskan dalam bab ini dan dalam buku ini adalah bentuk mengkristal dari
pengalaman puluhan tahun yang dinikmati oleh penulis. Mengikuti praktik sederhana
desain dapat dan memang mendorong dan memungkinkan pengembang untuk mematuhi prinsip dan
pola yang kalau tidak butuh bertahun-tahun untuk dipelajari.

Bibliografi
[XPE]: Dijelaskan Pemrograman Ekstrim: Rangkul Perubahan, Kent Beck, Addison-
Wesley, 1999.

[GOF]: Pola Desain: Elemen-elemen Perangkat Lunak Berorientasi Objek yang Dapat Digunakan Kembali, Gamma et al.,
Addison-Wesley, 1996.

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 163/365
3/12/2020 www.it-ebooks.info

Halaman 208

13
Konkurensi
oleh Brett L. Schuchert

“Objek adalah abstraksi dari pemrosesan. Thread adalah abstraksi dari jadwal. "

—James O. Coplien 1

1. Korespondensi pribadi.

177

www.it-ebooks.info

Halaman 209

178 Bab 13: Konkurensi

Menulis program bersama yang bersih itu sulit — sangat sulit. Lebih mudah menulis kode itu
dieksekusi dalam satu utas. Juga mudah untuk menulis kode multithreaded yang terlihat bagus di Internet
permukaan tetapi rusak di tingkat yang lebih dalam. Kode seperti itu berfungsi dengan baik sampai sistem ditempatkan

https://translate.googleusercontent.com/translate_f 164/365
3/12/2020 www.it-ebooks.info
dibawah tekanan.
Dalam bab ini kita membahas perlunya pemrograman bersamaan, dan kesulitannya
itu hadir. Kami kemudian menyajikan beberapa rekomendasi untuk mengatasi kesulitan-kesulitan itu,
dan menulis kode bersamaan bersih. Akhirnya, kami menyimpulkan dengan masalah yang berkaitan dengan pengujian
kode bersamaan.
Clean Concurrency adalah topik yang kompleks, layak untuk sebuah buku dengan sendirinya. Strategi kami dalam hal ini
buku ini untuk menyajikan ikhtisar di sini dan memberikan tutorial yang lebih terperinci dalam “Concurrency II”
di halaman 317. Jika Anda hanya ingin tahu tentang konkurensi, maka bab ini akan cukup untuk Anda
sekarang. Jika Anda perlu memahami konkurensi pada level yang lebih dalam, maka Anda harus membaca
melalui tutorial juga.

Mengapa konkurensi?
Concurrency adalah strategi decoupling. Ini membantu kita decouple apa yang akan dilakukan dari saat itu
selesai. Dalam aplikasi single-threaded apa dan kapan sangat kuat digabungkan
keadaan seluruh aplikasi seringkali dapat ditentukan dengan melihat stack backtrace. SEBUAH
programmer yang mendebug sistem seperti itu dapat mengatur breakpoint, atau urutan breakpoint,
dan mengetahui keadaan sistem dimana breakpoint terkena.
Memisahkan apa dari kapan dapat secara dramatis meningkatkan throughput dan struktur
bentuk aplikasi. Dari sudut pandang struktural, aplikasi ini tampak seperti banyak
Mereka berkolaborasi dengan komputer alih-alih satu loop utama. Ini dapat membuat sistem lebih mudah
untuk memahami dan menawarkan beberapa cara ampuh untuk memisahkan masalah.
Pertimbangkan, misalnya, model "Servlet" aplikasi Web standar. Sistem ini
berjalan di bawah payung wadah Web atau EJB yang mengelola sebagian
Rency untuk Anda. Servlet dijalankan secara tidak sinkron setiap kali permintaan Web masuk.
Programmer servlet tidak harus mengelola semua permintaan yang masuk. Pada prinsipnya,
setiap eksekusi servlet hidup di dunianya sendiri dan dipisahkan dari semua serv- lainnya
biarkan eksekusi.
Tentu saja jika semudah itu, bab ini tidak perlu. Bahkan, decou-
Pling yang disediakan oleh wadah Web jauh dari sempurna. Pemrogram Servlet harus
sangat sadar, dan sangat berhati-hati, untuk memastikan program bersamaan mereka benar. Tetap saja, itu
manfaat struktural dari model servlet signifikan.
Tetapi struktur bukan satu-satunya motif untuk mengadopsi konkurensi. Beberapa sistem memiliki
waktu respons dan kendala throughput yang membutuhkan solusi konkuren kode tangan.
Misalnya, pertimbangkan agregator informasi single-threaded yang memperoleh informasi
dari banyak situs Web yang berbeda dan menggabungkan informasi itu ke dalam ringkasan harian. Karena

www.it-ebooks.info

Halaman 210

Mengapa konkurensi? 179

sistem ini adalah single threaded, hits setiap situs Web pada gilirannya, selalu menyelesaikan satu sebelumnya
mulai selanjutnya. Run harian perlu dijalankan dalam waktu kurang dari 24 jam. Namun, karena lebih banyak
dan lebih banyak situs Web ditambahkan, waktu berkembang hingga dibutuhkan lebih dari 24 jam untuk berkumpul
semua data. Single-thread melibatkan banyak menunggu di soket Web untuk I / O untuk menyelesaikan.
Kita dapat meningkatkan kinerja dengan menggunakan algoritma multithreaded yang lebih dari hit
satu situs web sekaligus.
Atau pertimbangkan sistem yang menangani satu pengguna pada satu waktu dan hanya membutuhkan satu detik
waktu per pengguna. Sistem ini cukup responsif untuk beberapa pengguna, tetapi sebagai jumlah
pengguna meningkat, waktu respons sistem meningkat. Tidak ada pengguna yang ingin mengantre
di belakang 150 lainnya! Kami dapat meningkatkan waktu respons sistem ini dengan menangani
banyak pengguna secara bersamaan.
Atau pertimbangkan sistem yang menginterpretasikan kumpulan data besar tetapi hanya bisa memberikan solusi lengkap
tion setelah memproses semuanya. Mungkin setiap set data dapat diproses secara berbeda
komputer, sehingga banyak set data sedang diproses secara paralel.

Mitos dan Kesalahpahaman


Dan ada alasan kuat untuk mengadopsi konkurensi. Namun, seperti yang kami katakan sebelumnya,
konkurensi sulit . Jika Anda tidak terlalu berhati-hati, Anda dapat membuat beberapa situasi yang sangat buruk.
Pertimbangkan mitos umum dan kesalahpahaman ini:

https://translate.googleusercontent.com/translate_f 165/365
3/12/2020 www.it-ebooks.info

• Concurrency selalu meningkatkan kinerja.


Concurrency kadang - kadang dapat meningkatkan kinerja, tetapi hanya ketika ada banyak menunggu
waktu yang dapat dibagi antara beberapa utas atau beberapa prosesor. Tidak ada situasi-
asi itu sepele.
• Desain tidak berubah ketika menulis program bersamaan.
Bahkan, desain algoritma konkuren dapat sangat berbeda dari
desain sistem single-threaded. The decoupling dari apa dari kapan biasanya memiliki
efek besar pada struktur sistem.
• Memahami masalah konkurensi tidak penting ketika bekerja dengan sebuah wadah
seperti wadah Web atau EJB.
Bahkan, Anda sebaiknya tahu apa yang sedang dilakukan wadah Anda dan bagaimana cara menjaganya
masalah pembaruan serentak dan jalan buntu yang dijelaskan nanti dalam bab ini.

Berikut adalah beberapa gigitan suara yang lebih seimbang mengenai penulisan perangkat lunak bersamaan:

• Concurrency menimbulkan beberapa overhead, baik dalam kinerja maupun penulisan tambahan
kode.
• Konkurensi yang benar itu rumit, bahkan untuk masalah sederhana.

www.it-ebooks.info

Halaman 211

180 Bab 13: Konkurensi

• Bug konkurensi biasanya tidak dapat diulang, sehingga sering diabaikan sebagai satu kali 2
bukannya cacat sejati mereka.
• Konkurensi seringkali membutuhkan perubahan mendasar dalam strategi desain .

Tantangan
Apa yang membuat pemrograman konkuren begitu sulit? Pertimbangkan kelas sepele berikut:

kelas publik X {
private int lastIdUsed;

public int getNextId () {


return ++ lastIdUsed;
}
}

Katakanlah kita membuat instance X , setel field lastIdUsed menjadi 42, dan kemudian bagikan
contoh antara dua utas. Sekarang anggaplah kedua utas tersebut memanggil metode
getNextId () ; ada tiga kemungkinan hasil:

• Utas satu mendapat nilai 43, utas dua mendapat nilai 44, lastIdUsed adalah 44.
• Utas satu mendapat nilai 44, utas dua mendapat nilai 43, lastIdUsed adalah 44.
• Utas satu mendapat nilai 43, utas dua mendapat nilai 43, lastIdUsed adalah 43.

Hasil ketiga yang mengejutkan 3 terjadi ketika kedua utas saling menginjak. Ini terjadi
pena karena ada banyak jalur yang memungkinkan bahwa kedua benang dapat melewati yang satu itu
baris kode Java, dan beberapa jalur tersebut menghasilkan hasil yang salah. Betapa banyak berbeda
jalan di sana? Untuk benar-benar menjawab pertanyaan itu, kita perlu memahami apa yang dimaksud dengan Just-In-
Time Compiler tidak dengan kode byte yang dihasilkan, dan mengerti apa itu memori Java
Model dianggap atom.
Jawaban cepat, bekerja dengan hanya kode byte yang dihasilkan, adalah bahwa ada 12.870
jalur eksekusi 4 yang mungkin berbeda untuk kedua utas yang menjalankan dalam getNextId
metode. Jika jenis lastIdUsed diubah dari int ke long , jumlah yang mungkin
jalur meningkat menjadi 2.704.156. Tentu saja sebagian besar jalur tersebut menghasilkan hasil yang valid. Itu
Masalahnya adalah bahwa beberapa dari mereka tidak .

Prinsip Pertahanan Concurrency


Berikut ini adalah serangkaian prinsip dan teknik untuk mempertahankan sistem Anda dari
https://translate.googleusercontent.com/translate_f 166/365
3/12/2020 www.it-ebooks.info
masalah kode bersamaan.

2. Sinar kosmik, gangguan, dan sebagainya.


3. Lihat “Menggali Lebih Dalam” di halaman 323.
4. Lihat “Jalur Eksekusi yang Mungkin” di halaman 321.

www.it-ebooks.info

Halaman 212

Prinsip Pertahanan Concurrency 181

Prinsip Tanggung Jawab Tunggal


SRP 5 menyatakan bahwa metode / kelas / komponen tertentu harus memiliki satu alasan
perubahan. Desain concurrency cukup kompleks untuk menjadi alasan untuk mengubahnya sendiri
dan karena itu layak dipisahkan dari sisa kode. Sayangnya, itu semua terlalu
umum untuk detail implementasi konkurensi yang akan disematkan langsung ke pro
kode pengurang. Berikut adalah beberapa hal yang perlu dipertimbangkan:

• Kode yang terkait dengan konkurensi memiliki siklus pengembangan, perubahan, dan penyempurnaan sendiri.
• Kode yang terkait dengan konkurensi memiliki tantangannya sendiri, yang berbeda dari dan sering
lebih sulit daripada kode yang tidak berhubungan dengan mata uang.
• Jumlah cara gagal kode berbasis konkurensi yang gagal dibuat
cukup menantang tanpa menambah beban kode aplikasi di sekitarnya.

Rekomendasi : Pisahkan kode terkait konkurensi Anda dari kode lain . 6

Konsekuensi: Batasi Cakupan Data


Seperti yang kita lihat, dua utas yang memodifikasi bidang yang sama dari objek bersama dapat mengganggu masing-masing
lainnya, menyebabkan perilaku yang tidak terduga. Salah satu solusinya adalah menggunakan kata kunci yang disinkronkan untuk
melindungi bagian penting dalam kode yang menggunakan objek bersama. Penting untuk dibatasi
jumlah bagian kritis tersebut. Semakin banyak tempat, data yang dibagikan dapat diperbarui
lebih mungkin:

• Anda akan lupa untuk melindungi satu atau lebih dari tempat-tempat itu — secara efektif melanggar semua kode
yang mengubah data yang dibagikan itu.
• Akan ada duplikasi upaya yang diperlukan untuk memastikan semuanya efektif
dijaga (pelanggaran KERING 7 ).
• Akan sulit untuk menentukan sumber kegagalan, yang sudah cukup sulit
mencari.

Rekomendasi : Ambil hati enkapsulasi data; sangat membatasi akses apa pun
data yang dapat dibagikan.

Konsekuensi: Gunakan Salinan Data


Cara yang baik untuk menghindari data bersama adalah menghindari berbagi data di tempat pertama. Dalam beberapa situasi
memungkinkan untuk menyalin objek dan memperlakukannya sebagai hanya-baca. Dalam kasus lain mungkin saja
mungkin untuk menyalin objek, mengumpulkan hasil dari banyak utas di salinan ini dan kemudian
gabungkan hasilnya dalam satu utas.

5. [PPP]
6. Lihat “Contoh Klien / Server” di halaman 317.
7. [PRAG].

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 167/365
3/12/2020 www.it-ebooks.info
Halaman 213

182 Bab 13: Konkurensi

Jika ada cara mudah untuk menghindari berbagi objek, kode yang dihasilkan akan jauh lebih kecil kemungkinannya
menyebabkan masalah. Anda mungkin khawatir tentang biaya semua pembuatan objek tambahan. ini
Layak bereksperimen untuk mencari tahu apakah ini sebenarnya masalah. Namun, jika menggunakan salinan
objek memungkinkan kode untuk menghindari sinkronisasi, penghematan dalam menghindari kunci intrinsik
kemungkinan menebus biaya tambahan pembuatan dan pengumpulan sampah.

Konsekuensi: Utas Sebisa Mungkin Independen


Pertimbangkan untuk menulis kode utas Anda sehingga setiap utas ada di dunianya sendiri, berbagi
tidak ada data dengan utas lainnya. Setiap utas memproses satu permintaan klien, dengan semua permintaannya
data yang diperlukan berasal dari sumber yang tidak dibagi dan disimpan sebagai variabel lokal. Ini membuat
setiap utas itu berperilaku seolah-olah itu adalah satu-satunya utas di dunia dan tidak ada
persyaratan sinkronisasi.
Sebagai contoh, kelas yang subclass dari HttpServlet menerima semua informasinya
sebagai parameter diteruskan ke metode doGet dan doPost . Ini membuat setiap Servlet bertindak
seolah-olah memiliki mesin sendiri. Selama kode di Servlet hanya menggunakan variabel lokal,
tidak ada kemungkinan Servlet akan menyebabkan masalah sinkronisasi. Tentu saja,
sebagian besar aplikasi yang menggunakan Servlets akhirnya mengalami sumber daya bersama seperti database
koneksi.
Rekomendasi : Mencoba mem-partisi data menjadi subset independen dari yang dapat dilakukan
dioperasikan oleh utas independen, mungkin dalam prosesor yang berbeda.

Ketahui Perpustakaan Anda


Java 5 menawarkan banyak perbaikan untuk pengembangan bersamaan dari versi sebelumnya. Sana
beberapa hal yang perlu dipertimbangkan saat menulis kode berulir di Java 5:

• Gunakan koleksi thread-safe yang disediakan.


• Gunakan kerangka kerja pelaksana untuk menjalankan tugas yang tidak terkait.
• Gunakan solusi nonblocking bila memungkinkan.
• Beberapa kelas perpustakaan tidak aman untuk thread.

Koleksi Thread-Safe
Ketika Jawa masih muda, Doug Lea menulis buku mani 8 Concurrent Programming di
Jawa . Seiring dengan buku itu ia mengembangkan beberapa koleksi thread-safe, yang kemudian
menjadi bagian dari JDK dalam paket java.util.concurrent . Koleksi dalam paket itu-
usia aman untuk situasi multithreaded dan mereka berperforma baik. Bahkan, itu

8. [Lea99].

www.it-ebooks.info

Halaman 214

Ketahui Model Eksekusi Anda 183

Implementasi ConcurrentHashMap berkinerja lebih baik daripada HashMap di hampir semua situasi. Itu
juga memungkinkan untuk secara simultan membaca dan menulis, dan memiliki metode yang mendukung
operasi komposit umum yang tidak aman untuk thread. Jika Java 5 adalah
ment lingkungan, mulailah dengan ConcurrentHashMap .
Ada beberapa jenis kelas lain yang ditambahkan untuk mendukung konkurensi tingkat lanjut
rancangan. Berikut ini beberapa contoh:

ReentrantLock Kunci yang dapat diperoleh dalam satu metode dan dilepaskan dalam metode lain.

https://translate.googleusercontent.com/translate_f 168/365
3/12/2020 www.it-ebooks.info
Tiang sinyal Implementasi dari semaphore klasik, kunci dengan hitungan.
CountDownLatch Kunci yang menunggu sejumlah acara sebelum melepaskan semua
utas menunggu di atasnya. Ini memungkinkan semua utas memiliki peluang yang adil
mulai pada waktu yang hampir bersamaan.

Rekomendasi : Tinjau kelas yang tersedia untuk Anda. Dalam kasus Jawa, jadilah
akrab dengan java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks.

Ketahui Model Eksekusi Anda


Ada beberapa cara berbeda untuk mempartisi perilaku dalam aplikasi bersamaan. Untuk
cuss mereka kita perlu memahami beberapa definisi dasar.

Sumber Daya Terikat Sumber daya ukuran tetap atau angka yang digunakan dalam lingkungan bersamaan
ment. Contohnya termasuk koneksi database dan baca / ukuran tetap
tulis buffer.
Pengecualian Saling Hanya satu utas yang dapat mengakses data bersama atau sumber daya bersama di a
waktu.
Kelaparan Satu utas atau grup utas dilarang dilanjutkan
untuk waktu yang sangat lama atau selamanya. Misalnya, selalu membiarkan
ting thread yang berjalan cepat melalui pertama bisa kelaparan berjalan lebih lama
ning utas jika tidak ada akhir utas yang berjalan cepat.
Jalan buntu Dua atau lebih utas saling menunggu untuk selesai. Setiap utas
memiliki sumber yang dibutuhkan utas lainnya dan tidak ada yang bisa menyelesaikannya
sampai mendapat sumber daya lainnya.
Livelock Threads berbaris, masing-masing mencoba melakukan pekerjaan tetapi menemukan yang lain
"di jalan." Karena resonansi, utas terus berusaha
membuat kemajuan tetapi tidak bisa untuk waktu yang terlalu lama—
atau selamanya.

Dengan definisi ini, kita sekarang dapat mendiskusikan berbagai model eksekusi yang digunakan di
pemrograman bersamaan.

www.it-ebooks.info

Halaman 215

184 Bab 13: Konkurensi

Produser-Konsumen 9
Satu atau lebih utas produsen membuat beberapa pekerjaan dan menempatkannya di buffer atau antrian. Satu atau
lebih banyak utas konsumen memperoleh karya itu dari antrian dan melengkapinya. Antrian
antara produsen dan konsumen adalah sumber daya terikat . Ini berarti produsen harus
tunggu ruang kosong di antrian sebelum menulis dan konsumen harus menunggu sampai ada
sesuatu dalam antrian untuk dikonsumsi. Koordinasi antara produsen dan konsumen
melalui antrian melibatkan produsen dan konsumen saling memberi sinyal. Para produsen menulis
ke antrian dan memberi sinyal bahwa antrian tidak lagi kosong. Konsumen membaca dari antrian
dan menandakan bahwa antrian tidak lagi penuh. Keduanya berpotensi menunggu untuk diberitahu ketika mereka
dapat melanjutkan.

Pembaca-Penulis 10
Ketika Anda memiliki sumber daya bersama yang terutama berfungsi sebagai sumber informasi untuk dibaca
Namun, yang kadang-kadang diperbarui oleh penulis, throughput adalah masalah. Menekankan
throughput dapat menyebabkan kelaparan dan akumulasi informasi basi. Memungkinkan
pembaruan dapat memengaruhi throughput. Koordinasikan pembaca sehingga mereka tidak membaca sesuatu
penulis memperbarui dan sebaliknya adalah tindakan penyeimbang yang sulit. Penulis cenderung memblokir banyak
untuk jangka waktu yang lama, sehingga menyebabkan masalah throughput.
Tantangannya adalah menyeimbangkan kebutuhan pembaca dan penulis untuk memuaskan dengan benar
operasi, berikan hasil yang wajar dan hindari kelaparan. Strategi sederhana
membuat penulis menunggu sampai tidak ada pembaca sebelum mengizinkan penulis untuk melakukan
memperbarui. Namun, jika ada pembaca terus menerus, penulis akan kelaparan. Di sisi lain
tangan, jika ada penulis yang sering dan mereka diberi prioritas, throughput akan menderita. Temukan-

https://translate.googleusercontent.com/translate_f 169/365
3/12/2020 www.it-ebooks.info
Yang menyeimbangkan dan menghindari masalah pembaruan bersamaan adalah apa yang menjadi masalah.

Filsuf Bersantap 11
Bayangkan sejumlah filsuf duduk mengelilingi meja bundar. Garpu ditempatkan ke
kiri masing-masing filsuf. Ada semangkuk besar spageti di tengah meja. Itu
filsuf menghabiskan waktu berpikir kecuali mereka lapar. Begitu lapar, mereka memilih
naik garpu di kedua sisi mereka dan makan. Seorang filsuf tidak bisa makan kecuali dia memegang
dua garpu. Jika filsuf di sebelah kanan atau kirinya sudah menggunakan salah satu garpu dia
perlu, ia harus menunggu sampai filsuf itu selesai makan dan meletakkan garpu kembali.
Setelah seorang filsuf makan, dia meletakkan kedua garpunya kembali ke atas meja dan menunggu sampai dia
lapar lagi.
Ganti filsuf dengan utas dan garpu dengan sumber daya dan masalah ini serupa
lar ke banyak aplikasi perusahaan di mana proses bersaing untuk sumber daya. Kecuali perawatan-
dirancang sepenuhnya, sistem yang bersaing dengan cara ini dapat mengalami jalan buntu, livelock,
throughput, dan penurunan efisiensi.

9. http://en.wikipedia.org/wiki/Producer-consumer
10. http://en.wikipedia.org/wiki/Readers-writers_problem
11. http://en.wikipedia.org/wiki/Dining_philosophers_problem

www.it-ebooks.info

Halaman 216

Biarkan Bagian yang Disinkronkan Kecil 185

Sebagian besar masalah bersamaan yang kemungkinan akan Anda temui adalah beberapa variasi dari ini
tiga masalah. Pelajari algoritma ini dan tulis solusi menggunakan mereka sendiri
bahwa ketika Anda menemukan masalah bersamaan, Anda akan lebih siap untuk menyelesaikannya
masalah.
Rekomendasi : Pelajari algoritma dasar ini dan pahami solusinya.

Waspadai Ketergantungan Antara Metode yang Disinkronkan


Ketergantungan antara metode yang disinkronkan menyebabkan bug halus dalam kode bersamaan. Itu
Bahasa Jawa memiliki gagasan disinkronkan , yang melindungi metode individual. Bagaimana-
pernah, jika ada lebih dari satu metode yang disinkronkan pada kelas bersama yang sama, maka Anda
sistem dapat ditulis secara tidak benar. 12
Rekomendasi : Hindari menggunakan lebih dari satu metode pada objek bersama.
Akan ada saat ketika Anda harus menggunakan lebih dari satu metode pada objek yang dibagikan.
Ketika hal ini terjadi, ada tiga cara untuk membuat kode tersebut benar:

• Penguncian Berbasis Klien —Memiliki klien mengunci server sebelum memanggil yang pertama
metode dan pastikan sejauh kunci termasuk kode yang memanggil metode terakhir.
• Penguncian Berbasis Server —Dengan server membuat metode yang mengunci server, panggilan
semua metode, dan kemudian membuka kunci. Mintalah klien memanggil metode baru.
• Server Beradaptasi — buat perantara yang melakukan penguncian. Ini adalah ujian
Penguncian berbasis server, di mana server asli tidak dapat diubah.

Biarkan Bagian yang Disinkronkan Kecil


Kata kunci yang disinkronkan memperkenalkan kunci. Semua bagian kode dijaga oleh
kunci yang sama dijamin hanya memiliki satu utas yang mengeksekusinya pada waktu tertentu
waktu. Kunci mahal karena mereka membuat penundaan dan menambah overhead. Jadi kita tidak melakukannya
ingin mengotori kode kita dengan pernyataan yang disinkronkan . Di sisi lain, bagian penting
tions 13 harus dijaga. Jadi kami ingin merancang kode kami dengan beberapa bagian kritis
bisa jadi.
Beberapa programmer yang naif mencoba untuk mencapai hal ini dengan membuat bagian-bagian penting mereka
besar. Namun, memperluas sinkronisasi di luar bagian kritis minimal akan meningkat
pertikaian dan menurunkan kinerja. 14
Rekomendasi : Jaga agar bagian yang Anda sinkronkan sekecil mungkin.

https://translate.googleusercontent.com/translate_f 170/365
3/12/2020 www.it-ebooks.info
12. Lihat “Ketergantungan Antara Metode Dapat Memecah Kode Serentak” di halaman 329.
13. Bagian kritis adalah bagian kode mana saja yang harus dilindungi dari penggunaan simultan agar program menjadi benar.
14. Lihat “Meningkatkan Throughput” di halaman 333.

www.it-ebooks.info

Halaman 217

186 Bab 13: Konkurensi

Menulis Kode Shut-Down yang Benar Sulit


Menulis sistem yang dimaksudkan untuk tetap hidup dan berjalan selamanya berbeda dari menulis beberapa
sesuatu yang berfungsi untuk sementara dan kemudian dimatikan dengan anggun.
Shutdown yang anggun bisa sulit untuk diperbaiki. Masalah umum adalah kebuntuan, 15
dengan utas menunggu sinyal untuk melanjutkan yang tidak pernah datang.
Misalnya, bayangkan sebuah sistem dengan utas induk yang menghasilkan beberapa utas anak
dan kemudian menunggu semuanya selesai sebelum melepaskan sumber dayanya dan dimatikan. Bagaimana jika
salah satu thread yang dimunculkan menemui jalan buntu? Orang tua akan menunggu selamanya, dan sistem
tidak akan pernah ditutup.
Atau pertimbangkan sistem serupa yang telah diperintahkan untuk dimatikan. Orang tua memberi tahu semua
anak-anak menelurkan untuk meninggalkan tugas mereka dan menyelesaikan. Tapi bagaimana kalau dua anak itu
beroperasi sebagai pasangan produsen / konsumen. Misalkan produsen menerima sinyal
dari induk dan dengan cepat dimatikan. Konsumen mungkin mengharapkan pesan
bijak dari produsen dan diblokir dalam keadaan di mana ia tidak dapat menerima sinyal shutdown
nal. Itu bisa macet menunggu produser dan tidak pernah selesai, mencegah orang tua
finishing juga.
Situasi seperti ini sama sekali tidak jarang. Jadi jika Anda harus menulis kode bersamaan itu
melibatkan penutupan dengan anggun, berharap untuk menghabiskan banyak waktu Anda mendapatkan penutupan
turun untuk terjadi dengan benar.
Rekomendasi : Pikirkan tentang mematikan lebih awal dan membuatnya bekerja lebih awal. Itu akan terjadi
butuh waktu lebih lama dari yang Anda harapkan. Tinjau algoritma yang ada karena ini mungkin lebih sulit
dari yang Anda pikirkan.

Menguji Kode Berurutan


Membuktikan bahwa kode itu benar tidak praktis. Pengujian tidak menjamin kebenaran. Bagaimana-
pernah, pengujian yang baik dapat meminimalkan risiko. Ini semua benar dalam solusi single-threaded. Segera
karena ada dua atau lebih utas yang menggunakan kode yang sama dan bekerja dengan data bersama, hal-hal
dapatkan jauh lebih kompleks.
Rekomendasi : Tulis tes yang berpotensi memunculkan masalah lalu
sering jalankan, dengan berbagai konfigurasi program dan konfigurasi sistem
dan memuat. Jika tes gagal, lacak kegagalannya. Jangan abaikan kegagalan hanya karena
tes lulus pada menjalankan selanjutnya.
Itu banyak yang harus dipertimbangkan. Berikut ini beberapa butir yang lebih bagus
rekomendasi:

• Perlakukan kegagalan palsu sebagai masalah kandidat threading.


• Dapatkan kode nonthreaded Anda bekerja terlebih dahulu.

15. Lihat “Deadlock” di halaman 335.

www.it-ebooks.info

Halaman 218

https://translate.googleusercontent.com/translate_f 171/365
3/12/2020 www.it-ebooks.info

Menguji Kode Berurutan 187

• Buat kode ulir Anda dapat digunakan.


• Jadikan kode berulir Anda bisa disetel.
• Jalankan dengan lebih banyak utas daripada prosesor.
• Jalankan pada platform yang berbeda.
• Instrumentasi kode Anda untuk mencoba dan memaksa kegagalan.

Mengobati Kegagalan Palsu sebagai Masalah Calon Threading


Kode berulir menyebabkan banyak hal gagal sehingga “tidak bisa gagal.” Sebagian besar pengembang tidak memilikinya
perasaan intuitif untuk bagaimana threading berinteraksi dengan kode lain (termasuk penulis). Bug di
kode berulir mungkin menunjukkan gejala mereka sekali dalam seribu, atau satu juta, eksekusi.
Upaya untuk mengulangi sistem bisa membuat frustasi. Ini sering menyebabkan pengembang untuk menghapus
kegagalan sebagai sinar kosmik, kesalahan perangkat keras, atau sejenis "satu kali" lainnya. Yang terbaik adalah
berasumsi bahwa sekali saja tidak ada. Semakin lama "satu kali" ini diabaikan, semakin banyak kode
dibangun di atas pendekatan yang berpotensi salah.
Rekomendasi : Jangan abaikan kegagalan sistem hanya satu kali.

Dapatkan Kode Nonthreaded Anda Bekerja Pertama


Ini mungkin tampak jelas, tetapi tidak ada salahnya untuk memperkuatnya. Pastikan kode berfungsi di luar
penggunaannya di utas. Secara umum, ini berarti membuat POJO yang dipanggil oleh utas Anda.
POJO tidak sadar thread, dan karenanya dapat diuji di luar lingkungan berulir
ronment. Semakin banyak sistem Anda, Anda dapat menempatkan di POJO seperti itu, semakin baik.
Rekomendasi : Jangan mencoba untuk mengejar bug nonthreading dan bug threading
pada waktu bersamaan. Pastikan kode Anda berfungsi di luar utas .

Buat Kode Berurutan Anda Dapat Dicolokkan


Tulis kode pendukung konkurensi sehingga dapat dijalankan dalam beberapa konfigurasi:

• Satu utas, beberapa utas, bervariasi saat dijalankan


• Kode berulir berinteraksi dengan sesuatu yang bisa jadi nyata atau uji ganda.
• Jalankan dengan tes ganda yang berjalan cepat, lambat, variabel.
• Mengkonfigurasi tes sehingga dapat dijalankan untuk sejumlah iterasi.

Rekomendasi : Buat kode berbasis utas Anda agar dapat digunakan


dapat menjalankannya dalam berbagai konfigurasi.

Jadikan Kode Berulir Anda Dapat Ditularkan


Mendapatkan keseimbangan utas yang tepat biasanya membutuhkan kesalahan percobaan. Sejak awal, cari cara untuk
mengatur waktu kinerja sistem Anda di bawah konfigurasi yang berbeda. Izinkan jumlah

www.it-ebooks.info

Halaman 219

188 Bab 13: Konkurensi

utas agar mudah disetel. Pertimbangkan untuk mengubahnya saat sistem berjalan.
Pertimbangkan untuk memungkinkan penyesuaian diri berdasarkan throughput dan pemanfaatan sistem.

Jalankan dengan Lebih Banyak Thread daripada Prosesor


Hal-hal terjadi ketika sistem berganti tugas. Untuk mendorong pertukaran tugas, jalankan
dengan lebih banyak utas daripada prosesor atau inti. Semakin sering tugas Anda bertukar, semakin banyak
kemungkinan Anda akan menemukan kode yang tidak ada bagian kritis atau menyebabkan kebuntuan.

Jalankan pada Berbagai Platform


Pada pertengahan 2007 kami mengembangkan kursus tentang pemrograman bersamaan. Kursus

https://translate.googleusercontent.com/translate_f 172/365
3/12/2020 www.it-ebooks.info
pengembangan terjadi
berjalan di bawah VM.terutama
Tes yangdiditulis
bawahuntuk
OS X. Kelas disajikan
menunjukkan menggunakan
kondisi kegagalan Windows
tidak gagalXP
sebagai fre-
quently di lingkungan XP seperti yang mereka lakukan pada OS X.
Dalam semua kasus kode yang diuji diketahui salah. Ini hanya memperkuat fakta
bahwa sistem operasi yang berbeda memiliki kebijakan threading yang berbeda, yang masing-masing berdampak
eksekusi kode. Kode multithreaded berperilaku berbeda di lingkungan yang berbeda. 16
Anda harus menjalankan tes Anda di setiap lingkungan penyebaran potensial.
Rekomendasi : Jalankan kode berulir Anda di semua platform target lebih awal dan sering.

Instrumentasi Kode Anda untuk Mencoba dan Memaksa Kegagalan


Itu normal untuk cacat dalam kode bersamaan untuk disembunyikan. Tes sederhana seringkali tidak memaparkannya.
Memang, mereka sering bersembunyi selama pemrosesan normal. Mereka mungkin muncul sekali setiap beberapa
jam, atau hari, atau minggu!
Alasan bahwa threading bug bisa jarang terjadi, sporadis, dan sulit diulang, adalah itu
hanya beberapa jalur dari ribuan jalur yang mungkin melalui
Bagian nerable sebenarnya gagal. Jadi probabilitas bahwa jalur gagal diambil dapat dibintangi
sangat rendah. Ini membuat deteksi dan debug sangat sulit.
Bagaimana Anda dapat meningkatkan peluang Anda untuk menangkap kejadian langka seperti itu? Kamu bisa
instrumen kode Anda dan memaksanya untuk berjalan dalam urutan berbeda dengan menambahkan panggilan ke metode
seperti Object.wait () , Object.sleep () , Object.yield () dan Object.priority () .
Masing-masing metode ini dapat mempengaruhi urutan eksekusi, sehingga meningkatkan peluang
mendeteksi cacat. Lebih baik ketika kode yang rusak gagal sedini dan sesering mungkin.
Ada dua opsi untuk instrumentasi kode:

• Kode tangan
• Otomatis

16. Tahukah Anda bahwa model threading di Jawa tidak menjamin preemptive threading? Dukungan preemptive OS modern
threading, sehingga Anda mendapatkan "gratis." Meski begitu, itu tidak dijamin oleh JVM.

www.it-ebooks.info

Halaman 220

Menguji Kode Berurutan 189

Kode Tangan
Anda dapat memasukkan panggilan untuk menunggu () , tidur () , menghasilkan () , dan prioritas () dalam kode Anda dengan tangan. Itu
mungkin hanya hal yang harus dilakukan ketika Anda menguji sepotong kode yang sangat sulit.
Berikut adalah contoh melakukan hal itu:
String tersinkronisasi publik nextUrlOrNull () {
if (hasNext ()) {
String url = urlGenerator.next ();
Thread.yield (); // dimasukkan untuk pengujian.
perbaruiHasNext ();
kembalikan url;
}
kembali nol;
}

Panggilan yang dimasukkan untuk menghasilkan () akan mengubah jalur eksekusi yang diambil oleh kode dan
mungkin menyebabkan kode gagal di tempat yang tidak gagal sebelumnya. Jika kode itu rusak, itu
bukan karena Anda menambahkan panggilan untuk menghasilkan () . 17 Sebaliknya, kode Anda rusak dan ini sederhana
membuat kegagalan itu nyata.
Ada banyak masalah dengan pendekatan ini:

• Anda harus secara manual menemukan tempat yang tepat untuk melakukan ini.
• Bagaimana Anda tahu di mana harus menelepon dan panggilan seperti apa yang digunakan?
• Meninggalkan kode seperti itu di lingkungan produksi tidak perlu memperlambat kode.
• Ini pendekatan senapan. Anda mungkin atau mungkin tidak menemukan kekurangan. Memang, kemungkinannya tidak bersama Anda.

Yang kita butuhkan adalah cara untuk melakukan ini selama pengujian tetapi tidak dalam produksi. Kita juga perlu
mudah menggabungkan konfigurasi antara berbagai proses, yang menghasilkan peningkatan peluang
menemukan kesalahan dalam agregat.
Jelas, jika kita membagi sistem kita menjadi POJO yang tidak tahu apa-apa tentang threading dan
kelas yang mengontrol threading, akan lebih mudah untuk menemukan tempat yang tepat untuk instrumen

https://translate.googleusercontent.com/translate_f 173/365
3/12/2020 www.it-ebooks.info
Kode. Selain itu, kami dapat membuat banyak uji jig berbeda yang melibatkan POJO di bawah
berbagai rezim panggilan untuk tidur , menyerah , dan sebagainya.

Otomatis
Anda bisa menggunakan alat seperti Kerangka Berorientasi Aspek, CGLIB, atau ASM untuk diprogram
ically instrumen kode Anda. Misalnya, Anda bisa menggunakan kelas dengan metode tunggal:
kelas publik ThreadJigglePoint {
goncangan statis publik () {
}
}

17. Ini tidak sepenuhnya terjadi. Karena JVM tidak menjamin preemptive threading, algoritma tertentu mungkin selalu
bekerja pada OS yang tidak mendahului utas. Kebalikannya juga dimungkinkan tetapi untuk alasan yang berbeda.

www.it-ebooks.info

Halaman 221

190 Bab 13: Konkurensi

Anda dapat menambahkan panggilan ke ini di berbagai tempat dalam kode Anda:
String tersinkronisasi publik nextUrlOrNull () {
if (hasNext ()) {
ThreadJiglePoint.jiggle ();
String url = urlGenerator.next ();
ThreadJiglePoint.jiggle ();
perbaruiHasNext ();
ThreadJiglePoint.jiggle ();
kembalikan url;
}
kembali nol;
}

Sekarang Anda menggunakan aspek sederhana yang secara acak memilih di antara tidak melakukan apa pun, tidur, atau
menghasilkan
Atau bayangkan bahwa kelas ThreadJigglePoint memiliki dua implementasi. Penerapan pertama
Tuan bergoyang - goyang untuk tidak melakukan apa-apa dan digunakan dalam produksi. Yang kedua menghasilkan acak
nomor untuk memilih antara tidur, menyerah, atau hanya jatuh. Jika Anda menjalankan tes Anda
seribu kali dengan jiggling acak, Anda dapat membasmi beberapa kelemahan. Jika tes lulus, pada
Setidaknya Anda bisa mengatakan Anda telah melakukan uji tuntas. Meskipun agak sederhana, ini bisa menjadi alasan
opsi sonable sebagai pengganti alat yang lebih canggih.
Ada alat yang disebut ConTest, 18 yang dikembangkan oleh IBM yang melakukan hal serupa, tetapi itu
melakukannya dengan kecanggihan sedikit lebih.
Intinya adalah untuk menyatukan kode sehingga utas berjalan dalam urutan berbeda pada waktu yang berbeda
waktu. Kombinasi tes yang ditulis dengan baik dan jiggling dapat secara dramatis meningkatkan
kesalahan menemukan kesempatan.
Rekomendasi : Gunakan strategi jiggling untuk menemukan kesalahan.

Kesimpulan
Kode bersamaan sulit untuk diperbaiki. Kode yang mudah diikuti bisa menjadi malam-
marish ketika beberapa utas dan data bersama masuk ke dalam campuran. Jika Anda dihadapkan dengan tertulis-
Dalam kode konkuren, Anda perlu menulis kode bersih dengan keras atau jika tidak, wajah halus dan
kegagalan yang jarang.
Pertama dan terutama, ikuti Prinsip Tanggung Jawab Tunggal. Hancurkan sistem Anda menjadi
POJO yang memisahkan kode sadar thread dari kode thread-ignorant. Pastikan kapan Anda
sedang menguji kode sadar-thread Anda, Anda hanya mengujinya dan tidak ada yang lain. Ini menunjukkan
bahwa kode thread-aware Anda harus kecil dan fokus.
Ketahui kemungkinan sumber masalah konkurensi: beberapa utas beroperasi
data bersama, atau menggunakan kumpulan sumber daya umum. Kasus batas, seperti penutupan
bersih atau menyelesaikan iterasi loop, bisa sangat sulit.

18. http://www.alphaworks.ibm.com/tech/contest

https://translate.googleusercontent.com/translate_f 174/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 222

Bibliografi 191

Pelajari perpustakaan Anda dan ketahui algoritma dasar. Memahami bagaimana beberapa
fitur yang ditawarkan oleh perpustakaan mendukung pemecahan masalah yang mirip dengan yang mendasar
algoritma.
Pelajari cara menemukan wilayah kode yang harus dikunci dan menguncinya. Jangan dikunci
wilayah kode yang tidak perlu dikunci. Hindari menelepon satu bagian yang terkunci dari
lain. Ini membutuhkan pemahaman yang mendalam tentang apakah sesuatu dibagikan atau tidak. Menjaga
jumlah objek bersama dan ruang lingkup berbagi sesempit mungkin. Perubahan
desain objek dengan data bersama untuk mengakomodasi klien daripada memaksa klien
untuk mengelola keadaan bersama.
Masalah akan muncul. Yang tidak muncul lebih awal sering dihapuskan sebagai
kejadian waktu. Ini yang disebut sekali saja biasanya hanya terjadi di bawah beban atau pada
ingly kali acak. Oleh karena itu, Anda harus dapat menjalankan kode terkait thread Anda
banyak konfigurasi pada banyak platform secara berulang dan terus menerus. Testabilitas, yang
datang secara alami dari mengikuti Tiga Hukum TDD, menyiratkan beberapa tingkat kemampuan plug-in,
yang menawarkan dukungan yang diperlukan untuk menjalankan kode dalam rentang konfigurasi yang lebih luas.
Anda akan sangat meningkatkan peluang Anda menemukan kode yang salah jika Anda meluangkan waktu
untuk instrumen kode Anda. Anda dapat melakukannya dengan tangan atau menggunakan semacam otomatis
teknologi. Investasikan pada awal ini. Anda ingin menjalankan kode berbasis utas selama
mungkin sebelum Anda memasukkannya ke dalam produksi.
Jika Anda mengambil pendekatan yang bersih, peluang Anda untuk melakukannya dengan benar meningkat secara drastis.

Bibliografi
[Lea99]: Pemrograman Serentak di Jawa: Prinsip dan Pola Desain , 2d. ed.,
Doug Lea, Prentice Hall, 1999.

[PPP]: Pengembangan Perangkat Lunak Agile: Prinsip, Pola, dan Praktik , Robert C. Martin,
Prentice Hall, 2002.

[PRAG]: Programmer Pragmatis , Andrew Hunt, Dave Thomas, Addison-Wesley,


2000

www.it-ebooks.info

Halaman 223

https://translate.googleusercontent.com/translate_f 175/365
3/12/2020 www.it-ebooks.info

halaman ini sengaja dibiarkan kosong

www.it-ebooks.info

Halaman 224

14
Penyempurnaan Berturut-turut
Studi Kasus dari Parser Argumen Command-Line

https://translate.googleusercontent.com/translate_f 176/365
3/12/2020 www.it-ebooks.info

Bab ini adalah studi kasus dalam penyempurnaan berturut-turut. Anda akan melihat modul yang dimulai
baik tetapi tidak skala. Kemudian Anda akan melihat bagaimana modul itu di refactored dan dibersihkan.
Sebagian besar dari kita harus menguraikan argumen baris perintah dari waktu ke waktu. Jika kita
tidak memiliki utilitas yang mudah digunakan, maka kita cukup berjalan array string yang dilewatkan
ke dalam fungsi utama . Ada beberapa utilitas bagus yang tersedia dari berbagai sumber,

193

www.it-ebooks.info

Halaman 225

194 Bab 14: Penyempurnaan Berturut-turut

tetapi tidak satupun dari mereka melakukan apa yang saya inginkan. Jadi, tentu saja, saya memutuskan untuk menulis sendiri. Saya menelepon
itu: Args .
Args sangat mudah digunakan. Anda cukup membangun kelas Args dengan argumen input
KASIH dan string format, dan kemudian kueri instance Args untuk nilai argumen
KASIH. Perhatikan contoh sederhana berikut ini:

Listing 14-1
Penggunaan sederhana Args
public static public void (String [] args) {
coba {
Args arg = Args baru ("l, p #, d *", args);
boolean logging = arg.getBoolean ('l');
int port = arg.getInt ('p');
Direktori string = arg.getString ('d');
executeApplication (logging, port, direktori);
} catch (ArgsException e) {
System.out.printf ("Kesalahan argumen:% s \ n", e.errorMessage ());
}
}

Anda dapat melihat betapa sederhananya ini. Kami hanya membuat turunan dari kelas Args dengan dua
parameter. Parameter pertama adalah format, atau skema, string: "l, p #, d * . " Ini mendefinisikan tiga
argumen baris perintah. Yang pertama, –l , adalah argumen boolean. Yang kedua, -p , adalah bilangan bulat
argumen. Yang ketiga, -d , adalah argumen string. Parameter kedua ke konstruktor Args
hanyalah array argumen baris perintah yang diteruskan ke utama .
Jika konstruktor kembali tanpa melempar ArgsException , maka yang masuk
command-line diuraikan, dan instance Args siap untuk ditanyai. Metode seperti
getBoolean , getInteger , dan getString memungkinkan kita mengakses nilai argumen dengan
nama mereka.
Jika ada masalah, baik dalam format string atau dalam argumen baris perintah
sendiri, ArgsException akan dilempar. Deskripsi yang mudah tentang apa yang terjadi
salah dapat diambil dari metode errorMessage pengecualian.

Implementasi Args

https://translate.googleusercontent.com/translate_f 177/365
3/12/2020 www.it-ebooks.info
Listing 14-2 adalah implementasi dari kelas Args . Harap baca dengan cermat. saya telah bekerja
keras pada gaya dan struktur dan berharap itu layak ditiru.

Listing 14-2
Args.java
paket com.objectmentor.utilities.args;

impor com.objectmentor.utilities.args.ArgsException.ErrorCode statis. *;


import java.util. *;

Args kelas publik {


private map <Character, ArgumentMarshaler> marshalers;

www.it-ebooks.info

Halaman 226

Implementasi Args 195

Listing 14-2 (lanjutan)


Args.java
Set pribadi <Character> argsFound;
ListIterator pribadi <String> currentArgument;

public Args (String schema, String [] args) melempar ArgsException {


marshalers = HashMap baru <Character, ArgumentMarshaler> ();
argsFound = HashSet baru <Character> ();

parseSchema (skema);
parseArgumentStrings (Arrays.asList (args));
}

private void parseSchema (Skema string) melempar ArgsException {


untuk (elemen String: schema.split (","))
if (element.length ()> 0)
parseSchemaElement (element.trim ());
}

private void parseSchemaElement (elemen String) melempar ArgsException {


char elementId = element.charAt (0);
String elementTail = element.substring (1);
validateSchemaElementId (elementId);
if (elementTail.length () == 0)
marshalers.put (elementId, BooleanArgumentMarshaler ()) baru;
lain jika (elementTail.equals ("*"))
marshalers.put (elementId, StringArgumentMarshaler ()) baru;
lain jika (elementTail.equals ("#"))
marshalers.put (elementId, IntegerArgumentMarshaler ()) baru;
lain jika (elementTail.equals ("##"))
marshalers.put (elementId, DoubleArgumentMarshaler ()) baru;
lain jika (elementTail.equals ("[*]"))
marshalers.put (elementId, StringArrayArgumentMarshaler ()) baru;
lain
melempar ArgsException baru (INVALID_ARGUMENT_FORMAT, elementId, elementTail);
}

void pribadi validateSchemaElementId (char elementId) melempar ArgsException {


if (! Character.isLetter (elementId))
melempar ArgsException baru (INVALID_ARGUMENT_NAME, elementId, null);
}

private void parseArgumentStrings (Daftar <String> argsList) melempar ArgsException


{
untuk (currentArgument = argsList.listIterator (); currentArgument.hasNext ();)
{
String argString = currentArgument.next ();
if (argString.startsWith ("-")) {
parseArgumentCharacters (argString.substring (1));
} lain {
currentArgument.previous ();
istirahat;
}
}
}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 178/365
3/12/2020 www.it-ebooks.info

Halaman 227

196 Bab 14: Penyempurnaan Berturut-turut

Listing 14-2 (lanjutan)


Args.java
private void parseArgumentCharacters (String argChars) melempar ArgsException {
untuk (int i = 0; i <argChars.length (); i ++)
parseArgumentCharacter (argChars.charAt (i));
}

private void parseArgumentCharacter (char argChar) melempar ArgsException {


ArgumentMarshaler m = marshalers.get (argChar);
if (m == null) {
melempar ArgsException baru (UNEXPECTED_ARGUMENT, argChar, null);
} lain {
argsFound.add (argChar);
coba {
m.set (currentArgument);
} catch (ArgsException e) {
e.setErrorArgumentId (argChar);
lempar e;
}
}
}

public boolean has (char arg) {


return argsFound.contains (arg);
}

public int nextArgument () {


return currentArgument.nextIndex ();
}

public boolean getBoolean (char arg) {


return BooleanArgumentMarshaler.getValue (marshalers.get (arg));
}

public String getString (char arg) {


return StringArgumentMarshaler.getValue (marshalers.get (arg));
}

public int getInt (char arg) {


return IntegerArgumentMarshaler.getValue (marshalers.get (arg));
}

double publik getDouble (char arg) {


return DoubleArgumentMarshaler.getValue (marshalers.get (arg));
}

public String [] getStringArray (char arg) {


return StringArrayArgumentMarshaler.getValue (marshalers.get (arg));
}
}

Perhatikan bahwa Anda dapat membaca kode ini dari atas ke bawah tanpa banyak lompatan
sekitar atau melihat ke depan. Satu hal yang Anda mungkin harus melihat ke depan adalah
tion dari ArgumentMarshaler , yang saya sengaja tinggalkan. Setelah membaca kode ini dengan cermat,

www.it-ebooks.info

Halaman 228

Implementasi Args 197

Anda harus memahami apa antarmuka ArgumentMarshaler dan apa turunannya.


Saya akan menunjukkan beberapa di antaranya kepada Anda sekarang (Listing 14-3 hingga Listing 14-6).

Listing 14-3

https://translate.googleusercontent.com/translate_f 179/365
3/12/2020 www.it-ebooks.info
ArgumentMarshaler.java
antarmuka publik ArgumentMarshaler {
void set (Iterator <String> currentArgument) melempar ArgsException;
}

Listing 14-4
BooleanArgumentMarshaler.java
kelas publik BooleanArgumentMarshaler mengimplementasikan ArgumentMarshaler {
private boolean booleanValue = false;

public void set (Iterator <String> currentArgument) melempar ArgsException {


booleanValue = true;
}

getValue boolean publik statis (ArgumentMarshaler am) {


if (am! = null && am instanceof BooleanArgumentMarshaler)
return ((BooleanArgumentMarshaler) saya) .booleanValue;
lain
return false;
}
}

Listing 14-5
StringArgumentMarshaler.java
impor com.objectmentor.utilities.args.ArgsException.ErrorCode statis. *;

StringArgumentMarshaler kelas publik mengimplementasikan ArgumentMarshaler {


private String stringValue = "";

public void set (Iterator <String> currentArgument) melempar ArgsException {


coba {
stringValue = currentArgument.next ();
} catch (NoSuchElementException e) {
melempar ArgsException baru (MISSING_STRING);
}
}

getValue String statis publik (ArgumentMarshaler am) {


if (am! = null && am instanceof StringArgumentMarshaler)
return ((StringArgumentMarshaler) am) .stringValue;
lain
kembali "";
}
}

www.it-ebooks.info

Halaman 229

198 Bab 14: Penyempurnaan Berturut-turut

Listing 14-6
IntegerArgumentMarshaler.java
impor com.objectmentor.utilities.args.ArgsException.ErrorCode statis. *;

IntegerArgumentMarshaler kelas publik mengimplementasikan ArgumentMarshaler {


private int intValue = 0;

public void set (Iterator <String> currentArgument) melempar ArgsException {


Parameter string = nol;
coba {
parameter = currentArgument.next ();
intValue = Integer.parseInt (parameter);
} catch (NoSuchElementException e) {
melempar ArgsException baru (MISSING_INTEGER);
} catch (NumberFormatException e) {
melempar ArgsException baru (INVALID_INTEGER, parameter);
}
}

getValue int public static (ArgumentMarshaler am) {


if (am! = null && am instanceof IntegerArgumentMarshaler)
return ((IntegerArgumentMarshaler) saya) .intValue;
lain
return 0;
}
}

https://translate.googleusercontent.com/translate_f 180/365
3/12/2020 www.it-ebooks.info
Turunan ArgumentMarshaler lainnya cukup mereplikasi pola ini untuk ganda dan
Array string dan akan berfungsi untuk mengacaukan bab ini. Saya akan menyerahkannya kepada Anda sebagai latihan.
Satu informasi lain mungkin mengganggu Anda: definisi kode kesalahan
konstanta. Mereka berada di kelas ArgsException (Listing 14-7).

Listing 14-7
ArgsException.java
impor com.objectmentor.utilities.args.ArgsException.ErrorCode statis. *;

kelas publik ArgsException memperluas Pengecualian {


private char errorArgumentId = '\ 0';
private String errorParameter = null;
private ErrorCode errorCode = OK;

ArgsException publik () {}

ArgsException publik (String message) {super (message);}

public ArgsException (ErrorCode errorCode) {


this.errorCode = errorCode;
}

public ArgsException (ErrorCode errorCode, String errorParameter) {


this.errorCode = errorCode;
this.errorParameter = errorParameter;
}

www.it-ebooks.info

Halaman 230

Implementasi Args 199

Listing 14-7 (lanjutan)


ArgsException.java
public ArgsException (ErrorCode errorCode,
char errorArgumentId, String errorParameter) {
this.errorCode = errorCode;
this.errorParameter = errorParameter;
this.errorArgumentId = errorArgumentId;
}

public char getErrorArgumentId () {


return errorArgumentId;
}

kekosongan publik setErrorArgumentId (char errorArgumentId) {


this.errorArgumentId = errorArgumentId;
}

public String getErrorParameter () {


return errorParameter;
}

public void setErrorParameter (String errorParameter) {


this.errorParameter = errorParameter;
}

getErrorCode publik ErrorCode () {


return errorCode;
}

public void setErrorCode (ErrorCode errorCode) {


this.errorCode = errorCode;
}

public String errorMessage () {


switch (errorCode) {
huruf OK:
return "TILT: Seharusnya tidak sampai di sini.";
huruf UNEXPECTED_ARGUMENT:
return String.format ("Argument -% c tak terduga.", errorArgumentId);
huruf besar-kecil MISSING_STRING:
return String.format ("Tidak dapat menemukan parameter string untuk -% c.",
errorArgumentId);
case INVALID_INTEGER:
return String.format ("Argumen -% c mengharapkan bilangan bulat tetapi '% s'.",
errorArgumentId, errorParameter);
huruf besar-kecil MISSING_INTEGER:
return String.format ("Tidak dapat menemukan parameter integer untuk -% c.",
errorArgumentId);
case INVALID_DOUBLE:
return String.format ("Argumen -% c mengharapkan dobel tetapi '% s'.",

https://translate.googleusercontent.com/translate_f 181/365
3/12/2020 www.it-ebooks.info
errorArgumentId, errorParameter);
huruf besar-kecil MISSING_DOUBLE:
return String.format ("Tidak dapat menemukan parameter ganda untuk -% c.",
errorArgumentId);
case INVALID_ARGUMENT_NAME:
return String.format ("'% c' bukan nama argumen yang valid.",
errorArgumentId);

www.it-ebooks.info

Halaman 231

200 Bab 14: Penyempurnaan Berturut-turut

Listing 14-7 (lanjutan)


ArgsException.java
case INVALID_ARGUMENT_FORMAT:
return String.format ("'% s' bukan format argumen yang valid.",
errorParameter);
}
kembali "";
}

public enum ErrorCode {


Oke, INVALID_ARGUMENT_FORMAT, UNEXPECTED_ARGUMENT, INVALID_ARGUMENT_NAME,
MISSING_STRING,
MISSING_INTEGER, INVALID_INTEGER,
MISSING_DOUBLE, INVALID_DOUBLE}
}

Sungguh luar biasa betapa banyak kode yang diperlukan untuk menyempurnakan rincian dari kon- dis sederhana ini
kecuali Salah satu alasan untuk ini adalah bahwa kami menggunakan bahasa yang sangat bertele-tele. Jawa,
menjadi bahasa yang diketik secara statis, membutuhkan banyak kata untuk memenuhi jenis sistem
tem. Dalam bahasa seperti Ruby, Python, atau Smalltalk, program ini jauh lebih kecil. 1
Harap baca kodenya sekali lagi. Berikan perhatian khusus pada keadaannya
bernama, ukuran fungsi, dan pemformatan kode. Jika Anda berpengalaman
programmer, Anda mungkin memiliki beberapa quibbles di sana-sini dengan berbagai bagian gaya atau
struktur. Namun secara keseluruhan, saya harap Anda menyimpulkan bahwa program ini ditulis dengan baik dan
memiliki struktur yang bersih.
Misalnya, harus jelas bagaimana Anda akan menambahkan tipe argumen baru, seperti a
argumen tanggal atau argumen bilangan kompleks, dan bahwa penambahan semacam itu akan membutuhkan a
jumlah upaya sepele. Singkatnya, itu hanya akan membutuhkan turunan baru dari Argument-
Marshaler , fungsi getXXX baru , dan pernyataan kasus baru di parseSchemaElement
fungsi. Mungkin juga akan ada ArgsException.ErrorCode baru dan kesalahan baru
pesan.

Bagaimana Saya Melakukan Ini?


Biarkan saya mengatur pikiran Anda saat istirahat. Saya tidak hanya menulis program ini dari awal hingga akhir
bentuknya saat ini. Lebih penting lagi, saya tidak mengharapkan Anda untuk dapat menulis bersih dan
program elegan dalam satu pass. Jika kita telah belajar sesuatu selama beberapa dekade terakhir,
itu adalah bahwa pemrograman lebih merupakan keahlian daripada ilmu pengetahuan. Untuk menulis kode bersih, Anda harus
pertama-tama tulis kode kotor lalu bersihkan .
Ini seharusnya tidak mengejutkan Anda. Kami belajar kebenaran ini di sekolah dasar ketika kami
para guru mencoba (biasanya dengan sia-sia) untuk membuat kami menulis draf kasar komposisi kami. Itu
mereka mengatakan kepada kami bahwa kami harus menulis draf kasar, kemudian draf kedua, kemudian
hapus draft berikutnya sampai kami memiliki versi final kami. Menulis komposisi yang bersih, mereka
mencoba memberi tahu kami, adalah masalah penyempurnaan berturut-turut.

1. Saya baru-baru ini menulis ulang modul ini di Ruby. Ukurannya 1/7 dan memiliki struktur yang sedikit lebih baik.

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 182/365
3/12/2020 www.it-ebooks.info
Halaman 232

Args: The Rough Draft 201

Kebanyakan programmer baru (seperti kebanyakan siswa sekolah dasar) tidak mengikuti saran ini.
sangat baik. Mereka percaya bahwa tujuan utama adalah untuk membuat program bekerja. Setelah itu
"Bekerja," mereka pindah ke tugas berikutnya, meninggalkan program "bekerja" dalam keadaan apa pun
mereka akhirnya berhasil "bekerja." Kebanyakan programmer berpengalaman tahu bahwa ini profesional
bunuh diri.

Args: The Rough Draft


Listing 14-8 menunjukkan versi kelas Args sebelumnya . Berhasil." Dan itu berantakan.

Listing 14-8
Args.java (konsep pertama)
impor java.text.ParseException;
import java.util. *;

Args kelas publik {


skema String pribadi;
private String [] args;
private boolean valid = true;
Set pribadi <Character> terdugaArguments = TreeSet baru <Character> ();
private map <Character, Boolean> booleanArgs =
HashMap <Character, Boolean> () baru;
private map <Character, String> stringArgs = new HashMap <Character, String> ();
private map <Character, Integer> intArgs = new HashMap <Character, Integer> ();
Set pribadi <Character> argsFound = HashSet baru <Character> ();
Dokumen pribadi saat ini;
private char errorArgumentId = '\ 0';
private String errorParameter = "TILT";
private ErrorCode errorCode = ErrorCode.OK;

private enum ErrorCode {


Oke, MISSING_STRING, MISSING_INTEGER, INVALID_INTEGER, UNEXPECTED_ARGUMENT}

public Args (Skema string, String [] args) melempar ParseException {


this.schema = skema;
this.args = args;
valid = parse ();
}

parse boolean pribadi () melempar ParseException {


if (schema.length () == 0 && args.length == 0)
kembali benar;
parseSchema ();
coba {
parseArguments ();
} catch (ArgsException e) {
}
kembali valid;
}

private boolean parseSchema () melempar ParseException {


untuk (String elemen: schema.split (",")) {

www.it-ebooks.info

Halaman 233

202 Bab 14: Penyempurnaan Berturut-turut

Listing 14-8 (lanjutan)


Args.java (konsep pertama)
if (element.length ()> 0) {
String trimmedElement = element.trim ();
parseSchemaElement (trimmedElement);
}
}
kembali benar;
}

https://translate.googleusercontent.com/translate_f 183/365
3/12/2020 www.it-ebooks.info
private void parseSchemaElement (elemen String) melempar ParseException {
char elementId = element.charAt (0);
String elementTail = element.substring (1);
validateSchemaElementId (elementId);
if (isBooleanSchemaElement (elementTail))
parseBooleanSchemaElement (elementId);
lain jika (isStringSchemaElement (elementTail))
parseStringSchemaElement (elementId);
lain jika (isIntegerSchemaElement (elementTail)) {
parseIntegerSchemaElement (elementId);
} lain {
melempar ParseException baru (
String.format ("Argumen:% c memiliki format tidak valid:% s.",
elementId, elementTail), 0);
}
}

void pribadi validateSchemaElementId (char elementId) melempar ParseException {


if (! Character.isLetter (elementId)) {
melempar ParseException baru (
"Karakter buruk:" + elementId + "dalam format Args:" + schema, 0);
}
}

void parseBooleanSchemaElement pribadi (char elementId) {


booleanArgs.put (elementId, false);
}

private void parseIntegerSchemaElement (char elementId) {


intArgs.put (elementId, 0);
}

void parseStringSchemaElement pribadi (char elementId) {


stringArgs.put (elementId, "");
}

private boolean isStringSchemaElement (String elementTail) {


return elementTail.equals ("*");
}

boolean pribadi isBooleanSchemaElement (String elementTail) {


return elementTail.length () == 0;
}

private boolean isIntegerSchemaElement (String elementTail) {


mengembalikan elementTail.equals ("#");
}

www.it-ebooks.info

Halaman 234

Args: The Rough Draft 203

Listing 14-8 (lanjutan)


Args.java (konsep pertama)
private boolean parseArguments () melempar ArgsException {
untuk (currentArgument = 0; currentArgument <args.length; currentArgument ++)
{
String arg = args [currentArgument];
parseArgument (arg);
}
kembali benar;
}

private void parseArgument (String arg) melempar ArgsException {


if (arg.startsWith ("-"))
parseElements (arg);
}

private void parseElements (String arg) melempar ArgsException {


untuk (int i = 1; i <arg.length (); i ++)
parseElement (arg.charAt (i));
}

private void parseElement (char argChar) melempar ArgsException {


if (setArgument (argChar))
argsFound.add (argChar);
lain {
terdugaArguments.add (argChar);
errorCode = ErrorCode.UNEXPECTED_ARGUMENT;
valid = false;
}
}

private boolean setArgument (char argChar) melempar ArgsException {


if (isBooleanArg (argChar))

https://translate.googleusercontent.com/translate_f 184/365
3/12/2020 www.it-ebooks.info
setBooleanArg (argChar, true);
lain jika (isStringArg (argChar))
setStringArg (argChar);
lain jika (isIntArg (argChar))
setIntArg (argChar);
lain
return false;

kembali benar;
}

private boolean isIntArg (char argChar) {return intArgs.containsKey (argChar);}

kekosongan pribadi setIntArg (char argChar) melempar ArgsException {


currentArgument ++;
Parameter string = nol;
coba {
parameter = args [currentArgument];
intArgs.put (argChar, Integer baru (parameter));
} catch (ArrayIndexOutOfBoundsException e) {
valid = false;
errorArgumentId = argChar;
errorCode = ErrorCode.MISSING_INTEGER;

www.it-ebooks.info

Halaman 235

204 Bab 14: Penyempurnaan Berturut-turut

Listing 14-8 (lanjutan)


Args.java (konsep pertama)
melempar ArgsException baru ();
} catch (NumberFormatException e) {
valid = false;
errorArgumentId = argChar;
errorParameter = parameter;
errorCode = ErrorCode.INVALID_INTEGER;
melempar ArgsException baru ();
}
}

kekosongan pribadi setStringArg (char argChar) melempar ArgsException {


currentArgument ++;
coba {
stringArgs.put (argChar, args [currentArgument]);
} catch (ArrayIndexOutOfBoundsException e) {
valid = false;
errorArgumentId = argChar;
errorCode = ErrorCode.MISSING_STRING;
melempar ArgsException baru ();
}
}

private boolean isStringArg (char argChar) {


kembalikan stringArgs.containsKey (argChar);
}

kekosongan pribadi setBooleanArg (char argChar, nilai boolean) {


booleanArgs.put (argChar, value);
}

private boolean isBooleanArg (char argChar) {


return booleanArgs.containsKey (argChar);
}

kardinalitas publik () {
return argsFound.size ();
}

penggunaan String publik () {


if (schema.length ()> 0)
kembali "- [" + skema + "]";
lain
kembali "";
}

public String errorMessage () melempar Exception {


switch (errorCode) {
huruf OK:
throw Exception baru ("TILT: Seharusnya tidak sampai di sini.");
huruf UNEXPECTED_ARGUMENT:
return tak terdugaArgumentMessage ();
huruf besar-kecil MISSING_STRING:
return String.format ("Tidak dapat menemukan parameter string untuk -% c.",
errorArgumentId);

https://translate.googleusercontent.com/translate_f 185/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 236

Args: The Rough Draft 205

Listing 14-8 (lanjutan)


Args.java (konsep pertama)
case INVALID_INTEGER:
return String.format ("Argumen -% c mengharapkan bilangan bulat tetapi '% s'.",
errorArgumentId, errorParameter);
huruf besar-kecil MISSING_INTEGER:
return String.format ("Tidak dapat menemukan parameter integer untuk -% c.",
errorArgumentId);
}
kembali "";
}

private String terdugaArgumentMessage () {


StringBuffer message = new StringBuffer ("Argument (s) -");
untuk (char c: terdugaArguments) {
message.append (c);
}
message.append ("tak terduga.");

return message.toString ();


}

private boolean falseIfNull (Boolean b) {


return b! = null && b;
}

private int zeroIfNull (Integer i) {


return i == null? 0: i;
}

private String blankIfNull (String s) {


return s == null? "": s;
}

public String getString (char arg) {


return blankIfNull (stringArgs.get (arg));
}

public int getInt (char arg) {


return zeroIfNull (intArgs.get (arg));
}

public boolean getBoolean (char arg) {


return falseIfNull (booleanArgs.get (arg));
}

public boolean has (char arg) {


return argsFound.contains (arg);
}

boolean publik isValid () {


kembali valid;
}

kelas privat ArgsException memperluas Exception {


}
}

www.it-ebooks.info

Halaman 237

https://translate.googleusercontent.com/translate_f 186/365
3/12/2020 www.it-ebooks.info

206 Bab 14: Penyempurnaan Berturut-turut

Saya harap reaksi awal Anda terhadap kumpulan kode ini adalah “Saya tentu senang dia tidak meninggalkannya
seperti itu!" Jika Anda merasa seperti ini, maka ingatlah itulah yang akan dirasakan orang lain
tentang kode yang Anda tinggalkan dalam bentuk konsep kasar.
Sebenarnya "konsep kasar" mungkin adalah hal terbaik yang dapat Anda katakan tentang kode ini. Nya
jelas pekerjaan yang sedang berlangsung. Banyaknya variabel instan sangat menakutkan. Aneh
string seperti “TILT , ” yang HashSets dan TreeSets , dan try-catch-catch blok semua menambahkan hingga
tumpukan bernanah.
Saya tidak ingin menulis tumpukan bernanah. Memang, saya mencoba untuk menjaga hal-hal yang masuk akal
cakap terorganisir dengan baik. Anda mungkin bisa mengatakan itu dari pilihan fungsi dan variabel saya
nama dan fakta bahwa ada struktur kasar untuk program ini. Tapi, jelas, saya sudah membiarkannya
masalah menjauh dari saya.
Kekacauan itu dibangun secara bertahap. Versi sebelumnya tidak begitu jahat. Sebagai contoh,
Listing 14-9 menunjukkan versi sebelumnya di mana hanya argumen Boolean yang berfungsi.

Listing 14-9
Args.java (hanya Boolean)
package com.objectmentor.utilities.getopts;

import java.util. *;

Args kelas publik {


skema String pribadi;
private String [] args;
boolean pribadi valid;
Set pribadi <Character> terdugaArguments = TreeSet baru <Character> ();
private map <Character, Boolean> booleanArgs =
HashMap <Character, Boolean> () baru;
private int numberOfArguments = 0;

public Args (Skema string, argumen [] args) {


this.schema = skema;
this.args = args;
valid = parse ();
}

boolean publik isValid () {


kembali valid;
}

parse boolean pribadi () {


if (schema.length () == 0 && args.length == 0)
kembali benar;
parseSchema ();
parseArguments ();
kembali tak terdugaArguments.size () == 0;
}

parseSchema boolean pribadi () {


untuk (String elemen: schema.split (",")) {
parseSchemaElement (elemen);
}

www.it-ebooks.info

Halaman 238

Args: The Rough Draft 207

Listing 14-9 (lanjutan)


Args.java (hanya Boolean)
kembali benar;
}

void parseSchemaElement pribadi (elemen String) {


if (element.length () == 1) {
parseBooleanSchemaElement (elemen);
}
}

void parseBooleanSchemaElement pribadi (elemen String) {


char c = element.charAt (0);
if (Character.isLetter (c)) {
booleanArgs.put (c, false);

https://translate.googleusercontent.com/translate_f 187/365
3/12/2020 www.it-ebooks.info
} }

parseArguments boolean pribadi () {


untuk (String arg: args)
parseArgument (arg);
kembali benar;
}

private void parseArgument (String arg) {


if (arg.startsWith ("-"))
parseElements (arg);
}

private void parseElements (String arg) {


untuk (int i = 1; i <arg.length (); i ++)
parseElement (arg.charAt (i));
}

private void parseElement (char argChar) {


if (isBoolean (argChar)) {
numberOfArguments ++;
setBooleanArg (argChar, true);
} lain
terdugaArguments.add (argChar);
}

kekosongan pribadi setBooleanArg (char argChar, nilai boolean) {


booleanArgs.put (argChar, value);
}

private boolean isBoolean (char argChar) {


return booleanArgs.containsKey (argChar);
}

kardinalitas publik () {
mengembalikan numberOfArguments;
}

penggunaan String publik () {


if (schema.length ()> 0)
kembali "- [" + skema + "]";

www.it-ebooks.info

Halaman 239

208 Bab 14: Penyempurnaan Berturut-turut

Listing 14-9 (lanjutan)


Args.java (hanya Boolean)
lain
kembali "";
}

public String errorMessage () {


if (tak terdugaArguments.size ()> 0) {
return tak terdugaArgumentMessage ();
} lain
kembali "";
}

private String terdugaArgumentMessage () {


StringBuffer message = new StringBuffer ("Argument (s) -");
untuk (char c: terdugaArguments) {
message.append (c);
}
message.append ("tak terduga.");

return message.toString ();


}

public boolean getBoolean (char arg) {


return booleanArgs.get (arg);
}
}

Meskipun Anda dapat menemukan banyak keluhan di dalam kode ini, itu sebenarnya tidak terlalu buruk.
Ini ringkas dan sederhana dan mudah dimengerti. Namun, dalam kode ini mudah dilihat
benih-benih dari tumpukan bernanah kemudian. Cukup jelas bagaimana ini tumbuh menjadi kekacauan yang terakhir.
Perhatikan bahwa kekacauan terakhir hanya memiliki dua jenis argumen lebih dari ini: String dan
bilangan bulat . Penambahan hanya dua jenis argumen memiliki dampak negatif besar pada
Kode. Itu mengubahnya dari sesuatu yang seharusnya bisa dipertahankan
menjadi sesuatu yang saya harapkan akan penuh dengan bug dan kutil.

https://translate.googleusercontent.com/translate_f 188/365
3/12/2020 www.it-ebooks.info
Saya menambahkan dua tipe argumen secara bertahap. Pertama, saya menambahkan argumen String ,
yang menghasilkan ini:

Listing 14-10
Args.java (Boolean dan String)
package com.objectmentor.utilities.getopts;

impor java.text.ParseException;
import java.util. *;

Args kelas publik {


skema String pribadi;
private String [] args;
private boolean valid = true;
Set pribadi <Character> terdugaArguments = TreeSet baru <Character> ();
private map <Character, Boolean> booleanArgs =
HashMap <Character, Boolean> () baru;

www.it-ebooks.info

Halaman 240

Args: The Rough Draft 209

Listing 14-10 (lanjutan)


Args.java (Boolean dan String)
private map <Character, String> stringArgs =
HashMap <Character, String> () baru;
Set pribadi <Character> argsFound = HashSet baru <Character> ();
Dokumen pribadi saat ini;
private char errorArgument = '\ 0';

enum ErrorCode {
Oke, MISSING_STRING}

private ErrorCode errorCode = ErrorCode.OK;

public Args (Skema string, String [] args) melempar ParseException {


this.schema = skema;
this.args = args;
valid = parse ();
}

parse boolean pribadi () melempar ParseException {


if (schema.length () == 0 && args.length == 0)
kembali benar;
parseSchema ();
parseArguments ();
kembali valid;
}

private boolean parseSchema () melempar ParseException {


untuk (String elemen: schema.split (",")) {
if (element.length ()> 0) {
String trimmedElement = element.trim ();
parseSchemaElement (trimmedElement);
}
}
kembali benar;
}

private void parseSchemaElement (elemen String) melempar ParseException {


char elementId = element.charAt (0);
String elementTail = element.substring (1);
validateSchemaElementId (elementId);
if (isBooleanSchemaElement (elementTail))
parseBooleanSchemaElement (elementId);
lain jika (isStringSchemaElement (elementTail))
parseStringSchemaElement (elementId);
}

void pribadi validateSchemaElementId (char elementId) melempar ParseException {


if (! Character.isLetter (elementId)) {
melempar ParseException baru (
"Karakter buruk:" + elementId + "dalam format Args:" + schema, 0);
}

void parseStringSchemaElement pribadi (char elementId) {


stringArgs.put (elementId, "");
}

https://translate.googleusercontent.com/translate_f 189/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 241

210 Bab 14: Penyempurnaan Berturut-turut

Listing 14-10 (lanjutan)


Args.java (Boolean dan String)
private boolean isStringSchemaElement (String elementTail) {
return elementTail.equals ("*");
}

boolean pribadi isBooleanSchemaElement (String elementTail) {


return elementTail.length () == 0;
}

void parseBooleanSchemaElement pribadi (char elementId) {


booleanArgs.put (elementId, false);
}

parseArguments boolean pribadi () {


untuk (currentArgument = 0; currentArgument <args.length; currentArgument ++)
{
String arg = args [currentArgument];
parseArgument (arg);
}
kembali benar;
}

private void parseArgument (String arg) {


if (arg.startsWith ("-"))
parseElements (arg);
}

private void parseElements (String arg) {


untuk (int i = 1; i <arg.length (); i ++)
parseElement (arg.charAt (i));
}

private void parseElement (char argChar) {


if (setArgument (argChar))
argsFound.add (argChar);
lain {
terdugaArguments.add (argChar);
valid = false;
}
}

private boolean setArgument (char argChar) {


boolean set = true;
if (isBoolean (argChar))
setBooleanArg (argChar, true);
lain jika (isString (argChar))
setStringArg (argChar, "");
lain
set = false;

set kembali;
}

kekosongan pribadi setStringArg (char argChar, String s) {


currentArgument ++;
coba {

www.it-ebooks.info

Halaman 242

Args: The Rough Draft 211

https://translate.googleusercontent.com/translate_f 190/365
3/12/2020 www.it-ebooks.info

Listing 14-10 (lanjutan)


Args.java (Boolean dan String)
stringArgs.put (argChar, args [currentArgument]);
} catch (ArrayIndexOutOfBoundsException e) {
valid = false;
errorArgument = argChar;
errorCode = ErrorCode.MISSING_STRING;
}
}

private boolean isString (char argChar) {


kembalikan stringArgs.containsKey (argChar);
}

kekosongan pribadi setBooleanArg (char argChar, nilai boolean) {


booleanArgs.put (argChar, value);
}

private boolean isBoolean (char argChar) {


return booleanArgs.containsKey (argChar);
}

kardinalitas publik () {
return argsFound.size ();
}

penggunaan String publik () {


if (schema.length ()> 0)
kembali "- [" + skema + "]";
lain
kembali "";
}

public String errorMessage () melempar Exception {


if (tak terdugaArguments.size ()> 0) {
return tak terdugaArgumentMessage ();
} lain
switch (errorCode) {
huruf besar-kecil MISSING_STRING:
return String.format ("Tidak dapat menemukan parameter string untuk -% c.",
errorArgument);
huruf OK:
throw Exception baru ("TILT: Seharusnya tidak sampai di sini.");
}
kembali "";
}

private String terdugaArgumentMessage () {


StringBuffer message = new StringBuffer ("Argument (s) -");
untuk (char c: terdugaArguments) {
message.append (c);
}
message.append ("tak terduga.");

return message.toString ();


}

www.it-ebooks.info

Halaman 243

212 Bab 14: Penyempurnaan Berturut-turut

Listing 14-10 (lanjutan)


Args.java (Boolean dan String)
public boolean getBoolean (char arg) {
return falseIfNull (booleanArgs.get (arg));
}

private boolean falseIfNull (Boolean b) {


return b == null? false: b;
}

public String getString (char arg) {


return blankIfNull (stringArgs.get (arg));
}

private String blankIfNull (String s) {


return s == null? "": s;
}

public boolean has (char arg) {


return argsFound.contains (arg);
}

https://translate.googleusercontent.com/translate_f 191/365
3/12/2020 www.it-ebooks.info
boolean publik isValid () {
kembali valid;
}
}

Anda dapat melihat bahwa ini mulai lepas kendali. Ini masih tidak mengerikan, tetapi kekacauan itu
tentu mulai tumbuh. Ini tumpukan, tapi belum bernanah. Butuh penambahan
tipe argumen integer untuk mendapatkan tumpukan ini benar-benar berfermentasi dan bernanah.

Jadi saya berhenti


Saya memiliki setidaknya dua jenis argumen untuk ditambahkan, dan saya dapat mengatakan bahwa mereka akan membuat sesuatu
jauh lebih buruk. Jika saya melibas jalan saya ke depan, saya mungkin bisa membuatnya bekerja, tetapi saya akan melakukannya
tinggalkan kekacauan yang terlalu besar untuk diperbaiki. Jika struktur kode ini pernah terjadi
dipertahankan, sekarang saatnya untuk memperbaikinya.
Jadi saya berhenti menambahkan fitur dan mulai refactoring. Baru saja menambahkan String dan
argumen integer , saya tahu bahwa setiap tipe argumen memerlukan kode baru dalam tiga mayor
tempat Pertama, setiap tipe argumen memerlukan beberapa cara untuk menguraikan elemen skema secara berurutan
untuk memilih HashMap untuk tipe itu. Selanjutnya, setiap tipe argumen perlu diuraikan dalam
string baris perintah dan dikonversi ke tipe sejatinya. Akhirnya, setiap jenis argumen membutuhkan a
Metode getXXX sehingga dapat dikembalikan ke pemanggil sebagai tipe yang sebenarnya.
Banyak tipe berbeda, semuanya dengan metode yang sama — yang kedengarannya seperti kelas bagi saya. Dan sebagainya
yang ArgumentMarshaler konsep lahir.

Tentang Inkrementalisme
Salah satu cara terbaik untuk merusak program adalah dengan membuat perubahan besar pada strukturnya atas nama
perbaikan. Beberapa program tidak pernah pulih dari "peningkatan" tersebut. Masalahnya adalah
sangat sulit untuk membuat program bekerja dengan cara yang sama seperti sebelum "peningkatan."

www.it-ebooks.info

Halaman 244

Args: The Rough Draft 213

Untuk menghindari ini, saya menggunakan disiplin Test-Driven Development (TDD). Salah satu pusat
doktrin utama dari pendekatan ini adalah menjaga sistem tetap berjalan setiap saat. Dengan kata lain,
menggunakan TDD, saya tidak diizinkan untuk melakukan perubahan pada sistem yang merusak sistem itu.
Setiap perubahan yang saya buat harus menjaga agar sistem tetap berfungsi seperti sebelumnya.
Untuk mencapai hal ini, saya memerlukan serangkaian tes otomatis yang dapat saya jalankan dengan tingkah
Jika perilaku sistem tidak berubah. Untuk kelas Args saya telah membuat suite
tes unit dan penerimaan saat saya sedang membangun tumpukan bernanah. Tes unit adalah
ditulis dalam Java dan dikelola oleh JUnit . Tes penerimaan ditulis sebagai halaman wiki
di FitNesse . Saya bisa menjalankan tes ini kapan saja saya mau, dan jika mereka lulus, saya yakin
bahwa sistem itu berfungsi seperti yang saya tentukan.
Jadi saya melanjutkan untuk membuat sejumlah besar perubahan yang sangat kecil. Setiap perubahan memindahkan
struktur sistem menuju konsep ArgumentMarshaler . Namun setiap perubahan terus
sistem bekerja. Perubahan pertama yang saya buat adalah menambahkan kerangka
ArgumentMarshaller hingga akhir tumpukan bernanah (Listing 14-11).

Listing 14-11
ArgumentMarshaller ditambahkan ke Args.java
kelas privat ArgumentMarshaler {
private boolean booleanValue = false;

public void setBoolean (nilai boolean) {


booleanValue = nilai;
}

public boolean getBoolean () {return booleanValue;}


}

kelas privat BooleanArgumentMarshaler memperluas ArgumentMarshaler {


}

kelas privat StringArgumentMarshaler memperluas ArgumentMarshaler {


}

kelas privat IntegerArgumentMarshaler memperluas ArgumentMarshaler {


}
}

Jelas, ini tidak akan merusak apa pun. Jadi saya membuat modifikasi paling sederhana

https://translate.googleusercontent.com/translate_f 192/365
3/12/2020 www.it-ebooks.info
Aku bisa,untuk
argumen yang mengambil
akan pecah sesedikit mungkin. Saya mengubah HashMap untuk Boolean
ArgumentMarshaler .

private map <Character, ArgumentMarshaler > booleanArgs =


HashMap <Character, ArgumentMarshaler > () baru;

Ini mematahkan beberapa pernyataan, yang dengan cepat saya perbaiki.


...
void parseBooleanSchemaElement pribadi (char elementId) {
booleanArgs.put (elementId, BooleanArgumentMarshaler () ) baru;
}
..

www.it-ebooks.info

Halaman 245

214 Bab 14: Penyempurnaan Berturut-turut

kekosongan pribadi setBooleanArg (char argChar, nilai boolean) {


booleanArgs. get (argChar) .setBoolean (value);
}
...
public boolean getBoolean (char arg) {
return falseIfNull (booleanArgs.get (arg). getBoolean () );
}

Perhatikan bagaimana perubahan ini persis di area yang saya sebutkan sebelumnya: parse ,
set , dan dapatkan untuk tipe argumen. Sayangnya, sekecil perubahan ini, beberapa
tes mulai gagal. Jika Anda perhatikan getBoolean dengan cermat , Anda akan melihatnya jika Anda memanggilnya
'y , ' tetapi tidak ada argumen y , maka booleanArgs.get ('y') akan mengembalikan nol , dan fungsi
tion akan melempar NullPointerException . Fungsi falseIfNull telah digunakan untuk melindungi
menentang ini, tetapi perubahan yang saya buat menyebabkan fungsi itu menjadi tidak relevan.
Inkrementalisme menuntut saya agar ini bekerja dengan cepat sebelum membuat yang lain
perubahan. Memang perbaikannya tidak terlalu sulit. Saya hanya harus memindahkan cek untuk nol . Dulu
tidak lagi boolean menjadi nol yang harus saya periksa; itu adalah ArgumentMarshaller .
Pertama, saya menghapus panggilan falseIfNull di fungsi getBoolean . Itu tidak berguna sekarang, jadi
Saya juga menghilangkan fungsi itu sendiri. Tes masih gagal dengan cara yang sama, jadi saya yakin
penyok bahwa saya belum memperkenalkan kesalahan baru.
public boolean getBoolean (char arg) {
return booleanArgs.get (arg) .getBoolean ();
}

Selanjutnya, saya membagi fungsi menjadi dua baris dan menempatkan ArgumentMarshaller ke dalam variasinya sendiri.
dapat bernama argumentMarshaller . Saya tidak peduli dengan nama variabel panjang; itu sangat buruk
berlebihan dan mengacaukan fungsi. Jadi saya menyingkatnya menjadi pagi [N5].
public boolean getBoolean (char arg) {
Args.ArgumentMarshaler am = booleanArgs.get (arg);
return am .getBoolean ();
}

Dan kemudian saya memasukkan logika deteksi nol.


public boolean getBoolean (char arg) {
Args.ArgumentMarshaler am = booleanArgs.get (arg);
return am! = null && am.getBoolean ();
}

Argumen string
Menambahkan argumen String sangat mirip dengan menambahkan argumen boolean . Saya harus berubah
yang HashMap dan mendapatkan parse , set , dan mendapatkan fungsi bekerja. Seharusnya tidak ada jaminan
dalam apa yang mengikuti kecuali, mungkin, bahwa saya tampaknya meletakkan semua implementasi marshalling
mentasi di kelas dasar ArgumentMarshaller alih-alih mendistribusikannya ke turunannya.
private map <Character , ArgumentMarshaler > stringArgs =
HashMap <Character , ArgumentMarshaler > () baru;
...

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 193/365
3/12/2020 www.it-ebooks.info

Halaman 246

Argumen string 215

void parseStringSchemaElement pribadi (char elementId) {


stringArgs.put (elementId , StringArgumentMarshaler () ) baru;
}
...
kekosongan pribadi setStringArg (char argChar) melempar ArgsException {
currentArgument ++;
coba {
stringArgs .get (argChar) .setString (args [currentArgument]);
} catch (ArrayIndexOutOfBoundsException e) {
valid = false;
errorArgumentId = argChar;
errorCode = ErrorCode.MISSING_STRING;
melempar ArgsException baru ();
}
}
...
public String getString (char arg) {
Args.ArgumentMarshaler am = stringArgs.get (arg);
return am == null? "" : am.getString ();
}
...
kelas privat ArgumentMarshaler {
private boolean booleanValue = false;
stringValue string pribadi;

public void setBoolean (nilai boolean) {


booleanValue = nilai;
}

public boolean getBoolean () {


kembalikan booleanValue;
}

kekosongan publik setString (String s) {


stringValue = s;
}

public String getString () {


mengembalikan stringValue == null? "": stringValue;
}
}

Sekali lagi, perubahan ini dilakukan satu per satu dan sedemikian rupa sehingga tes disimpan
berlari, jika tidak lewat. Ketika tes pecah, saya memastikan untuk lulus lagi sebelum melanjutkan
melanjutkan dengan perubahan berikutnya.
Sekarang Anda seharusnya sudah bisa melihat maksud saya. Setelah saya mendapatkan semua marshalling saat ini
perilaku ke dalam kelas dasar ArgumentMarshaler , saya akan mulai mendorong perilaku itu
turun ke derivatif. Ini akan memungkinkan saya untuk menjaga semuanya berjalan sementara saya secara bertahap
ubah bentuk program ini.
Langkah selanjutnya yang jelas adalah memindahkan fungsionalitas argumen int ke dalam
ArgumentMarshaler . Sekali lagi, tidak ada kejutan.

private map <Character , ArgumentMarshaler > intArgs =


HashMap <Character , ArgumentMarshaler > () baru;
...

www.it-ebooks.info

Halaman 247

216 Bab 14: Penyempurnaan Berturut-turut

private void parseIntegerSchemaElement (char elementId) {


intArgs.put (elementId, IntegerArgumentMarshaler () ) baru;
}
...
kekosongan pribadi setIntArg (char argChar) melempar ArgsException {

https://translate.googleusercontent.com/translate_f 194/365
3/12/2020 www.it-ebooks.info
currentArgument
Parameter string =++;
nol;
coba {
parameter = args [currentArgument];
intArgs .get (argChar) .setInteger (Integer.parseInt (parameter));
} catch (ArrayIndexOutOfBoundsException e) {
valid = false;
errorArgumentId = argChar;
errorCode = ErrorCode.MISSING_INTEGER;
melempar ArgsException baru ();
} catch (NumberFormatException e) {
valid = false;
errorArgumentId = argChar;
errorParameter = parameter;
errorCode = ErrorCode.INVALID_INTEGER;
melempar ArgsException baru ();
}
}
...
public int getInt (char arg) {
Args.ArgumentMarshaler am = intArgs.get (arg);
return am == null? 0 : am.getInteger ();
}
...
kelas privat ArgumentMarshaler {
private boolean booleanValue = false;
stringValue string pribadi;
Nilai integer int pribadi;

public void setBoolean (nilai boolean) {


booleanValue = nilai;
}

public boolean getBoolean () {


kembalikan booleanValue;
}

kekosongan publik setString (String s) {


stringValue = s;
}

public String getString () {


mengembalikan stringValue == null? "": stringValue;
}

public void setInteger (int i) {


integerValue = i;
}

public int getInteger () {


mengembalikan integerValue;
}
}

www.it-ebooks.info

Halaman 248

Argumen string 217

Dengan semua marshalling dipindahkan ke ArgumentMarshaler , saya mulai mendorong fungsional-


ke dalam turunannya. Langkah pertama adalah memindahkan fungsi setBoolean ke dalam
BooleanArgumentMarshaller dan pastikan itu dipanggil dengan benar. Jadi saya membuat abstrak
mengatur metode.

kelas abstrak pribadi ArgumentMarshaler {


protected boolean booleanValue = false;
stringValue string pribadi;
Nilai integer int pribadi;

public void setBoolean (nilai boolean) {


booleanValue = nilai;
}

public boolean getBoolean () {


kembalikan booleanValue;
}

kekosongan publik setString (String s) {


stringValue = s;
}

public String getString () {


mengembalikan stringValue == null? "": stringValue;
}

public void setInteger (int i) {


integerValue = i;

https://translate.googleusercontent.com/translate_f 195/365
3/12/2020 www.it-ebooks.info
}

public int getInteger () {


mengembalikan integerValue;
}

public abstrak void set (String s);


}

Kemudian saya mengimplementasikan metode set di BooleanArgumentMarshaller .


kelas privat BooleanArgumentMarshaler memperluas ArgumentMarshaler {
public void set (String s) {
booleanValue = true;
}
}

Dan akhirnya saya mengganti panggilan ke setBoolean dengan panggilan untuk mengatur .
kekosongan pribadi setBooleanArg (char argChar, nilai boolean) {
booleanArgs.get (argChar) .set ("true");
}

Semua tes masih berlalu. Karena perubahan ini menyebabkan set akan dikerahkan ke Boolean-
ArgumentMarshaler , saya menghapus metode setBoolean dari basis ArgumentMarshaler
kelas.
Perhatikan bahwa fungsi set abstrak mengambil argumen String , tetapi implementasinya
di BooleanArgumentMarshaller tidak menggunakannya. Saya menempatkan argumen itu di sana karena saya
tahu bahwa StringArgumentMarshaller dan IntegerArgumentMarshaller akan menggunakannya.

www.it-ebooks.info

Halaman 249

218 Bab 14: Penyempurnaan Berturut-turut

Selanjutnya, saya ingin menggunakan metode get ke BooleanArgumentMarshaler . Menyebarkan


dapatkan fungsi selalu jelek karena tipe pengembalian harus Object , dan dalam hal ini
perlu dilemparkan ke Boolean .
public boolean getBoolean (char arg) {
Args.ArgumentMarshaler am = booleanArgs.get (arg);
return am! = null && (Boolean) am. dapatkan ();
}

Hanya untuk mendapatkan ini untuk dikompilasi, saya menambahkan fungsi get ke ArgumentMarshaler .
kelas abstrak pribadi ArgumentMarshaler {
...

Obyek publik dapatkan () {


kembali nol;
}
}

Ini dikompilasi dan jelas gagal dalam tes. Membuat tes berfungsi lagi hanyalah sebuah
peduli pembuatan mendapatkan abstrak dan mengimplementasikannya dalam BooleanAgumentMarshaler .
kelas abstrak pribadi ArgumentMarshaler {
protected boolean booleanValue = false;
...

Objek abstrak publik dapatkan ();


}

kelas privat BooleanArgumentMarshaler memperluas ArgumentMarshaler {


public void set (String s) {
booleanValue = true;
}

Obyek publik dapatkan () {


kembalikan booleanValue;
}
}

Sekali lagi tes lulus. Jadi, dapatkan dan atur penyebaran ke BooleanArgumentMarshaler !
Ini memungkinkan saya untuk menghapus fungsi getBoolean lama dari ArgumentMarshaler , pindahkan
variabel booleanValue dilindungi hingga BooleanArgumentMarshaler , dan menjadikannya pribadi .
Saya melakukan pola perubahan yang sama untuk Strings . Saya menggunakan kedua set dan mendapatkan , menghapus
fungsi yang tidak digunakan, dan memindahkan variabel.
kekosongan pribadi setStringArg (char argChar) melempar ArgsException {
currentArgument ++;
coba {
stringArgs.get (argChar). set (args [currentArgument]);
} catch (ArrayIndexOutOfBoundsException e) {

https://translate.googleusercontent.com/translate_f 196/365
3/12/2020 www.it-ebooks.info
valid = false;
errorArgumentId = argChar;
errorCode = ErrorCode.MISSING_STRING;
melempar ArgsException baru ();
}
}

www.it-ebooks.info

Halaman 250

Argumen string 219

...
public String getString (char arg) {
Args.ArgumentMarshaler am = stringArgs.get (arg);
return am == null? "": (String) pagi. dapatkan ();
}
...
kelas abstrak pribadi ArgumentMarshaler {
Nilai integer int pribadi;

public void setInteger (int i) {


integerValue = i;
}

public int getInteger () {


mengembalikan integerValue;
}

public abstrak void set (String s);

Objek abstrak publik dapatkan ();


}

kelas privat BooleanArgumentMarshaler memperluas ArgumentMarshaler {


private boolean booleanValue = false;

public void set (String s) {


booleanValue = true;
}

Obyek publik dapatkan () {


kembalikan booleanValue;
}
}

kelas privat StringArgumentMarshaler memperluas ArgumentMarshaler {


private String stringValue = "";

public void set (String s) {


stringValue = s;
}

Obyek publik dapatkan () {


mengembalikan stringValue;
}
}

kelas privat IntegerArgumentMarshaler memperluas ArgumentMarshaler {

public void set (String s) {

Obyek publik dapatkan () {


kembali nol;
}
}
}

www.it-ebooks.info

Halaman 251
https://translate.googleusercontent.com/translate_f 197/365
3/12/2020 www.it-ebooks.info

220 Bab 14: Penyempurnaan Berturut-turut

Akhirnya, saya mengulangi proses untuk bilangan bulat . Ini hanya sedikit lebih rumit
karena bilangan bulat perlu diuraikan, dan operasi parse dapat melempar pengecualian. Tapi
hasilnya lebih baik karena seluruh konsep NumberFormatException dimakamkan di Internet
IntegerArgumentMarshaler .

private boolean isIntArg (char argChar) {return intArgs.containsKey (argChar);}

kekosongan pribadi setIntArg (char argChar) melempar ArgsException {


currentArgument ++;
Parameter string = nol;
coba {
parameter = args [currentArgument];
intArgs.get (argChar). set (parameter);
} catch (ArrayIndexOutOfBoundsException e) {
valid = false;
errorArgumentId = argChar;
errorCode = ErrorCode.MISSING_INTEGER;
melempar ArgsException baru ();
} catch ( ArgsException e) {
valid = false;
errorArgumentId = argChar;
errorParameter = parameter;
errorCode = ErrorCode.INVALID_INTEGER;
lempar e ;
}
}
...
kekosongan pribadi setBooleanArg (char argChar) {
coba {
booleanArgs.get (argChar) .set ("true");
} catch (ArgsException e) {
}
}
...
public int getInt (char arg) {
Args.ArgumentMarshaler am = intArgs.get (arg);
return am == null? 0: (Integer) pagi. dapatkan ();
}
...
kelas abstrak pribadi ArgumentMarshaler {
public abstrak void set (String s) melempar ArgsException;
Objek abstrak publik dapatkan ();
}
...
kelas privat IntegerArgumentMarshaler memperluas ArgumentMarshaler {
private int intValue = 0;

public void set (String s) melempar ArgsException {


coba {
intValue = Integer.parseInt (s);
} catch (NumberFormatException e) {
melempar ArgsException baru ();
}
}

Obyek publik dapatkan () {


mengembalikan nilai;
}
}

www.it-ebooks.info

Halaman 252

Argumen string 221

Tentu saja, tes terus berjalan. Selanjutnya, saya menyingkirkan tiga peta berbeda di
bagian atas algoritma. Ini membuat keseluruhan sistem jauh lebih umum. Namun saya
tidak dapat menyingkirkan mereka hanya dengan menghapusnya karena itu akan merusak sistem.
Sebagai gantinya, saya menambahkan Peta baru untuk ArgumentMarshaler dan kemudian satu per satu mengubah
metode untuk menggunakannya, bukan tiga peta asli.
Args kelas publik {
...
private map <Character, ArgumentMarshaler> booleanArgs =
HashMap <Character, ArgumentMarshaler> () baru;

https://translate.googleusercontent.com/translate_f 198/365
3/12/2020 www.it-ebooks.info
private map <Character, ArgumentMarshaler> stringArgs =
HashMap <Character, ArgumentMarshaler> () baru;
private map <Character, ArgumentMarshaler> intArgs =
HashMap <Character, ArgumentMarshaler> () baru;
private map <Character, ArgumentMarshaler> marshalers =
HashMap <Character, ArgumentMarshaler> () baru;
...
void parseBooleanSchemaElement pribadi (char elementId) {
ArgumentMarshaler m = BooleanArgumentMarshaler baru ();
booleanArgs.put (elementId, m);
marshalers.put (elementId, m);
}

private void parseIntegerSchemaElement (char elementId) {


ArgumentMarshaler m = new IntegerArgumentMarshaler ();
intArgs.put (elementId, m);
marshalers.put (elementId, m);
}

void parseStringSchemaElement pribadi (char elementId) {


ArgumentMarshaler m = new StringArgumentMarshaler ();
stringArgs.put (elementId, m);
marshalers.put (elementId, m);
}

Tentu saja semua tes masih berlalu. Selanjutnya, saya mengubah isBooleanArg dari ini:
private boolean isBooleanArg (char argChar) {
return booleanArgs.containsKey (argChar);
}

untuk ini:
private boolean isBooleanArg (char argChar) {
ArgumentMarshaler m = marshalers.get (argChar);
mengembalikan contoh BooleanArgumentMarshaler;
}

Tes masih berlalu. Jadi saya membuat perubahan yang sama ke isIntArg dan isStringArg .
private boolean isIntArg (char argChar) {
ArgumentMarshaler m = marshalers.get (argChar);
kembalikan contoh IntegerArgumentMarshaler;
}

private boolean isStringArg (char argChar) {


ArgumentMarshaler m = marshalers.get (argChar);
mengembalikan instanceof StringArgumentMarshaler;
}

www.it-ebooks.info

Halaman 253

222 Bab 14: Penyempurnaan Berturut-turut

Tes masih berlalu. Jadi saya menghilangkan semua panggilan duplikat ke marshalers.get sebagai berikut:
private boolean setArgument (char argChar) melempar ArgsException {
ArgumentMarshaler m = marshalers.get (argChar);
if (isBooleanArg ( m ))
setBooleanArg (argChar);
lain jika (isStringArg ( m ))
setStringArg (argChar);
lain jika (isIntArg ( m ))
setIntArg (argChar);
lain
return false;

kembali benar;
}

private boolean isIntArg ( ArgumentMarshaler m ) {


kembalikan contoh IntegerArgumentMarshaler;
}

private boolean isStringArg ( ArgumentMarshaler m ) {


mengembalikan instanceof StringArgumentMarshaler;
}

private boolean isBooleanArg ( ArgumentMarshaler m ) {


mengembalikan contoh BooleanArgumentMarshaler;
}

Ini tidak meninggalkan alasan yang baik untuk tiga metode isxxxArg . Jadi saya memberi tahu mereka:
private boolean setArgument (char argChar) melempar ArgsException {
ArgumentMarshaler m = marshalers.get (argChar);
if ( m instance of BooleanArgumentMarshaler )
setBooleanArg (argChar);
lain jika ( contoh StringArgumentMarshaler )

https://translate.googleusercontent.com/translate_f 199/365
3/12/2020 www.it-ebooks.info
setStringArg (argChar);
lain jika ( contoh IntegerArgumentMarshaler )
setIntArg (argChar);
lain
return false;

kembali benar;
}

Selanjutnya, saya mulai menggunakan peta marshaler di fungsi yang ditetapkan , melanggar penggunaan yang lain
tiga peta. Saya mulai dengan boolean .
private boolean setArgument (char argChar) melempar ArgsException {
ArgumentMarshaler m = marshalers.get (argChar);
if (m instance of BooleanArgumentMarshaler)
setBooleanArg ( m );
lain jika (contoh StringArgumentMarshaler)
setStringArg (argChar);
lain jika (contoh IntegerArgumentMarshaler)
setIntArg (argChar);
lain
return false;

www.it-ebooks.info

Halaman 254

Argumen string 223

kembali benar;
}
...
kekosongan pribadi setBooleanArg ( ArgumentMarshaler m ) {
coba {
m .set ("true"); // was: booleanArgs.get (argChar) .set ("true");
} catch (ArgsException e) {
}
}

Tes masih berlalu, jadi saya melakukan hal yang sama dengan String dan Integer . Ini memungkinkan saya untuk
parut beberapa kode manajemen pengecualian jelek ke dalam fungsi setArgument .
private boolean setArgument (char argChar) melempar ArgsException {
ArgumentMarshaler m = marshalers.get (argChar);
coba {
if (m instance of BooleanArgumentMarshaler)
setBooleanArg (m);
lain jika (contoh StringArgumentMarshaler)
setStringArg ( m );
lain jika (contoh IntegerArgumentMarshaler)
setIntArg ( m );
lain
return false;
} catch (ArgsException e) {
valid = false;
errorArgumentId = argChar;
lempar e;
}
kembali benar;
}

kekosongan pribadi setIntArg ( ArgumentMarshaler m ) melempar ArgsException {


currentArgument ++;
Parameter string = nol;
coba {
parameter = args [currentArgument];
m .set (parameter);
} catch (ArrayIndexOutOfBoundsException e) {
errorCode = ErrorCode.MISSING_INTEGER;
melempar ArgsException baru ();
} catch (ArgsException e) {
errorParameter = parameter;
errorCode = ErrorCode.INVALID_INTEGER;
lempar e;
}
}

kekosongan pribadi setStringArg ( ArgumentMarshaler m ) melempar ArgsException {


currentArgument ++;
coba {
m .set (args [currentArgument]);
} catch (ArrayIndexOutOfBoundsException e) {
errorCode = ErrorCode.MISSING_STRING;
melempar ArgsException baru ();
}
}

https://translate.googleusercontent.com/translate_f 200/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 255

224 Bab 14: Penyempurnaan Berturut-turut

Saya hampir bisa menghapus tiga peta lama. Pertama, saya perlu mengubah
fungsi getBoolean dari ini:
public boolean getBoolean (char arg) {
Args.ArgumentMarshaler am = booleanArgs.get (arg);
return am! = null && (Boolean) am.get ();
}

untuk ini:
public boolean getBoolean (char arg) {
Args.ArgumentMarshaler am = marshalers.get (arg);
boolean b = false;
coba {
b = am! = null && (Boolean) am.get ();
} catch (ClassCastException e) {
b = salah;
}
kembali b;
}
Perubahan terakhir ini mungkin mengejutkan. Kenapa tiba-tiba saya memutuskan untuk berurusan
yang ClassCastException ? Alasannya adalah bahwa saya memiliki satu set unit test dan set terpisah
tes penerimaan ditulis dalam FitNesse. Ternyata tes FitNesse memastikan bahwa jika
Anda menelepon getBoolean dengan argumen nonboolean, Anda salah . Tes unit tidak.
Hingga saat ini saya hanya menjalankan unit test. 2
Perubahan terakhir ini memungkinkan saya untuk mencabut penggunaan lain dari peta boolean :

void parseBooleanSchemaElement pribadi (char elementId) {


ArgumentMarshaler m = BooleanArgumentMarshaler baru ();
booleanArgs.put (elementId, m);
marshalers.put (elementId, m);
}

Dan sekarang kita bisa menghapus peta boolean .


Args kelas publik {
...
private map <Character, ArgumentMarshaler> booleanArgs =
HashMap <Character, ArgumentMarshaler> () baru;
private map <Character, ArgumentMarshaler> stringArgs =
HashMap <Character, ArgumentMarshaler> () baru;
private map <Character, ArgumentMarshaler> intArgs =
HashMap <Character, ArgumentMarshaler> () baru;
private map <Character, ArgumentMarshaler> marshalers =
HashMap <Character, ArgumentMarshaler> () baru;
...

Selanjutnya, saya memigrasi argumen String dan Integer dengan cara yang sama dan melakukan sedikit
bersihkan dengan booleans .
void parseBooleanSchemaElement pribadi (char elementId) {
marshalers.put (elementId, BooleanArgumentMarshaler () ) baru;
}

2. Untuk mencegah kejutan lebih lanjut dari jenis ini, saya menambahkan tes unit baru yang melibatkan semua tes FitNesse.

www.it-ebooks.info

Halaman 256

https://translate.googleusercontent.com/translate_f 201/365
3/12/2020 www.it-ebooks.info

Argumen string 225

private void parseIntegerSchemaElement (char elementId) {


marshalers.put (elementId, IntegerArgumentMarshaler () ) baru;
}

void parseStringSchemaElement pribadi (char elementId) {


marshalers.put (elementId, StringArgumentMarshaler () ) baru;
}
...
public String getString (char arg) {
Args.ArgumentMarshaler am = marshalers .get (arg);
coba {
return am == null? "": (String) am.get ();
} catch (ClassCastException e) {
kembali "";
}
}

public int getInt (char arg) {


Args.ArgumentMarshaler am = marshalers .get (arg);
coba {
return am == null? 0: (Integer) am.get ();
} catch (Exception e) {
return 0;
}
}
...
Args kelas publik {
...
private map <Character, ArgumentMarshaler> stringArgs =
HashMap <Character, ArgumentMarshaler> () baru;
private map <Character, ArgumentMarshaler> intArgs =
HashMap <Character, ArgumentMarshaler> () baru;
private map <Character, ArgumentMarshaler> marshalers =
HashMap <Character, ArgumentMarshaler> () baru;
...

Selanjutnya, saya sebaris tiga metode parse karena mereka tidak berbuat banyak lagi:
private void parseSchemaElement (elemen String) melempar ParseException {
char elementId = element.charAt (0);
String elementTail = element.substring (1);
validateSchemaElementId (elementId);
if (isBooleanSchemaElement (elementTail))
marshalers.put (elementId, BooleanArgumentMarshaler ()) baru;
lain jika (isStringSchemaElement (elementTail))
marshalers.put (elementId, StringArgumentMarshaler ()) baru;
lain jika (isIntegerSchemaElement (elementTail)) {
marshalers.put (elementId, IntegerArgumentMarshaler ()) baru;
} lain {
melempar ParseException baru (String.format (
"Argumen:% c memiliki format tidak valid:% s.", ElementId, elementTail), 0);
}
}

Oke, jadi sekarang mari kita lihat keseluruhan gambar lagi. Listing 14-12 menunjukkan arus
bentuk kelas Args .

www.it-ebooks.info

Halaman 257

226 Bab 14: Penyempurnaan Berturut-turut

Listing 14-12
Args.java (Setelah refactoring pertama)
package com.objectmentor.utilities.getopts;

impor java.text.ParseException;
import java.util. *;

Args kelas publik {


skema String pribadi;
private String [] args;
private boolean valid = true;
Set pribadi <Character> terdugaArguments = TreeSet baru <Character> ();
private map <Character, ArgumentMarshaler> marshalers =
HashMap <Character, ArgumentMarshaler> () baru;
Set pribadi <Character> argsFound = HashSet baru <Character> ();
Dokumen pribadi saat ini;

https://translate.googleusercontent.com/translate_f 202/365
3/12/2020 www.it-ebooks.info
private char errorArgumentId = '\ 0';
private String errorParameter = "TILT";
private ErrorCode errorCode = ErrorCode.OK;

private enum ErrorCode {


Oke, MISSING_STRING, MISSING_INTEGER, INVALID_INTEGER, UNEXPECTED_ARGUMENT}

public Args (Skema string, String [] args) melempar ParseException {


this.schema = skema;
this.args = args;
valid = parse ();
}

parse boolean pribadi () melempar ParseException {


if (schema.length () == 0 && args.length == 0)
kembali benar;
parseSchema ();
coba {
parseArguments ();
} catch (ArgsException e) {
}
kembali valid;
}

private boolean parseSchema () melempar ParseException {


untuk (String elemen: schema.split (",")) {
if (element.length ()> 0) {
String trimmedElement = element.trim ();
parseSchemaElement (trimmedElement);
}
}
kembali benar;
}

private void parseSchemaElement (elemen String) melempar ParseException {


char elementId = element.charAt (0);
String elementTail = element.substring (1);
validateSchemaElementId (elementId);
if (isBooleanSchemaElement (elementTail))
marshalers.put (elementId, BooleanArgumentMarshaler ()) baru;
lain jika (isStringSchemaElement (elementTail))
marshalers.put (elementId, StringArgumentMarshaler ()) baru;

www.it-ebooks.info

Halaman 258

Argumen string 227

Listing 14-12 (lanjutan)


Args.java (Setelah refactoring pertama)
lain jika (isIntegerSchemaElement (elementTail)) {
marshalers.put (elementId, IntegerArgumentMarshaler ()) baru;
} lain {
melempar ParseException baru (String.format (
"Argumen:% c memiliki format tidak valid:% s.", ElementId, elementTail), 0);
}
}

void pribadi validateSchemaElementId (char elementId) melempar ParseException {


if (! Character.isLetter (elementId)) {
melempar ParseException baru (
"Karakter buruk:" + elementId + "dalam format Args:" + schema, 0);
}
}

private boolean isStringSchemaElement (String elementTail) {


return elementTail.equals ("*");
}

boolean pribadi isBooleanSchemaElement (String elementTail) {


return elementTail.length () == 0;
}

private boolean isIntegerSchemaElement (String elementTail) {


mengembalikan elementTail.equals ("#");
}

private boolean parseArguments () melempar ArgsException {


untuk (currentArgument = 0; currentArgument <args.length; currentArgument ++) {
String arg = args [currentArgument];
parseArgument (arg);
}
kembali benar;
}

private void parseArgument (String arg) melempar ArgsException {


if (arg.startsWith ("-"))

https://translate.googleusercontent.com/translate_f 203/365
3/12/2020 www.it-ebooks.info
parseElements (arg);
}

private void parseElements (String arg) melempar ArgsException {


untuk (int i = 1; i <arg.length (); i ++)
parseElement (arg.charAt (i));
}

private void parseElement (char argChar) melempar ArgsException {


if (setArgument (argChar))
argsFound.add (argChar);
lain {
terdugaArguments.add (argChar);
errorCode = ErrorCode.UNEXPECTED_ARGUMENT;
valid = false;
}
}

www.it-ebooks.info

Halaman 259

228 Bab 14: Penyempurnaan Berturut-turut

Listing 14-12 (lanjutan)


Args.java (Setelah refactoring pertama)
private boolean setArgument (char argChar) melempar ArgsException {
ArgumentMarshaler m = marshalers.get (argChar);
coba {
if (m instance of BooleanArgumentMarshaler)
setBooleanArg (m);
lain jika (contoh StringArgumentMarshaler)
setStringArg (m);
lain jika (contoh IntegerArgumentMarshaler)
setIntArg (m);
lain
return false;
} catch (ArgsException e) {
valid = false;
errorArgumentId = argChar;
lempar e;
}
kembali benar;
}

kekosongan pribadi setIntArg (ArgumentMarshaler m) melempar ArgsException {


currentArgument ++;
Parameter string = nol;
coba {
parameter = args [currentArgument];
m.set (parameter);
} catch (ArrayIndexOutOfBoundsException e) {
errorCode = ErrorCode.MISSING_INTEGER;
melempar ArgsException baru ();
} catch (ArgsException e) {
errorParameter = parameter;
errorCode = ErrorCode.INVALID_INTEGER;
lempar e;
}
}

kekosongan pribadi setStringArg (ArgumentMarshaler m) melempar ArgsException {


currentArgument ++;
coba {
m.set (args [currentArgument]);
} catch (ArrayIndexOutOfBoundsException e) {
errorCode = ErrorCode.MISSING_STRING;
melempar ArgsException baru ();
}
}

kekosongan pribadi setBooleanArg (ArgumentMarshaler m) {


coba {
m.set ("true");
} catch (ArgsException e) {
}
}

kardinalitas publik () {
return argsFound.size ();
}

https://translate.googleusercontent.com/translate_f 204/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info

Halaman 260

Argumen string 229

Listing 14-12 (lanjutan)


Args.java (Setelah refactoring pertama)
penggunaan String publik () {
if (schema.length ()> 0)
kembali "- [" + skema + "]";
lain
kembali "";
}

public String errorMessage () melempar Exception {


switch (errorCode) {
huruf OK:
throw Exception baru ("TILT: Seharusnya tidak sampai di sini.");
huruf UNEXPECTED_ARGUMENT:
return tak terdugaArgumentMessage ();
huruf besar-kecil MISSING_STRING:
return String.format ("Tidak dapat menemukan parameter string untuk -% c.",
errorArgumentId);
case INVALID_INTEGER:
return String.format ("Argumen -% c mengharapkan bilangan bulat tetapi '% s'.",
errorArgumentId, errorParameter);
huruf besar-kecil MISSING_INTEGER:
return String.format ("Tidak dapat menemukan parameter integer untuk -% c.",
errorArgumentId);
}
kembali "";
}

private String terdugaArgumentMessage () {


StringBuffer message = new StringBuffer ("Argument (s) -");
untuk (char c: terdugaArguments) {
message.append (c);
}
message.append ("tak terduga.");

return message.toString ();


}

public boolean getBoolean (char arg) {


Args.ArgumentMarshaler am = marshalers.get (arg);
boolean b = false;
coba {
b = am! = null && (Boolean) am.get ();
} catch (ClassCastException e) {
b = salah;
}
kembali b;
}

public String getString (char arg) {


Args.ArgumentMarshaler am = marshalers.get (arg);
coba {
return am == null? "": (String) am.get ();
} catch (ClassCastException e) {
kembali "";
}
}

www.it-ebooks.info

Halaman 261

230 Bab 14: Penyempurnaan Berturut-turut

https://translate.googleusercontent.com/translate_f 205/365
3/12/2020 www.it-ebooks.info
Listing 14-12 (lanjutan)
Args.java (Setelah refactoring pertama)
public int getInt (char arg) {
Args.ArgumentMarshaler am = marshalers.get (arg);
coba {
return am == null? 0: (Integer) am.get ();
} catch (Exception e) {
return 0;
}
}

public boolean has (char arg) {


return argsFound.contains (arg);
}

boolean publik isValid () {


kembali valid;
}

kelas privat ArgsException memperluas Exception {


}

kelas abstrak pribadi ArgumentMarshaler {


public abstrak void set (String s) melempar ArgsException;
Objek abstrak publik dapatkan ();
}

kelas privat BooleanArgumentMarshaler memperluas ArgumentMarshaler {


private boolean booleanValue = false;

public void set (String s) {


booleanValue = true;
}

Obyek publik dapatkan () {


kembalikan booleanValue;
}
}

kelas privat StringArgumentMarshaler memperluas ArgumentMarshaler {


private String stringValue = "";

public void set (String s) {


stringValue = s;
}

Obyek publik dapatkan () {


mengembalikan stringValue;
}
}

kelas privat IntegerArgumentMarshaler memperluas ArgumentMarshaler {


private int intValue = 0;

public void set (String s) melempar ArgsException {


coba {
intValue = Integer.parseInt (s);

www.it-ebooks.info

Halaman 262

Argumen string 231

Listing 14-12 (lanjutan)


Args.java (Setelah refactoring pertama)
} catch (NumberFormatException e) {
melempar ArgsException baru ();
}
}

Obyek publik dapatkan () {


mengembalikan nilai;
}
}
}

Setelah semua itu berhasil, ini sedikit mengecewakan. Strukturnya sedikit lebih baik, tetapi kita masih
memiliki semua variabel di atas; masih ada jenis huruf yang mengerikan di setArgument ; dan
semua fungsi yang diset benar-benar jelek. Belum lagi semua pemrosesan kesalahan. Kita masih
punya banyak pekerjaan di depan kita.
Saya benar-benar ingin menyingkirkan case-type itu di setArgument [G23]. Apa yang saya suka
setArgument adalah panggilan tunggal ke ArgumentMarshaler.set . Ini berarti saya harus mendorong

https://translate.googleusercontent.com/translate_f 206/365
3/12/2020 www.it-ebooks.info
setIntArg , setStringArg , dan setBooleanArg ke dalam ArgumentMarshaler yang sesuai
turunannya. Tapi ada masalah.
Jika Anda memperhatikan setIntArg , Anda akan melihat bahwa ia menggunakan dua variabel instan: args
dan currentArg . Untuk memindahkan setIntArg ke BooleanArgumentMarshaler , saya harus lulus
baik args dan currentArgs sebagai argumen fungsi. Itu kotor [F1]. Saya lebih suka melewati satu
argumen bukannya dua. Untungnya, ada solusi sederhana. Kita dapat mengonversi args
Array ke dalam daftar dan meneruskan Iterator ke fungsi yang ditetapkan . Yang berikut membawa saya
sepuluh langkah, melewati semua tes setelah masing-masing. Tapi saya hanya akan menunjukkan hasilnya. Anda harus
mampu mengetahui apa sebagian besar langkah kecil itu.
Args kelas publik {
skema String pribadi;
private String [] args;
private boolean valid = true;
Set pribadi <Character> terdugaArguments = TreeSet baru <Character> ();
private map <Character, ArgumentMarshaler> marshalers =
HashMap <Character, ArgumentMarshaler> () baru;
Set pribadi <Character> argsFound = HashSet baru <Character> ();
Iterator pribadi <String> currentArgument;
private char errorArgumentId = '\ 0';
private String errorParameter = "TILT";
private ErrorCode errorCode = ErrorCode.OK;
Daftar pribadi <String> argsList;

private enum ErrorCode {


Oke, MISSING_STRING, MISSING_INTEGER, INVALID_INTEGER, UNEXPECTED_ARGUMENT}

public Args (Skema string, String [] args) melempar ParseException {


this.schema = skema;
argsList = Arrays.asList (args);
valid = parse ();
}

www.it-ebooks.info

Halaman 263

232 Bab 14: Penyempurnaan Berturut-turut

parse boolean pribadi () melempar ParseException {


if (schema.length () == 0 && argsList.size () == 0)
kembali benar;
parseSchema ();
coba {
parseArguments ();
} catch (ArgsException e) {
}
kembali valid;
}
---
private boolean parseArguments () melempar ArgsException {
for (currentArgument = argsList.iterator () ; currentArgument. hasNext () ;) {
String arg = currentArgument. selanjutnya () ;
parseArgument (arg);
}

kembali benar;
}
---
kekosongan pribadi setIntArg (ArgumentMarshaler m) melempar ArgsException {
Parameter string = nol;
coba {
parameter = currentArgument. selanjutnya () ;
m.set (parameter);
} catch ( NoSuchElementException e) {
errorCode = ErrorCode.MISSING_INTEGER;
melempar ArgsException baru ();
} catch (ArgsException e) {
errorParameter = parameter;
errorCode = ErrorCode.INVALID_INTEGER;
lempar e;
}
}

kekosongan pribadi setStringArg (ArgumentMarshaler m) melempar ArgsException {


coba {
m.set (currentArgument .next () );
} catch ( NoSuchElementException e) {
errorCode = ErrorCode.MISSING_STRING;
melempar ArgsException baru ();
}
}

Ini adalah perubahan sederhana yang membuat semua tes lulus. Sekarang kita bisa mulai memindahkan set
fungsi turun ke turunan yang sesuai. Pertama, saya perlu melakukan perubahan berikut

https://translate.googleusercontent.com/translate_f 207/365
3/12/2020 www.it-ebooks.info
dalam setArgument :
private boolean setArgument (char argChar) melempar ArgsException {
ArgumentMarshaler m = marshalers.get (argChar);
jika (m == null)
return false;
coba {
if (m instance of BooleanArgumentMarshaler)
setBooleanArg (m);
lain jika (contoh StringArgumentMarshaler)
setStringArg (m);
lain jika (contoh IntegerArgumentMarshaler)
setIntArg (m);

www.it-ebooks.info

Halaman 264

Argumen string 233

lain
return false;
} catch (ArgsException e) {
valid = false;
errorArgumentId = argChar;
lempar e;
}
kembali benar;
}

Perubahan ini penting karena kami ingin sepenuhnya menghilangkan rantai if-else .
Oleh karena itu, kami perlu mengeluarkan kondisi kesalahan darinya.
Sekarang kita dapat mulai memindahkan fungsi yang ditetapkan . Fungsi setBooleanArg adalah sepele, jadi
kami akan menyiapkan yang pertama. Tujuan kami adalah mengubah fungsi setBooleanArg menjadi hanya
menangkal BooleanArgumentMarshaler .
private boolean setArgument (char argChar) melempar ArgsException {
ArgumentMarshaler m = marshalers.get (argChar);
jika (m == null)
return false;
coba {
if (m instance of BooleanArgumentMarshaler)
setBooleanArg (m, currentArgument );
lain jika (contoh StringArgumentMarshaler)
setStringArg (m);
lain jika (contoh IntegerArgumentMarshaler)
setIntArg (m);

} catch (ArgsException e) {
valid = false;
errorArgumentId = argChar;
lempar e;
}
kembali benar;
}
---
kekosongan pribadi setBooleanArg (ArgumentMarshaler m,
Iterator <String> currentArgument)
melempar ArgsException {
coba {
m.set ("true");
catch (ArgsException e) {
}
}

Bukankah kita baru saja memasukkan proses pengecualian itu? Taruh barang-barang agar kamu bisa menerima
mereka keluar lagi sangat umum di refactoring. Kecilnya langkah dan kebutuhan untuk
tetap menjalankan tes berarti Anda sering berpindah-pindah. Refactoring sangat mirip
memecahkan kubus Rubik. Ada banyak langkah kecil yang diperlukan untuk mencapai tujuan besar. Setiap
langkah memungkinkan berikutnya.
Mengapa kita melewatkan iterator ketika setBooleanArg tentu tidak membutuhkannya? Karena
setIntArg dan setStringArg akan! Dan karena saya ingin menggunakan ketiga fungsi ini
melalui metode abstrak di ArgumentMarshaller , saya harus meneruskannya ke setBooleanArg .

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 208/365
3/12/2020 www.it-ebooks.info

Halaman 265

234 Bab 14: Penyempurnaan Berturut-turut

Jadi sekarang setBooleanArg tidak berguna. Jika ada fungsi yang diatur di ArgumentMarshaler , kami
bisa menyebutnya langsung. Jadi sudah waktunya untuk membuat fungsi itu! Langkah pertama adalah menambahkan yang baru
metode abstrak ke ArgumentMarshaler .
kelas abstrak pribadi ArgumentMarshaler {
set abstrak kekosongan publik (Iterator <String> currentArgument)
melempar ArgsException;
public abstrak void set (String s) melempar ArgsException;
Objek abstrak publik dapatkan ();
}

Tentu saja ini memecah semua turunannya. Jadi mari kita terapkan metode baru di masing-masing.

kelas privat BooleanArgumentMarshaler memperluas ArgumentMarshaler {


private boolean booleanValue = false;

public void set (Iterator <String> currentArgument) melempar ArgsException {


booleanValue = true;
}

public void set (String s) {


booleanValue = true;
}

Obyek publik dapatkan () {


kembalikan booleanValue;
}
}

kelas privat StringArgumentMarshaler memperluas ArgumentMarshaler {


private String stringValue = "";

public void set (Iterator <String> currentArgument) melempar ArgsException {


}

public void set (String s) {


stringValue = s;
}

Obyek publik dapatkan () {


mengembalikan stringValue;
}
}

kelas privat IntegerArgumentMarshaler memperluas ArgumentMarshaler {


private int intValue = 0;

public void set (Iterator <String> currentArgument) melempar ArgsException {


}

public void set (String s) melempar ArgsException {


coba {
intValue = Integer.parseInt (s);
} catch (NumberFormatException e) {
melempar ArgsException baru ();
}
}

www.it-ebooks.info

Halaman 266

Argumen string 235

Obyek publik dapatkan () {


mengembalikan nilai;
}
}
Dan sekarang kita bisa menghilangkan setBooleanArg !

https://translate.googleusercontent.com/translate_f 209/365
3/12/2020 www.it-ebooks.info
private boolean setArgument (char argChar) melempar ArgsException {
ArgumentMarshaler m = marshalers.get (argChar);
jika (m == null)
return false;
coba {
if (m instance of BooleanArgumentMarshaler)
m.set (currentArgument);
lain jika (contoh StringArgumentMarshaler)
setStringArg (m);
lain jika (contoh IntegerArgumentMarshaler)
setIntArg (m);

} catch (ArgsException e) {
valid = false;
errorArgumentId = argChar;
lempar e;
}
kembali benar;
}

Tes semua lulus, dan fungsi yang ditetapkan digunakan untuk BooleanArgumentMarshaler !
Sekarang kita dapat melakukan hal yang sama untuk String dan Integer .
private boolean setArgument (char argChar) melempar ArgsException {
ArgumentMarshaler m = marshalers.get (argChar);
jika (m == null)
return false;
coba {
if (m instance of BooleanArgumentMarshaler)
m.set (currentArgument);
lain jika (contoh StringArgumentMarshaler)
m.set (currentArgument);
lain jika (contoh IntegerArgumentMarshaler)
m.set (currentArgument);

} catch (ArgsException e) {
valid = false;
errorArgumentId = argChar;
lempar e;
}
kembali benar;
}
---
kelas privat StringArgumentMarshaler memperluas ArgumentMarshaler {
private String stringValue = "";

public void set (Iterator <String> currentArgument) melempar ArgsException {


coba {
stringValue = currentArgument.next ();
} catch (NoSuchElementException e) {
errorCode = ErrorCode.MISSING_STRING;

www.it-ebooks.info

Halaman 267

236 Bab 14: Penyempurnaan Berturut-turut

melempar ArgsException baru ();


}
}

public void set (String s) {


}

Obyek publik dapatkan () {


mengembalikan stringValue;
}
}

kelas privat IntegerArgumentMarshaler memperluas ArgumentMarshaler {


private int intValue = 0;

public void set (Iterator <String> currentArgument) melempar ArgsException {


Parameter string = nol;
coba {
parameter = currentArgument.next ();
set (parameter);
} catch (NoSuchElementException e) {
errorCode = ErrorCode.MISSING_INTEGER;
melempar ArgsException baru ();
} catch (ArgsException e) {
errorParameter = parameter;
errorCode = ErrorCode.INVALID_INTEGER;
lempar e;
}
}

https://translate.googleusercontent.com/translate_f 210/365
3/12/2020 www.it-ebooks.info

public void set (String s) melempar ArgsException {


coba {
intValue = Integer.parseInt (s);
} catch (NumberFormatException e) {
melempar ArgsException baru ();
}
}

Obyek publik dapatkan () {


mengembalikan nilai;
}
}

Dan begitulah kudeta de grace : Jenis hurufnya bisa dilepas! Sentuh!


private boolean setArgument (char argChar) melempar ArgsException {
ArgumentMarshaler m = marshalers.get (argChar);
jika (m == null)
return false;
coba {
m.set (currentArgument);
kembali benar;
} catch (ArgsException e) {
valid = false;
errorArgumentId = argChar;
lempar e;
}
}

www.it-ebooks.info

Halaman 268

Argumen string 237

Sekarang kita dapat menyingkirkan beberapa fungsi crufty di IntegerArgumentMarshaler dan membersihkannya
sedikit.
kelas privat IntegerArgumentMarshaler memperluas ArgumentMarshaler {
private int intValue = 0

public void set (Iterator <String> currentArgument) melempar ArgsException {


Parameter string = nol;
coba {
parameter = currentArgument.next ();
intValue = Integer.parseInt (parameter);
} catch (NoSuchElementException e) {
errorCode = ErrorCode.MISSING_INTEGER;
melempar ArgsException baru ();
} catch ( NumberFormatException e) {
errorParameter = parameter;
errorCode = ErrorCode.INVALID_INTEGER;
melempar ArgsException baru ();
}
}

Obyek publik dapatkan () {


mengembalikan nilai;
}
}

Kami juga dapat mengubah ArgumentMarshaler menjadi antarmuka.


antarmuka pribadi ArgumentMarshaler {
void set (Iterator <String> currentArgument) melempar ArgsException;
Objek dapatkan ();
}

Jadi sekarang mari kita lihat betapa mudahnya menambahkan tipe argumen baru ke struktur kita. Itu harus
membutuhkan sedikit perubahan, dan perubahan itu harus diisolasi. Pertama, kita mulai dengan menambahkan
kasus uji baru untuk memeriksa apakah argumen ganda berfungsi dengan benar.
public void testSimpleDoublePresent () melempar Exception {
Args args = Args baru ("x ##", String baru [] {"-x", "42.3"});
assertTrue (args.isValid ());
assertEquals (1, args.cardinality ());
assertTrue (args.has ('x'));
assertEquals (42.3, args.getDouble ('x'), .001);
}

Sekarang kita membersihkan kode parsing skema dan menambahkan deteksi ## untuk double
tipe argumen.
private void parseSchemaElement (elemen String) melempar ParseException {
char elementId = element.charAt (0);
String elementTail = element.substring (1);
validateSchemaElementId (elementId);

https://translate.googleusercontent.com/translate_f 211/365
3/12/2020 www.it-ebooks.info
if (elementTail. length () == 0 )
marshalers.put (elementId, BooleanArgumentMarshaler ()) baru;
kalau tidak (elementTail. equals ("*") )
marshalers.put (elementId, StringArgumentMarshaler ()) baru;
lain jika (elementTail. equals ("#") )
marshalers.put (elementId, IntegerArgumentMarshaler ()) baru;

www.it-ebooks.info

Halaman 269

238 Bab 14: Penyempurnaan Berturut-turut

lain jika (elementTail.equals ("##"))


marshalers.put (elementId, DoubleArgumentMarshaler ()) baru;
lain
melempar ParseException baru (String.format (
"Argumen:% c memiliki format tidak valid:% s.", ElementId, elementTail), 0);
}

Selanjutnya, kita menulis kelas DoubleArgumentMarshaler .


kelas pribadi DoubleArgumentMarshaler mengimplementasikan ArgumentMarshaler {
private double doubleValue = 0;

public void set (Iterator <String> currentArgument) melempar ArgsException {


Parameter string = nol;
coba {
parameter = currentArgument.next ();
doubleValue = Double.parseDouble (parameter);
} catch (NoSuchElementException e) {
errorCode = ErrorCode.MISSING_DOUBLE;
melempar ArgsException baru ();
} catch (NumberFormatException e) {
errorParameter = parameter;
errorCode = ErrorCode.INVALID_DOUBLE;
melempar ArgsException baru ();
}
}

Obyek publik dapatkan () {


kembalikan nilai ganda;
}
}

Ini memaksa kita untuk menambahkan ErrorCode baru .


private enum ErrorCode {
Oke, MISSING_STRING, MISSING_INTEGER, INVALID_INTEGER, UNEXPECTED_ARGUMENT,
MISSING_DOUBLE, INVALID_DOUBLE }

Dan kita membutuhkan fungsi getDouble .


double publik getDouble (char arg) {
Args.ArgumentMarshaler am = marshalers.get (arg);
coba {
return am == null? 0: (Gandakan) am.get ();
} catch (Exception e) {
return 0,0;
}
}

Dan semua tes lulus! Itu cukup menyakitkan. Jadi sekarang mari kita pastikan semua kesalahan
pemrosesan bekerja dengan benar. Test case berikutnya memeriksa apakah ada kesalahan yang dinyatakan jika ada
string yang tidak dapat diuraikan dimasukkan ke argumen ## .
public void testInvalidDouble () melempar Exception {
Args args = Args baru ("x ##", String baru [] {"-x", "Empat puluh dua"});
assertFalse (args.isValid ());
assertEquals (0, args.cardinality ());
assertFalse (args.has ('x'));
assertEquals (0, args.getInt ('x'));

www.it-ebooks.info

Halaman 270

https://translate.googleusercontent.com/translate_f 212/365
3/12/2020 www.it-ebooks.info

Argumen string 239

assertEquals ("Argument -x mengharapkan double tetapi 'Forty two'.",


args.errorMessage ());
}
---
public String errorMessage () melempar Exception {
switch (errorCode) {
huruf OK:
throw Exception baru ("TILT: Seharusnya tidak sampai di sini.");
huruf UNEXPECTED_ARGUMENT:
return tak terdugaArgumentMessage ();
huruf besar-kecil MISSING_STRING:
return String.format ("Tidak dapat menemukan parameter string untuk -% c.",
errorArgumentId);
case INVALID_INTEGER:
return String.format ("Argumen -% c mengharapkan bilangan bulat tetapi '% s'.",
errorArgumentId, errorParameter);
huruf besar-kecil MISSING_INTEGER:
return String.format ("Tidak dapat menemukan parameter integer untuk -% c.",
errorArgumentId);
case INVALID_DOUBLE:
return String.format ("Argumen -% c mengharapkan dobel tetapi '% s'.",
errorArgumentId, errorParameter);
huruf besar-kecil MISSING_DOUBLE:
return String.format ("Tidak dapat menemukan parameter ganda untuk -% c.",
errorArgumentId);
}
kembali "";
}
Dan tes lulus. Tes selanjutnya memastikan kami mendeteksi argumen ganda yang hilang dengan benar.
public void testMissingDouble () melempar Exception {
Args args = Args baru ("x ##", String baru [] {"- x"});
assertFalse (args.isValid ());
assertEquals (0, args.cardinality ());
assertFalse (args.has ('x'));
assertEquals (0,0, args.getDouble ('x'), 0,01);
assertEquals ("Tidak dapat menemukan parameter ganda untuk -x.",
args.errorMessage ());
}

Ini berlalu seperti yang diharapkan. Kami menulisnya hanya untuk kelengkapan.
Kode pengecualian cukup jelek dan tidak benar-benar termasuk dalam kelas Args . Kita
juga membuang ParseException , yang bukan milik kita. Jadi mari kita gabungkan semua
pengecualian ke kelas ArgsException tunggal dan pindahkan ke modulnya sendiri.
kelas publik ArgsException memperluas Pengecualian {
private char errorArgumentId = '\ 0';
private String errorParameter = "TILT";
private ErrorCode errorCode = ErrorCode.OK;

ArgsException publik () {}

ArgsException publik (String message) {super (message);}

public enum ErrorCode {


Oke, MISSING_STRING, MISSING_INTEGER, INVALID_INTEGER, UNEXPECTED_ARGUMENT,
MISSING_DOUBLE, INVALID_DOUBLE}
}
---

www.it-ebooks.info

Halaman 271

240 Bab 14: Penyempurnaan Berturut-turut

Args kelas publik {


...
private char errorArgumentId = '\ 0';
private String errorParameter = "TILT";
ArgsException pribadi .ErrorCode errorCode = ArgsException .ErrorCode.OK;
Daftar pribadi <String> argsList;

public Args (String schema, String [] args) melempar ArgsException {


this.schema = skema;
argsList = Arrays.asList (args);
valid = parse ();
}

https://translate.googleusercontent.com/translate_f 213/365
3/12/2020 www.it-ebooks.info

private boolean parse () throws ArgsException {


if (schema.length () == 0 && argsList.size () == 0)
kembali benar;
parseSchema ();
coba {
parseArguments ();
} catch ( ArgsException e) {
}
kembali valid;
}

private boolean parseSchema () melempar ArgsException {


...
}

private void parseSchemaElement (elemen String) melempar ArgsException {


...
lain
melempar ArgsException baru (
String.format ("Argumen:% c memiliki format tidak valid:% s.",
elementId, elementTail));
}

void pribadi validateSchemaElementId (char elementId) melempar ArgsException {


if (! Character.isLetter (elementId)) {
melempar ArgsException baru (
"Karakter buruk:" + elementId + "dalam format Args:" + schema);
}
}

...

private void parseElement (char argChar) melempar ArgsException {


if (setArgument (argChar))
argsFound.add (argChar);
lain {
terdugaArguments.add (argChar);
errorCode = ArgsException .ErrorCode.UNEXPECTED_ARGUMENT;
valid = false;
}
}

...

www.it-ebooks.info

Halaman 272

Argumen string 241

kelas privat StringArgumentMarshaler mengimplementasikan ArgumentMarshaler {


private String stringValue = "";

public void set (Iterator <String> currentArgument) melempar ArgsException {


coba {
stringValue = currentArgument.next ();
} catch (NoSuchElementException e) {
errorCode = ArgsException .ErrorCode.MISSING_STRING;
melempar ArgsException baru ();
}
}

Obyek publik dapatkan () {


mengembalikan stringValue;
}
}

kelas privat IntegerArgumentMarshaler mengimplementasikan ArgumentMarshaler {


private int intValue = 0;

public void set (Iterator <String> currentArgument) melempar ArgsException {


Parameter string = nol;
coba {
parameter = currentArgument.next ();
intValue = Integer.parseInt (parameter);
} catch (NoSuchElementException e) {
errorCode = ArgsException.ErrorCode.MISSING_INTEGER;
melempar ArgsException baru ();
} catch (NumberFormatException e) {
errorParameter = parameter;
errorCode = ArgsException .ErrorCode.INVALID_INTEGER;
melempar ArgsException baru ();
}
}

https://translate.googleusercontent.com/translate_f 214/365
3/12/2020 www.it-ebooks.info
Obyek publik dapatkan () {
mengembalikan nilai;
}
}

kelas pribadi DoubleArgumentMarshaler mengimplementasikan ArgumentMarshaler {


private double doubleValue = 0;

public void set (Iterator <String> currentArgument) melempar ArgsException {


Parameter string = nol;
coba {
parameter = currentArgument.next ();
doubleValue = Double.parseDouble (parameter);
} catch (NoSuchElementException e) {
errorCode = ArgsException .ErrorCode.MISSING_DOUBLE;
melempar ArgsException baru ();
} catch (NumberFormatException e) {
errorParameter = parameter;
errorCode = ArgsException .ErrorCode.INVALID_DOUBLE;
melempar ArgsException baru ();
}
}

www.it-ebooks.info

Halaman 273

242 Bab 14: Penyempurnaan Berturut-turut

Obyek publik dapatkan () {


kembalikan nilai ganda;
}
}
}

Ini bagus. Sekarang satu-satunya pengecualian yang dilontarkan oleh Args adalah ArgsException . Bergerak
ArgsException ke dalam modulnya sendiri berarti kita dapat memindahkan banyak hal lain
kode dukungan kesalahan ke dalam modul itu dan keluar dari modul Args . Ini memberikan yang alami dan
tempat yang jelas untuk meletakkan semua kode itu dan akan sangat membantu kami membersihkan modul Args
meneruskan.
Jadi sekarang kita telah benar-benar memisahkan pengecualian dan kode kesalahan dari Args
modul. (Lihat Listing 14-13 hingga Listing 14-16.) Hal ini dicapai melalui serangkaian
sekitar 30 langkah kecil, menjaga agar tes tetap berjalan di antara setiap langkah.

Listing 14-13
ArgsTest.java
paket com.objectmentor.utilities.args;

import junit.framework.TestCase;

ArgsTest kelas publik memperluas TestCase {


public void testCreateWithNoSchemaOrArguments () melempar Exception {
Args args = Args baru ("", String baru [0]);
assertEquals (0, args.cardinality ());
}

public void testWithNoSchemaButWithOneArgument () melempar Exception {


coba {
Args baru ("", String baru [] {"- x"});
gagal();
} catch (ArgsException e) {
assertEquals (ArgsException.ErrorCode.UNEXPECTED_ARGUMENT,
e.getErrorCode ());
assertEquals ('x', e.getErrorArgumentId ());
}
}

public void testWithNoSchemaButWithMultipleArguments () melempar Exception {


coba {
Args baru ("", String baru [] {"- x", "-y"});
gagal();
} catch (ArgsException e) {
assertEquals (ArgsException.ErrorCode.UNEXPECTED_ARGUMENT,
e.getErrorCode ());
assertEquals ('x', e.getErrorArgumentId ());
}

public void testNonLetterSchema () melempar Exception {


coba {
Args baru ("*", String baru [] {});
gagal ("Konstruktor Args seharusnya memberikan pengecualian");

https://translate.googleusercontent.com/translate_f 215/365
3/12/2020 www.it-ebooks.info
} catch (ArgsException e) {

www.it-ebooks.info

Halaman 274

Argumen string 243

Listing 14-13 (lanjutan)


ArgsTest.java
assertEquals (ArgsException.ErrorCode.INVALID_ARGUMENT_NAME,
e.getErrorCode ());
assertEquals ('*', e.getErrorArgumentId ());
}
}

public void testInvalidArgumentFormat () melempar Exception {


coba {
Args baru ("f ~", String baru [] {});
gagal ("Konstruktor Args harus memiliki pengecualian melempar");
} catch (ArgsException e) {
assertEquals (ArgsException.ErrorCode.INVALID_FORMAT, e.getErrorCode ());
assertEquals ('f', e.getErrorArgumentId ());
}
}

public void testSimpleBooleanPresent () melempar Exception {


Args args = Args baru ("x", String baru [] {"- x"});
assertEquals (1, args.cardinality ());
assertEquals (true, args.getBoolean ('x'));
}

public void testSimpleStringPresent () melempar Exception {


Args args = Args baru ("x *", String baru [] {"- x", "param"});
assertEquals (1, args.cardinality ());
assertTrue (args.has ('x'));
assertEquals ("param", args.getString ('x'));
}

public void testMissingStringArgument () melempar Exception {


coba {
Args baru ("x *", String baru [] {"- x"});
gagal();
} catch (ArgsException e) {
assertEquals (ArgsException.ErrorCode.MISSING_STRING, e.getErrorCode ());
assertEquals ('x', e.getErrorArgumentId ());
}
}

public void testSpacesInFormat () melempar Exception {


Args args = Args baru ("x, y", String baru [] {"- xy"});
assertEquals (2, args.cardinality ());
assertTrue (args.has ('x'));
assertTrue (args.has ('y'));
}

public void testSimpleIntPresent () melempar Exception {


Args args = Args baru ("x #", String baru [] {"- x", "42"});
assertEquals (1, args.cardinality ());
assertTrue (args.has ('x'));
assertEquals (42, args.getInt ('x'));
}

public void testInvalidInteger () melempar Exception {


coba {
Args baru ("x #", String baru [] {"- x", "Empat puluh dua"});

www.it-ebooks.info

Halaman 275

https://translate.googleusercontent.com/translate_f 216/365
3/12/2020 www.it-ebooks.info

244 Bab 14: Penyempurnaan Berturut-turut

Listing 14-13 (lanjutan)


ArgsTest.java
gagal();
} catch (ArgsException e) {
assertEquals (ArgsException.ErrorCode.INVALID_INTEGER, e.getErrorCode ());
assertEquals ('x', e.getErrorArgumentId ());
assertEquals ("Empat puluh dua", e.getErrorParameter ());
}

public void testMissingInteger () melempar Exception {


coba {
Args baru ("x #", String baru [] {"- x"});
gagal();
} catch (ArgsException e) {
assertEquals (ArgsException.ErrorCode.MISSING_INTEGER, e.getErrorCode ());
assertEquals ('x', e.getErrorArgumentId ());
}
}

public void testSimpleDoublePresent () melempar Exception {


Args args = Args baru ("x ##", String baru [] {"- x", "42.3"});
assertEquals (1, args.cardinality ());
assertTrue (args.has ('x'));
assertEquals (42.3, args.getDouble ('x'), .001);
}

public void testInvalidDouble () melempar Exception {


coba {
Args baru ("x ##", String baru [] {"- x", "Empat puluh dua"});
gagal();
} catch (ArgsException e) {
assertEquals (ArgsException.ErrorCode.INVALID_DOUBLE, e.getErrorCode ());
assertEquals ('x', e.getErrorArgumentId ());
assertEquals ("Empat puluh dua", e.getErrorParameter ());
}
}

public void testMissingDouble () melempar Exception {


coba {
Args baru ("x ##", String baru [] {"- x"});
gagal();
} catch (ArgsException e) {
assertEquals (ArgsException.ErrorCode.MISSING_DOUBLE, e.getErrorCode ());
assertEquals ('x', e.getErrorArgumentId ());
}
}
}

Listing 14-14
ArgsExceptionTest.java
ArgsExceptionTest kelas publik memperluas TestCase {
public void testUnexpectedMessage () melempar Exception {
ArgsException e =

www.it-ebooks.info

Halaman 276

Argumen string 245

Listing 14-14 (lanjutan)


ArgsExceptionTest.java
ArgsException baru (ArgsException.ErrorCode.UNEXPECTED_ARGUMENT,
'x', null);
assertEquals ("Argumen -x tak terduga.", e.errorMessage ());
}

public void testMissingStringMessage () melempar Exception {


ArgsException e = ArgsException baru (ArgsException.ErrorCode.MISSING_STRING,
'x', null);
assertEquals ("Tidak dapat menemukan parameter string untuk -x.", e.errorMessage ());
}

public void testInvalidIntegerMessage () melempar Exception {


ArgsException e =
ArgsException baru (ArgsException.ErrorCode.INVALID_INTEGER,

https://translate.googleusercontent.com/translate_f 217/365
3/12/2020 www.it-ebooks.info
'x', "Empat puluh
assertEquals ("Argument -x mengharapkan dua");
integer tetapi 'Forty two'.",
e.errorMessage ());
}

public void testMissingIntegerMessage () melempar Exception {


ArgsException e =
ArgsException baru (ArgsException.ErrorCode.MISSING_INTEGER, 'x', null);
assertEquals ("Tidak dapat menemukan parameter integer untuk -x.", e.errorMessage ());
}

public void testInvalidDoubleMessage () melempar Exception {


ArgsException e = ArgsException baru (ArgsException.ErrorCode.INVALID_DOUBLE,
'x', "Empat puluh dua");
assertEquals ("Argument -x mengharapkan double tetapi 'Forty two'.",
e.errorMessage ());
}

public void testMissingDoubleMessage () melempar Exception {


ArgsException e = ArgsException baru (ArgsException.ErrorCode.MISSING_DOUBLE,
'x', null);
assertEquals ("Tidak dapat menemukan parameter ganda untuk -x.", e.errorMessage ());
}
}

Listing 14-15
ArgsException.java
kelas publik ArgsException memperluas Pengecualian {
private char errorArgumentId = '\ 0';
private String errorParameter = "TILT";
private ErrorCode errorCode = ErrorCode.OK;

ArgsException publik () {}

ArgsException publik (String message) {super (message);}

public ArgsException (ErrorCode errorCode) {


this.errorCode = errorCode;
}

www.it-ebooks.info

Halaman 277

246 Bab 14: Penyempurnaan Berturut-turut

Listing 14-15 (lanjutan)


ArgsException.java
public ArgsException (ErrorCode errorCode, String errorParameter) {
this.errorCode = errorCode;
this.errorParameter = errorParameter;
}

ArgsException publik (ErrorCode errorCode, char errorArgumentId,


String errorParameter) {
this.errorCode = errorCode;
this.errorParameter = errorParameter;
this.errorArgumentId = errorArgumentId;
}

public char getErrorArgumentId () {


return errorArgumentId;
}

kekosongan publik setErrorArgumentId (char errorArgumentId) {


this.errorArgumentId = errorArgumentId;
}

public String getErrorParameter () {


return errorParameter;
}

public void setErrorParameter (String errorParameter) {


this.errorParameter = errorParameter;
}

getErrorCode publik ErrorCode () {


return errorCode;
}

public void setErrorCode (ErrorCode errorCode) {


this.errorCode = errorCode;
}

public String errorMessage () melempar Exception {


switch (errorCode) {

https://translate.googleusercontent.com/translate_f 218/365
3/12/2020 www.it-ebooks.info
huruf OK:
throw Exception baru ("TILT: Seharusnya tidak sampai di sini.");
huruf UNEXPECTED_ARGUMENT:
return String.format ("Argument -% c tak terduga.", errorArgumentId);
huruf besar-kecil MISSING_STRING:
return String.format ("Tidak dapat menemukan parameter string untuk -% c.",
errorArgumentId);
case INVALID_INTEGER:
return String.format ("Argumen -% c mengharapkan bilangan bulat tetapi '% s'.",
errorArgumentId, errorParameter);
huruf besar-kecil MISSING_INTEGER:
return String.format ("Tidak dapat menemukan parameter integer untuk -% c.",
errorArgumentId);
case INVALID_DOUBLE:
return String.format ("Argumen -% c mengharapkan dobel tetapi '% s'.",
errorArgumentId, errorParameter);

www.it-ebooks.info

Halaman 278

Argumen string 247

Listing 14-15 (lanjutan)


ArgsException.java
huruf besar-kecil MISSING_DOUBLE:
return String.format ("Tidak dapat menemukan parameter ganda untuk -% c.",
errorArgumentId);
}
kembali "";
}

public enum ErrorCode {


Oke, INVALID_FORMAT, UNEXPECTED_ARGUMENT, INVALID_ARGUMENT_NAME,
MISSING_STRING,
MISSING_INTEGER, INVALID_INTEGER,
MISSING_DOUBLE, INVALID_DOUBLE}
}

Listing 14-16
Args.java
Args kelas publik {
skema String pribadi;
private map <Character, ArgumentMarshaler> marshalers =
HashMap <Character, ArgumentMarshaler> () baru;
Set pribadi <Character> argsFound = HashSet baru <Character> ();
Iterator pribadi <String> currentArgument;
Daftar pribadi <String> argsList;

public Args (String schema, String [] args) melempar ArgsException {


this.schema = skema;
argsList = Arrays.asList (args);
parse ();
}

private void parse () throws ArgsException {


parseSchema ();
parseArguments ();
}

private boolean parseSchema () melempar ArgsException {


untuk (String elemen: schema.split (",")) {
if (element.length ()> 0) {
parseSchemaElement (element.trim ());
}
}
kembali benar;
}

private void parseSchemaElement (elemen String) melempar ArgsException {


char elementId = element.charAt (0);
String elementTail = element.substring (1);
validateSchemaElementId (elementId);
if (elementTail.length () == 0)
marshalers.put (elementId, BooleanArgumentMarshaler ()) baru;
lain jika (elementTail.equals ("*"))
marshalers.put (elementId, StringArgumentMarshaler ()) baru;

https://translate.googleusercontent.com/translate_f 219/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info

Halaman 279

248 Bab 14: Penyempurnaan Berturut-turut

Listing 14-16 (lanjutan)


Args.java
lain jika (elementTail.equals ("#"))
marshalers.put (elementId, IntegerArgumentMarshaler ()) baru;
lain jika (elementTail.equals ("##"))
marshalers.put (elementId, DoubleArgumentMarshaler ()) baru;
lain
membuang ArgsException baru (ArgsException.ErrorCode.INVALID_FORMAT,
elementId, elementTail);
}

void pribadi validateSchemaElementId (char elementId) melempar ArgsException {


if (! Character.isLetter (elementId)) {
membuang ArgsException baru (ArgsException.ErrorCode.INVALID_ARGUMENT_NAME,
elementId, null);
}
}

private void parseArguments () melempar ArgsException {


for (currentArgument = argsList.iterator (); currentArgument.hasNext ();) {
String arg = currentArgument.next ();
parseArgument (arg);
}
}

private void parseArgument (String arg) melempar ArgsException {


if (arg.startsWith ("-"))
parseElements (arg);
}

private void parseElements (String arg) melempar ArgsException {


untuk (int i = 1; i <arg.length (); i ++)
parseElement (arg.charAt (i));
}

private void parseElement (char argChar) melempar ArgsException {


if (setArgument (argChar))
argsFound.add (argChar);
lain {
buang ArgsException baru (ArgsException.ErrorCode.UNEXPECTED_ARGUMENT,
argChar, null);
}
}

private boolean setArgument (char argChar) melempar ArgsException {


ArgumentMarshaler m = marshalers.get (argChar);
jika (m == null)
return false;
coba {
m.set (currentArgument);
kembali benar;
} catch (ArgsException e) {
e.setErrorArgumentId (argChar);
lempar e;
}
}

www.it-ebooks.info

Halaman 280

Argumen string 249

https://translate.googleusercontent.com/translate_f 220/365
3/12/2020 www.it-ebooks.info
Listing 14-16 (lanjutan)
Args.java
kardinalitas publik () {
return argsFound.size ();
}

penggunaan String publik () {


if (schema.length ()> 0)
kembali "- [" + skema + "]";
lain
kembali "";
}

public boolean getBoolean (char arg) {


ArgumentMarshaler am = marshalers.get (arg);
boolean b = false;
coba {
b = am! = null && (Boolean) am.get ();
} catch (ClassCastException e) {
b = salah;
}
kembali b;
}

public String getString (char arg) {


ArgumentMarshaler am = marshalers.get (arg);
coba {
return am == null? "": (String) am.get ();
} catch (ClassCastException e) {
kembali "";
}
}

public int getInt (char arg) {


ArgumentMarshaler am = marshalers.get (arg);
coba {
return am == null? 0: (Integer) am.get ();
} catch (Exception e) {
return 0;
}
}

double publik getDouble (char arg) {


ArgumentMarshaler am = marshalers.get (arg);
coba {
return am == null? 0: (Gandakan) am.get ();
} catch (Exception e) {
return 0,0;
}
}

public boolean has (char arg) {


return argsFound.contains (arg);
}
}

www.it-ebooks.info

Halaman 281

250 Bab 14: Penyempurnaan Berturut-turut

Mayoritas perubahan pada kelas Args adalah penghapusan. Banyak kode baru saja didapat
pindah dari Args dan dimasukkan ke dalam ArgsException . Bagus. Kami juga memindahkan semua
ArgumentMarshaller ke dalam file mereka sendiri. Lebih bagus!
Sebagian besar desain perangkat lunak yang baik hanya tentang partisi — pembuatan yang sesuai
tempat untuk meletakkan berbagai jenis kode. Pemisahan kekhawatiran ini membuat kode ini banyak
lebih mudah dimengerti dan dipelihara.
Dari minat khusus adalah menampilkan pesan kesalahan metode ArgsException . Jelas itu adalah vio-
lation dari SRP untuk menempatkan pemformatan pesan kesalahan ke dalam Args . Args harus tentang
pemrosesan argumen, bukan tentang format pesan kesalahan. Namun, apakah itu
benar-benar masuk akal untuk memasukkan kode pemformatan pesan kesalahan ke ArgsException ?
Terus terang, ini kompromi. Pengguna yang tidak menyukai pesan kesalahan yang disediakan oleh
ArgsException harus menulis sendiri. Tetapi kenyamanan memiliki kesalahan kalengan
pesan yang sudah disiapkan untuk Anda tidak signifikan.
Dengan sekarang harus jelas bahwa kita berada dalam jarak yang sangat dekat dengan solusi akhir itu
muncul di awal bab ini. Saya akan menyerahkan transformasi terakhir kepada Anda sebagai latihan.

https://translate.googleusercontent.com/translate_f 221/365
3/12/2020 www.it-ebooks.info
Kesimpulan
Kode tidak cukup untuk bekerja. Kode yang berfungsi sering rusak parah. Programmer
yang memuaskan diri mereka hanya dengan kode kerja berlaku tidak profesional. Mereka
mungkin takut bahwa mereka tidak punya waktu untuk memperbaiki struktur dan desain kode mereka, tetapi saya
tidak setuju. Tidak ada yang memiliki efek penurunan yang lebih dalam dan jangka panjang pada pengembangan
proyek daripada kode yang buruk. Jadwal yang buruk dapat diperbaiki, persyaratan yang buruk dapat diperbaiki
didenda. Dinamika tim yang buruk dapat diperbaiki. Tapi kode busuk dan fermentasi yang buruk, menjadi
berat yang tak terhindarkan yang menyeret tim ke bawah. Berkali-kali saya telah melihat tim mengerjakan sesuatu
untuk merangkak karena, dengan tergesa-gesa, mereka menciptakan banyak kode ganas yang selamanya
setelah itu mendominasi takdir mereka.
Tentu saja kode yang buruk dapat dibersihkan. Tapi ini sangat mahal. Sebagai kode membusuk, mod
ules menyindir satu sama lain, menciptakan banyak ketergantungan tersembunyi dan kusut
cies. Menemukan dan memecahkan ketergantungan lama adalah tugas yang panjang dan sulit. Di samping itu,
menjaga kode bersih relatif mudah. Jika Anda membuat kekacauan di modul di pagi hari, itu benar
mudah dibersihkan di sore hari. Lebih baik lagi, jika Anda membuat kekacauan lima menit yang lalu, itu
sangat mudah untuk membersihkannya sekarang.
Jadi solusinya adalah terus menjaga kode Anda sebersih dan sesederhana mungkin.
Jangan biarkan busuk mulai.

www.it-ebooks.info

Halaman 282

15
JUnit Internal

https://translate.googleusercontent.com/translate_f 222/365
3/12/2020 www.it-ebooks.info

JUnit adalah salah satu kerangka Java yang paling terkenal. Sebagai kerangka kerja, itu sederhana
konsepsi, tepat dalam definisi, dan elegan dalam implementasi. Tapi apa kodenya
terlihat seperti? Dalam bab ini kita akan mengkritik contoh yang diambil dari kerangka JUnit.

251

www.it-ebooks.info

Halaman 283

252 Bab 15: JUnit Internal

Kerangka Kerja JUnit


JUnit telah memiliki banyak penulis, tetapi itu dimulai dengan Kent Beck dan Eric Gamma bersama pada a
Pesawat ke Atlanta. Kent ingin belajar Java, dan Eric ingin belajar tentang Kent's Small-
kerangka pengujian bicara. "Apa yang bisa lebih alami bagi beberapa Geeks di sempit
perempat daripada mengeluarkan laptop kami dan mulai coding? " 1 Setelah tiga jam dari ketinggian
kerja, mereka telah menulis dasar-dasar JUnit.
Modul yang akan kita lihat adalah bit kode yang pintar yang membantu mengidentifikasi perbandingan string
kesalahan anak. Modul ini disebut ComparisonCompactor . Diberikan dua string yang berbeda,
seperti ABCDE dan ABXDE, itu akan mengekspos perbedaannya dengan menghasilkan string seperti
<... B [X] D ...> .
Saya bisa menjelaskannya lebih lanjut, tetapi test case melakukan pekerjaan yang lebih baik. Jadi lihat Daftar 15-1
dan Anda akan memahami persyaratan modul ini secara mendalam. Saat Anda melakukannya,
kritik struktur tes. Bisakah mereka lebih sederhana atau lebih jelas?

Listing 15-1
ComparisonCompactorTest.java
paket junit.tests.framework;

import junit.framework.ComparisonCompactor;
import junit.framework.TestCase;

ComparisonCompactorTest kelas publik memperluas TestCase {

public void testMessage () {


Kegagalan string = ComparisonCompactor baru (0, "b", "c"). Compact ("a");
assertTrue ("a expected: <[b]> but was: <[c]>". equals (failure));
}

public void testStartSame () {


Kegagalan string = ComparisonCompactor baru (1, "ba", "bc"). Compact (null);
assertEquals ("diharapkan: <b [a]> tetapi sebelumnya: <b [c]>", gagal);
}

public void testEndSame () {


Kegagalan string = ComparisonCompactor baru (1, "ab", "cb"). Compact (null);
assertEquals ("diharapkan: <[a] b> tetapi sebelumnya: <[c] b>", kegagalan);
}

public void testSame () {


Kegagalan string = ComparisonCompactor baru (1, "ab", "ab"). Compact (null);
assertEquals ("diharapkan: <ab> tetapi tadinya: <ab>", gagal);
}

public void testNoContextStartAndEndSame () {


Kegagalan string = ComparisonCompactor baru (0, "abc", "adc"). Compact (null);
assertEquals ("diharapkan: <... [b] ...> tetapi adalah: <... [d] ...>", kegagalan);
}

1. JUnit Pocket Guide , Kent Beck, O'Reilly, 2004, hlm. 43.

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 223/365
3/12/2020 www.it-ebooks.info

Halaman 284

Kerangka Kerja JUnit 253

Listing 15-1 (lanjutan)


ComparisonCompactorTest.java
public void testStartAndEndContext () {
Kegagalan string = ComparisonCompactor baru (1, "abc", "adc"). Compact (null);
assertEquals ("diharapkan: <a [b] c> tetapi adalah: <a [d] c>", kegagalan);
}

public void testStartAndEndContextWithEllipses () {


Kegagalan string =
ComparisonCompactor baru (1, "abcde", "abfde"). compact (null);
assertEquals ("diharapkan: <... b [c] d ...> tetapi adalah: <... b [f] d ...>", kegagalan);
}

public void testComparisonErrorStartSameComplete () {


Kegagalan string = ComparisonCompactor baru (2, "ab", "abc"). Compact (null);
assertEquals ("diharapkan: <ab []> tetapi adalah: <ab [c]>", kegagalan);
}

public void testComparisonErrorEndSameComplete () {


Kegagalan string = ComparisonCompactor baru (0, "bc", "abc"). Compact (null);
assertEquals ("diharapkan: <[] ...> tetapi sebelumnya: <[a] ...>", kegagalan);
}

public void testComparisonErrorEndSameCompleteContext () {


Kegagalan string = ComparisonCompactor baru (2, "bc", "abc"). Compact (null);
assertEquals ("diharapkan: <[] bc> tetapi sebelumnya: <[a] bc>", kegagalan);
}

public void testComparisonErrorOverlapingMatches () {


Kegagalan string = ComparisonCompactor baru (0, "abc", "abbc"). Compact (null);
assertEquals ("diharapkan: <... [] ...> tetapi adalah: <... [b] ...>", kegagalan);
}

public void testComparisonErrorOverlapingMatchesContext () {


Kegagalan string = ComparisonCompactor baru (2, "abc", "abbc"). Compact (null);
assertEquals ("diharapkan: <ab [] c> tetapi adalah: <ab [b] c>", kegagalan);
}

public void testComparisonErrorOverlapingMatches2 () {


Kegagalan string = ComparisonCompactor baru (0, "abcdde",
"abcde"). compact (null);
assertEquals ("diharapkan: <... [d] ...> tetapi adalah: <... [] ...>", kegagalan);
}

public void testComparisonErrorOverlapingMatches2Context () {


Kegagalan string =
ComparisonCompactor baru (2, "abcdde", "abcde"). compact (null);
assertEquals ("diharapkan: <... cd [d] e> tetapi adalah: <... cd [] e>", kegagalan);
}

public void testComparisonErrorWithActualNull () {


Kegagalan string = ComparisonCompactor baru (0, "a", null) .compact (null);
assertEquals ("diharapkan: <a> tetapi sebelumnya: <null>", gagal);
}

public void testComparisonErrorWithActualNullContext () {


Kegagalan string = ComparisonCompactor baru (2, "a", null) .compact (null);

www.it-ebooks.info

Halaman 285

254 Bab 15: JUnit Internal

Listing 15-1 (lanjutan)


ComparisonCompactorTest.java
assertEquals ("diharapkan: <a> tetapi sebelumnya: <null>", gagal);
}

https://translate.googleusercontent.com/translate_f 224/365
3/12/2020 www.it-ebooks.info
public void testComparisonErrorWithExpectedNull () {
Kegagalan string = ComparisonCompactor baru (0, null, "a"). Compact (null);
assertEquals ("diharapkan: <null> tetapi sebelumnya: <a>", gagal);
}

public void testComparisonErrorWithExpectedNullContext () {


Kegagalan string = ComparisonCompactor baru (2, null, "a"). Compact (null);
assertEquals ("diharapkan: <null> tetapi sebelumnya: <a>", gagal);
}

public void testBug609972 () {


Kegagalan string = ComparisonCompactor baru (10, "S & P500", "0"). Compact (null);
assertEquals ("diharapkan: <[S & P50] 0> tetapi sebelumnya: <[] 0>", kegagalan);
}
}

Saya menjalankan analisis cakupan kode pada ComparisonCompactor menggunakan tes ini. Kode
100 persen tertutup. Setiap baris kode, setiap pernyataan if dan untuk loop, dijalankan oleh
tes. Ini memberi saya tingkat kepercayaan yang tinggi bahwa kode itu berfungsi dan tingkat tinggi
menghormati pengerjaan penulis.
Kode untuk ComparisonCompactor ada di Listing 15-2. Luangkan waktu sejenak untuk melihat ini
kode. Saya pikir Anda akan merasa dipartisi dengan baik, cukup ekspresif, dan sederhana
struktur. Setelah Anda selesai, maka kami akan memilih nits bersama-sama.

Listing 15-2
ComparisonCompactor.java (Asli)
paket junit.framework;

ComparisonCompactor kelas publik {

private final static ELLIPSIS = "...";


final private static static DELTA_END = "]";
final private static static DELTA_START = "[";

private int fContextLength;


String pribadi fDirkirakan;
String pribadi fActual;
fPrefix pribadi int;
int pribadi fSuffix;

ComparisonCompactor publik (int contextLength,


String yang diharapkan,
String aktual) {
fContextLength = contextLength;
fExpected = diharapkan;
fActual = aktual;
}

www.it-ebooks.info

Halaman 286

Kerangka Kerja JUnit 255

Listing 15-2 (lanjutan)


ComparisonCompactor.java (Asli)
public String compact (pesan String) {
if (fExpected == null || fActual == null || areStringsEqual ())
return Assert.format (pesan, fExpected, fActual);

findCommonPrefix ();
findCommonSuffix ();
String diharapkan = compactString (fExpected);
String aktual = compactString (fActual);
return Assert.format (pesan, diharapkan, aktual);
}

private String compactString (Sumber string) {


Hasil string = DELTA_START +
source.substring (fPrefix, source.length () -
fSuffix + 1) + DELTA_END;
if (fPrefix> 0)
result = computeCommonPrefix () + hasil;
jika (fSuffix> 0)
hasil = hasil + computeCommonSuffix ();
hasil pengembalian;
}

private void findCommonPrefix () {


fPrefix = 0;
int end = Math.min (fExpected.length (), fActual.length ());
untuk (; fPrefix <end; fPrefix ++) {

https://translate.googleusercontent.com/translate_f 225/365
3/12/2020 www.it-ebooks.info
if (fExpected.charAt
istirahat; (fPrefix)! = fActual.charAt (fPrefix))
}
}

private void findCommonSuffix () {


int expectedSuffix = fExpected.length () - 1;
int actualSuffix = fActual.length () - 1;
untuk (;
actualSuffix> = fPrefix && expectedSuffix> = fPrefix;
actualSuffix--, expectedSuffix--) {
if (fExpected.charAt (expectedSuffix)! = fActual.charAt (actualSuffix))
istirahat;
}
fSuffix = fExpected.length () - expectedSuffix;
}

private String computeCommonPrefix () {


return (fPrefix> fContextLength? ELLIPSIS: "") +
fExpected.substring (Math.max (0, fPrefix - fContextLength),
fPrefix);
}

private String computeCommonSuffix () {


int end = Math.min (fExpected.length () - fSuffix + 1 + fContextLength,
fExpected.length ());
return fExpected.substring (fExpected.length () - fSuffix + 1, end) +
(fExpected.length () - fSuffix + 1 <fExpected.length () -
fContextLength? ELLIPSIS: "");
}

www.it-ebooks.info

Halaman 287

256 Bab 15: JUnit Internal

Listing 15-2 (lanjutan)


ComparisonCompactor.java (Asli)
private boolean areStringsEqual () {
return fExpected.equals (fActual);
}
}

Anda mungkin memiliki beberapa keluhan tentang modul ini. Ada beberapa ungkapan panjang
dan beberapa +1 aneh dan sebagainya. Namun secara keseluruhan modul ini cukup bagus. Bagaimanapun, itu
mungkin terlihat seperti Listing 15-3.

Listing 15-3
ComparisonCompator.java (defactored)
paket junit.framework;

ComparisonCompactor kelas publik {


ctxt int pribadi;
private String s1;
private String s2;
pfx pribadi int;
private int sfx;

ComparisonCompactor publik (int ctxt, String s1, String s2) {


this.ctxt = ctxt;
this.s1 = s1;
this.s2 = s2;
}

public String compact (String msg) {


if (s1 == null || s2 == null || s1.equals (s2))
return Assert.format (msg, s1, s2);

pfx = 0;
untuk (; pfx <Math.min (s1.length (), s2.length ()); pfx ++) {
if (s1.charAt (pfx)! = s2.charAt (pfx))
istirahat;
}
int sfx1 = s1.length () - 1;
int sfx2 = s2.length () - 1;
untuk (; sfx2> = pfx && sfx1> = pfx; sfx2--, sfx1--) {
if (s1.charAt (sfx1)! = s2.charAt (sfx2))
istirahat;
}
sfx = s1.length () - sfx1;
String cmp1 = compactString (s1);
String cmp2 = compactString (s2);
return Assert.format (msg, cmp1, cmp2);
}

https://translate.googleusercontent.com/translate_f 226/365
3/12/2020 www.it-ebooks.info
private String compactString (String s) {
Hasil string =
"[" + s.substring (pfx, s.length () - sfx + 1) + "]";
jika (pfx> 0)
result = (pfx> ctxt? "...": "") +
s1.substring (Math.max (0, pfx - ctxt), pfx) + hasil;

www.it-ebooks.info

Halaman 288

Kerangka Kerja JUnit 257

Listing 15-3 (lanjutan)


ComparisonCompator.java (defactored)
if (sfx> 0) {
int end = Math.min (s1.length () - sfx + 1 + ctxt, s1.length ());
hasil = hasil + (s1.substring (s1.length () - sfx + 1, akhir) +
(s1.length () - sfx + 1 <s1.length () - ctxt? "...": ""));
}
hasil pengembalian;
}

Meskipun penulis meninggalkan modul ini dalam kondisi sangat baik, Boy Scout Rule 2 memberi tahu
kita kita harus membiarkannya lebih bersih daripada yang kita temukan. Jadi, bagaimana kita bisa memperbaiki yang asli
kode dalam Listing 15-2?
Hal pertama yang saya tidak pedulikan adalah f awalan untuk variabel anggota [N6]. Hari ini
lingkungan membuat lingkup pengkodean seperti ini berlebihan. Jadi mari kita hilangkan semua f .
private int contextLength;
String pribadi diharapkan;
String pribadi aktual;
awalan int pribadi;
akhiran int pribadi;

Selanjutnya, kami memiliki kondisi tidak terenkapsulasi di awal fungsi ringkas


[G28].
public String compact (pesan String) {
if (diharapkan == null || aktual == null || areStringsEqual ())
return Assert.format (pesan, diharapkan, aktual);

findCommonPrefix ();
findCommonSuffix ();
String diharapkan = compactString (this.expected);
String aktual = compactString (this.actual);
return Assert.format (pesan, diharapkan, aktual);
}

Persyaratan ini harus dirangkum untuk memperjelas maksud kami. Jadi mari kita ekstrak suatu metode
itu menjelaskannya.
public String compact (pesan String) {
if ( shouldNotCompact () )
return Assert.format (pesan, diharapkan, aktual);

findCommonPrefix ();
findCommonSuffix ();
String diharapkan = compactString (this.expected);
String aktual = compactString (this.actual);
return Assert.format (pesan, diharapkan, aktual);
}

2. Lihat “Aturan Pramuka” di halaman 14.

www.it-ebooks.info

Halaman 289

https://translate.googleusercontent.com/translate_f 227/365
3/12/2020 www.it-ebooks.info

258 Bab 15: JUnit Internal

private boolean shouldNotCompact () {


return diharapkan == null || aktual == null || areStringsEqual ();
}

Saya tidak banyak peduli untuk this.expected dan this.actual notasi dalam kompak func-
tion. Ini terjadi ketika kami mengubah nama f yang diharapkan menjadi yang diharapkan . Kenapa disana?
variabel dalam fungsi ini yang memiliki nama yang sama dengan variabel anggota? Bukan mereka
mewakili sesuatu yang lain [N4]? Kita harus membuat nama tidak ambigu.
String compactExpected = compactString ( diharapkan );
String compactActual = compactString ( aktual );

Negatif sedikit lebih sulit untuk dipahami daripada yang positif [G29]. Jadi mari kita ubah itu jika
pernyataan di atas kepalanya dan membalikkan rasa kondisional.
public String compact (pesan String) {
if ( canBeCompacted ()) {
findCommonPrefix ();
findCommonSuffix ();
String compactExpected = compactString (diharapkan);
String compactActual = compactString (aktual);
return Assert.format (pesan, compactExpected, compactActual);
} lain {
return Assert.format (pesan, diharapkan, aktual);
}
}

private boolean canBeCompacted () {


pengembalian yang diharapkan ! = null && aktual ! = null &&! areStringsEqual ();
}

Nama fungsinya aneh [N7]. Meskipun tidak memadatkan string, itu


sebenarnya mungkin tidak memadatkan string jika canBeCompacted mengembalikan false . Jadi penamaan ini
fungsi compact menyembunyikan efek samping dari pemeriksaan kesalahan. Perhatikan juga fungsinya
mengembalikan pesan yang diformat, bukan hanya string yang dipadatkan. Demikian nama fungsinya
harus benar-benar formatCompactedComparison . Itu membuatnya jauh lebih baik saat dibaca
dengan argumen fungsi:
public String formatCompactedComparison (String message) {

Tubuh pernyataan if adalah tempat pemadatan sebenarnya dari yang diharapkan dan aktual
string dilakukan. Kita harus mengekstraknya sebagai metode bernama compactExpectedAndActual . Bagaimana-
pernah, kami ingin fungsi formatCompactedComparison melakukan semua pemformatan. Itu
fungsi compact ... seharusnya tidak melakukan apa pun selain memadatkan [G30]. Jadi mari kita bagi sebagai berikut:
...
private String compact Diharapkan;
private String compactActual;

...

public String formatCompactedComparison (String message) {


if (canBeCompacted ()) {
compactExpectedAndActual ();
return Assert.format (pesan, compactExpected, compactActual);
} lain {

www.it-ebooks.info

Halaman 290

Kerangka Kerja JUnit 259

return Assert.format (pesan, diharapkan, aktual);


}
}

private void compactExpectedAndActual () {


findCommonPrefix ();
findCommonSuffix ();
compactExpected = compactString (diharapkan);
compactActual = compactString (aktual);
}

Perhatikan bahwa ini mengharuskan kami untuk mempromosikan compactExpected dan compactActual to member

https://translate.googleusercontent.com/translate_f 228/365
3/12/2020 www.it-ebooks.info
variabel. Saya tidak suka cara dua baris terakhir dari variabel pengembalian fungsi baru,
tapi dua yang pertama tidak. Mereka tidak menggunakan konvensi yang konsisten [G11]. Jadi kita harus melakukannya
ubah findCommonPrefix dan findCommonSuffix untuk mengembalikan nilai awalan dan akhiran.
private void compactExpectedAndActual () {
prefixIndex = findCommonPrefix ();
suffixIndex = findCommonSuffix ();
compactExpected = compactString (diharapkan);
compactActual = compactString (aktual);
}

private int findCommonPrefix () {


Indeks awalan int = 0;
int end = Math.min (expected.length (), actual.length ());
untuk (; Indeks awalan <end; Indeks awalan ++) {
if (expected.charAt ( Indeks awalan )! = actual.charAt ( Indeks awalan ))
istirahat;
}
kembalikan prefixIndex;
}

private int findCommonSuffix () {


int expectedSuffix = expected.length () - 1;
int actualSuffix = actual.length () - 1;
untuk (; actualSuffix> = indeks awalan && expectedSuffix> = prefixIndex;
actualSuffix--, expectedSuffix--) {
if (expected.charAt (expectedSuffix)! = actual.charAt (actualSuffix))
istirahat;
}
return expected.length () - expectedSuffix;
}

Kita juga harus mengubah nama variabel anggota menjadi sedikit lebih akurat
[N1]; Bagaimanapun, keduanya adalah indeks.
Inspeksi findCommonSuffix secara hati-hati memperlihatkan kopling sementara yang tersembunyi [G31]; Itu
tergantung pada fakta bahwa prefixIndex dihitung oleh findCommonPrefix . Jika dua fungsi ini
Tions dipanggil rusak, akan ada sesi debugging yang sulit di depan. Jadi, untuk
mengekspos kopling temporal ini, mari kita cariCommonSuffix mengambil prefixIndex sebagai
argumen.
private void compactExpectedAndActual () {
prefixIndex = findCommonPrefix ();
suffixIndex = findCommonSuffix ( prefixIndex );

www.it-ebooks.info

Halaman 291

260 Bab 15: JUnit Internal

compactExpected = compactString (diharapkan);


compactActual = compactString (aktual);
}

private int findCommonSuffix ( int prefixIndex ) {


int expectedSuffix = expected.length () - 1;
int actualSuffix = actual.length () - 1;
untuk (; actualSuffix> = prefixIndex && expectedSuffix> = prefixIndex;
actualSuffix--, expectedSuffix--) {
if (expected.charAt (expectedSuffix)! = actual.charAt (actualSuffix))
istirahat;
}
return expected.length () - expectedSuffix;
}

Saya tidak begitu senang dengan ini. Lewat dari PrefixIndex argumen adalah sewenang-wenang sedikit
[G32]. Ini berfungsi untuk menetapkan pemesanan tetapi tidak menjelaskan kebutuhan untuk itu
Memerintah. Pemrogram lain mungkin membatalkan apa yang telah kami lakukan karena tidak ada indikator
bahwa parameter tersebut benar-benar diperlukan. Jadi mari kita lakukan taktik yang berbeda.
private void compactExpectedAndActual () {
findCommonPrefixAndSuffix ();
compactExpected = compactString (diharapkan);
compactActual = compactString (aktual);
}

private void findCommonPrefixAndSuffix () {


findCommonPrefix ();
int expectedSuffix = expected.length () - 1;
int actualSuffix = actual.length () - 1;
untuk (;
actualSuffix> = prefixIndex && expectedSuffix> = prefixIndex;
actualSuffix--, diharapkanSuffix--
){
if (expected.charAt (expectedSuffix)! = actual.charAt (actualSuffix))

https://translate.googleusercontent.com/translate_f 229/365
3/12/2020 www.it-ebooks.info
istirahat;
}
suffixIndex = expected.length () - expectedSuffix;
}

private void findCommonPrefix () {


prefixIndex = 0;
int end = Math.min (expected.length (), actual.length ());
untuk (; prefixIndex <end; prefixIndex ++)
if (expected.charAt (prefixIndex)! = actual.charAt (prefixIndex))
istirahat;
}

Kami menempatkan findCommonPrefix dan findCommonSuffix kembali seperti semula , mengubah


nama findCommonSuffix untuk menemukanCommon PrefixAnd Suffix dan memanggilnya findCommon-
Awalan sebelum melakukan hal lain. Itu menetapkan sifat temporal dari dua fungsi
dalam cara yang jauh lebih dramatis daripada solusi sebelumnya. Ini juga menunjukkan betapa jeleknya
findCommonPrefixAndSuffix adalah. Mari kita bersihkan sekarang.

private void findCommonPrefixAndSuffix () {


findCommonPrefix ();
int suffixLength = 1;

www.it-ebooks.info

Halaman 292

Kerangka Kerja JUnit 261

untuk (;! suffixOverlapsPrefix (suffixLength); suffixLength ++) {


if (charFromEnd (diharapkan, suffixLength)! =
charFromEnd (aktual, akhiran Panjang))
istirahat;
}
suffixIndex = suffixLength;
}

char charFromEnd pribadi (String s, int i) {


return s.charAt (s.length () - i);}

private boolean suffixOverlapsPrefix (int suffixLength) {


return actual.length () - suffixLength <prefixLength ||
expected.length () - suffixLength <prefixLength;
}

Ini jauh lebih baik. Itu memperlihatkan bahwa suffixIndex benar-benar panjang dari suffix
dan tidak disebutkan namanya. Hal yang sama berlaku untuk prefixIndex , meskipun dalam kasus itu "indeks" dan
"Panjang" sama artinya. Meski begitu, itu lebih konsisten untuk menggunakan "panjang." Masalahnya adalah
bahwa variabel suffixIndex tidak berbasis nol; ini berdasarkan 1 dan jadi bukan panjang sebenarnya. Ini
juga alasan bahwa semua +1 itu ada di computeCommonSuffix [G33]. Jadi mari kita perbaiki itu.
Hasilnya ada di Listing 15-4.

Listing 15-4
ComparisonCompactor.java (sementara)
ComparisonCompactor kelas publik {
...
panjang pribadi akhiran int ;
...
private void findCommonPrefixAndSuffix () {
findCommonPrefix ();
suffixLength = 0;
untuk (;! suffixOverlapsPrefix (suffixLength); suffixLength ++) {
if (charFromEnd (diharapkan, suffixLength)! =
charFromEnd (aktual, akhiran Panjang))
istirahat;
}
}

char charFromEnd pribadi (String s, int i) {


return s.charAt (s.length () - i - 1 );
}

private boolean suffixOverlapsPrefix (int suffixLength) {


return actual.length () - suffixLength <= prefixLength ||
expected.length () - suffixLength <= prefixLength;
}

...
private String compactString (Sumber string) {
Hasil string =
DELTA_START +
source.substring (prefixLength, source.length () - suffixLength ) +
DELTA_END;
if (prefixLength> 0)
result = computeCommonPrefix () + hasil;

https://translate.googleusercontent.com/translate_f 230/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 293

262 Bab 15: JUnit Internal

Listing 15-4 (lanjutan)


ComparisonCompactor.java (sementara)
if ( suffixLength > 0)
hasil = hasil + computeCommonSuffix ();
hasil pengembalian;
}

...
private String computeCommonSuffix () {
int end = Math.min (expected.length () - suffixLength +
contextLength, expected.length ()
);
kembali
expected.substring (expected.length () - suffixLength , end) +
(expected.length () - suffixLength <
expected.length () - contextLength?
ELLIPSIS: "");
}

Kami mengganti +1 dalam computeCommonSuffix dengan -1 di charFromEnd , di mana ia membuatnya


akal sehat, dan dua operator <= dalam suffixOverlapsPrefix , di mana mereka juga membuat sempurna
merasakan. Ini memungkinkan kami untuk mengubah nama suffixIndex menjadi suffixLength , sangat
ing keterbacaan kode.
Namun ada masalah. Saat saya menghilangkan +1, saya perhatikan baris berikut
di compactString :
if (suffixLength> 0)

Lihatlah pada Listing 15-4. Secara hak, karena suffixLength sekarang satu kurang dari itu
dulu, saya harus mengubah > operator ke >> operator. Tapi itu tidak masuk akal. Itu
masuk akal sekarang! Ini berarti bahwa itu tidak masuk akal dan mungkin bug.
Yah, bukan bug. Setelah analisis lebih lanjut kita melihat bahwa pernyataan if sekarang mencegah a
akhiran panjang nol dari ditambahkan. Sebelum kami melakukan perubahan, pernyataan if adalah
tidak berfungsi karena suffixIndex tidak pernah kurang dari satu!
Ini menimbulkan pertanyaan baik jika pernyataan di compactString ! Sepertinya mereka
keduanya bisa dihilangkan. Jadi mari kita komentari mereka dan jalankan tes. Mereka lewat! Begitu
mari kita merestrukturisasi compactString untuk menghilangkan pernyataan if yang asing dan membuatnya
fungsinya jauh lebih sederhana [G9].
private String compactString (Sumber string) {
kembali
computeCommonPrefix () +
DELTA_START +
source.substring (prefixLength, source.length () - suffixLength) +
DELTA_END +
computeCommonSuffix ();
}

Ini jauh lebih baik! Sekarang kita melihat bahwa fungsi compactString hanya menyusun
fragmen bersama. Kita mungkin bisa membuatnya lebih jelas. Memang, ada banyak sedikit

www.it-ebooks.info

Halaman 294

https://translate.googleusercontent.com/translate_f 231/365
3/12/2020 www.it-ebooks.info

Kerangka Kerja JUnit 263

pembersihan yang bisa kami lakukan. Tapi alih-alih menyeret Anda melalui sisa perubahan, saya hanya akan
memperlihatkan hasil pada Listing 15-5.

Listing 15-5
ComparisonCompactor.java (final)
paket junit.framework;

ComparisonCompactor kelas publik {

private final static ELLIPSIS = "...";


final private static static DELTA_END = "]";
final private static static DELTA_START = "[";

private int contextLength;


String pribadi diharapkan;
String pribadi aktual;
prefixLength int pribadi;
panjang pribadi akhiran int;

ComparisonCompactor publik (
int contextLength, String diharapkan, String aktual
){
this.contextLength = contextLength;
this.expected = diharapkan;
this.actual = aktual;
}

public String formatCompactedComparison (String message) {


String compactExpected = diharapkan;
String compactActual = aktual;
if (shouldBeCompacted ()) {
findCommonPrefixAndSuffix ();
compactExpected = compact (diharapkan);
compactActual = compact (aktual);
}
return Assert.format (pesan, compactExpected, compactActual);
}

private boolean shouldBeCompacted () {


kembali! shouldNotBeCompacted ();
}

private boolean shouldNotBeCompacted () {


return diharapkan == null ||
aktual == null ||
diharapkan. sama dengan (aktual);
}

private void findCommonPrefixAndSuffix () {


findCommonPrefix ();
suffixLength = 0;
untuk (;! suffixOverlapsPrefix (); suffixLength ++) {
if (charFromEnd (diharapkan, suffixLength)! =
charFromEnd (aktual, akhiran Panjang)
)

www.it-ebooks.info

Halaman 295

264 Bab 15: JUnit Internal

Listing 15-5 (lanjutan)


ComparisonCompactor.java (final)
istirahat;
}
}

char charFromEnd pribadi (String s, int i) {


return s.charAt (s.length () - i - 1);
}

suffixOverlapsPrefix pribadi () {)
return actual.length () - suffixLength <= prefixLength ||
expected.length () - suffixLength <= prefixLength;
}

private void findCommonPrefix () {


prefixLength = 0;

https://translate.googleusercontent.com/translate_f 232/365
3/12/2020 www.it-ebooks.info
int end = Math.min (expected.length (), actual.length ());
untuk (; prefixLength <end; prefixLength ++)
if (expected.charAt (prefixLength)! = actual.charAt (prefixLength))
istirahat;
}

private String compact (String s) {


kembalikan StringBuilder baru ()
.append (startingEllipsis ())
.append (startingContext ())
. tambahkan (DELTA_START)
.append (delta)
. tambahkan (DELTA_END)
.append (endingContext ())
.append (endingEllipsis ())
.toString ();
}

private String startingEllipsis () {


kembalikan prefixLength> contextLength? ELLIPSIS: "";
}

private String startingContext () {


int contextStart = Math.max (0, prefixLength - contextLength);
int contextEnd = prefixLength;
kembalikan yang diharapkan. substring (contextStart, contextEnd);
}

delta String pribadi (String s) {


int deltaStart = prefixLength;
int deltaEnd = s.length () - suffixLength;
return s.substring (deltaStart, deltaEnd);
}

private String endingContext () {


int contextStart = expected.length () - suffixLength;
int contextEnd =
Math.min (contextStart + contextLength, expected.length ());
kembalikan yang diharapkan. substring (contextStart, contextEnd);
}

www.it-ebooks.info

Halaman 296

Kesimpulan 265

Listing 15-5 (lanjutan)


ComparisonCompactor.java (final)
private String endingEllipsis () {
return (suffixLength> contextLength? ELLIPSIS: "");
}
}

Ini sebenarnya cukup cantik. Modul ini dipisahkan menjadi sekelompok fungsi analisis
dan fungsi sintesis lainnya. Mereka diurutkan secara topologi sehingga
definisi masing-masing fungsi muncul tepat setelah digunakan. Semua fungsi analisis muncul
pertama, dan semua fungsi sintesis muncul terakhir.
Jika Anda perhatikan dengan teliti, Anda akan melihat bahwa saya membalik beberapa keputusan yang saya buat
sebelumnya dalam bab ini. Sebagai contoh, saya sebutkan beberapa metode yang diekstraksi kembali
formatCompactedComparison , dan saya mengubah arti shouldNotBeCompacted expression
sion. Ini tipikal. Seringkali satu refactoring mengarah ke yang lain yang mengarah pada kehancuran
pertama. Refactoring adalah proses berulang penuh trial and error, mau tidak mau konvergen
sesuatu yang kita rasakan layak menjadi seorang profesional.

Kesimpulan
Dan jadi kami telah memenuhi Aturan Kepanduan. Kami meninggalkan modul ini sedikit lebih bersih dari
kami menemukannya. Bukan berarti itu belum bersih. Para penulis telah melakukan pekerjaan yang sangat baik dengannya.
Tetapi tidak ada modul yang kebal dari peningkatan, dan kita masing-masing memiliki tanggung jawab untuk melakukannya
tinggalkan kode sedikit lebih baik daripada yang kita temukan.

https://translate.googleusercontent.com/translate_f 233/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 297

halaman ini sengaja dibiarkan kosong

https://translate.googleusercontent.com/translate_f 234/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info

Halaman 298

16
SerialDate Refactoring

Jika Anda pergi ke http://www.jfree.org/jcommon/index.php , Anda akan menemukan perpustakaan JCommon.


Jauh di dalam perpustakaan itu ada paket bernama org.jfree.date . Di dalam paket itu
ada kelas bernama SerialDate . Kami akan menjelajahi kelas itu.
Penulis SerialDate adalah David Gilbert. David jelas seorang yang berpengalaman dan
programmer petent. Seperti yang akan kita lihat, ia menunjukkan tingkat profesionalisme yang signifikan dan
disiplin dalam kodenya. Untuk semua maksud dan tujuan, ini adalah "kode yang baik." Dan saya
akan merobeknya berkeping-keping.

267

www.it-ebooks.info

Halaman 299

268 Bab 16: Tanggal Refactoring Serial

Ini bukan kegiatan kedengkian. Saya juga tidak berpikir bahwa saya jauh lebih baik daripada David

https://translate.googleusercontent.com/translate_f 235/365
3/12/2020 www.it-ebooks.info
bahwa saya entah bagaimana memiliki hak untuk menghakimi kodenya. Memang, jika Anda menemukan beberapa
kode saya, saya yakin Anda bisa menemukan banyak hal untuk dikeluhkan.
Tidak, ini bukan kegiatan nastiness atau arogance. Apa yang akan saya lakukan bukanlah apa-apa
lebih dan tidak kurang dari ulasan profesional. Itu adalah sesuatu yang kita semua seharusnya
melakukan nyaman. Dan itu adalah sesuatu yang harus kita sambut ketika itu dilakukan untuk kita. ini
hanya melalui kritik seperti inilah kita akan belajar. Dokter melakukannya. Pilot melakukannya. Pengacara melakukannya
Itu. Dan kami programmer perlu belajar bagaimana melakukannya juga.
Satu hal lagi tentang David Gilbert: David lebih dari sekadar programmer yang baik.
David memiliki keberanian dan niat baik untuk menawarkan kodenya kepada masyarakat luas secara gratis.
Dia menempatkannya di tempat terbuka bagi semua orang untuk melihat dan mengundang penggunaan publik dan pengawasan publik. Ini
dilakukan dengan baik!
SerialDate (Listing B-1, halaman 349) adalah kelas yang mewakili tanggal di Jawa. Kenapa harus
kelas yang mewakili tanggal, ketika Java sudah memiliki java.util.Date dan
java.util.Calendar , dan lainnya? Penulis menulis kelas ini sebagai tanggapan terhadap rasa sakit yang saya
sudah sering merasakan sendiri. Komentar dalam pembukaannya Javadoc (baris 67) menjelaskannya dengan baik. Kita
bisa berdalih tentang niatnya, tapi aku pasti harus berurusan dengan masalah ini, dan aku
selamat datang di kelas yang membahas tentang kencan bukan kali.

Pertama, Jadikan Bekerja


Ada beberapa tes unit dalam kelas bernama SerialDateTests (Listing B-2, halaman 366). Itu
tes semua lulus. Sayangnya pemeriksaan cepat dari tes menunjukkan bahwa mereka tidak menguji setiap
hal [T1]. Misalnya, melakukan pencarian "Temukan Penggunaan" pada metode MonthCodeToQuarter
(baris 334) menunjukkan bahwa itu tidak digunakan [F4]. Oleh karena itu, unit test tidak mengujinya.
Jadi saya menyalakan Clover untuk melihat apa yang dicakup unit tes dan apa yang tidak. Semanggi
melaporkan bahwa unit test hanya menjalankan 91 dari 185 pernyataan yang dapat dieksekusi di SerialDate
(~ 50 persen) [T2]. Peta cakupan tampak seperti selimut tambal sulam, dengan sekumpulan besar unex-
kode ecuted berserakan di seluruh kelas.
Adalah tujuan saya untuk sepenuhnya memahami dan juga memperbaiki kelas ini. Saya tidak bisa melakukan itu
tanpa cakupan tes yang jauh lebih besar. Jadi saya menulis sendiri suite yang sepenuhnya independen
unit test (Listing B-4, halaman 374).
Ketika Anda melihat melalui tes-tes ini, Anda akan mencatat bahwa banyak dari mereka yang berkomentar.
Tes-tes ini tidak lulus. Mereka mewakili perilaku yang saya pikir seharusnya SerialDate miliki. Begitu pula
Saya refactor SerialDate , saya akan bekerja untuk membuat tes ini lulus juga.
Bahkan dengan beberapa tes dikomentari, Clover melaporkan bahwa tes unit baru
mengeksekusi 170 (92 persen) dari 185 pernyataan yang dapat dieksekusi. Ini cukup bagus, dan saya
pikir kita akan bisa mendapatkan nomor ini lebih tinggi.
Beberapa tes komentar pertama (baris 23-63) agak sombong di pihak saya. Itu
Program tidak dirancang untuk lulus tes ini, tetapi perilaku itu tampak jelas [G2] bagi saya.

www.it-ebooks.info

Halaman 300

Pertama, Jadikan Bekerja 269

Saya tidak yakin mengapa metode testWeekdayCodeToString ditulis di tempat pertama, tetapi
karena ada di sana, tampak jelas bahwa itu tidak boleh peka terhadap huruf besar-kecil. Menulis tes ini
sepele [T3]. Membuat mereka lulus bahkan lebih mudah; Saya baru saja mengubah baris 259 dan 263 menjadi
gunakan equalsIgnoreCase .
Saya meninggalkan tes di baris 32 dan baris 45 berkomentar karena tidak jelas bagi saya itu
singkatan "tue" dan "thurs" harus didukung.
Tes pada saluran 153 dan saluran 154 tidak lulus. Jelas, mereka harus [G2]. Kita bisa dengan mudah
perbaiki ini, dan tes pada saluran 163 hingga saluran 213, dengan membuat perubahan berikut pada
fungsi stringToMonthCode .

457 if ((hasil <1) || (hasil> 12)) {


hasil = -1;
458 untuk (int i = 0; i <monthNames.length; i ++) {
459 if (s.equalsIgnoreCase (shortMonthNames [i])) {
460 hasil = i +1;
461 istirahat;
462 }
463 if (s.equalsIgnoreCase (monthNames [i])) {
464 hasil = i +1;
465 istirahat;

https://translate.googleusercontent.com/translate_f 236/365
3/12/2020 www.it-ebooks.info
466
467 } }
468 }

Tes yang dikomentari pada baris 318 memperlihatkan bug dalam metode getFollowingDayOfWeek
(baris 672). 25 Desember 2004, adalah hari Sabtu. Sabtu berikutnya adalah 1 Januari,
2005. Namun, ketika kami menjalankan tes, kami melihat bahwa getFollowingDayOfWeek mengembalikan Decem-
ber 25 sebagai hari Sabtu yang mengikuti 25 Desember. Jelas, ini salah [G3], [T1]. Kita
lihat masalah pada baris 685. Ini adalah kesalahan kondisi batas tipikal [T5]. Seharusnya dibaca sebagai
berikut:

685 if (baseDOW> = targetWeekday) {

Sangat menarik untuk dicatat bahwa fungsi ini adalah target dari perbaikan sebelumnya. Perubahan
sejarah (baris 43) menunjukkan bahwa "bug" diperbaiki di getPreviousDayOfWeek , getFollowing-
DayOfWeek , dan getNearestDayOfWeek [T6].
The testGetNearestDayOfWeek unit uji (baris 329), yang menguji getNearestDayOfWeek
metode (jalur 705), tidak dimulai selama dan selengkap sekarang ini. Saya menambahkan banyak
kasus uji untuk itu karena kasus uji awal saya tidak semuanya lulus [T6]. Anda bisa melihat polanya
kegagalan dengan melihat kasus uji mana yang dikomentari. Pola itu mengungkapkan [T7].
Ini menunjukkan bahwa algoritma gagal jika hari terdekat di masa depan. Jelas ada beberapa
jenis kesalahan kondisi batas [T5].
Pola cakupan tes yang dilaporkan oleh Clover juga menarik [T8]. Jalur 719
tidak pernah dieksekusi! Ini berarti bahwa pernyataan if pada baris 718 selalu salah. Tentu
cukup, lihat kode menunjukkan bahwa ini pasti benar. The menyesuaikan variabel selalu neg-
ative dan sebagainya tidak bisa lebih besar atau sama dengan 4. Jadi algoritma ini salah.

www.it-ebooks.info

Halaman 301

270 Bab 16: Tanggal Refactoring Serial

Algoritma yang tepat ditunjukkan di bawah ini:

int delta = targetDOW - base.getDayOfWeek ();


int positiveDelta = delta + 7;
penyesuaian int = positiveDelta% 7;
jika (sesuaikan> 3)
sesuaikan - = 7;

mengembalikan SerialDate.addDays (sesuaikan, pangkalan);

Akhirnya, tes pada baris 417 dan baris 429 dapat dilakukan untuk lulus hanya dengan melempar
IllegalArgumentException alih-alih mengembalikan string kesalahan dari weekInMonthToString
dan relativeToString .
Dengan perubahan ini semua tes unit lulus, dan saya percaya SerialDate sekarang berfungsi. Jadi sekarang
saatnya untuk membuatnya "benar."

Kemudian buat itu benar


Kita akan berjalan dari atas ke bawah SerialDate , memperbaikinya seiring berjalannya waktu
sepanjang. Meskipun Anda tidak akan melihat ini dalam diskusi, saya akan menjalankan semua JCommon
unit test, termasuk unit test saya yang ditingkatkan untuk SerialDate , setelah setiap perubahan yang saya lakukan. Begitu
yakinlah bahwa setiap perubahan yang Anda lihat di sini berfungsi untuk semua JCommon .
Mulai dari baris 1, kita melihat rim komentar dengan informasi lisensi, hak cipta,
penulis, dan ubah riwayat. Saya mengakui bahwa ada beberapa legalitas tertentu yang perlu diperhatikan
ditangani, sehingga hak cipta dan lisensi harus tetap ada. Di sisi lain, perubahannya
tory adalah sisa dari tahun 1960-an. Kami memiliki alat kontrol kode sumber yang melakukan ini untuk kami sekarang.
Riwayat ini harus dihapus [C1].
Daftar impor yang dimulai pada baris 61 dapat dipersingkat dengan menggunakan java.text. * Dan
java.util. * . [J1]
Saya mengernyit saat memformat HTML di Javadoc (baris 67). Memiliki file sumber dengan
lebih dari satu bahasa di dalamnya mengganggu saya. Komentar ini memiliki empat bahasa di dalamnya: Java,
Bahasa Inggris, Javadoc, dan html [G1]. Dengan banyaknya bahasa yang digunakan, sulit untuk menyimpannya
lurus. Sebagai contoh, posisi yang bagus dari baris 71 dan baris 72 hilang ketika Javadoc
dihasilkan, namun siapa yang ingin melihat <ul> dan <li> dalam kode sumber? Strategi yang lebih baik
mungkin hanya mengelilingi seluruh komentar dengan <pre> sehingga pemformatan itu
jelas dalam kode sumber disimpan dalam Javadoc. 1
https://translate.googleusercontent.com/translate_f 237/365
3/12/2020 www.it-ebooks.info

Baris 86 adalah deklarasi kelas. Mengapa kelas ini bernama SerialDate ? Apa sig
Apa arti “serial” dunia? Apakah karena kelas tersebut berasal dari Serializable ? Bahwa
sepertinya tidak mungkin.

1. Suatu solusi yang lebih baik lagi bagi Javadoc untuk menyajikan semua komentar seperti yang telah diformat sebelumnya, sehingga komentar tersebut muncul
sama dalam kode dan dokumen.

www.it-ebooks.info

Halaman 302

Kemudian buat itu benar 271

Saya tidak akan membuat Anda menebak. Saya tahu mengapa (atau setidaknya saya pikir saya tahu mengapa) kata itu
"Serial" digunakan. Petunjuknya ada dalam konstanta SERIAL_LOWER_BOUND dan
SERIAL_UPPER_BOUND pada baris 98 dan baris 101. Petunjuk yang lebih baik ada di komentar
yang dimulai pada baris 830. Kelas ini bernama SerialDate karena diimplementasikan menggunakan a
"Nomor seri," yang merupakan jumlah hari sejak 30 Desember 1899.
Saya punya dua masalah dengan ini. Pertama, istilah "nomor seri" tidak benar-benar benar.
Ini mungkin quibble, tetapi representasi lebih merupakan offset relatif daripada jumlah seri
ber. Istilah "nomor seri" lebih terkait dengan penanda identifikasi produk daripada
tanggal. Jadi saya tidak menemukan nama ini sangat deskriptif [N1]. Istilah yang lebih deskriptif
mungkin "ordinal."
Masalah kedua lebih signifikan. Nama SerialDate menyiratkan implementa-
tion. Kelas ini adalah kelas abstrak. Tidak perlu menyiratkan apa pun tentang
penerapan. Memang, ada alasan bagus untuk menyembunyikan implementasinya! Jadi saya menemukan ini
nama berada pada level abstraksi yang salah [N2]. Menurut pendapat saya, nama kelas ini
seharusnya hanya Date .
Sayangnya, sudah ada terlalu banyak kelas di perpustakaan Java bernama Date , jadi
ini mungkin bukan nama terbaik untuk dipilih. Karena kelas ini adalah tentang hari, bukan
waktu, saya dianggap menamakannya Hari , tetapi nama ini juga banyak digunakan di tempat lain. Dalam
akhirnya, saya memilih DayDate sebagai kompromi terbaik.
Mulai sekarang dalam diskusi ini saya akan menggunakan istilah DayDate . Saya serahkan pada Anda untuk mengingat-
ber bahwa daftar yang Anda cari masih menggunakan SerialDate .
Saya mengerti mengapa DayDate mewarisi dari Sebanding dan Serializable . Tetapi mengapa itu terjadi?
mewarisi dari MonthConstants ? Kelas MonthConstants (Listing B-3, halaman 372) hanya a
sekelompok konstanta akhir statis yang menentukan bulan. Mewarisi dari kelas dengan
stants adalah trik lama yang digunakan programmer Java sehingga mereka bisa menghindari penggunaan ekspresi.
Sions seperti MonthConstants.January , tapi itu ide yang buruk [J2]. MonthConstants harus benar-benar
sebuah enum.

DayDate kelas abstrak publik mengimplementasikan Sebanding,


Serializable {
public static enum Bulan {
1 JANUARI),
FEBRUARI (2),
MARET (3),
April (4),
MEI (5),
JUNI (6),
JULI (7),
AGUSTUS (8),
SEPTEMBER (9),
OKTOBER (10),
NOVEMBER (11),
DESEMBER (12);

Bulan (indeks int) {


this.index = indeks;
}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 238/365
3/12/2020 www.it-ebooks.info

Halaman 303

272 Bab 16: Tanggal Refactoring Serial

Bulan statis publik dibuat (int monthIndex) {


untuk (Bulan m: Month.values ()) {
if (m.index == monthIndex)
mengembalikan m;
}
melempar IllegalArgumentException baru ("Indeks bulan tidak valid" + monthIndex);
}
indeks int final publik;
}

Mengubah MonthConstants ke enum ini memaksa beberapa perubahan pada kelas DayDate
dan semua pengguna itu. Butuh waktu satu jam untuk membuat semua perubahan. Namun, fungsinya pun demikian
dulu mengambil int selama sebulan, sekarang membutuhkan enumerator Bulan . Ini artinya kita bisa sembuh
dari isValidMonthCode metode (baris 326), dan semua kesalahan kode bulan memeriksa seperti
itu di monthCodeToQuarter (baris 356) [G5].
Selanjutnya, kami memiliki baris 91, serialVersionUID . Variabel ini digunakan untuk mengontrol serializer.
Jika kami mengubahnya, maka DayDate yang ditulis dengan versi perangkat lunak yang lebih lama tidak akan
dapat dibaca lagi dan akan menghasilkan InvalidClassException . Jika Anda tidak mendeklarasikan
variabel serialVersionUID , maka kompiler secara otomatis menghasilkan satu untuk Anda, dan itu
akan berbeda setiap kali Anda melakukan perubahan pada modul. Saya tahu bahwa semua dokumen
KASIH merekomendasikan kontrol manual dari variabel ini, tetapi tampaknya bagi saya bahwa
trol serialisasi jauh lebih aman [G4]. Lagi pula, saya lebih suka men-debug
InvalidClassException daripada perilaku aneh yang akan terjadi jika saya lupa mengubah
serialVersionUID . Jadi saya akan menghapus variabel — setidaknya untuk saat ini. 2
Saya menemukan komentar pada baris 93 berlebihan. Komentar yang berlebihan hanyalah tempat untuk mengumpulkan
kebohongan dan informasi yang salah [C2]. Jadi saya akan menyingkirkannya dan sejenisnya.
Komentar di baris 97 dan baris 100 berbicara tentang nomor seri, yang saya bahas
sebelumnya [C1]. Variabel yang mereka gambarkan adalah tanggal paling awal dan terbaru yang mungkin
DayDate dapat menjelaskan. Ini bisa dibuat sedikit lebih jelas [N1].

public int static final EARLIEST_DATE_ORDINAL = 2; // 1/1/1900


LATEST_DATE_ORDINAL public int static final = 2958465; // 12/31/9999

Tidak jelas bagi saya mengapa EARLIEST_DATE_ORDINAL adalah 2 bukannya 0. Ada petunjuk di
komentar pada baris 829 yang menunjukkan bahwa ini ada hubungannya dengan cara kencan
diwakili dalam Microsoft Excel. Ada wawasan yang jauh lebih dalam yang disediakan dalam turunan dari
DayDate disebut SpreadsheetDate (Listing B-5, halaman 382). Komentar pada baris 71 menjelaskan
masalah dengan baik.
Masalah yang saya miliki dengan ini adalah bahwa masalah ini tampaknya terkait dengan implementasi-
tion dari SpreadsheetDate dan tidak ada hubungannya dengan DayDate . Dari sini saya simpulkan

2. Beberapa pengulas teks ini telah mengambil pengecualian untuk keputusan ini. Mereka berpendapat bahwa dalam kerangka kerja open source itu
lebih baik untuk menegaskan kontrol manual atas ID seri sehingga perubahan kecil pada perangkat lunak tidak menyebabkan tanggal serial lama menjadi
tidak valid Ini adalah poin yang adil. Namun, setidaknya kegagalan, meskipun mungkin merepotkan, memiliki penyebab yang jelas. Di sisi lain
tangan, jika penulis kelas lupa untuk memperbarui ID, maka mode kegagalan tidak terdefinisi dan mungkin diam. saya
pikir moral sebenarnya dari cerita ini adalah Anda tidak boleh berharap untuk deserialize lintas versi.

www.it-ebooks.info

Halaman 304

Kemudian buat itu benar 273

EARLIEST_DATE_ORDINAL dan LATEST_DATE_ORDINAL tidak benar-benar termasuk dalam DayDate dan


harus dipindahkan ke SpreadsheetDate [G6].
Memang, pencarian kode menunjukkan bahwa variabel-variabel ini hanya digunakan dalam
Tanggal Spreadsheet . Tidak ada yang digunakan dalam DayDate , atau di kelas lain dalam kerangka kerja JCommon
mereka. Oleh karena itu, saya akan memindahkannya ke SpreadsheetDate .

https://translate.googleusercontent.com/translate_f 239/365
3/12/2020 www.it-ebooks.info
Variabel berikutnya, MINIMUM_YEAR_SUPPORTED , dan MAXIMUM_YEAR_SUPPORTED (baris 104
dan baris 107), berikan sesuatu dilema. Tampak jelas bahwa jika DayDate adalah abstrak
kelas yang tidak memberikan bayangan implementasi, maka seharusnya tidak memberi tahu kami
sekitar tahun minimum atau maksimum. Sekali lagi, saya tergoda untuk memindahkan variabel-variabel ini ke bawah
ke dalam SpreadsheetDate [G6]. Namun, pencarian cepat dari pengguna variabel ini menunjukkan
yang digunakan oleh satu kelas lain: RelativeDayOfWeekRule (Listing B-6, halaman 390). Kami melihat itu
penggunaan pada baris 177 dan baris 178 dalam fungsi getDate , di mana mereka digunakan untuk memeriksanya
argumen untuk getDate adalah tahun yang valid. Dilema adalah bahwa pengguna kelas abstrak
membutuhkan informasi tentang implementasinya.
Yang perlu kita lakukan adalah memberikan informasi ini tanpa mencemari DayDate itu sendiri.
Biasanya, kami akan mendapatkan informasi implementasi dari turunan.
Namun, fungsi getDate tidak lulus turunan dari DayDate . Namun demikian,
mengembalikan contoh seperti itu, yang berarti bahwa di suatu tempat pasti membuatnya. Baris 187
melalui baris 205 memberikan petunjuk. Contoh DayDate sedang dibuat oleh salah satu
tiga fungsi, getPreviousDayOfWeek , getNearestDayOfWeek , atau getFollowingDayOfWeek .
Melihat kembali daftar DayDate , kita melihat bahwa fungsi-fungsi ini (baris 638-724) semuanya kembali
tanggal yang dibuat oleh addDays (baris 571), yang memanggil createInstance (baris 808), yang membuatnya
Tanggal Spreadsheet ! [G7].
Umumnya ide buruk bagi kelas dasar untuk mengetahui tentang turunannya. Untuk memperbaiki ini, kami
harus menggunakan pola A BSTRACT F ACTORY 3 dan membuat DayDateFactory . Pabrik ini akan
buat instance DayDate yang kita butuhkan dan juga dapat menjawab pertanyaan tentang
implementasi, seperti tanggal maksimum dan minimum.

kelas abstrak publik DayDateFactory {


private static DayDateFactory factory = SpreadsheetDateFactory baru ();
public static batal setInstance (pabrik DayDateFactory) {
DayDateFactory.factory = pabrik;
}

abstrak DayDate _makeDate abstrak (int ordinal);


DayDate abstrak yang dilindungi _makeDate (int day, DayDate.Month month, int year);
abstrak DayDate _makeDate abstrak (hari int, bulan int, tahun int);
abstrak DayDate _makeDate abstrak (tanggal java.util.Date);
abstrak int _getMinimumYear ();
abstrak int _getMaximumYear ();

makeDate statis publik DayDate (int ordinal) {


return factory._makeDate (ordinal);
}

3. [GOF].

www.it-ebooks.info

Halaman 305

274 Bab 16: Tanggal Refactoring Serial

makeDate statis DayDate publik (hari int, DayDate. Bulan tak bertanggal, tahun int) {
return factory._makeDate (hari, bulan, tahun);
}

makeDate statis DayDate publik (hari int, bulan int, tahun int) {
return factory._makeDate (hari, bulan, tahun);
}

makeDate statis publik DayDate (tanggal java.util.Date) {


return factory._makeDate (date);
}

public static int getMinimumYear () {


return factory._getMinimumYear ();
}

public static int getMaximumYear () {


return factory._getMaximumYear ();
}
}

Kelas pabrik ini menggantikan CreateInstance metode dengan makeDate metode, yang
meningkatkan nama sedikit [N1]. Ini default ke SpreadsheetDateFactory tetapi bisa
berubah kapan saja untuk menggunakan pabrik yang berbeda. Metode statis yang mendelegasikan ke abstrak
metode menggunakan kombinasi S INGLETON , 4 D ECORATOR , 5 dan A BSTRACT F ACTORY
pola yang saya temukan bermanfaat.
The SpreadsheetDateFactory terlihat seperti ini.

https://translate.googleusercontent.com/translate_f 240/365
3/12/2020 www.it-ebooks.info

SpreadsheetDateFactory kelas publik memperpanjang DayDateFactory {


DayDate publik _makeDate (int ordinal) {
kembalikan SpreadsheetDate baru (ordinal);
}

DayDate publik _makeDate (int day, DayDate.Month month, int year) {


kembalikan SpreadsheetDate baru (hari, bulan, tahun);
}

DayDate publik _makeDate (hari int, bulan int, tahun int) {


kembalikan SpreadsheetDate baru (hari, bulan, tahun);
}

DayDate publik _makeDate (Tanggal tanggal) {


kalender GregorianCalendar akhir = GregorianCalendar baru ();
calendar.setTime (tanggal);
kembalikan SpreadsheetDate baru (
calendar.get (Calendar.DATE),
DayDate.Month.make (calendar.get (Calendar.MONTH) +1),
calendar.get (Calendar.YEAR));
}

4. Ibid.
5. Ibid.

www.it-ebooks.info

Halaman 306

Kemudian buat itu benar 275

protected int _getMinimumYear () {


kembalikan SpreadsheetDate.MINIMUM_YEAR_SUPPORTED;
}

protected int _getMaximumYear () {


kembalikan SpreadsheetDate.MAXIMUM_YEAR_SUPPORTED;
}
}

Seperti yang Anda lihat, saya sudah memindahkan MINIMUM_YEAR_SUPPORTED dan


MAXIMUM_YEAR_SUPPORTED variabel ke dalam SpreadsheetDate , di mana mereka berada [G6].
Masalah berikutnya di DayDate adalah konstanta hari yang dimulai pada baris 109. Ini seharusnya
benar-benar menjadi enum lain [J3]. Kami telah melihat pola ini sebelumnya, jadi saya tidak akan mengulanginya di sini. Anda akan
lihat di daftar akhir.
Selanjutnya, kita melihat serangkaian tabel dimulai dengan LAST_DAY_OF_MONTH di baris 140. Yang pertama
masalah dengan tabel ini adalah bahwa komentar yang menggambarkannya berlebihan [C3]. Mereka
nama sudah cukup. Jadi saya akan menghapus komentar.
Tampaknya tidak ada alasan yang baik bahwa tabel ini bukan pribadi [G8], karena ada a
fungsi statis lastDayOfMonth yang menyediakan data yang sama.
Tabel selanjutnya, AGGREGATE_DAYS_TO_END_OF_MONTH , sedikit lebih misterius karena
tidak digunakan di mana pun dalam kerangka kerja JCommon [G9]. Jadi saya menghapusnya.
Hal yang sama berlaku untuk LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_MONTH.
Tabel selanjutnya, AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH , hanya digunakan di Spread-
sheetDate (baris 434 dan baris 473). Ini menimbulkan pertanyaan apakah harus dipindahkan
ke SpreadsheetDate . Argumen untuk tidak memindahkannya adalah bahwa tabel tidak spesifik untuk yang mana pun
implementasi tertentu [G6]. Di sisi lain, tidak ada implementasi selain
SpreadsheetDate sebenarnya ada, dan karenanya tabel harus dipindahkan dekat ke tempat itu
digunakan [G10].
Yang menjadi dasar argumen saya adalah bahwa untuk menjadi konsisten [G11], kita harus membuat
tabel pribadi dan memaparkannya melalui fungsi seperti julianDateOfLastDayOfMonth . Tak seorangpun
sepertinya butuh fungsi seperti itu. Selain itu, tabel dapat dipindahkan kembali ke DayDate dengan mudah
jika ada implementasi baru DayDate membutuhkannya. Jadi saya memindahkannya.
Hal yang sama berlaku untuk tabel, LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_MONTH .
Selanjutnya, kita melihat tiga set konstanta yang dapat diubah menjadi enum (baris 162-205).
Yang pertama dari tiga memilih seminggu dalam sebulan. Saya mengubahnya menjadi enum bernama
WeekInMonth .

public enum WeekInMonth {


PERTAMA (1), KEDUA (2), KETIGA (3), EMPAT (4), TERAKHIR (0);
indeks int final publik;

https://translate.googleusercontent.com/translate_f 241/365
3/12/2020 www.it-ebooks.info
WeekInMonth (indeks int) {
this.index = indeks;
}
}

www.it-ebooks.info

Halaman 307

276 Bab 16: Tanggal Refactoring Serial

Rangkaian konstanta kedua (baris 177–187) sedikit lebih tidak jelas. The INCLUDE_NONE ,
INCLUDE_FIRST , INCLUDE_SECOND , dan INCLUDE_BOTH konstanta digunakan untuk menggambarkan apakah
tanggal titik akhir yang menentukan rentang harus dimasukkan dalam rentang itu. Secara matematis,
ini dijelaskan menggunakan istilah “interval terbuka,” “interval setengah terbuka,” dan “interval tertutup”.
val. " Saya pikir lebih jelas menggunakan nomenklatur matematika [N3], jadi saya mengubahnya menjadi
enum bernama DateInterval dengan CLOSED , CLOSED_LEFT , CLOSED_RIGHT , dan OPEN enumerators.
Set konstanta ketiga (baris 18–205) menggambarkan apakah pencarian untuk suatu tertentu
hari dalam seminggu harus menghasilkan contoh terakhir, berikutnya, atau terdekat. Memutuskan apa yang harus dihubungi
ini paling sulit. Pada akhirnya, saya memilih WeekdayRange dengan LAST , NEXT , dan NEAREST
enumerator.
Anda mungkin tidak setuju dengan nama yang saya pilih. Mereka masuk akal bagi saya, tetapi mereka
mungkin tidak masuk akal bagi Anda. Intinya adalah bahwa mereka sekarang dalam bentuk yang memudahkan mereka
untuk mengubah [J3]. Mereka tidak lulus sebagai bilangan bulat lagi; mereka dilewatkan sebagai simbol. saya bisa
gunakan fungsi "ganti nama" dari IDE saya untuk mengubah nama, atau tipe, tanpa
khawatir bahwa saya melewatkan beberapa -1 atau 2 di suatu tempat dalam kode atau bahwa beberapa argumen int dec-
Larasi tidak dijelaskan dengan baik.
Bidang uraian pada baris 208 tampaknya tidak digunakan oleh siapa pun. Saya menghapusnya bersama
dengan accessor dan mutator [G9].
Saya juga menghapus konstruktor default yang rusak pada baris 213 [G12]. Kompiler akan melakukannya
buat itu untuk kita.
Kita dapat melewati metode isValidWeekdayCode (baris 216-238) karena kita menghapus
itu ketika kita membuat enumerasi Hari .
Ini membawa kita ke metode stringToWeekdayCode (baris 242-270). Javadocs itu
jangan menambahkan banyak ke tanda tangan metode hanya kekacauan [C3], [G12]. Satu-satunya nilai ini
Javadoc menambahkan adalah deskripsi nilai pengembalian -1 . Namun, karena kami berubah ke
Hari pencacahan, komentar itu sebenarnya salah [C2]. Metode sekarang melempar
IllegalArgumentException . Jadi saya menghapus Javadoc.
Saya juga menghapus semua kata kunci terakhir dalam argumen dan deklarasi variabel. Sejauh
Saya tahu, mereka menambahkan tidak ada nilai nyata tetapi memang menambah kekacauan [G12]. Menghilangkan final
terbang di hadapan beberapa kebijaksanaan konvensional. Misalnya, Robert Simmons 6 dengan kuat
merekomendasikan kami untuk “. . . sebarkan final ke seluruh kode Anda. " Jelas saya tidak setuju. Saya pikir itu
ada beberapa kegunaan yang baik untuk final , seperti konstanta akhir sesekali , tetapi sebaliknya
kata kunci menambah sedikit nilai dan menciptakan banyak kekacauan. Mungkin saya merasa seperti ini karena
jenis kesalahan yang mungkin ditangkap akhir sudah tertangkap oleh tes unit yang saya tulis.
Saya tidak peduli dengan duplikat jika pernyataan [G5] di dalam for loop (baris 259 dan
baris 263), jadi saya menghubungkan mereka ke dalam pernyataan if tunggal menggunakan || operator. Saya juga menggunakan
yang hari pencacahan untuk mengarahkan untuk lingkaran dan membuat beberapa perubahan kosmetik lainnya.
Terpikir oleh saya bahwa metode ini bukan milik DayDate . Benar-benar
fungsi parse Day . Jadi saya memindahkannya ke enumerasi Day . Namun, itu yang membuat hari itu

6. [Simmons04], hlm. 73.

www.it-ebooks.info

Halaman 308

https://translate.googleusercontent.com/translate_f 242/365
3/12/2020 www.it-ebooks.info

Kemudian buat itu benar 277

enumerasi cukup besar. Karena konsep Day tidak tergantung pada DayDate , saya pindah
yang hari pencacahan luar DayDate kelas ke file sumber sendiri [G13].
Saya juga memindahkan fungsi berikutnya, weekdayCodeToString (baris 272–286) ke Hari
enumerasi dan menyebutnya toString .

public enum Day {


SENIN (Kalender. Senin),
SELASA (Kalender. SELASA),
WEDNESDAY (Calendar.WEDNESDAY), s
KAMIS (Kalender. KAMIS),
JUMAT (Kalender.FRIDAY),
SABTU (Kalender.SATURDAY),
MINGGU (Kalender. MINGGU);

indeks int final publik;


DateFormatSymbols statis pribadi dateSymbols = DateFormatSymbols baru ();

Day (int day) {


indeks = hari;
}

public static Day make (int index) melempar IllegalArgumentException {


untuk (Hari d: Day.values ())
if (d.index == indeks)
kembali d;
melempar IllegalArgumentException baru (
String.format ("Indeks hari ilegal:% d.", Indeks));
}

public static Day parse (String s) melempar IllegalArgumentException {


String [] shortWeekdayNames =
dateSymbols.getShortWeekdays ();
String [] weekDayNames =
dateSymbols.getWeekdays ();

s = s.trim ();
untuk (Hari hari: Nilai hari) ()) {
if (s.equalsIgnoreCase (shortWeekdayNames [day.index]) ||
s.equalsIgnoreCase (weekDayNames [day.index])) {
hari kembali;
}
}
melempar IllegalArgumentException baru (
String.format ("% s bukan string hari kerja yang valid", s));
}

public String toString () {


return dateSymbols.getWeekdays () [index];
}
}

Ada dua fungsi getMonths (baris 288-316). Yang pertama memanggil yang kedua. Itu
kedua tidak pernah dipanggil oleh siapa pun kecuali yang pertama. Karena itu, saya pisahkan keduanya menjadi satu dan
mereka sangat disederhanakan [G9], [G12], [F4]. Akhirnya, saya mengubah nama menjadi sedikit lebih mandiri
deskriptif [N1].

www.it-ebooks.info

Halaman 309

278 Bab 16: Tanggal Refactoring Serial

public static String [] getMonthNames () {


return dateFormatSymbols.getMonths ();
}

Fungsi isValidMonthCode (baris 326-346) dibuat tidak relevan oleh Bulan


enum, jadi saya menghapusnya [G9].
Fungsi monthCodeToQuarter (baris 356-375) berbau F EATURE E NVY 7 [G14]
dan mungkin termasuk dalam Month enum sebagai metode bernama quarter . Jadi saya menggantinya.

kuartal int publik () {

https://translate.googleusercontent.com/translate_f 243/365
3/12/2020 www.it-ebooks.info
return 1 + (index-1) / 3;
}

Ini membuat Month enum cukup besar untuk berada di kelasnya sendiri. Jadi saya memindahkannya
DayDate konsisten dengan Hari enum [G11], [G13].
Dua metode selanjutnya diberi nama monthCodeToString (baris 377-426). Sekali lagi, kita melihat
pola satu metode memanggil kembarannya dengan bendera. Biasanya merupakan ide yang buruk untuk mengibarkan bendera
sebagai argumen untuk suatu fungsi, terutama ketika bendera itu hanya memilih format out-
masukkan [G15]. Saya mengganti nama, menyederhanakan, dan merestrukturisasi fungsi-fungsi ini dan memindahkannya ke
Bulan enum [N1], [N3], [C3], [G14].

public String toString () {


return dateFormatSymbols.getMonths () [index - 1];
}

public String toShortString () {


return dateFormatSymbols.getShortMonths () [index - 1];
}

Metode selanjutnya adalah stringToMonthCode (baris 428-472). Saya menamainya, memindahkannya ke


Bulan enum, dan menyederhanakannya [N1], [N3], [C3], [G14], [G12].

parse Bulan statis publik (String s) {


s = s.trim ();
untuk (Bulan m: Nilai Bulan) ())
if (m.matches)
mengembalikan m;

coba {
return make (Integer.parseInt (s));
}
catch (NumberFormatException e) {}
throw baru IllegalArgumentException ("Bulan tidak valid");
}

7. [Refactoring].

www.it-ebooks.info

Halaman 310

Kemudian buat itu benar 279

kecocokan boolean pribadi (String s) {


return s.equalsIgnoreCase (toString ()) ||
s.equalsIgnoreCase (toShortString ());
}

The isLeapYear metode (baris 495-517) dapat dibuat sedikit lebih ekspresif [G16].

boolean publik statis isLeapYear (int year) {


boolean keempat = tahun% 4 == 0;
keseratus boolean = tahun% 100 == 0;
boolean fourHundredth = tahun% 400 == 0;
kembalikan keempat && (! keseratus || empat Keseratus);
}

Fungsi selanjutnya, leapYearCount (baris 519-536) tidak benar-benar termasuk dalam DayDate .
Tidak ada yang menyebutnya kecuali dua metode dalam SpreadsheetDate . Jadi saya mendorongnya ke bawah [G6].
Fungsi lastDayOfMonth (baris 538-560) menggunakan LAST_DAY_OF_MONTH
Himpunan. Array ini benar-benar termasuk dalam Month enum [G17], jadi saya memindahkannya ke sana. Saya juga menyederhanakan
Fied fungsi dan membuatnya sedikit lebih ekspresif [G16].

public static int lastDayOfMonth (Bulan bulan, tahun int) {


if (month == Month.FEBRUARY && isLeapYear (tahun))
return month.lastDay () + 1;
lain
return month.lastDay ();
}

Sekarang segalanya mulai menjadi sedikit lebih menarik. Fungsi selanjutnya adalah addDays (baris 562–
576). Pertama-tama, karena fungsi ini beroperasi pada variabel DayDate , seharusnya tidak
menjadi statis [G18]. Jadi saya mengubahnya menjadi metode instan. Kedua, ini memanggil fungsi

https://translate.googleusercontent.com/translate_f 244/365
3/12/2020 www.it-ebooks.info
toSerial . Fungsi ini harus diubah namanya menjadiOtinal [N1]. Akhirnya, metodenya bisa
disederhanakan.

addDays DayDate publik (int days) {


return DayDateFactory.makeDate (toOrdinal () + days);
}

Hal yang sama berlaku untuk addMonths (baris 578-602). Itu harus berupa metode instance [G18].
Algoritma ini agak rumit, jadi saya menggunakan E XPLAINING T EMPORARY V ARIABLES 8 [G19]
untuk membuatnya lebih transparan. Saya juga berganti nama menjadi metode getYYY untuk getYear [N1].

addMonths DayDate publik (int bulan) {


int thisMonthAsOrdinal = 12 * getYear () + getMonth (). index - 1;
int resultMonthAsOrdinal = thisMonthAsOrdinal + bulan;
int resultYear = resultMonthAsOrdinal / 12;
Month resultMonth = Month.make (resultMonthAsOrdinal% 12 +1);

8. [Beck97].

www.it-ebooks.info

Halaman 311

280 Bab 16: Tanggal Refactoring Serial

int lastDayOfResultMonth = lastDayOfMonth (resultMonth, resultYear);


int resultDay = Math.min (getDayOfMonth (), lastDayOfResultMonth);
return DayDateFactory.makeDate (resultDay, resultMonth, resultYear);
}

Fungsi addYears (baris 604-626) tidak memberikan kejutan di atas yang lain.

DayDate publik plusYears (int years) {


int resultYear = getYear () + tahun;
int lastDayOfMonthInResultYear = lastDayOfMonth (getMonth (), resultYear);
int resultDay = Math.min (getDayOfMonth (), lastDayOfMonthInResultYear);
return DayDateFactory.makeDate (resultDay, getMonth (), resultYear);
}

Ada sedikit rasa gatal di benak saya yang mengganggu saya tentang perubahan
metode ini dari statis ke instance. Apakah ungkapan date.addDays (5) membuatnya
jelas bahwa objek tanggal tidak berubah dan instance DayDate baru dikembalikan?
Atau apakah itu secara keliru menyiratkan bahwa kita menambahkan lima hari ke objek tanggal ? Kamu mungkin
tidak berpikir itu adalah masalah besar, tetapi sedikit kode yang terlihat seperti berikut ini bisa sangat
menipu [G20].
Tanggal DayDate = DateFactory.makeDate (5, Month.DECEMBER, 1952);
date.addDays (7); // tanggal benjolan satu minggu.

Seseorang yang membaca kode ini kemungkinan besar hanya akan menerima bahwa addDays mengubah
objek tanggal . Jadi kita perlu nama yang memecahkan ambiguitas ini [N4]. Jadi saya mengubah nama menjadi
plusDays dan plusMonths . Tampaknya bagi saya bahwa maksud dari metode ini ditangkap dengan baik oleh

Tanggal DayDate = oldDate.plusDays (5);

sedangkan yang berikut ini tidak cukup lancar membaca bagi pembaca untuk menerima begitu saja
objek tanggal berubah:
date.plusDays (5);

Algoritma terus menjadi lebih menarik. getPreviousDayOfWeek (baris 628–


660) berfungsi tetapi sedikit rumit. Setelah beberapa memikirkan tentang apa yang sebenarnya terjadi
[G21], saya dapat menyederhanakannya dan menggunakan E XPLAINING T EMPORARY V ARIABLES [G19] untuk
membuatnya lebih jelas. Saya juga mengubahnya dari metode statis ke metode contoh [G18], dan
menyingkirkan metode instance duplikat [G5] (baris 997-1008).

public DayDate getPreviousDayOfWeek (Hari targetDayOfWeek) {


int offsetToTarget = targetDayOfWeek.index - getDayOfWeek (). index;
jika (offsetToTarget> = 0)
offsetToTarget - = 7;
return plusDays (offsetToTarget);
}

Analisis dan hasil yang persis sama terjadi untuk getFollowingDayOfWeek (baris 662-693).

publik DayDate getFollowingDayOfWeek (Hari targetDayOfWeek) {


int offsetToTarget = targetDayOfWeek.index - getDayOfWeek (). index;
jika (offsetToTarget <= 0)

https://translate.googleusercontent.com/translate_f 245/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 312

Kemudian buat itu benar 281

offsetToTarget + = 7;
return plusDays (offsetToTarget);
}

Fungsi selanjutnya adalah getNearestDayOfWeek (baris 695-726), yang kami koreksi kembali
pada halaman 270. Tetapi perubahan yang saya buat saat itu tidak konsisten dengan pola saat ini di
dua fungsi terakhir [G11]. Jadi saya membuatnya konsisten dan menggunakan beberapa E XPLAINING T EMPO-
RARY V ARIABLES [G19] untuk memperjelas algoritme.

public DayDate getNearestDayOfWeek (targetDay Hari terakhir) {


int offsetToThisWeeksTarget = targetDay.index - getDayOfWeek (). index;
int offsetToFutureTarget = (offsetToThisWeeksTarget + 7)% 7;
int offsetToPreviousTarget = offsetToFutureTarget - 7;

if (offsetToFutureTarget> 3)
return plusDays (offsetToPreviousTarget);
lain
return plusDays (offsetToFutureTarget);
}

The getEndOfCurrentMonth metode (garis 728-740) adalah sedikit aneh karena merupakan
metode instance yang membuat iri [G14] kelasnya sendiri dengan mengambil argumen DayDate . Saya membuatnya
metode contoh yang benar dan mengklarifikasi beberapa nama.

publik DayDate getEndOfMonth () {


Bulan bulan = getMonth ();
int year = getYear ();
int lastDay = lastDayOfMonth (bulan, tahun);
return DayDateFactory.makeDate (lastDay, bulan, tahun);
}

Refactoring weekInMonthToString (baris 742-761) ternyata sangat menarik


memang. Menggunakan alat refactoring IDE saya, saya pertama kali memindahkan metode ke WeekInMonth
enum yang saya buat kembali di halaman 275. Kemudian saya mengganti nama metode menjadi toString . Selanjutnya, saya
mengubahnya dari metode statis ke metode contoh. Semua tes masih berlalu. (Bisakah kamu
tebak kemana saya pergi?)
Selanjutnya, saya menghapus metode sepenuhnya! Lima pernyataan gagal (baris 411-415, Listing B-4,
halaman 374). Saya mengubah baris ini untuk menggunakan nama-nama enumerator ( PERTAMA,
KEDUA ,. . .). Semua tes lulus. Bisakah kamu melihat mengapa? Bisakah Anda juga melihat mengapa masing-masing dari ini
langkah-langkah itu perlu? Alat refactoring memastikan bahwa semua penelepon sebelumnya
weekInMonthToString sekarang dipanggil toString pada enumerator weekInMonth karena semua
merator menerapkan toString untuk sekadar mengembalikan nama mereka. . . .
Sayangnya, saya agak terlalu pintar. Seanggun rantai refactor- yang indah
Namun, akhirnya saya menyadari bahwa satu-satunya pengguna fungsi ini adalah tes yang baru saja saya modifikasi.
ified, jadi saya menghapus tes.
Menipu saya sekali, malu pada Anda. Menipu saya dua kali, malu pada saya! Jadi setelah menentukan itu
tidak lain dari tes yang disebut relativeToString (baris 765-781), saya cukup menghapusnya
fungsi dan tesnya.

www.it-ebooks.info

Halaman 313

https://translate.googleusercontent.com/translate_f 246/365
3/12/2020 www.it-ebooks.info
282 Bab 16: Tanggal Refactoring Serial

Kami akhirnya berhasil mencapai metode abstrak dari kelas abstrak ini. Dan yang pertama
sesuai sebagaimana mereka datang: toSerial (baris 838–844). Kembali ke halaman 279 Saya telah berubah
nama toOrdinal . Setelah melihatnya dalam konteks ini, saya memutuskan nama seharusnya
diubah menjadi getOrdinalDay .
Metode abstrak berikutnya adalah toDate (baris 838–844). Itu mengubah DayDate menjadi
java.util.Date . Mengapa metode ini abstrak? Jika kita melihat implementasinya di
SpreadsheetDate (baris 198–207, Listing B-5, halaman 382), kita melihat bahwa itu tidak bergantung pada
apapun dalam implementasi kelas itu [G6]. Jadi saya mendorongnya.
Metode getYYYY , getMonth , dan getDayOfMonth abstrak dengan baik. Namun demikian
Metode getDayOfWeek adalah metode lain yang harus ditarik dari SpreadSheetDate
karena itu tidak bergantung pada apa pun yang tidak dapat ditemukan di DayDate [G6]. Atau apakah itu?
Jika Anda perhatikan dengan teliti (baris 247, Listing B-5, halaman 382), Anda akan melihat algoritma tersebut
secara implisit tergantung pada asal usul hari ordinal (dengan kata lain, hari minggu
hari 0). Jadi meskipun fungsi ini tidak memiliki ketergantungan fisik yang tidak dapat dipindahkan
ke DayDate , itu memang memiliki ketergantungan logis.
Ketergantungan logis seperti ini mengganggu saya [G22]. Jika sesuatu yang logis tergantung
implementasi, maka sesuatu yang fisik juga harus. Juga, menurut saya itu
algoritma itu sendiri bisa generik dengan bagian yang jauh lebih kecil dari itu tergantung pada
implementasi [G6].
Jadi saya membuat metode abstrak di DayDate bernama getDayOfWeekForOrdinalZero dan
menerapkannya di SpreadsheetDate untuk mengembalikan Day.SATURDAY . Lalu saya memindahkan getDayOfWeek
metode hingga DayDate dan mengubahnya untuk memanggil getOrdinalDay dan getDayOfWeekForOrdinal-
Nol .

Hari publik getDayOfWeek () {


Day startingDay = getDayOfWeekForOrdinalZero ();
int startingOffset = startingDay.index - Day.SUNDAY.index;
return Day.make ((getOrdinalDay () + startingOffset)% 7 + 1);
}

Sebagai catatan tambahan, perhatikan baik-baik komentar di baris 895 hingga baris 899. Apakah ini
pengulangan benar-benar diperlukan? Seperti biasa, saya menghapus komentar ini bersama dengan yang lainnya.
Metode selanjutnya adalah membandingkan (baris 902-913). Sekali lagi, metode ini tidak tepat
abstrak [G6], jadi saya menarik implementasinya ke DayDate . Juga, namanya tidak
cukup berkomunikasi [N1]. Metode ini sebenarnya mengembalikan selisih hari sejak
argumen. Jadi saya mengubah nama menjadi daysSince . Saya juga mencatat bahwa tidak ada tes
untuk metode ini, jadi saya menulisnya.
Enam fungsi berikutnya (baris 915-980) adalah semua metode abstrak yang harus diimplementasikan
mented dalam DayDate . Jadi saya menarik semuanya dari SpreadsheetDate .
Fungsi terakhir, isInRange (baris 982–995) juga perlu ditarik dan diperbaiki.
tored. The saklar pernyataan adalah sedikit jelek [G23] dan dapat diganti dengan memindahkan kasus
ke dalam enum DateInterval .

www.it-ebooks.info

Halaman 314

Kemudian buat itu benar 283

public enum DateInterval {


BUKA {
public boolean isIn (int d, int left, int right) {
kembali d> kiri && d <kanan;
}
},
CLOSED_LEFT {
public boolean isIn (int d, int left, int right) {
kembali d> = kiri && d <kanan;
}
},
CLOSED_RIGHT {
public boolean isIn (int d, int left, int right) {
kembali d> kiri && d <= kanan;
}
},
TUTUP {
public boolean isIn (int d, int left, int right) {
kembali d> = kiri && d <= kanan;

https://translate.googleusercontent.com/translate_f 247/365
3/12/2020 www.it-ebooks.info
}
};

boolean abstrak publik isIn (int d, int left, int right);


}

boolean publik isInRange (DayDate d1, DayDate d2, DateInterval interval) {


int left = Math.min (d1.getOrdinalDay (), d2.getOrdinalDay ());
int right = Math.max (d1.getOrdinalDay (), d2.getOrdinalDay ());
return interval.isIn (getOrdinalDay (), kiri, kanan);
}

Itu membawa kita ke akhir DayDate . Jadi sekarang kita akan membuat satu lagi melewati keseluruhan
kelas untuk melihat seberapa baik mengalir.
Pertama, komentar pembuka sudah lama ketinggalan zaman, jadi saya mempersingkat dan memperbaikinya [C2].
Selanjutnya, saya memindahkan semua enum yang tersisa ke file mereka sendiri [G12].
Selanjutnya, saya memindahkan variabel statis ( dateFormatSymbols ) dan tiga metode statis
( getMonthNames , isLeapYear , lastDayOfMonth ) ke dalam kelas baru bernama DateUtil [G6].
Saya memindahkan metode abstrak ke atas di tempat asalnya [G24].
Saya mengubah Month.make menjadi Month.fromInt [N1] dan melakukan hal yang sama untuk semua enum lainnya.
Saya juga membuat accessor toInt () untuk semua enum dan menjadikan bidang indeks pribadi.
Ada beberapa duplikasi menarik [G5] di plusYears dan plusMonths saya
dapat menghilangkan dengan mengekstraksi metode baru bernama correctLastDayOfMonth , membuat
ketiga metode ini jauh lebih jelas.
Saya menyingkirkan sihir nomor 1 [G25], menggantinya dengan Month.JANUARY.toInt () atau
Day.SUNDAY.toInt () , yang sesuai. Saya menghabiskan sedikit waktu dengan SpreadsheetDate , membersihkan
Algoritma sedikit. Hasil akhirnya terdapat dalam Listing B-7, halaman 394, sampai
Listing B-16, halaman 405.

www.it-ebooks.info

Halaman 315

284 Bab 16: Tanggal Refactoring Serial

Menariknya cakupan kode di DayDate telah menurun hingga 84,9 persen! Ini bukan
karena lebih sedikit fungsionalitas sedang diuji; melainkan karena kelasnya telah menyusut sangat banyak
bahwa beberapa garis yang terbuka memiliki bobot yang lebih besar. DayDate sekarang memiliki 45 dari 53 eksekusi
pernyataan yang bisa dicakup oleh tes. Garis yang terbuka sangat sepele sehingga tidak layak
pengujian.

Kesimpulan
Jadi sekali lagi kita mengikuti Aturan Kepanduan. Kami telah memeriksa kodenya dengan sedikit lebih bersih
daripada saat kami memeriksanya. Butuh sedikit waktu, tetapi itu sepadan. Cakupan tes adalah
meningkat, beberapa bug diperbaiki, kode diklarifikasi dan menyusut. Orang berikutnya
lihat kode ini mudah-mudahan akan lebih mudah untuk ditangani daripada yang kita lakukan. Orang itu juga akan
mungkin bisa membersihkannya sedikit lebih banyak daripada yang kita lakukan.

Bibliografi
[GOF]: Pola Desain: Elemen-elemen Perangkat Lunak Berorientasi Objek yang Dapat Digunakan Kembali , Gamma et al.,
Addison-Wesley, 1996.

[Simmons04]: Hardcore Java , Robert Simmons, Jr., O'Reilly, 2004.

[Refactoring]: Refactoring: Meningkatkan Desain Kode yang Ada , Martin Fowler et al.,
Addison-Wesley, 1999.

[Beck97]: Pola Praktek Terbaik Smalltalk , Kent Beck, Prentice Hall, 1997.

https://translate.googleusercontent.com/translate_f 248/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 316

17
Bau dan Heuristik

Dalam bukunya yang indah , Refactoring , 1 Martin Fowler mengidentifikasi banyak “Kode
Bau. " Daftar berikut termasuk banyak aroma Martin dan menambahkan lebih banyak dari saya
sendiri. Itu juga termasuk mutiara dan heuristik lain yang saya gunakan untuk mempraktikkan perdagangan saya.

1. [Refactoring].

285

www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 249/365
3/12/2020 www.it-ebooks.info

Halaman 317

286 Bab 17: Bau dan Heuristik

Saya menyusun daftar ini dengan menelusuri beberapa program dan refactoring yang berbeda
mereka. Ketika saya membuat setiap perubahan, saya bertanya pada diri sendiri mengapa saya membuat perubahan itu dan kemudian menulis
alasan di sini. Hasilnya adalah daftar hal-hal yang agak bau bagi saya ketika saya membaca
kode.
Daftar ini dimaksudkan untuk dibaca dari atas ke bawah dan juga untuk digunakan sebagai referensi.
Ada referensi silang untuk setiap heuristik yang menunjukkan kepada Anda di mana itu direferensikan dalam
teks lainnya dalam “Lampiran C” di halaman 409.

Komentar

C1: Informasi yang Tidak Pantas


Tidak pantas bagi komentar untuk menyimpan informasi yang lebih baik disimpan dalam sistem yang berbeda.
tem seperti sistem kontrol kode sumber Anda, sistem pelacakan masalah Anda, atau lainnya
sistem pencatatan. Ubah histori, misalnya, hanya mengacaukan file sumber dengan
volume teks sejarah dan tidak menarik. Secara umum, meta-data seperti penulis, terakhir
tanggal diubah, nomor SPR, dan sebagainya tidak boleh muncul dalam komentar. Komentar harus
dicadangkan untuk catatan teknis tentang kode dan desain.

C2: Komentar Usang


Komentar yang sudah menjadi tua, tidak relevan, dan salah sudah usang. Komentar menjadi tua
segera. Yang terbaik adalah tidak menulis komentar yang akan menjadi usang. Jika Anda menemukan usang
komentar, yang terbaik adalah memperbaruinya atau menyingkirkannya secepat mungkin. Komentar usang
cenderung bermigrasi jauh dari kode yang pernah mereka gambarkan. Mereka menjadi pulau mengambang
tidak relevan dan salah arah dalam kode.

C3: Komentar Redundan


Sebuah komentar berlebihan jika menggambarkan sesuatu yang cukup menggambarkan dirinya sendiri. Untuk
contoh:
i ++; // peningkatan i

Contoh lain adalah Javadoc yang mengatakan tidak lebih dari (atau bahkan kurang dari) fungsinya
tanda tangan:
/ **
* @param sellRequest
* @return
* @throws ManagedComponentException
*/
SellResponse beginSellItem publik (SellRequest sellRequest)
melempar ManagedComponentException

Komentar harus mengatakan hal-hal yang tidak dapat dikatakan sendiri oleh kode.

www.it-ebooks.info

Halaman 318

Lingkungan Hidup 287

C4: Komentar yang Ditulis dengan Buruk

https://translate.googleusercontent.com/translate_f 250/365
3/12/2020 www.it-ebooks.info

Sebuah komentar yang layak ditulis layak ditulis dengan baik. Jika Anda akan menulis komentar,
luangkan waktu untuk memastikan itu adalah komentar terbaik yang dapat Anda tulis. Pilih kata-kata Anda
hati-hati. Gunakan tata bahasa dan tanda baca yang benar. Jangan mengoceh. Jangan nyatakan yang jelas.
Bersikap singkat.

C5: Kode Keluar-Komentar


Itu membuat saya gila melihat hamparan kode yang dikomentari. Siapa yang tahu berapa usianya
adalah? Siapa yang tahu apakah itu bermakna atau tidak? Namun tidak ada yang akan menghapusnya karena semua orang
mengasumsikan orang lain membutuhkannya atau memiliki rencana untuk itu.
Kode itu duduk di sana dan membusuk, semakin tidak relevan dengan setiap hari. Itu
memanggil fungsi yang tidak ada lagi. Ini menggunakan variabel yang namanya telah berubah. Itu mengikuti
konvensi yang lama usang. Ini mencemari modul yang mengandungnya dan mengalihkan perhatian
orang yang mencoba membacanya. Kode yang dikomentari adalah kekejian .
Ketika Anda melihat kode berkomentar, hapus! Jangan khawatir, kontrol kode sumber
sistem masih mengingatnya. Jika ada yang benar-benar membutuhkannya, ia dapat kembali dan memeriksa a
versi sebelumnya. Jangan menderita kode komentar untuk bertahan.

Lingkungan Hidup

E1: Build Membutuhkan Lebih Dari Satu Langkah


Membangun proyek harus menjadi operasi sepele tunggal. Anda tidak harus memeriksa banyak
potongan-potongan kecil dari kontrol kode sumber. Anda tidak perlu membutuhkan urutan misterius
skrip tergantung mands atau konteks untuk membangun elemen individu. Kamu harus
tidak perlu mencari dekat dan jauh untuk semua berbagai JAR ekstra kecil, file XML, dan lainnya
artefak yang dibutuhkan sistem. Anda harus dapat memeriksa sistem dengan satu sim-
perintah perintah dan kemudian mengeluarkan satu perintah sederhana lainnya untuk membangunnya.
svn dapatkan mySystem
cd mySystem
semut semua

E2: Tes Membutuhkan Lebih Dari Satu Langkah


Anda harus dapat menjalankan semua tes unit hanya dengan satu perintah. Dalam kasus terbaik Anda
dapat menjalankan semua tes dengan mengklik satu tombol di IDE Anda. Dalam kasus terburuk Anda harus
dapat mengeluarkan satu perintah sederhana dalam sebuah shell. Mampu menjalankan semua tes begitu
mendasar dan sangat penting sehingga harus cepat, mudah, dan jelas dilakukan.

www.it-ebooks.info

Halaman 319

288 Bab 17: Bau dan Heuristik

Fungsi
F1: Terlalu Banyak Argumen
Fungsinya harus memiliki sejumlah kecil argumen. Tidak ada argumen yang terbaik, diikuti oleh
satu, dua, dan tiga. Lebih dari tiga sangat dipertanyakan dan harus dihindari dengan prasangka.
udice. (Lihat "Argumen Fungsi" pada halaman 40.)

F2: Argumen Keluaran


Argumen output berlawanan dengan intuisi. Pembaca berharap argumen menjadi input, bukan out-
menempatkan. Jika fungsi Anda harus mengubah status sesuatu, minta ia mengubah status
objek itu dipanggil. (Lihat “Argumen Keluaran” di halaman 45.)

F3: Tandai Argumen


Argumen Boolean dengan keras menyatakan bahwa fungsi melakukan lebih dari satu hal. Mereka
membingungkan dan harus dihilangkan. (Lihat “Tandai Argumen” di halaman 41.)

https://translate.googleusercontent.com/translate_f 251/365
3/12/2020 www.it-ebooks.info
F4: Fungsi Mati
Metode yang tidak pernah dipanggil harus dibuang. Menjaga kode mati di sekitar adalah sia-sia.
Jangan takut untuk menghapus fungsinya. Ingat, sistem kontrol kode sumber Anda masih
ingat itu.

Umum
G1: Beberapa Bahasa dalam Satu File Sumber
Lingkungan pemrograman modern saat ini memungkinkan untuk menempatkan banyak bahasa yang berbeda
menjadi satu file sumber. Misalnya, file sumber Java mungkin berisi potongan XML,
HTML, YAML, JavaDoc, Bahasa Inggris, JavaScript, dan sebagainya. Sebagai contoh lain, selain
HTML, file JSP mungkin berisi Java, sintaksis perpustakaan tag, komentar bahasa Inggris, Javadocs,
XML, JavaScript, dan sebagainya. Ini membingungkan di terbaik dan ceroboh paling buruk di terburuk.
Yang ideal adalah agar file sumber mengandung satu, dan hanya satu, bahasa. Secara realistis, kami
mungkin harus menggunakan lebih dari satu. Tetapi kita harus bersusah payah untuk meminimalkan keduanya
jumlah dan luasnya bahasa tambahan dalam file sumber kami.

G2: Perilaku Jelas Tidak Diimplementasikan


Mengikuti "Prinsip Kejutan Paling Sedikit," 2 fungsi atau kelas apa pun harus menerapkan
perilaku yang bisa diharapkan oleh programmer lain. Sebagai contoh, pertimbangkan a
fungsi yang menerjemahkan nama hari ke enum yang mewakili hari.

2. Atau “Prinsip Ketinggian yang Paling Hilang ”: http://en.wikipedia.org/wiki/


Principle_of_least_astonishment

www.it-ebooks.info

Halaman 320

Umum 289

Hari hari = DayDate.StringToDay (String dayName);

Kami berharap string "Senin" akan diterjemahkan ke Day.MONDAY . Kami juga harapkan
singkatan umum yang harus diterjemahkan, dan kami berharap fungsi tersebut diabaikan
kasus.
Ketika perilaku yang jelas tidak diterapkan, pembaca dan pengguna kode tidak bisa
lebih lama bergantung pada intuisi mereka tentang nama fungsi. Mereka kehilangan kepercayaan pada aslinya
penulis dan harus kembali membaca rincian kode.

G3: Perilaku Tidak Benar di Batas


Tampaknya jelas untuk mengatakan bahwa kode harus berperilaku dengan benar. Masalahnya adalah kita jarang
menyadari betapa rumitnya perilaku yang benar. Pengembang sering menulis fungsi itu
mereka pikir akan berhasil, dan kemudian mempercayai intuisi mereka daripada berusaha untuk membuktikan
bahwa kode mereka bekerja di semua kasus sudut dan batas.
Tidak ada pengganti untuk uji tuntas. Setiap kondisi batas, setiap sudut
kasus, setiap kekhasan dan pengecualian mewakili sesuatu yang dapat membingungkan dan elegan
algoritma intuitif. Jangan mengandalkan intuisi Anda . Cari setiap syarat batas dan
tulis tes untuk itu.

G4: Override Safeties


Chernobyl meleleh karena manajer pabrik mengalahkan setiap mekanisme keselamatan
satu per satu. Keamanan membuat tidak nyaman untuk menjalankan percobaan. Itu
Hasilnya adalah eksperimen itu tidak berjalan, dan dunia melihat ini adalah warga sipil besar pertama
bencana nuklir.
Beresiko untuk mengesampingkan safeties. Melakukan kontrol manual atas serialVersionUID mungkin
diperlukan, tetapi selalu berisiko. Mematikan peringatan kompiler tertentu (atau semua peringatan!)
mungkin membantu Anda membangun agar berhasil, tetapi dengan risiko sesi debugging yang tak ada habisnya. Belok-
Tidak lulus tes dan mengatakan pada diri sendiri bahwa Anda akan lulus nanti sama buruknya dengan berpura-pura
kartu kredit Anda adalah uang gratis.

G5: Duplikasi
Ini adalah salah satu aturan terpenting dalam buku ini, dan Anda harus menanggapinya dengan sangat serius.
Hampir setiap penulis yang menulis tentang desain perangkat lunak menyebutkan aturan ini. Dave Thomas

https://translate.googleusercontent.com/translate_f 252/365
3/12/2020 www.it-ebooks.info
dan
salahAndy Hunt menyebutnya
satu prinsip prinsip
inti dari Extreme KERING 3 (Don't
Programming Repeat Yourself).
dan menyebutnya: Kent
"Sekali, Beck
dan berhasil
hanya sekali."
Ron Jeffries menempati peringkat kedua aturan ini, tepat di bawah mendapatkan semua tes untuk lulus.
Setiap kali Anda melihat duplikasi dalam kode, itu merupakan peluang yang terlewatkan
abstraksi. Duplikasi itu mungkin bisa menjadi subroutine atau mungkin yang lain
kelas langsung. Dengan melipat duplikasi menjadi abstraksi seperti itu, Anda meningkatkan
ulary dari bahasa desain Anda. Pemrogram lain dapat menggunakan fasilitas abstrak

3. [PRAG].

www.it-ebooks.info

Halaman 321

290 Bab 17: Bau dan Heuristik

kamu buat. Pengodean menjadi lebih cepat dan lebih sedikit kesalahan karena Anda telah meningkatkan
tingkat abstraksi.
Bentuk duplikasi yang paling jelas adalah ketika Anda memiliki rumpun kode identik itu
terlihat seperti beberapa programmer menjadi liar dengan mouse, menempelkan kode yang sama berulang dan
lagi. Ini harus diganti dengan metode sederhana.
Bentuk yang lebih halus adalah sakelar / kasing atau rantai if / else yang muncul berulang kali
berbagai modul, selalu menguji untuk serangkaian kondisi yang sama. Ini harus diganti
dengan polimorfisme.
Masih lebih halus adalah modul yang memiliki algoritma yang sama, tetapi tidak berbagi
baris kode yang serupa. Ini masih duplikasi dan harus diatasi dengan menggunakan T EM-
PLATE M ETHOD , 4 atau S TRATEGI 5 pola.
Memang, sebagian besar pola desain yang telah muncul dalam lima belas tahun terakhir adalah sama.
cara-cara terkenal untuk menghilangkan duplikasi. Demikian juga Bentuk Normal Codd adalah strategi
ingin menghilangkan duplikasi dalam skema basis data. OO sendiri adalah strategi pengorganisasian
modul dan menghilangkan duplikasi. Tidak mengherankan, begitu pula pemrograman terstruktur.
Saya pikir intinya telah dibuat. Temukan dan hilangkan duplikasi di mana pun Anda bisa.

G6: Kode di Level Abstraksi yang Salah


Penting untuk membuat abstraksi yang memisahkan konsep umum tingkat yang lebih tinggi dari yang lebih rendah
konsep tingkat rinci. Terkadang kita melakukan ini dengan membuat kelas abstrak untuk menampung
konsep tingkat tinggi dan turunannya untuk memegang konsep tingkat bawah. Ketika kita melakukan ini,
kita perlu memastikan bahwa pemisahan selesai. Kami ingin semua konsep tingkat bawah
berada dalam turunan dan semua konsep tingkat yang lebih tinggi berada di kelas dasar.
Misalnya, konstanta, variabel, atau fungsi utilitas yang hanya berkaitan dengan detail
implementasi tidak harus ada di kelas dasar. Kelas dasar harus tahu apa-
tentang mereka.
Aturan ini juga berkaitan dengan file sumber, komponen, dan modul. Perangkat lunak yang bagus
desain mensyaratkan bahwa kita memisahkan konsep pada tingkat yang berbeda dan menempatkannya dalam berbeda
wadah. Kadang-kadang wadah ini adalah kelas dasar atau turunan dan terkadang mereka
adalah file sumber, modul, atau komponen. Apa pun masalahnya, pemisahan itu perlu
harus lengkap. Kami tidak ingin konsep tingkat bawah dan lebih tinggi dicampur bersama.
Pertimbangkan kode berikut:
antarmuka publik Stack {
Objek pop () melempar EmptyException;
void push (Object o) melempar FullException;
double persenFull ();

4. [GOF].
5. [GOF].

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 253/365
3/12/2020 www.it-ebooks.info

Halaman 322

Umum 291

class EmptyException memperpanjang Exception {}


kelas FullException memperluas Pengecualian {}
}

Fungsi persenFull berada pada level abstraksi yang salah. Meskipun ada
banyak implementasi Stack di mana konsep kepenuhan itu masuk akal, ada yang lain
implementasi yang tidak bisa tahu seberapa penuh mereka. Jadi fungsinya
lebih baik ditempatkan di antarmuka turunan seperti BoundedStack .
Mungkin Anda berpikir bahwa implementasinya bisa saja mengembalikan nol jika stack
tak terbatas. Masalahnya adalah tidak ada tumpukan yang benar-benar tidak terbatas. Kamu tidak bisa
benar-benar mencegah OutOfMemoryException dengan memeriksa
stack.percentFull () <50.0.

Menerapkan fungsi untuk mengembalikan 0 berarti berbohong.


Intinya adalah bahwa Anda tidak bisa berbohong atau berpura-pura keluar dari abstraksi yang salah tempat. Iso-
abstraksi terakhir adalah salah satu hal tersulit yang dilakukan pengembang perangkat lunak, dan tidak ada
perbaikan cepat ketika Anda salah.

G7: Kelas Dasar Tergantung pada Derivatifnya


Alasan paling umum untuk mempartisi konsep ke dalam kelas dasar dan turunan adalah begitu
bahwa konsep kelas dasar tingkat yang lebih tinggi dapat independen dari turunan tingkat yang lebih rendah
konsep kelas. Oleh karena itu, ketika kita melihat kelas dasar yang menyebutkan nama turunannya
Kami menduga ada masalah. Secara umum, kelas dasar seharusnya tidak tahu tentang mereka
turunannya.
Tentu saja ada pengecualian untuk aturan ini. Terkadang jumlah turunannya adalah
benar-benar diperbaiki, dan kelas dasar memiliki kode yang memilih antara turunannya. Kami melihat ini a
banyak dalam implementasi mesin negara terbatas. Namun, dalam hal ini turunan dan pangkalan
kelas sangat digabungkan dan selalu digunakan bersama dalam file jar yang sama. Secara umum
jika kita ingin dapat menggunakan turunan dan pangkalan di file jar yang berbeda.
Menyebarkan turunan dan basis di file jar yang berbeda dan memastikan file jar dasar
tidak tahu apa-apa tentang isi file jar turunan yang memungkinkan kami untuk menggunakan sistem kami
dalam komponen diskrit dan independen. Ketika komponen tersebut dimodifikasi, mereka dapat
dipindahtugaskan tanpa harus menggunakan kembali komponen dasar. Ini berarti bahwa
dampak perubahan sangat berkurang, dan sistem pemeliharaan di lapangan dibuat banyak
lebih sederhana.

G8: Terlalu Banyak Informasi


Modul yang terdefinisi dengan baik memiliki antarmuka yang sangat kecil yang memungkinkan Anda melakukan banyak hal dengan sedikit.
Modul yang tidak terdefinisi dengan buruk memiliki antarmuka yang lebar dan dalam yang memaksa Anda untuk menggunakan banyak yang berbeda
isyarat untuk menyelesaikan hal-hal sederhana. Antarmuka yang terdefinisi dengan baik tidak menawarkan banyak fungsi
tergantung pada ketergantungan, sehingga kopling rendah. Antarmuka yang tidak didefinisikan dengan baik menyediakan banyak fungsi
tions yang harus Anda panggil, sehingga kopling tinggi.

www.it-ebooks.info

Halaman 323

292 Bab 17: Bau dan Heuristik

Pengembang perangkat lunak yang baik belajar untuk membatasi apa yang mereka paparkan di antarmuka mereka
kelas dan modul. Semakin sedikit metode yang dimiliki kelas, semakin baik. Semakin sedikit variabel fungsi
tion tahu tentang, semakin baik. Semakin sedikit variabel instan yang dimiliki kelas, semakin baik.
Sembunyikan data Anda. Sembunyikan fungsi utilitas Anda. Sembunyikan konstanta dan temporari Anda.
Jangan membuat kelas dengan banyak metode atau banyak variabel instan. Jangan membuat banyak

https://translate.googleusercontent.com/translate_f 254/365
3/12/2020 www.it-ebooks.info
variabel dan fungsi yang dilindungi untuk subclass Anda. Berkonsentrasi pada menjaga antarmuka
sangat ketat dan sangat kecil. Bantu tetap rendah dengan membatasi informasi.

G9: Kode Mati


Kode mati adalah kode yang tidak dieksekusi. Anda menemukannya di badan pernyataan if yang memeriksa
untuk suatu kondisi yang tidak dapat terjadi. Anda menemukannya di blok tangkapan percobaan yang tidak pernah melempar .
Anda menemukannya dalam metode utilitas kecil yang tidak pernah dipanggil atau beralih / kondisi kondisi itu
tidak pernah terjadi.
Masalah dengan kode mati adalah bahwa setelah beberapa saat mulai berbau. Semakin tua, maka
menjadi lebih kuat dan lebih bau. Ini karena kode mati tidak sepenuhnya
diperbarui saat desain berubah. Masih mengkompilasi , tetapi tidak mengikuti konvensi yang lebih baru atau
aturan Itu ditulis pada saat sistem berbeda . Ketika Anda menemukan kode mati, lakukan
hal yang benar. Berikan penguburan yang layak. Hapus dari sistem.

G10: Pemisahan Vertikal


Variabel dan fungsi harus didefinisikan dekat dengan tempat mereka digunakan. Variabel lokal
harus dinyatakan tepat di atas penggunaan pertama mereka dan harus memiliki cakupan vertikal kecil. Kita
tidak ingin variabel lokal menyatakan ratusan baris jauh dari penggunaannya.
Fungsi pribadi harus didefinisikan tepat di bawah penggunaan pertama mereka. Fungsi pribadi
termasuk dalam ruang lingkup seluruh kelas, tetapi kami masih ingin membatasi jarak vertikal
antara doa dan definisi. Menemukan fungsi pribadi seharusnya menjadi masalah
pemindaian ke bawah dari penggunaan pertama.

G11: Inkonsistensi
Jika Anda melakukan sesuatu dengan cara tertentu, lakukan semua hal serupa dengan cara yang sama. Ini kembali
dengan prinsip paling tidak mengejutkan. Hati-hati dengan konvensi yang Anda pilih, dan sekali
dipilih, hati-hati untuk terus mengikuti mereka.
Jika dalam fungsi tertentu Anda menggunakan variabel bernama respons untuk menampung
HttpServletResponse , lalu gunakan nama variabel yang sama secara konsisten di fungsi lainnya
yang menggunakan objek HttpServletResponse . Jika Anda memberi nama metode processVerificationRequest ,
kemudian gunakan nama yang mirip, seperti processDeletionRequest , untuk metode yang memproses
jenis permintaan lainnya.
Konsistensi sederhana seperti ini, ketika diterapkan dengan andal, dapat membuat kode lebih mudah
baca dan modifikasi.

www.it-ebooks.info

Halaman 324

Umum 293

G12: Kekacauan
Apa gunanya konstruktor default tanpa implementasi? Yang harus dilakukan hanyalah kekacauan
kode dengan artefak yang tidak berarti. Variabel yang tidak digunakan, fungsi yang tidak pernah
disebut, komentar yang tidak menambahkan informasi, dan sebagainya. Semua ini berantakan dan
harus disingkirkan. Jaga file sumber Anda bersih, teratur, dan bebas dari kekacauan.

G13: Kopling Buatan


Hal-hal yang tidak saling bergantung tidak boleh digabungkan secara artifisial. Sebagai contoh,
enum umum tidak boleh terkandung dalam kelas yang lebih spesifik karena ini memaksa
seluruh aplikasi untuk mengetahui tentang kelas-kelas yang lebih spesifik ini. Hal yang sama berlaku untuk umum
tujuan fungsi statis dideklarasikan di kelas tertentu.
Secara umum kopling buatan adalah kopling antara dua modul yang melayani no
tujuan langsung. Ini adalah hasil dari meletakkan variabel, konstan, atau fungsi sementara
lokasi yang nyaman, meskipun tidak tepat. Ini malas dan ceroboh.
Luangkan waktu untuk mencari tahu di mana fungsi, konstanta, dan variabel seharusnya
dideklarasikan. Jangan hanya melemparkannya ke tempat yang paling nyaman dan meninggalkannya
sana.

G14: Cemburu pada Fitur


Ini adalah salah satu dari bau kode Martin Fowler. 6 Metode kelas harus tertarik

https://translate.googleusercontent.com/translate_f 255/365
3/12/2020 www.it-ebooks.info
variabel dan fungsi dari kelas mereka termasuk, dan bukan variabel dan fungsi
dari kelas lain. Ketika suatu metode menggunakan accessors dan mutators dari beberapa objek lain untuk
memanipulasi data di dalam objek itu, lalu membuat iri lingkup kelas yang lain
obyek. Ia berharap bahwa ia berada di dalam kelas lain sehingga dapat memiliki akses langsung ke Internet
variabel itu memanipulasi. Sebagai contoh:
HourlyPayCalculator kelas publik {
Uang publik, menghitungWeeklyPay (HourlyEmployee e) {
int tenthRate = e.getTenthRate (). getPennies ();
int tenthsWorked = e.getTenthsWorked ();
int straightTime = Math.min (400, tenthsWorked);
int overTime = Math.max (0, tenthsWorked - straightTime);
int straightPay = straightTime * tenthRate;
int overtimePay = (int) Math.round (overTime * tenthRate * 1.5);
kembalikan Uang baru (straightPay + overtimePay);
}
}

The calculateWeeklyPay metode mencapai ke HourlyEmployee objek untuk mendapatkan data pada
yang beroperasi. The calculateWeeklyPay metode iri lingkup HourlyEmployee . Itu
" Berharap " bahwa itu bisa berada di dalam HourlyEmployee .

6. [Refactoring].

www.it-ebooks.info

Halaman 325

294 Bab 17: Bau dan Heuristik

Semua sederajat, kami ingin menghilangkan Fitur Envy karena mengekspos internal
dari satu kelas ke kelas lainnya. Namun, kadang-kadang, Fitur Envy adalah kejahatan yang perlu. Pertimbangkan
berikut:
kelas publik HourlyEmployeeReport {
karyawan HourlyEmployee pribadi;

HourlyEmployeeReport publik (HourlyEmployee e) {


this.employee = e;
}

String reportHours () {
return String.format (
"Nama:% s \ tJam:% d.% 1d \ n",
employee.getName (),
employee.getTenthsWorked () / 10,
employee.getTenthsWorked ()% 10);
}
}

Jelas, metode reportHours membuat iri kelas HourlyEmployee . Di sisi lain, kita
tidak ingin HourlyEmployee harus tahu tentang format laporan. Memindahkan itu untuk-
mat string ke dalam kelas HourlyEmployee akan melanggar beberapa prinsip berorientasi objek
rancangan. 7 Ini akan memasangkan HourlyEmployee ke format laporan, memaparkannya ke perubahan
dalam format itu.

G15: Argumen Pemilih


Hampir tidak ada yang lebih keji dari argumen palsu yang menjuntai di akhir a
panggilan fungsi. Apa artinya? Apa yang akan berubah jika itu benar ? Tidak hanya itu
tujuan argumen pemilih sulit diingat, setiap argumen pemilih menggabungkan
banyak fungsi menjadi satu. Argumen pemilih hanyalah cara malas untuk menghindari pemisahan yang besar
berfungsi menjadi beberapa fungsi yang lebih kecil. Mempertimbangkan:
public int calculWeeklyPay (boolean lembur) {
int tenthRate = getTenthRate ();
int tenthsWorked = getTenthsWorked ();
int straightTime = Math.min (400, tenthsWorked);
int overTime = Math.max (0, tenthsWorked - straightTime);
int straightPay = straightTime * tenthRate;
double overtimeRate = lembur? 1.5: 1.0 * tenthRate;
int overtimePay = (int) Math.round (overTime * overtimeRate);
return straightPay + overtimePay;
}

Anda memanggil fungsi ini dengan benar jika lembur dibayar sebagai waktu setengah, dan dengan a
salah jika lembur dibayarkan sebagai waktu lurus. Sudah cukup buruk sehingga Anda harus ingat apa
calculWeeklyPay (false) berarti setiap kali Anda sengaja menemukannya. Tetapi

https://translate.googleusercontent.com/translate_f 256/365
3/12/2020 www.it-ebooks.info

7. Secara khusus, Prinsip Tanggung Jawab Tunggal, Prinsip Tertutup Terbuka, dan Prinsip Penutupan Umum. Lihat [PPP].

www.it-ebooks.info

Halaman 326

Umum 295

rasa malu sebenarnya dari fungsi seperti ini adalah bahwa penulis melewatkan kesempatan untuk menulis
berikut:
straightPay int publik () {
return getTenthsWorked () * getTenthRate ();
}

public int overTimePay () {


int overTimeTenths = Math.max (0, getTenthsWorked () - 400);
int overTimePay = overTimeBonus (overTimeTenths);
return straightPay () + overTimePay;
}

private int overTimeBonus (int overTimeTenths) {


bonus ganda = 0,5 * getTenthRate () * overTimeTenths;
return (int) Math.round (bonus);
}

Tentu saja, penyeleksi tidak perlu boolean . Mereka dapat berupa enum, bilangan bulat, atau yang lainnya
tipe argumen yang digunakan untuk memilih perilaku fungsi. Secara umum lebih baik
memiliki banyak fungsi selain untuk meneruskan beberapa kode ke fungsi untuk memilih perilaku.

G16: Maksud Tersembunyi


Kami ingin kode menjadi seekspresi mungkin. Ekspresi run-on, notasi Hongaria,
dan angka ajaib semua mengaburkan niat penulis. Misalnya, inilah overTimePay
berfungsi seperti yang mungkin muncul:
public int m_otCalc () {
kembalikan iThsWkd * iThsRte +
(int) Math.round (0,5 * iThsRte *
Math.max (0, iThsWkd - 400)
);
}

Kecil dan padat seperti ini mungkin muncul, itu juga hampir tidak bisa ditembus. Itu layak
ing waktu untuk membuat maksud kode kami terlihat oleh pembaca kami.

G17: Tanggung Jawab yang salah tempat


Salah satu keputusan paling penting yang dapat dibuat oleh pengembang perangkat lunak adalah di mana harus meletakkan kode.
Misalnya, kemana perginya PI ? Haruskah itu di kelas Matematika ? Mungkin itu
termasuk dalam kelas Trigonometri ? Atau mungkin di kelas Circle ?
Prinsip paling tidak mengejutkan mulai berlaku di sini. Kode harus ditempatkan di tempat a
pembaca tentu mengharapkannya. The PI konstan harus pergi ke mana fungsi trigonometri
dinyatakan. Konstanta OVERTIME_RATE harus dinyatakan dalam HourlyPay-
Kelas kalkulator .
Terkadang kita menjadi "pintar" tentang di mana harus meletakkan fungsionalitas tertentu. Kami akan memasukkannya ke dalam
fungsi yang nyaman bagi kami, tetapi tidak selalu intuitif untuk pembaca. Sebagai contoh,
mungkin kita perlu mencetak laporan dengan total jam kerja seorang karyawan. Kita

www.it-ebooks.info

Halaman 327

https://translate.googleusercontent.com/translate_f 257/365
3/12/2020 www.it-ebooks.info

296 Bab 17: Bau dan Heuristik

dapat meringkas jam-jam itu dalam kode yang mencetak laporan, atau kita dapat mencoba menjalankan
total dalam kode yang menerima kartu waktu.
Salah satu cara untuk mengambil keputusan ini adalah dengan melihat nama-nama fungsinya. Katakan saja itu
modul laporan kami memiliki fungsi bernama getTotalHours . Katakan juga modul itu
menerima kartu waktu memiliki fungsi saveTimeCard . Manakah dari dua fungsi ini, dengan namanya,
menyiratkan bahwa ia menghitung total? Jawabannya harus jelas.
Jelas, kadang-kadang ada alasan kinerja mengapa total harus dihitung
sebagai kartu waktu diterima daripada ketika laporan dicetak. Tidak apa-apa, tapi namanya
fungsi harus mencerminkan ini. Misalnya, harus ada computeRunning-
Fungsi TotalOfHours dalam modul timecard.

G18: Statis yang Tidak Pantas

Math.max (double a, double b) adalah metode statis yang bagus. Itu tidak beroperasi pada satu
contoh; memang, itu konyol untuk harus mengatakan Matematika baru () .max ( a, b) atau bahkan a.max (b) .
Semua data yang digunakan maksimal berasal dari dua argumennya, dan bukan dari “memiliki” apa pun
obyek. Lebih tepatnya, hampir tidak ada kemungkinan bahwa kita ingin Math.max menjadi
polimorfik.
Namun, kadang-kadang, kami menulis fungsi statis yang seharusnya tidak statis. Sebagai contoh,
mempertimbangkan:
HourlyPayCalculator.calculatePay (karyawan, lemburRate).

Sekali lagi, ini sepertinya fungsi statis yang masuk akal . Itu tidak beroperasi pada hal tertentu
objek dan mendapatkan semua data itu dari argumen itu. Namun, ada kemungkinan yang masuk akal
kami ingin fungsi ini menjadi polimorfik. Kami mungkin ingin menerapkan beberapa perbedaan
algoritma untuk menghitung pembayaran per jam, misalnya, OvertimeHourlyPayCalculator dan
StraightTimeHourlyPayCalculator . Jadi dalam hal ini fungsinya tidak boleh statis. Itu
harus merupakan fungsi anggota tidak statis dari Karyawan .
Secara umum Anda harus memilih metode nonstatic daripada metode statis. Saat ragu,
membuat fungsi tidak statis. Jika Anda benar-benar ingin fungsi statis, pastikan ada
tidak mungkin Anda ingin berperilaku polimorfik.

G19: Gunakan Variabel Penjelasan


Kent Beck menulis tentang ini dalam bukunya yang kecil Smalltalk Best Practice Patterns 8 dan lagi
baru-baru ini dalam Pola Implementasi bukunya yang sama hebatnya . 9 Salah satu yang lebih kuat-
cara terbaik untuk membuat program dapat dibaca adalah memecah kalkulasi menjadi nilai tengah
ues yang disimpan dalam variabel dengan nama yang bermakna.

8. [Beck97], hlm. 108.


9. [Beck07].

www.it-ebooks.info

Halaman 328

Umum 297

Pertimbangkan contoh ini dari FitNesse:


Matcher match = headerPattern.matcher (line);
if (match.find ())
{
Kunci string = match.group (1);
Nilai string = match.group (2);
header.put (key.toLowerCase (), nilai);
}

Penggunaan variabel penjelas yang sederhana memperjelas bahwa kelompok yang cocok pertama adalah
yang utama, dan kelompok cocok kedua adalah nilai .

https://translate.googleusercontent.com/translate_f 258/365
3/12/2020 www.it-ebooks.info
Sulit bagaimana
luar biasa untuk berlebihan.
modul Lebih
burambanyak
tiba-tibavariabel penjelas
bisa menjadi umumnya
transparan lebihdengan
hanya baik daripada
istirahatlebih sedikit. Itu
ing perhitungan menjadi nilai-nilai menengah yang dinamai.

G20: Nama Fungsi Harus Mengatakan Apa Yang Mereka Lakukan


Lihatlah kode ini:
Date newDate = date.add (5);

Apakah Anda berharap ini menambah lima hari ke tanggal? Atau minggu, atau berjam-jam? Apakah tanggalnya?
misalnya diubah atau apakah fungsinya mengembalikan Tanggal baru tanpa mengubah yang lama?
Anda tidak dapat mengetahui dari fungsi apa panggilan itu dilakukan .
Jika fungsi menambahkan lima hari ke tanggal dan mengubah tanggal, maka itu harus dipanggil
addDaysTo atau meningkatByDays . Sebaliknya, jika fungsi tersebut mengembalikan tanggal baru
lima hari kemudian tetapi tidak mengubah tanggal contoh, itu harus disebut daysLater atau
hari sejak .
Jika Anda harus melihat implementasi (atau dokumentasi) fungsi untuk mengetahuinya
apa fungsinya, maka Anda harus bekerja untuk menemukan nama yang lebih baik atau mengatur ulang fungsinya
bahwa itu dapat ditempatkan di fungsi dengan nama yang lebih baik.

G21: Memahami Algoritma


Banyak kode yang sangat lucu ditulis karena orang tidak meluangkan waktu untuk memahami
algoritma. Mereka mendapatkan sesuatu untuk bekerja dengan memasukkan cukup jika pernyataan dan bendera,
tanpa berhenti untuk mempertimbangkan apa yang sebenarnya terjadi.
Pemrograman seringkali merupakan eksplorasi. Anda pikir Anda tahu algoritma yang tepat untuk
sesuatu, tetapi kemudian Anda mengotak-atiknya, mendorong dan menyodoknya, sampai Anda mendapatkannya
bekerja." Bagaimana Anda tahu itu "berhasil"? Karena itu melewati ujian yang dapat Anda pikirkan.
Tidak ada yang salah dengan pendekatan ini. Memang, seringkali itu adalah satu-satunya cara untuk mendapatkan
berfungsi untuk melakukan apa yang Anda pikir seharusnya. Namun, tidak cukup untuk meninggalkan kutipan
menandai sekitar kata "bekerja."

www.it-ebooks.info

Halaman 329

298 Bab 17: Bau dan Heuristik

Sebelum Anda menganggap diri Anda selesai dengan suatu fungsi, pastikan Anda mengerti
bagaimana itu bekerja. Itu tidak cukup baik sehingga melewati semua tes. Anda harus tahu 10 bahwa
solusinya benar.
Seringkali cara terbaik untuk mendapatkan pengetahuan dan pemahaman ini adalah dengan memperbaiki fungsi.
Menjadi sesuatu yang begitu bersih dan ekspresif sehingga jelas cara kerjanya.

G22: Jadikan Ketergantungan Logis Fisik


Jika satu modul tergantung satu sama lain, ketergantungan itu harus fisik, bukan hanya logis.
Modul dependen tidak boleh membuat asumsi (dengan kata lain, ketergantungan logis
cies) tentang modul tempat ia bergantung. Melainkan harus secara eksplisit meminta modul itu untuk semua
informasi itu tergantung pada.
Misalnya, bayangkan Anda sedang menulis fungsi yang mencetak laporan teks biasa
jam kerja oleh karyawan. Satu kelas bernama HourlyReporter mengumpulkan semua data menjadi a
formulir yang nyaman dan kemudian meneruskannya ke HourlyReportFormatter untuk mencetaknya. (Lihat Listing 17-1.)

Listing 17-1
HourlyReporter.java
HourlyReporter kelas publik {
formatter HourlyReportFormatter pribadi;
halaman Daftar pribadi <LineItem>;
PAGE_SIZE int akhir pribadi = 55;

HourlyReporter publik (HourlyReportFormatter formatter) {


this.formatter = formatter;
halaman = ArrayList baru <LineItem> ();
}

public void generateReport (Daftar <HourlyEmployee> karyawan) {


untuk (HourlyEmployee e: employee) {

https://translate.googleusercontent.com/translate_f 259/365
3/12/2020 www.it-ebooks.info
addLineItemToPage (e);
if (page.size () == PAGE_SIZE)
printAndClearItemList ();
}
if (page.size ()> 0)
printAndClearItemList ();
}

private void printAndClearItemList () {


formatter.format (halaman);
page.clear ();
}

private void addLineItemToPage (HourlyEmployee e) {


Item LineItem = LineItem baru ();
item.name = e.getName ();
item.hours = e.getTenthsWorked () / 10;

10. Ada perbedaan antara mengetahui bagaimana kode bekerja dan mengetahui apakah algoritma akan melakukan pekerjaan yang diminta.
Menjadi tidak yakin bahwa suatu algoritma sesuai sering merupakan fakta kehidupan. Tidak yakin apa yang kode Anda lakukan hanyalah kemalasan.

www.it-ebooks.info

Halaman 330

Umum 299

Listing 17-1 (lanjutan)


HourlyReporter.java
item.tenths = e.getTenthsWorked ()% 10;
page.add (item);
}

LineItem kelas publik {


nama String publik;
jam int publik;
persepuluhan publik;
}
}

Kode ini memiliki ketergantungan logis yang belum secara fisik. Bisakah Anda menemukannya? Itu
adalah PAGE_SIZE konstan . Mengapa HourlyReporter harus mengetahui ukuran halaman? Halaman
ukuran harus menjadi tanggung jawab HourlyReportFormatter .
Fakta bahwa PAGE_SIZE dideklarasikan di HourlyReporter mewakili kesalahan penempatan
tanggung jawab [G17] yang menyebabkan HourlyReporter menganggap bahwa ia mengetahui ukuran halaman
seharusnya menjadi. Asumsi semacam itu adalah ketergantungan logis. HourlyReporter tergantung pada
fakta bahwa HourlyReportFormatter dapat menangani ukuran halaman 55. Jika beberapa implementasi
HourlyReportFormatter tidak dapat menangani ukuran seperti itu, maka akan ada kesalahan.
Kami dapat secara fisik ketergantungan ini dengan membuat metode baru di HourlyReport-
Formatter bernama getMaxPageSize () . HourlyReporter kemudian akan memanggil fungsi itu daripada
menggunakan konstanta PAGE_SIZE .

G23: Memilih Polimorfisme untuk If / Else atau Switch / Case


Ini mungkin tampak saran aneh mengingat topik Bab 6. Setelah semua, dalam bab I
membuat titik bahwa pernyataan pergantian mungkin sesuai di bagian sistem
di mana menambahkan fungsi baru lebih mungkin daripada menambahkan tipe baru.
Pertama, kebanyakan orang menggunakan pernyataan switch karena itu adalah solusi brute force yang jelas,
bukan karena itu solusi yang tepat untuk situasi tersebut. Jadi heuristik ini ada di sini untuk mengingatkan kita
pertimbangkan polimorfisme sebelum menggunakan sakelar.
Kedua, kasus-kasus di mana fungsi lebih mudah berubah daripada jenis relatif jarang. Begitu
setiap pernyataan pergantian harus dicurigai.
Saya menggunakan aturan "O NE S WITCH " berikut: Mungkin tidak ada lebih dari satu status saklar
untuk jenis seleksi tertentu. Kasus dalam pernyataan switch itu harus membuat polymor-
objek phic yang menggantikan pernyataan switch lainnya di sistem.

G24: Ikuti Konvensi Standar


Setiap tim harus mengikuti standar pengkodean berdasarkan norma industri yang sama. Cod ini
ing standar harus menentukan hal-hal seperti di mana mendeklarasikan variabel instan; bagaimana cara menyebutkan namanya
kelas, metode, dan variabel; tempat memasang kawat gigi; dan seterusnya. Tim seharusnya tidak perlu
dokumen untuk menggambarkan konvensi ini karena kode mereka memberikan contoh.

https://translate.googleusercontent.com/translate_f 260/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 331

300 Bab 17: Bau dan Heuristik

Setiap orang di tim harus mengikuti konvensi ini. Ini artinya setiap tim
Anggota harus cukup dewasa untuk menyadari bahwa tidak masalah sedikit pun di mana Anda meletakkannya
kawat gigi selama Anda semua sepakat di mana menempatkannya.
Jika Anda ingin tahu konvensi apa yang saya ikuti, Anda akan melihatnya di refactored
kode pada Listing B-7 pada halaman 394, sampai Listing B-14.

G25: Ganti Bilangan Ajaib dengan Konstanta Bernama


Ini mungkin salah satu aturan tertua dalam pengembangan perangkat lunak. Saya ingat pernah membacanya di
akhir tahun enam puluhan dalam buku pengantar COBOL, FORTRAN, dan PL / 1. Secara umum itu buruk
ide untuk memiliki angka mentah dalam kode Anda. Anda harus menyembunyikannya di belakang konstanta yang dinamai.
Misalnya, angka 86.400 harus disembunyikan di belakang konstanta
SECONDS_PER_DAY . Jika Anda mencetak 55 baris per halaman, maka konstanta 55 harus disembunyikan.
ruang kerja di belakang LINES_PER_PAGE yang konstan .
Beberapa konstanta sangat mudah untuk dikenali sehingga tidak selalu membutuhkan konstanta bernama
bersembunyi di belakang selama mereka digunakan bersama dengan kode yang sangat jelas. Untuk
contoh:
ganda milesWalked = feetWalked / 5280.0;
int dailyPay = hourlyRate * 8;
lingkar ganda = radius * Math.PI * 2;

Apakah kita benar-benar membutuhkan konstanta FEET_PER_MILE , WORK_HOURS_PER_DAY , dan DUA di


contoh di atas? Jelas, kasus terakhir tidak masuk akal. Ada beberapa formula di mana
stant lebih baik ditulis sebagai angka mentah. Anda mungkin berdalih tentang
WORK_HOURS_PER_DAY kasus karena hukum atau konvensi dapat berubah. Di sisi lain
Sebaliknya, formula itu berbunyi sangat baik dengan angka 8 di dalamnya sehingga saya enggan menambahkan 17 ekstra
karakter menjadi beban pembaca. Dan dalam kasus FEET_PER_MILE , angka 5280 begitu
konstanta yang sangat terkenal dan sangat unik sehingga pembaca akan mengenalinya bahkan jika itu berdiri
sendirian di halaman tanpa konteks di sekitarnya.
Konstanta seperti 3.141592653589793 juga sangat terkenal dan mudah dikenali.
Namun, peluang kesalahan terlalu besar untuk membuat mereka mentah. Setiap kali seseorang melihat
3.1415927535890793, mereka tahu bahwa itu adalah p, dan karena itu mereka gagal untuk mencermati itu. (Apakah kamu
tangkap kesalahan satu digit?) Kami juga tidak ingin orang menggunakan 3.14, 3.14159, 3.142, dan seterusnya
sebagainya Oleh karena itu, adalah hal yang baik bahwa Math.PI telah ditentukan untuk kita.
Istilah "Angka Ajaib" tidak hanya berlaku untuk angka. Itu berlaku untuk token apa pun
yang memiliki nilai yang tidak menggambarkan diri. Sebagai contoh:

assertEquals (7777, Employee.find ("John Doe"). employeeNumber ());

Ada dua angka ajaib dalam pernyataan ini. Yang pertama jelas 7777
apa artinya itu mungkin tidak jelas. Angka ajaib kedua adalah "John Doe," dan lagi
niatnya tidak jelas.
Ternyata "John Doe" adalah nama karyawan # 7777 dalam data uji yang terkenal-
basis dibuat oleh tim kami. Semua orang di tim tahu itu ketika Anda terhubung ke ini

www.it-ebooks.info

Halaman 332

https://translate.googleusercontent.com/translate_f 261/365
3/12/2020 www.it-ebooks.info
Umum 301

database, itu akan memiliki beberapa karyawan yang sudah dimasak ke dalamnya dengan nilai-nilai terkenal
dan atribut. Ternyata juga "John Doe" mewakili satu-satunya karyawan yang bekerja per jam di
database uji itu. Jadi tes ini harus benar-benar dibaca:
assertEquals (
HOURLY_EMPLOYEE_ID,
Temukan Karyawan (HOURLY_EMPLOYEE_NAME) .employeeNumber ());

G26: Jadilah Tepat


Mengharapkan kecocokan pertama menjadi satu - satunya kecocokan dengan suatu query mungkin naif. Menggunakan floating
angka titik untuk mewakili mata uang hampir bersifat kriminal. Menghindari kunci dan / atau transaksi
manajemen karena Anda tidak berpikir pembaruan bersamaan kemungkinan besar malas. Mendeklarasikan
variabel untuk menjadi ArrayList ketika Daftar akan karena terlalu membatasi. Membuat semua
kemampuan yang dilindungi secara default tidak cukup menghambat.
Ketika Anda membuat keputusan dalam kode Anda, pastikan Anda membuatnya dengan tepat . Tahu kenapa
Anda telah membuatnya dan bagaimana Anda akan berurusan dengan pengecualian apa pun. Jangan malas tentang pra-
keputusan Anda. Jika Anda memutuskan untuk memanggil fungsi yang mungkin mengembalikan null , pastikan
Anda memeriksa nol . Jika Anda meminta apa yang menurut Anda merupakan satu-satunya catatan dalam database,
pastikan kode Anda memeriksa untuk memastikan tidak ada orang lain. Jika Anda perlu berurusan dengan
rency, gunakan bilangan bulat 11 dan berurusan dengan pembulatan yang tepat. Jika ada kemungkinan
pembaruan bersamaan, pastikan Anda menerapkan semacam mekanisme penguncian.
Ambiguitas dan ketidaktepatan dalam kode merupakan hasil dari ketidaksepakatan atau kemalasan.
Dalam kedua kasus mereka harus dihilangkan.

G27: Struktur atas Konvensi


Menegakkan keputusan desain dengan struktur di atas konvensi. Konvensi penamaan itu baik,
tetapi mereka lebih rendah dari struktur yang memaksa kepatuhan. Misalnya, beralih / kasing dengan
enumerasi dengan nama baik lebih rendah dari kelas dasar dengan metode abstrak. Tidak ada satupun
dipaksa untuk mengimplementasikan pergantian / pernyataan kasus dengan cara yang sama setiap kali; tapi dasar
kelas benar-benar menegakkan bahwa kelas konkret menerapkan semua metode abstrak.

G28: Meringkas Kondisional


Logika Boolean cukup sulit untuk dipahami tanpa harus melihatnya dalam konteks if
atau pernyataan sementara . Ekstrak fungsi yang menjelaskan maksud kondisional.
Sebagai contoh:
if (shouldBeDeleted (timer))

lebih disukai daripada


if (timer.hasExpired () &&! timer.isRecurrent ())

11. Atau lebih baik lagi, kelas Uang yang menggunakan bilangan bulat.

www.it-ebooks.info

Halaman 333

302 Bab 17: Bau dan Heuristik

G29: Hindari Persyaratan Negatif


Negatif hanya sedikit lebih sulit untuk dipahami daripada yang positif. Jadi, jika memungkinkan,
juga harus dinyatakan sebagai positif. Sebagai contoh:
if (buffer.shouldCompact ())

lebih disukai daripada


if (! buffer.shouldNotCompact ())

G30: Fungsi Seharusnya Melakukan Satu Hal


Sering tergoda untuk membuat fungsi yang memiliki banyak bagian yang melakukan serangkaian
operasi. Fungsi semacam ini melakukan lebih dari satu hal , dan harus diubah menjadi
banyak fungsi yang lebih kecil, yang masing-masing melakukan satu hal .

https://translate.googleusercontent.com/translate_f 262/365
3/12/2020 www.it-ebooks.info
Sebagai contoh:
void pay publik () {
untuk (Karyawan e: karyawan) {
if (e.isPayday ()) {
Pembayaran uang = e.calculatePay ();
e.deliverPay (bayar);
}
}
}

Bit kode ini melakukan tiga hal. Itu melingkupi semua karyawan, memeriksa untuk melihat apakah
setiap karyawan harus dibayar, dan kemudian membayar karyawan tersebut. Kode ini akan lebih baik
ditulis sebagai:
void pay publik () {
untuk (Karyawan e: karyawan)
payIfN Diperlukan (e);
}

private void payIfN Diperlukan (Karyawan e) {


if (e.isPayday ())
calculAndDeliverPay (e);
}

private void calculAndDeliverPay (Karyawan e) {


Pembayaran uang = e.calculatePay ();
e.deliverPay (bayar);
}

Masing-masing fungsi melakukan satu hal. (Lihat “Do One Thing” di halaman 35.)

G31: Kopling Temporal Tersembunyi


Kopling temporal sering diperlukan, tetapi Anda tidak harus menyembunyikan kopling. Struktur
argumen fungsi Anda sedemikian rupa sehingga urutan di mana mereka harus dipanggil jelas
ous. Pertimbangkan yang berikut ini:

www.it-ebooks.info

Halaman 334

Umum 303

MoogDiver kelas publik {


Gradien gradien;
Daftar <Spline> splines;

public void dive (String alasan) {


saturateGradient ();
reticulateSplines ();
diveForMoog (alasan);
}
...
}

Urutan ketiga fungsi itu penting. Anda harus menjenuhkan gradien sebelum Anda
dapat reticulate splines, dan hanya dengan begitu Anda bisa menyelam untuk moog. Sayangnya, kodenya
tidak memaksakan penggabungan temporal ini. Pemrogram lain dapat memanggil reticulate-
Splines sebelum saturateGradient dipanggil, yang mengarah ke UnsaturatedGradientException .
Solusi yang lebih baik adalah:
MoogDiver kelas publik {
Gradien gradien;
Daftar <Spline> splines;

public void dive (String alasan) {


Gradient gradient = saturateGradient ();
Daftar <Spline> splines = reticulateSplines (gradien);
diveForMoog (splines, reason);
}
...
}

Ini memperlihatkan kopling sementara dengan membuat brigade ember. Setiap fungsi menghasilkan a
akibatnya fungsi berikutnya perlu, sehingga tidak ada cara yang masuk akal untuk memanggilnya rusak.
Anda mungkin mengeluh bahwa ini meningkatkan kompleksitas fungsi, dan Anda akan menjadi
Baik. Tetapi kompleksitas sintaksis ekstra itu memperlihatkan kompleksitas temporal yang sebenarnya dari situasi tersebut.
Perhatikan bahwa saya membiarkan variabel instan di tempat. Saya kira mereka dibutuhkan oleh
metode di kelas. Meski begitu, saya ingin argumen yang ada membuat temporal
kopling eksplisit.

https://translate.googleusercontent.com/translate_f 263/365
3/12/2020 www.it-ebooks.info
G32: Jangan Sewenang-wenang
Punya alasan bagaimana Anda menyusun kode Anda, dan pastikan bahwa alasan itu komunikatif
ditentukan oleh struktur kode. Jika suatu struktur tampak sewenang-wenang, orang lain akan merasa diberdayakan
untuk mengubahnya. Jika struktur muncul secara konsisten di seluruh sistem, orang lain akan menggunakannya
dan menjaga konvensi. Sebagai contoh, saya baru saja menggabungkan perubahan ke FitNesse dan
menemukan bahwa salah satu penumpang kami telah melakukan ini:
kelas publik AliasLinkWidget meluas ParentWidget
{
public static class VariableExpandingWidgetRoot {
...

...
}

www.it-ebooks.info

Halaman 335

304 Bab 17: Bau dan Heuristik

Masalah dengan ini adalah bahwa VariableExpandingWidgetRoot tidak perlu


di dalam ruang lingkup AliasLinkWidget . Selain itu, kelas yang tidak terkait lainnya memanfaatkan
AliasLinkWidget.VariableExpandingWidgetRoot . Kelas-kelas ini tidak perlu diketahui
tentang AliasLinkWidget .
Mungkin programmer telah memasukkan VariableExpandingWidgetRoot ke dalamnya
AliasWidget sebagai masalah kenyamanan, atau mungkin dia pikir itu benar-benar perlu
masuk ke dalam AliasWidget . Apa pun alasannya, hasilnya berakhir dengan sewenang-wenang. Pub-
Kelas-kelas lic yang bukan utilitas dari beberapa kelas lain tidak boleh dibatasi di dalam kelas lain
kelas. Konvensi ini untuk mempublikasikannya di tingkat atas paket mereka.

G33: Meringkas Ketentuan Batas


Kondisi batas sulit untuk dilacak. Letakkan pemrosesan untuk mereka di satu tempat.
Jangan biarkan mereka membocorkan seluruh kode. Kami tidak ingin segerombolan +1 s dan -1 s tersebar di sini
dan kamu Pertimbangkan contoh sederhana ini dari FIT:
if (level + 1 <tags.length)
{
parts = Parse baru (isi, tag, level + 1, offset + endTag);
body = null;
}

Perhatikan bahwa level +1 muncul dua kali. Ini adalah kondisi batas yang harus dirangkum
lated dalam variabel bernama sesuatu seperti nextLevel .
int nextLevel = level + 1;
if (nextLevel <tags.length)
{
parts = Parse baru (isi, tag, nextLevel, offset + endTag);
body = null;
}

G34: Fungsi Seharusnya Menurunkan Hanya Satu Tingkat Abstraksi


Semua pernyataan dalam suatu fungsi harus ditulis pada tingkat abstraksi yang sama,
yang seharusnya satu tingkat di bawah operasi yang dijelaskan oleh nama fungsi. Ini
mungkin yang paling sulit dari heuristik ini untuk ditafsirkan dan diikuti. Padahal idenya jelas
cukup, manusia terlalu bagus dalam memadukan tingkat abstraksi yang mulus. Mempertimbangkan,
misalnya, kode berikut diambil dari FitNesse:
public String render () melempar Exception
{
StringBuffer html = new StringBuffer ("<hr");
if (size> 0)
html.append ("size = \" "). append (size + 1) .append (" \ "");
html.append (">");

return html.toString ();


}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 264/365
3/12/2020 www.it-ebooks.info

Halaman 336

Umum 305

Belajar sebentar dan Anda bisa melihat apa yang terjadi. Fungsi ini membangun HTML
tag yang menggambarkan aturan horizontal di seluruh halaman. Tinggi aturan itu ditentukan dalam
variabel ukuran .
Sekarang lihat lagi. Metode ini mencampur setidaknya dua tingkat abstraksi. Yang pertama adalah
gagasan bahwa aturan horizontal memiliki ukuran. Yang kedua adalah sintaks dari tag HR itu sendiri.
Kode ini berasal dari HruleWidget modul di FitNesse. Modul ini mendeteksi deretan
empat atau lebih tanda hubung dan mengubahnya menjadi tag HR yang sesuai. Semakin banyak tanda hubung, maka
lebih besar ukurannya.
Saya refactored sedikit kode ini sebagai berikut. Perhatikan bahwa saya mengubah nama bidang ukuran
untuk mencerminkan tujuan sebenarnya. Itu memegang jumlah tanda hubung ekstra.
public String render () melempar Exception
{
HtmlTag jam = HtmlTag baru ("jam");
jika (extraDash> 0)
hr.addAttribute ("size", hrSize (extraDash));
return hr.html ();
}

private String hrSize (tinggi int)


{
int hrSize = tinggi + 1;
return String.format ("% d", hrSize);
}

Perubahan ini memisahkan dua level abstraksi dengan baik. Fungsi render cukup
menyusun tag HR, tanpa harus tahu apa-apa tentang sintaks HTML dari tag itu.
The HtmlTag modul mengurus semua masalah sintaks jahat.
Memang, dengan melakukan perubahan ini saya menangkap kesalahan yang halus. Kode asli tidak dimasukkan
slash penutup pada tag HR, sebagai standar XHTML akan memilikinya. (Dengan kata lain, itu
dipancarkan <hr> alih-alih <jam /> .) Modul HtmlTag telah diubah agar sesuai
XHTML dulu.
Memisahkan tingkat abstraksi adalah salah satu fungsi terpenting dari refactor-
dan itu salah satu yang paling sulit dilakukan dengan baik. Sebagai contoh, lihat kode di bawah ini. Ini
adalah upaya pertama saya untuk memisahkan tingkat abstraksi di HruleWidget.render
Metode .

public String render () melempar Exception


{
HtmlTag jam = HtmlTag baru ("jam");
if (size> 0) {
hr.addAttribute ("size", "" + (size + 1));
}
return hr.html ();
}

Tujuan saya, pada titik ini, adalah untuk menciptakan pemisahan yang diperlukan dan mendapatkan tes untuk lulus.
Saya mencapai tujuan itu dengan mudah, tetapi hasilnya adalah fungsi yang masih memiliki level campuran
abstraksi. Dalam hal ini level campuran adalah konstruksi dari tag HR dan

www.it-ebooks.info

Halaman 337

306 Bab 17: Bau dan Heuristik

interpretasi dan pemformatan variabel ukuran . Ini menunjukkan bahwa ketika Anda istirahat a

https://translate.googleusercontent.com/translate_f 265/365
3/12/2020 www.it-ebooks.info
berfungsi
dikaburkandioleh
sepanjang garis
struktur abstraksi, Anda sering menemukan garis abstraksi baru
sebelumnya.

G35: Simpan Data yang Dapat Dikonfigurasi di Tingkat Tinggi


Jika Anda memiliki konstanta seperti nilai default atau konfigurasi yang diketahui dan diharapkan
pada abstraksi tingkat tinggi, jangan dimakamkan di fungsi tingkat rendah. Paparkan itu sebagai argumen
ment ke fungsi tingkat rendah yang dipanggil dari fungsi tingkat tinggi. Pertimbangkan yang berikut ini
kode dari FitNesse:
public static void main (String [] args) melempar Exception
{
Argumen argumen = parseCommandLine (args);
...
}

Argumen kelas publik


{
String publik akhir statis DEFAULT_PATH = ".";
public String akhir statis DEFAULT_ROOT = "FitNesseRoot";
DEFAULT_PORT int static final publik = 80;
DEFAULT_VERSION_DAYS public int static final = 14;
...
}

Argumen baris perintah diuraikan dalam baris yang dapat dieksekusi pertama dari FitNesse. Itu
nilai default dari argumen tersebut ditentukan di bagian atas kelas Argument . Kamu tidak
harus mencari tingkat rendah dari sistem untuk pernyataan seperti ini:
if (arguments.port == 0) // gunakan 80 secara default

Konstanta konfigurasi berada pada level yang sangat tinggi dan mudah diubah. Mereka mendapatkannya
diturunkan ke seluruh aplikasi. Level aplikasi yang lebih rendah tidak dimiliki
nilai-nilai konstanta ini.

G36: Hindari Navigasi Transitif


Secara umum kami tidak ingin satu modul tahu banyak tentang kolaboratornya. Lebih spesifik
Secara spesifik, jika A berkolaborasi dengan B , dan B berkolaborasi dengan C , kami tidak ingin modul yang digunakan
Sebuah tahu tentang C . (Misalnya, kami tidak ingin a.getB (). GetC (). DoSomething ();. )
Ini kadang-kadang disebut Hukum Demeter. Programmer Pragmatis menyebutnya
"Menulis Kode Pemalu." 12 Dalam kedua kasus itu turun untuk memastikan bahwa modul tahu
hanya tentang kolaborator langsung mereka dan tidak tahu peta navigasi keseluruhan
sistem.
Jika banyak modul menggunakan beberapa bentuk pernyataan a.getB (). GetC () , maka itu akan menjadi
sulit untuk mengubah desain dan arsitektur untuk menempatkan sebuah Q antara B dan C . Kamu akan

12. [PRAG], hlm. 138.

www.it-ebooks.info

Halaman 338

Jawa 307

harus menemukan setiap instance dari a.getB (). getC () dan mengonversinya menjadi a.getB (). getQ (). getC () .
Beginilah arsitektur menjadi kaku. Terlalu banyak modul yang tahu terlalu banyak tentang
Arsitektur.
Sebaliknya, kami ingin kolaborator langsung kami menawarkan semua layanan yang kami butuhkan. Kita
seharusnya tidak harus berkeliaran melalui objek grafik sistem, mencari metode kita
ingin menelepon. Sebaliknya, kita seharusnya bisa mengatakan:
myCollaborator.doSomething ().

Jawa
J1: Hindari Daftar Impor Panjang dengan Menggunakan Wildcard
Jika Anda menggunakan dua kelas atau lebih dari satu paket, maka impor seluruh paket dengan
paket impor. *;

Daftar panjang impor menakutkan bagi pembaca. Kami tidak ingin mengacaukan bagian atas kami
modul dengan 80 jalur impor. Sebaliknya kami ingin impor menjadi pernyataan singkat
tentang paket mana kami berkolaborasi.

https://translate.googleusercontent.com/translate_f 266/365
3/12/2020 www.it-ebooks.info
Impor khusus merupakan dependensi yang sulit, sedangkan impor wildcard tidak. Jika Anda
mengimpor kelas, maka kelas itu harus ada. Tetapi jika Anda mengimpor paket dengan
kartu, tidak ada kelas tertentu yang perlu ada. Pernyataan impor hanya menambahkan paket ke
jalur pencarian saat mencari nama. Jadi tidak ada ketergantungan sebenarnya yang diciptakan oleh itu
impor, dan oleh karena itu berfungsi untuk menjaga modul kami kurang ditambah.
Ada kalanya daftar panjang impor tertentu dapat bermanfaat. Misalnya, jika
Anda berurusan dengan kode lawas dan Anda ingin mengetahui kelas apa yang perlu Anda bangun
mengejek dan bertopik untuk, Anda dapat berjalan di daftar impor khusus untuk mengetahui yang sebenarnya
nama-nama yang memenuhi syarat dari semua kelas tersebut dan kemudian menempatkan rintisan yang sesuai di tempat. Namun,
penggunaan ini untuk impor khusus sangat jarang. Selain itu, sebagian besar IDE modern akan memungkinkan Anda
untuk mengkonversi impor wildcard ke daftar impor khusus dengan satu perintah. Begitu
bahkan dalam kasus lama lebih baik mengimpor wildcard.
Impor wildcard kadang-kadang dapat menyebabkan konflik nama dan ambiguitas. Dua kelas
dengan nama yang sama, tetapi dalam paket yang berbeda, perlu diimpor secara khusus, atau di
Paling tidak memenuhi syarat khusus ketika digunakan. Ini bisa menjadi gangguan tetapi cukup jarang digunakan
impor wildcard umumnya masih lebih baik daripada impor khusus.

J2: Jangan Mewarisi Konstanta


Saya telah melihat ini beberapa kali dan itu selalu membuat saya meringis. Seorang programmer menempatkan beberapa
konstanta dalam antarmuka dan kemudian mendapatkan akses ke konstanta tersebut dengan mewarisi antar
wajah. Lihatlah kode berikut:
kelas publik HourlyEmployee memperpanjang Employee {
persepuluh pribadi intBekerja;
private double hourlyRate;

www.it-ebooks.info

Halaman 339

308 Bab 17: Bau dan Heuristik

menghitung Uang publik () {


int straightTime = Math.min (tenthsWorked, TENTHS_PER_WEEK);
int overTime = tenthsWorked - straightTime;
kembalikan Uang baru (
hourlyRate * (sepersepuluh kerjaan + OVERTIME_RATE * lembur)
);
}
...
}

Mana konstanta TENTHS_PER_WEEK dan OVERTIME_RATE berasal? Mereka mungkin melakukannya


berasal dari kelas Karyawan ; jadi mari kita lihat itu:

public abstrak class Pegawai mengimplementasikan PayrollConstants {


boolean abstrak publik isPayday ();
public abstract Money calculPay ();
public abstrak void deliverPay (Pembayaran uang);
}

Tidak, tidak di sana. Tapi lalu dimana? Perhatikan baik-baik Karyawan kelas . Ini mengimplementasikan
Konstanta Penggajian .

antarmuka publik PayrollConstants {


TENTHS_PER_WEEK public int static final = 400;
double final public static OVERTIME_RATE = 1.5;
}

Ini adalah praktik yang mengerikan! Konstanta disembunyikan di bagian atas hierarki warisan.
Ya! Jangan gunakan warisan sebagai cara untuk menipu aturan pelingkupan bahasa. Gunakan statis
impor saja.
impor PayrollConstants statis. *;

kelas publik HourlyEmployee memperpanjang Employee {


persepuluh pribadi intBekerja;
private double hourlyRate;

menghitung Uang publik () {


int straightTime = Math.min (tenthsWorked, TENTHS_PER_WEEK);
int overTime = tenthsWorked - straightTime;
kembalikan Uang baru (
hourlyRate * (sepersepuluh kerjaan + OVERTIME_RATE * lembur)
);
}
...
}

https://translate.googleusercontent.com/translate_f 267/365
3/12/2020 www.it-ebooks.info
J3: Konstanta versus Enum
Sekarang enum s telah ditambahkan ke bahasa (Java 5), gunakan! Jangan terus menggunakan
trik lama public int static final . Arti int bisa hilang. Arti dari
enum s tidak bisa, karena mereka milik enumerasi yang dinamai.
Terlebih lagi, pelajari sintaks untuk enum s dengan hati-hati. Mereka dapat memiliki metode dan bidang.
Ini membuat mereka alat yang sangat kuat yang memungkinkan ekspresi dan fleksibilitas lebih banyak daripada
int s. Pertimbangkan variasi ini pada kode penggajian:

www.it-ebooks.info

Halaman 340

Nama 309

kelas publik HourlyEmployee memperpanjang Employee {


persepuluh pribadi intBekerja;
Nilai HourlyPayGrade;

menghitung Uang publik () {


int straightTime = Math.min (tenthsWorked, TENTHS_PER_WEEK);
int overTime = tenthsWorked - straightTime;
kembalikan Uang baru (
grade.rate () * (tenthsWorked + OVERTIME_RATE * overTime)
);
}
...
}

publik en HourlyPayGrade {
MAGANG {
kurs ganda publik () {
return 1.0;
}
},
LEUTENANT_JOURNEYMAN {
kurs ganda publik () {
return 1.2;
}
},
JOURNEYMAN {
kurs ganda publik () {
return 1.5;
}
},
MASTER {
kurs ganda publik () {
return 2.0;
}
};

tingkat ganda abstrak publik ();


}

Nama

N1: Pilih Nama Deskriptif


Jangan terlalu cepat memilih nama. Pastikan namanya deskriptif. Ingat bahwa
makna cenderung melayang ketika perangkat lunak berevolusi, sehingga sering mengevaluasi kembali kesesuaian
nama yang Anda pilih.
Ini bukan hanya rekomendasi "merasa-enak". Nama dalam perangkat lunak adalah 90 persen
apa yang membuat perangkat lunak dapat dibaca. Anda perlu meluangkan waktu untuk memilihnya dengan bijak dan menyimpannya
mereka relevan. Nama terlalu penting untuk diperlakukan secara sembarangan.
Pertimbangkan kode di bawah ini. Apa fungsinya? Jika saya tunjukkan kode dengan dipilih
nama, itu akan sangat masuk akal bagi Anda, tapi seperti ini itu hanya hodge-podge simbol
dan angka ajaib.

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 268/365
3/12/2020 www.it-ebooks.info

Halaman 341

310 Bab 17: Bau dan Heuristik

public int x () {
int q = 0;
int z = 0;
untuk (int kk = 0; kk <10; kk ++) {
if (l [z] == 10)
{
q + = 10 + (l [z + 1] + l [z + 2]);
z + = 1;
}
lain jika (l [z] + l [z + 1] == 10)
{
q + = 10 + l [z + 2];
z + = 2;
} lain {
q + = l [z] + l [z + 1];
z + = 2;
}
}
return q;
}

Berikut adalah kode cara menulisnya. Cuplikan ini sebenarnya kurang lengkap
dari yang di atas. Namun Anda dapat langsung menyimpulkan apa yang ingin dilakukan, dan Anda bisa
sangat mungkin menulis fungsi yang hilang berdasarkan makna yang disimpulkan. Angka ajaib-
Ini bukan lagi keajaiban, dan struktur algoritmenya sangat deskriptif.
skor int publik () {
skor int = 0;
bingkai int = 0;
untuk (int frameNumber = 0; frameNumber <10; frameNumber ++) {
if (isStrike (frame)) {
skor + = 10 + nextTwoBallsForStrike (bingkai);
bingkai + = 1;
} lain jika (isSpare (frame)) {
skor + = 10 + nextBallForSpare (bingkai);
bingkai + = 2;
} lain {
skor + = twoBallsInFrame (bingkai);
bingkai + = 2;
}
}
skor kembali;
}

Kekuatan dari nama yang dipilih dengan hati-hati adalah bahwa mereka membebani struktur kode
dengan deskripsi. Kelebihan itu menetapkan harapan pembaca tentang apa yang orang lain miliki
fungsi dalam modul lakukan. Anda dapat menyimpulkan implementasi isStrike () dengan melihat
kode di atas. Ketika Anda membaca metode isStrike , itu akan menjadi “cukup banyak apa yang Anda
diharapkan. " 13
private boolean isStrike (int frame) {
return rolls [frame] == 10;
}

13. Lihat kutipan Ward Cunningham di halaman 11.

www.it-ebooks.info

Halaman 342

Nama 311

N2: Pilih Nama di Tingkat Abstraksi yang Tepat


Jangan memilih nama yang mengkomunikasikan implementasi; pilih nama yang mencerminkan level
abstraksi dari kelas atau fungsi tempat Anda bekerja. Ini sulit dilakukan. Sekali lagi, orang-orang
terlalu baik dalam pencampuran tingkat abstraksi. Setiap kali Anda melewati Anda
kode, Anda mungkin akan menemukan beberapa variabel yang dinamai pada tingkat yang terlalu rendah. Kamu harus mengambil

https://translate.googleusercontent.com/translate_f 269/365
3/12/2020 www.it-ebooks.info
kesempatan untuk mengubah nama-nama itu ketika Anda menemukannya. Membuat kode dapat dibaca
membutuhkan dedikasi untuk perbaikan berkelanjutan. Pertimbangkan antarmuka Modem di bawah ini:
Modem antarmuka publik {
panggilan boolean (String phoneNumber);
boolean disconnect ();
boolean send (char c);
char recv ();
String getConnectedPhoneNumber ();
}

Pada awalnya ini terlihat baik-baik saja. Semua fungsi tampaknya sesuai. Memang untuk banyak aplikasi
mereka. Tetapi sekarang pertimbangkan sebuah aplikasi di mana beberapa modem tidak terhubung
panggilan. Sebaliknya mereka terhubung secara permanen dengan menyatukan mereka secara susah payah (pikirkan
modem kabel yang menyediakan akses Internet ke sebagian besar rumah saat ini). Mungkin beberapa
terhubung dengan mengirim nomor port ke switch melalui koneksi USB. Jelas gagasan itu
nomor telepon berada pada tingkat abstraksi yang salah. Strategi penamaan yang lebih baik untuk ini
skenario mungkin:
Modem antarmuka publik {
boolean connect (String connectionLocator);
boolean disconnect ();
boolean send (char c);
char recv ();
String getConnectedLocator ();
}

Sekarang nama tidak membuat komitmen tentang nomor telepon. Mereka masih bisa digunakan
untuk nomor telepon, atau mereka dapat digunakan untuk segala jenis strategi koneksi lainnya.

N3: Gunakan Nomenklatur Standar Bila Mungkin


Nama lebih mudah dimengerti jika didasarkan pada konvensi atau penggunaan yang ada. Untuk ujian-
hai, jika Anda menggunakan pola D ECORATOR , Anda harus menggunakan kata Penghias dalam nama
dari kelas dekorasi. Misalnya, AutoHangupModemDecorator mungkin nama a
kelas yang menghiasi Modem dengan kemampuan untuk menutup telepon secara otomatis pada akhir sesi.
Pola hanyalah satu jenis standar. Di Jawa, misalnya, fungsi yang dikonversi
objek ke representasi string sering dinamai toString . Lebih baik mengikuti
seperti ini daripada menciptakan milik Anda.
Tim akan sering menciptakan sistem nama standar mereka sendiri untuk proyek tertentu.
Eric Evans menyebut ini sebagai bahasa di mana - mana untuk proyek ini. 14 Kode Anda harus digunakan

14. [DDD].

www.it-ebooks.info

Halaman 343

312 Bab 17: Bau dan Heuristik

istilah dari bahasa ini secara luas. Singkatnya, semakin banyak Anda dapat menggunakan nama
kelebihan dengan makna khusus yang relevan dengan proyek Anda, semakin mudah untuk itu
pembaca untuk mengetahui apa yang dibicarakan kode Anda.

N4: Nama yang Tidak Mendua


Pilih nama yang membuat cara kerja suatu fungsi atau variabel menjadi tidak ambigu. Mempertimbangkan
contoh ini dari FitNesse:
private String doRename () melempar Exception
{
if (refactorReferences)
renameReferences ();
renamePage ();

pathToRename.removeNameFromEnd ();
pathToRename.addNameToEnd (newName);
kembalikan PathParser.render (pathToRename);
}

Nama fungsi ini tidak mengatakan fungsi apa yang dilakukan kecuali secara luas dan tidak jelas
ketentuan Ini ditekankan oleh fakta bahwa ada fungsi bernama renamePage di dalam
fungsi bernama doRename ! Apa yang diceritakan namanya tentang perbedaan antara
dua fungsi? Tidak ada.
Nama yang lebih baik untuk fungsi itu adalah renamePageAndOptionallyAllReferences . Ini mungkin
tampak panjang, dan memang begitu, tetapi hanya dipanggil dari satu tempat di modul, jadi itu jelas
nilai melebihi panjangnya.

https://translate.googleusercontent.com/translate_f 270/365
3/12/2020 www.it-ebooks.info

N5: Gunakan Nama Panjang untuk Lingkup Panjang


Panjang nama harus terkait dengan panjang ruang lingkup. Anda dapat menggunakan sangat singkat
nama variabel untuk cakupan kecil, tetapi untuk cakupan besar Anda harus menggunakan nama yang lebih panjang.
Nama variabel seperti i dan j baik-baik saja jika cakupannya panjangnya lima baris. Pertimbangkan ini
cuplikan dari "Game Bowling" standar lama:
private void rollMany (int n, int pins)
{
untuk (int i = 0; i <n; i ++)
g.roll (pin);
}

Ini sangat jelas dan akan dikaburkan jika variabel saya diganti dengan
hal yang mengganggu seperti rollCount . Di sisi lain, variabel dan fungsi dengan nama pendek
kehilangan arti jarak jauh. Jadi semakin panjang cakupan namanya, semakin panjang dan
lebih tepatnya nama seharusnya.

N6: Hindari Penyandian


Nama tidak boleh dikodekan dengan jenis atau informasi ruang lingkup. Awalan seperti m_ atau f
tidak berguna di lingkungan saat ini. Juga proyek dan / atau pengkodean subsistem seperti

www.it-ebooks.info

Halaman 344

Tes 313

vis_ (untuk sistem pencitraan visual) mengganggu dan mubazir. Sekali lagi, lingkungan saat ini
KASIH menyediakan semua informasi itu tanpa harus memotong nama. Simpan milikmu
nama bebas polusi Hongaria.

N7: Nama Harus Menjelaskan Efek Samping


Nama harus menggambarkan semua fungsi atau variabel, kelas, atau kelas. Jangan bersembunyi
efek samping dengan nama. Jangan gunakan kata kerja sederhana untuk menggambarkan fungsi yang lebih berfungsi
dari sekedar tindakan sederhana itu. Misalnya, pertimbangkan kode ini dari TestNG:
getOos ObjectOutputStream publik () melempar IOException {
if (m_oos == null) {
m_oos = ObjectOutputStream baru (m_socket.getOutputStream ());
}
kembalikan m_oos;
}

Fungsi ini sedikit lebih banyak daripada “oos”; itu menciptakan "oos" jika belum
sudah makan. Dengan demikian, nama yang lebih baik adalah createOrReturnOos .

Tes

T1: Tes Tidak Cukup


Berapa banyak tes yang harus di test suite? Sayangnya, metrik banyak digunakan programmer
adalah "Itu sepertinya cukup." Test suite harus menguji semua yang mungkin bisa pecah.
Tes tidak mencukupi selama ada kondisi yang belum dieksplorasi oleh
tes atau perhitungan yang belum divalidasi.

T2: Gunakan Alat Cakupan!


Alat pertanggungan melaporkan kesenjangan dalam strategi pengujian Anda. Mereka membuatnya mudah untuk menemukan modul,
kelas, dan fungsi yang tidak cukup diuji. Sebagian besar IDE memberi Anda indikasi visual,
menandai garis-garis yang tercakup dalam warna hijau dan garis-garis yang tidak tertutup warna merah. Ini membuatnya
cepat dan mudah ditemukan jika atau menangkap pernyataan yang mayatnya belum diperiksa.

T3: Jangan Lewatkan Tes Sepele


Mereka mudah ditulis dan nilai dokumenter mereka lebih tinggi daripada biaya produksi
mereka.

T4: Tes yang Diabaikan Adalah Pertanyaan tentang Ambiguitas

https://translate.googleusercontent.com/translate_f 271/365
3/12/2020 www.it-ebooks.info
Terkadang kita tidak yakin tentang detail perilaku karena persyaratannya
tidak jelas. Kami dapat mengungkapkan pertanyaan kami tentang persyaratan sebagai tes yang dikomentari
keluar, atau sebagai tes yang dijelaskan dengan @Ignore . Yang Anda pilih tergantung pada apakah
ambiguitas adalah tentang sesuatu yang akan dikompilasi atau tidak.

www.it-ebooks.info

Halaman 345

314 Bab 17: Bau dan Heuristik

T5: Uji Kondisi Batas


Berhati-hatilah untuk menguji kondisi batas. Kita sering mendapatkan bagian tengah dari suatu algoritma
benar tetapi salah menilai batas.

T6: Menguji Bug Dekat Secara Lengkap


Bug cenderung berkumpul. Saat Anda menemukan bug dalam suatu fungsi, sebaiknya lakukan secara lengkap
menguji fungsi itu. Anda mungkin akan menemukan bahwa bug itu tidak sendirian.

T7: Pola Kegagalan Mengungkap


Terkadang Anda dapat mendiagnosis masalah dengan menemukan pola dalam cara kasus uji gagal.
Ini adalah argumen lain untuk membuat kasus uji selengkap mungkin. Tes lengkap
case, dipesan dengan cara yang masuk akal, memaparkan pola.
Sebagai contoh sederhana, anggaplah Anda memperhatikan bahwa semua tes dengan input lebih besar dari lima
karakter gagal? Atau bagaimana jika ada tes yang lulus angka negatif ke argumen kedua
ment fungsi gagal? Terkadang hanya melihat pola merah dan hijau pada tes
laporan sudah cukup untuk memicu "Aha!" yang mengarah ke solusi. Lihat kembali halaman 267 ke
lihat contoh yang menarik dari ini dalam contoh SerialDate .

T8: Pola Cakupan Tes Dapat Diungkap


Melihat kode yang dijalankan atau tidak oleh tes yang lulus memberikan petunjuk mengapa
tes gagal gagal.

T9: Tes Harus Cepat


Tes lambat adalah tes yang tidak akan berjalan. Ketika segalanya menjadi ketat, itu adalah tes lambat yang akan dilakukan
turun dari suite. Jadi, lakukan apa yang harus Anda lakukan agar tes Anda tetap cepat.

Kesimpulan
Daftar heuristik dan bau ini hampir tidak bisa dikatakan lengkap. Memang saya tidak yakin
bahwa daftar tersebut dapat pernah lengkap. Tapi mungkin kelengkapan seharusnya bukan tujuan,
karena apa yang dilakukan daftar ini adalah menyiratkan sistem nilai.
Memang, sistem nilai telah menjadi tujuan, dan topik, buku ini. Kode bersih adalah
tidak ditulis dengan mengikuti seperangkat aturan. Anda tidak menjadi pengrajin perangkat lunak dengan belajar-
Daftar heuristik. Profesionalisme dan keahlian berasal dari nilai-nilai yang mendorong
disiplin ilmu.

www.it-ebooks.info

Halaman 346

https://translate.googleusercontent.com/translate_f 272/365
3/12/2020 www.it-ebooks.info

Bibliografi 315

Bibliografi
[Refactoring]: Refactoring: Meningkatkan Desain Kode yang Ada , Martin Fowler et al.,
Addison-Wesley, 1999.

[PRAG]: Programmer Pragmatis , Andrew Hunt, Dave Thomas, Addison-Wesley,


2000

[GOF]: Pola Desain: Elemen-elemen Perangkat Lunak Berorientasi Objek yang Dapat Digunakan Kembali , Gamma et al.,
Addison-Wesley, 1996.

[Beck97]: Pola Praktek Terbaik Smalltalk , Kent Beck, Prentice Hall, 1997.

[Beck07]: Pola Implementasi , Kent Beck, Addison-Wesley, 2008.

[PPP]: Pengembangan Perangkat Lunak Agile: Prinsip, Pola, dan Praktik , Robert C. Martin,
Prentice Hall, 2002.

[DDD]: Desain Berbasis Domain , Eric Evans, Addison-Wesley, 2003.

www.it-ebooks.info

Halaman 347

halaman ini sengaja dibiarkan kosong

https://translate.googleusercontent.com/translate_f 273/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 348

Lampiran A
Konkurensi II
oleh Brett L. Schuchert

Apendiks ini mendukung dan memperkuat bab Concurrency di halaman 177. Ada tertulis
sebagai serangkaian topik independen dan Anda umumnya dapat membacanya dalam urutan apa pun. Ada
beberapa duplikasi antar bagian untuk memungkinkan pembacaan tersebut.

Contoh Klien / Server


https://translate.googleusercontent.com/translate_f 274/365
3/12/2020 www.it-ebooks.info

Bayangkan aplikasi klien / server sederhana. Server duduk dan menunggu mendengarkan pada soket
klien untuk terhubung. Klien menghubungkan dan mengirim permintaan.

Server
Ini adalah versi sederhana dari aplikasi server. Sumber lengkap untuk contoh ini tersedia-
dapat dimulai pada halaman 343, Client / Server Nonthreaded .
ServerSocket serverSocket = ServerSocket baru (8009);

while (keepProcessing) {
coba {
Soket soket = serverSocket.accept ();
proses (soket);
} catch (Exception e) {
menangani (e);
}
}

317

www.it-ebooks.info

Halaman 349

318 Lampiran A: Concurrency II

Aplikasi sederhana ini menunggu koneksi, memproses pesan masuk, dan kemudian
lagi menunggu permintaan klien berikutnya. Berikut adalah kode klien yang menghubungkan ini
server:
private void connectSendReceive (int i) {
coba {
Socket = Socket baru ("localhost", PORT);
MessageUtils.sendMessage (socket, Integer.toString (i));
MessageUtils.getMessage (socket);
socket.close ();
} catch (Exception e) {
e.printStackTrace ();
}

Seberapa baik kinerja pasangan klien / server ini? Bagaimana kita dapat secara formal menggambarkan kinerja itu?
mance Berikut adalah tes yang menyatakan bahwa kinerja "dapat diterima":

@Test (batas waktu = 10000)


public void shouldRunInUnder10Seconds () melempar Exception {
Utas [] utas = createThreads ();
startAllThreadsw (utas);
waitForAllThreadsToFinish (utas);
}

Pengaturan ditinggalkan untuk menjaga contoh sederhana (lihat " ClientTest.java " di halaman 344). Ini
tes menegaskan bahwa itu harus selesai dalam 10.000 milidetik.
Ini adalah contoh klasik memvalidasi throughput suatu sistem. Sistem ini seharusnya
menyelesaikan serangkaian permintaan klien dalam sepuluh detik. Selama server dapat memproses masing-masing
permintaan klien individu dalam waktu, tes akan berlalu.
Apa yang terjadi jika tes gagal? Pendek mengembangkan semacam loop polling acara,
tidak banyak yang dapat dilakukan dalam satu utas yang akan membuat kode ini lebih cepat. Akan
menggunakan beberapa utas menyelesaikan masalah? Mungkin saja, tetapi kita perlu tahu di mana waktunya
dihabiskan. Ada dua kemungkinan:

• I / O — menggunakan soket, menghubungkan ke database, menunggu pertukaran memori virtual,


dan seterusnya.
• Prosesor — perhitungan numerik, pemrosesan ekspresi reguler, pengumpulan sampah,
dan seterusnya.

Sistem biasanya memiliki beberapa dari masing-masing, tetapi untuk operasi yang diberikan cenderung mendominasi. Jika
kode terikat prosesor, lebih banyak pemrosesan perangkat keras dapat meningkatkan throughput, membuat
tes lulus kami. Tetapi hanya ada begitu banyak siklus CPU yang tersedia, jadi tambahkan utas ke a
masalah yang terikat prosesor tidak akan membuatnya berjalan lebih cepat.
Di sisi lain, jika proses I / O terikat, maka konkurensi dapat meningkatkan efisiensi.
efisiensi. Ketika satu bagian dari sistem menunggu I / O, bagian lain dapat menggunakan waktu tunggu itu
untuk memproses sesuatu yang lain, membuat penggunaan CPU yang tersedia menjadi lebih efektif.

https://translate.googleusercontent.com/translate_f 275/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 350

Contoh Klien / Server 319

Menambahkan Threading
Asumsikan untuk saat itu bahwa tes kinerja gagal. Bagaimana kita dapat meningkatkan
dimasukkan sehingga tes kinerja lulus? Jika metode proses server terikat I / O,
maka di sini adalah salah satu cara untuk membuat server menggunakan utas (cukup ubah processMessage ):

proses batal (final Socket socket) {


if (socket == null)
kembali;

Runnable clientHandler = Runnable baru () {


public void run () {
coba {
Pesan string = MessageUtils.getMessage (socket);
MessageUtils.sendMessage (soket, "Diproses:" + pesan);
closeIgnoringException (socket);
} catch (Exception e) {
e.printStackTrace ();
}
}
};

ClientConnection = Utas baru (clientHandler);


clientConnection.start ();
}

Asumsikan bahwa perubahan ini menyebabkan tes lulus; 1 kode selesai, benar?

Pengamatan Server
Server yang diperbarui menyelesaikan tes dengan sukses hanya dalam satu detik. Sayangnya,
solusi ini agak naif dan memperkenalkan beberapa masalah baru.
Berapa banyak utas yang dapat dibuat server kami? Kode tidak menetapkan batas, jadi kita bisa
layak mencapai batas yang diberlakukan oleh Java Virtual Machine (JVM). Untuk banyak sistem sederhana
ini mungkin sudah cukup. Tetapi bagaimana jika sistem dimaksudkan untuk mendukung banyak pengguna di publik
bersih? Jika terlalu banyak pengguna terhubung pada saat yang sama, sistem mungkin terhenti.
Tetapi sisihkan masalah perilaku untuk sementara waktu. Solusi yang ditampilkan memiliki masalah
kebersihan dan strukturnya. Berapa banyak tanggung jawab yang dimiliki kode server?

• Manajemen koneksi soket


• Pemrosesan klien
• Kebijakan Threading
• Kebijakan mematikan server

Sayangnya, semua tanggung jawab ini hidup dalam fungsi proses . Selain itu,
kode melintasi berbagai tingkatan abstraksi. Jadi, sekecil apa pun fungsi prosesnya, itu
perlu partisi ulang.

1. Anda dapat memverifikasi itu sendiri dengan mencoba kode sebelum dan sesudah. Tinjau kode yang tidak dibaca mulai dari halaman 343.
Tinjau kode berulir mulai dari halaman 346.

www.it-ebooks.info

Halaman 351

https://translate.googleusercontent.com/translate_f 276/365
3/12/2020 www.it-ebooks.info
320 Lampiran A: Concurrency II

Server memiliki beberapa alasan untuk berubah; karena itu melanggar Tanggung Jawab Tunggal
Prinsip. Agar sistem bersamaan tetap bersih, manajemen utas harus dijaga agar beberapa,
tempat yang terkontrol dengan baik. Terlebih lagi, kode apa pun yang mengelola utas tidak boleh melakukan apa pun
selain manajemen utas. Mengapa? Jika karena alasan lain selain itu pelacakan
masalah mata uang cukup sulit tanpa harus melepaskan masalah nonkonversi lainnya di
waktu yang sama.
Jika kita membuat kelas terpisah untuk masing-masing tanggung jawab yang tercantum di atas, termasuk
tanggung jawab manajemen utas, maka ketika kita mengubah strategi manajemen utas,
perubahan akan berdampak pada keseluruhan kode kurang dan tidak akan mencemari tanggung jawab lainnya. Ini
juga membuatnya lebih mudah untuk menguji semua tanggung jawab lainnya tanpa harus khawatir
tentang threading. Berikut adalah versi terbaru yang melakukan hal itu:
public void run () {
while (keepProcessing) {
coba {
ClientConnection clientConnection = connectionManager.awaitClient ();
ClientRequestProcessor requestProcessor
= ClientRequestProcessor (clientConnection) baru;
clientScheduler.schedule (requestProcessor);
} catch (Exception e) {
e.printStackTrace ();
}

}
connectionManager.shutdown ();

Ini sekarang memfokuskan semua hal yang terkait dengan thread ke satu tempat, clientScheduler . Jika ada
masalah konkurensi, hanya ada satu tempat untuk dilihat:
ClientScheduler antarmuka publik {
membatalkan jadwal (ClientRequestProcessor requestProcessor);
}

Kebijakan saat ini mudah diterapkan:


ThreadPerRequestScheduler kelas publik mengimplementasikan ClientScheduler {
jadwal void publik (final ClientRequestProcessor requestProcessor) {
Runnable runnable = Runnable baru () {
public void run () {
requestProcessor.process ();
}
};

Utas = Utas baru (runnable);


thread.start ();
}
}

Setelah mengisolasi semua manajemen utas menjadi satu tempat, jauh lebih mudah untuk berubah
cara kami mengontrol utas. Misalnya, pindah ke kerangka kerja Java 5 Executor
melibatkan penulisan kelas baru dan memasukkannya (Listing A-1).

www.it-ebooks.info

Halaman 352

Kemungkinan Jalur Eksekusi 321

Daftar A-1
ExecutorClientScheduler.java
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

ExecutorClientScheduler kelas publik mengimplementasikan ClientScheduler {


Pelaksana pelaksana;

ExecutorClientScheduler publik (int availableThreads) {


executor = Executors.newFixedThreadPool (availableThreads);
}

jadwal void publik (final ClientRequestProcessor requestProcessor) {


Runnable runnable = Runnable baru () {
public void run () {
requestProcessor.process ();
}
};

https://translate.googleusercontent.com/translate_f 277/365
3/12/2020 www.it-ebooks.info
} executor.execute (runnable);
}

Kesimpulan
Memperkenalkan konkurensi dalam contoh khusus ini menunjukkan cara untuk meningkatkan
throughput suatu sistem dan satu cara untuk memvalidasi throughput tersebut melalui kerangka pengujian
kerja. Memfokuskan semua kode konkurensi ke dalam sejumlah kecil kelas adalah contohnya
menerapkan Prinsip Tanggung Jawab Tunggal. Dalam hal pemrograman bersamaan, ini
menjadi sangat penting karena kompleksitasnya.

Kemungkinan Jalur Eksekusi


Tinjau metode incrementValue , metode Java satu baris tanpa perulangan atau percabangan:
IdGenerator kelas publik {
int lastIdUsed;

public int incrementValue () {


return ++ lastIdUsed;
}
}

Abaikan integer overflow dan asumsikan bahwa hanya satu utas yang memiliki akses ke satu instance
dari IdGenerator . Dalam hal ini ada satu jalur eksekusi dan satu dijamin
hasil:

• Nilai yang dikembalikan sama dengan nilai lastIdUsed , yang keduanya lebih besar
daripada sebelum memanggil metode.

www.it-ebooks.info

Halaman 353

322 Lampiran A: Concurrency II

Apa yang terjadi jika kita menggunakan dua utas dan membiarkan metode ini tidak berubah? Apa itu
hasil yang mungkin jika setiap utas memanggil kenaikan nilai sekali? Berapa banyak kemungkinan jalur
eksekusi ada di sana? Pertama, hasilnya (anggap lastIdUsed dimulai dengan nilai 93):

• Thread 1 mendapat nilai 94, thread 2 mendapat nilai 95, dan lastIdUsed sekarang 95.
• Thread 1 mendapat nilai 95, thread 2 mendapat nilai 94, dan lastIdUsed sekarang 95.
• Thread 1 mendapat nilai 94, thread 2 mendapat nilai 94, dan lastIdUsed sekarang 94.

Hasil akhirnya, meski mengejutkan, adalah mungkin. Untuk melihat bagaimana hasil yang berbeda ini mungkin
Pertama, kita perlu memahami jumlah kemungkinan jalur eksekusi dan bagaimana Java
Mesin Virtual mengeksekusi mereka.

Jumlah Jalur
Untuk menghitung jumlah kemungkinan jalur eksekusi, kita akan mulai dengan byte- yang dihasilkan
kode. Satu baris java ( return ++ lastIdUsed; ) menjadi instruksi delapan byte-code. Itu
adalah mungkin untuk kedua utas untuk interleave pelaksanaan delapan instruksi ini
cara dealer kartu menyisipkan kartu saat ia mengocok deck. 2 Bahkan dengan hanya delapan kartu
masing-masing tangan, ada sejumlah besar hasil yang dikocok.
Untuk kasus sederhana dari instruksi N ini secara berurutan, tidak ada perulangan atau kondisional, dan T
utas, jumlah total jalur eksekusi yang mungkin sama dengan
()!NT
--------------
T
N!

Menghitung Kemungkinan Pemesanan

Ini berasal dari email dari Paman Bob ke Brett:


Dengan N langkah dan T utas ada T * N langkah total. Sebelum setiap langkah
ada saklar konteks yang memilih antara utas T. Setiap jalur bisa
dengan demikian diwakili sebagai string angka yang menunjukkan konteks switch.
Dengan langkah-langkah A dan B dan utas 1 dan 2, enam jalur yang mungkin adalah 1122,

https://translate.googleusercontent.com/translate_f 278/365
3/12/2020 www.it-ebooks.info
1212, 1221, 2112, 2121, dan 2211. Atau, dalam hal langkah-langkahnya adalah A1B1A2B2,
A1A2B1B2, A1A2B2B1, A2A1B1B2, A2A1B2B1, dan A2B2A1B1. Untuk
tiga utas urutannya adalah 112233, 112323, 113223, 113232, 112233,
121233, 121323, 121332, 123132, 123123,. . . .
Salah satu karakteristik dari string ini adalah bahwa harus selalu ada N
contoh masing-masing T . Jadi string 111111 tidak valid karena memiliki enam
contoh 1 dan nol contoh 2 dan 3.

2. Ini sedikit penyederhanaan. Namun, untuk tujuan diskusi ini, kita dapat menggunakan model penyederhanaan ini.

www.it-ebooks.info

Halaman 354

Kemungkinan Jalur Eksekusi 323

Menghitung Kemungkinan Pemesanan (lanjutan)

Jadi kami ingin permutasi dari N 1, N 2,. . . dan NT . Ini adalah


benar-benar hanya permutasi dari N * T yang diambil N * T pada suatu waktu, yaitu
( N * T ) !, tetapi dengan semua duplikat dihapus. Jadi triknya adalah menghitung
duplikat dan kurangi dari ( N * T ) !.
Diberi dua langkah dan dua utas, ada berapa duplikat? Setiap
string empat digit memiliki dua 1s dan dua 2s. Masing-masing pasangan itu bisa saja
bertukar tanpa mengubah arti string. Anda dapat menukar 1s atau
keduanya 2s, atau tidak. Jadi ada empat isomorf untuk setiap string, yang
berarti ada tiga duplikat. Jadi tiga dari empat opsi tersebut
duplikat; alternatifnya salah satu dari empat permutasi TIDAK duplikat
kandang. 4! * .25 = 6. Jadi alasan ini sepertinya berhasil.
Ada berapa duplikat? Dalam hal N = 2 dan T = 2, I
bisa menukar 1s, 2s, atau keduanya. Dalam kasus di mana N = 2 dan T = 3, I
dapat menukar 1s, 2s, 3s, 1s dan 2s, 1s dan 3s, atau 2s dan 3s. Menukar-
ping hanyalah permutasi dari N . Katakanlah ada P permutasi dari N .
Jumlah cara yang berbeda untuk mengatur mereka permutasi adalah P ** T .
Jadi jumlah kemungkinan isomorphs adalah N ! ** T . Dan begitu jumlahnya
path adalah ( T * N )! / ( N ! ** T ). Sekali lagi, dalam kasus T = 2, N = 2 kita mendapatkan 6 (24/4).
Untuk N = 2 dan T = 3 kita mendapatkan 720/8 = 90.
Untuk N = 3 dan T = 3 kita mendapatkan 9! / 6 ^ 3 = 1680.

Untuk kasus sederhana kami dari satu baris kode Java, yang setara dengan delapan baris kode byte
dan dua utas, jumlah total kemungkinan jalur eksekusi adalah 12.870. Jika jenisnya
lastIdUsed adalah panjang , maka setiap baca / tulis menjadi dua operasi, bukan satu, dan
jumlah pemesanan yang memungkinkan menjadi 2.704.156.
Apa yang terjadi jika kita melakukan satu perubahan pada metode ini?
vr incrementValue publik yang disinkronkan () {
++ lastIdUsed;
}

Jumlah jalur eksekusi yang mungkin menjadi dua untuk dua utas dan N! dalam
kasus umum.

Menggali lebih dalam


Bagaimana dengan hasil yang mengejutkan bahwa dua utas keduanya dapat memanggil metode sekali (sebelumnya
kami menambahkan disinkronkan ) dan mendapatkan hasil numerik yang sama? Bagaimana mungkin? Pertama
hal pertama.
Apa itu operasi atom? Kita dapat mendefinisikan operasi atom sebagai operasi apa pun itu
tidak terputus. Misalnya, dalam kode berikut, baris 5, tempat 0 ditugaskan
lastid , adalah atom karena menurut model Java Memory, penugasannya menjadi 32-bit
nilai tidak terputus.

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 279/365
3/12/2020 www.it-ebooks.info

Halaman 355

324 Lampiran A: Concurrency II

01: kelas publik Contoh {


02: int lastId;
03:
04: resetId public void () {
05: nilai = 0;
06:}
07:
08: public int getNextId () {
09: nilai ++;
10:}
11:}

Apa yang terjadi jika kita mengubah tipe lastId dari int ke long ? Apakah jalur 5 masih berupa atom?
Tidak sesuai dengan spesifikasi JVM. Itu bisa atom pada prosesor tertentu,
tetapi menurut spesifikasi JVM, penetapan nilai 64-bit apa pun membutuhkan dua
Penugasan 32-bit. Ini berarti bahwa antara penugasan 32-bit pertama dan yang kedua
Tugas 32-bit, beberapa utas lainnya dapat menyelinap masuk dan mengubah salah satu nilai.
Bagaimana dengan operator pra-kenaikan, ++, pada saluran 9? Operator pra-kenaikan bisa
terganggu, jadi itu bukan atom. Untuk memahami, mari kita tinjau kode byte dari keduanya
metode secara rinci.
Sebelum kita melangkah lebih jauh, berikut adalah tiga definisi yang penting:

• Bingkai — Setiap permohonan metode membutuhkan bingkai. Bingkai termasuk pengembalian


alamat, setiap parameter yang dilewatkan ke metode dan variabel lokal yang didefinisikan dalam
metode. Ini adalah teknik standar yang digunakan untuk mendefinisikan tumpukan panggilan, yang digunakan oleh
bahasa modern untuk memungkinkan pemanggilan fungsi / metode dasar dan untuk memungkinkan
doa rekursif.
• Variabel lokal — Variabel apa pun yang ditentukan dalam ruang lingkup metode. Semua metode non-statis
ods memiliki setidaknya satu variabel, ini , yang mewakili objek saat ini, objek
yang menerima pesan terbaru (di utas saat ini), yang menyebabkan
doa metode.
• Operand stack — Banyak instruksi di Java Virtual Machine menggunakan parame-
ters. Tumpukan operan adalah tempat parameter tersebut diletakkan. Tumpukan adalah standar
struktur data last-in, first-out (LIFO).

Berikut ini adalah kode byte yang dihasilkan untuk resetId () :

Mnemonik Deskripsi Operan


Tumpukan setelah
ALOAD 0 Muat variabel 0 ke dalam tumpukan operan. ini
Apa variabel ke-0? Hal ini ini ., Saat ini
obyek. Ketika metode dipanggil, itu
penerima pesan, contoh dari Contoh ,
didorong ke dalam array variabel lokal
bingkai dibuat untuk permohonan metode. Ini adalah
selalu variabel pertama dimasukkan ke dalam setiap instance
metode.

www.it-ebooks.info

Halaman 356

Kemungkinan Jalur Eksekusi 325

Mnemonik Deskripsi Operan

https://translate.googleusercontent.com/translate_f 280/365
3/12/2020 www.it-ebooks.info
Tumpukan setelah
ICONST_0 Masukkan nilai konstan 0 ke tumpukan operan. ini 0
PUTFIELD lastId Simpan nilai teratas pada tumpukan (yaitu 0) ke dalam <empty>
nilai bidang objek yang dirujuk oleh
referensi objek satu dari atas
tumpukan, ini .

Tiga instruksi ini dijamin atom karena, meskipun utasnya


mengeksekusi mereka dapat terganggu setelah salah satu dari mereka, informasi untuk
Instruksi PUTFIELD (nilai konstan 0 di atas tumpukan dan referensi ke
ini di bawah bagian atas, bersama dengan nilai bidang) tidak dapat disentuh oleh utas lainnya.
Jadi ketika penugasan terjadi, kami dijamin bahwa nilai 0 akan disimpan di
nilai bidang. Operasi itu atomik. Operan semua berurusan dengan informasi lokal ke Internet
metode, sehingga tidak ada gangguan antara beberapa utas.
Jadi jika tiga instruksi ini dijalankan oleh sepuluh utas, ada 4.38679733629e + 24
kemungkinan pemesanan. Namun, hanya ada satu hasil yang mungkin, jadi urutannya berbeda
tidak relevan. Kebetulan hasil yang sama dijamin lama untuk kasus ini
demikian juga. Mengapa? Kesepuluh utas memberikan nilai konstan. Bahkan jika mereka saling berhubungan
satu sama lain, hasil akhirnya sama.
Dengan operasi ++ dalam metode getNextId , akan ada masalah.
Asumsikan lastId memegang 42 pada awal metode ini. Berikut adalah kode byte untuk ini
metode baru:

Mnemonik Deskripsi Operan


Tumpukan setelah
ALOAD 0 Muat ini ke tumpukan operan ini

DUP Salin bagian atas tumpukan. Kami sekarang memiliki dua ini , ini
salinan ini di tumpukan operan.
DAPATKAN lastId Ambil nilai field lastId dari ini , 42
objek menunjuk ke atas tumpukan ( ini ) dan
simpan nilai itu kembali ke tumpukan.
ICONST_1 Dorong konstanta integer 1 pada tumpukan. ini , 42, 1
SAYA MENAMBAHKAN Integer menambahkan dua nilai teratas pada operan ini , 43
susun dan simpan hasilnya kembali ke operan
tumpukan.
DUP_X1 Gandakan nilainya 43 dan taruh sebelum ini . 43, ini , 43
Nilai PUTFIELD Simpan nilai teratas pada tumpukan operan, 43, ke 43
nilai bidang objek saat ini, diwakili oleh
nilai next-to-top pada tumpukan operan, ini .
SAYA KEMBALI mengembalikan nilai teratas (dan hanya) pada stack. <empty>

www.it-ebooks.info

Halaman 357

326 Lampiran A: Concurrency II

Bayangkan kasus di mana utas pertama menyelesaikan tiga instruksi pertama, hingga dan
termasuk GETFIELD, dan kemudian terputus. Utas kedua mengambil alih dan melakukan
seluruh metode, menambahkan lastId oleh satu; kembali 43. Kemudian utas pertama mengambil
di mana itu tinggalkan; 42 masih berada di tumpukan operan karena itu adalah nilai lastId ketika itu
dieksekusi GETFIELD. Ia menambahkan satu untuk mendapatkan 43 lagi dan menyimpan hasilnya. Nilai 43 adalah
kembali ke utas pertama juga. Hasilnya adalah bahwa salah satu kenaikan hilang karena
utas pertama menginjak utas kedua setelah utas kedua memotong utas pertama.
Membuat metode getNexId () tersinkronisasi memperbaiki masalah ini.

Kesimpulan
Pemahaman yang mendalam tentang kode byte tidak diperlukan untuk memahami bagaimana utas bisa
saling menginjak. Jika Anda dapat memahami contoh yang satu ini, itu harus menunjukkan
fleksibilitas beberapa utas saling menginjak, yang merupakan pengetahuan yang cukup.
Yang sedang berkata, apa contoh sepele ini menunjukkan adalah kebutuhan untuk memahami
model memori yang cukup untuk mengetahui apa yang bisa dan tidak aman. Ini adalah kesalahpahaman umum itu
operator ++ (sebelum atau sesudah kenaikan) adalah atomik, dan jelas tidak. Ini artinya kamu
perlu tahu:

https://translate.googleusercontent.com/translate_f 281/365
3/12/2020 www.it-ebooks.info
• Di mana ada objek / nilai yang dibagikan
• Kode yang dapat menyebabkan masalah baca / pembaruan bersamaan
• Bagaimana menjaga agar isu-isu konkuren tersebut tidak terjadi

Mengetahui Perpustakaan Anda

Kerangka Pelaksana
Seperti yang ditunjukkan dalam ExecutorClientScheduler.java di halaman 321, Executor membingkai
pekerjaan yang diperkenalkan di Java 5 memungkinkan untuk eksekusi canggih menggunakan kolam ulir. Ini adalah sebuah
kelas dalam paket java.util.concurrent .
Jika Anda membuat benang dan tidak menggunakan kolam renang benang atau yang menggunakan tulisan tangan
satu, Anda harus mempertimbangkan untuk menggunakan Pelaksana . Ini akan membuat kode Anda lebih bersih, lebih mudah diikuti
rendah, dan lebih kecil.
The Pelaksana kerangka kerja akan renang benang, mengubah ukuran secara otomatis, dan benang buat ulang
jika diperlukan. Ini juga mendukung masa depan, sebuah konstruksi pemrograman bersamaan yang umum. Itu
Kerangka kerja pelaksana bekerja dengan kelas yang mengimplementasikan Runnable dan juga bekerja dengan
kelas yang mengimplementasikan antarmuka Callable . A Callable terlihat seperti Runnable , tetapi bisa
mengembalikan hasil, yang merupakan kebutuhan umum dalam solusi multithreaded.
Masa depan berguna ketika kode perlu menjalankan banyak operasi independen dan
tunggu hingga keduanya selesai:

public String processRequest (String message) melempar Exception {


Callable <String> makeExternalCall = new Callable <String> () {

www.it-ebooks.info

Halaman 358

Mengetahui Perpustakaan Anda 327

public String call () melempar Exception {


Hasil string = "";
// buat permintaan eksternal
hasil pengembalian;
}
};

Future <String> result = executorService.submit (makeExternalCall);


String partialResult = doSomeLocalProcessing ();
return result.get () + partialResult;
}

Dalam contoh ini, metode mulai mengeksekusi objek makeExternalCall . Metode ini
dapat memproses pengolahan lainnya. Baris terakhir memanggil result.get () , yang memblokir hingga masa depan
selesai.

Solusi Nonblocking
Java 5 VM mengambil keuntungan dari desain prosesor modern, yang mendukung keandalan,
pembaruan nonblocking. Pertimbangkan, misalnya, kelas yang menggunakan sinkronisasi (dan karenanya
fore blocking) untuk menyediakan pembaruan nilai yang aman:
ObjectWithValue kelas publik {
nilai int pribadi;
public void disinkronkan incrementValue () {++ value; }
getValue int publik () {nilai balik; }
}

Java 5 memiliki serangkaian kelas baru untuk situasi seperti ini: AtomicBoolean ,
AtomicInteger , dan AtomicReference adalah tiga contoh; ada beberapa lagi. Kita dapat
tulis ulang kode di atas untuk menggunakan pendekatan nonblocking sebagai berikut:
ObjectWithValue kelas publik {
nilai AtomicInteger pribadi = AtomicInteger baru (0);

public void incrementValue () {


value.incrementAndGet ();
}
getValue int publik () {
return value.get ();
}
}

Meskipun ini menggunakan objek bukan primitif dan mengirim pesan seperti
incrementAndGet () alih-alih ++, kinerja kelas ini hampir selalu mengalahkan
versi sebelumnya. Dalam beberapa kasus hanya akan sedikit lebih cepat, tetapi dalam kasus di mana itu akan terjadi

https://translate.googleusercontent.com/translate_f 282/365
3/12/2020 www.it-ebooks.info
lambat sebenarnya tidak ada.
Bagaimana ini mungkin? Prosesor modern memiliki operasi yang biasanya disebut Bandingkan
dan Swap (CAS) . Operasi ini analog dengan penguncian basis data secara optimis, sedangkan
versi yang disinkronkan adalah analog dengan penguncian pesimistis.

www.it-ebooks.info

Halaman 359

328 Lampiran A: Concurrency II

Kata kunci yang disinkronkan selalu memperoleh kunci, bahkan ketika utas kedua tidak
mencoba memperbarui nilai yang sama. Meskipun kinerja kunci intrinsik telah
ditingkatkan dari versi ke versi, masih mahal.
Versi nonblocking dimulai dengan asumsi bahwa banyak utas umumnya dilakukan
tidak memodifikasi nilai yang sama cukup sering sehingga masalah akan muncul. Sebaliknya, itu efisien
mendeteksi apakah situasi seperti itu telah terjadi dan coba lagi sampai pembaruan berhasil -
sepenuhnya. Deteksi ini hampir selalu lebih murah daripada memperoleh kunci, bahkan dalam jumlah sedang
situasi pertengkaran tinggi.
Bagaimana Mesin Virtual mencapai ini? Operasi CAS bersifat atomik. Logi
Pada dasarnya, operasi CAS terlihat seperti berikut:
int variableBeingSet;

void simulateNonBlockingSet (int newValue) {


int currentValue;
lakukan {
currentValue = variableBeingSet
} while (currentValue! = compareAndSwap (currentValue, newValue));
}

int disinkronkan compareAndSwap (int currentValue, int newValue) {


if (variableBeingSet == currentValue) {
variableBeingSet = newValue;
kembalikan Nilai saat ini;
}
return variableBeingSet;
}

Ketika suatu metode mencoba untuk memperbarui variabel bersama, operasi CAS memverifikasi itu
variabel yang ditetapkan masih memiliki nilai terakhir yang diketahui. Jika demikian, maka variabel diubah. Jika
tidak, maka variabel tidak disetel karena utas lain berhasil menghalangi. Itu
metode yang melakukan upaya (menggunakan operasi CAS) melihat bahwa perubahan itu tidak dilakukan
dan coba lagi.

Kelas Tidak Aman-Aman


Ada beberapa kelas yang secara inheren tidak aman thread. Berikut ini beberapa contoh:

• SimpleDateFormat
• Koneksi Basis Data
• Kontainer di java.util
• Servlet

Perhatikan bahwa beberapa kelas koleksi memiliki metode individual yang aman-utas. Namun,
operasi apa pun yang melibatkan pemanggilan lebih dari satu metode tidak. Misalnya, jika Anda melakukannya
tidak ingin mengganti sesuatu di HashTable karena sudah ada di sana, Anda dapat menulis
kode berikut:
if (! hashTable.containsKey (someKey)) {
hashTable.put (someKey, SomeValue baru ());
}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 283/365
3/12/2020 www.it-ebooks.info

Halaman 360

Ketergantungan Antara Metode Dapat Memecah Kode Serentak 329

Setiap metode individu aman-utas. Namun, utas lain mungkin menambahkan nilai dalam
antara tombol berisiKey dan melakukan panggilan. Ada beberapa opsi untuk memperbaiki masalah ini.

• Kunci HashTable terlebih dahulu, dan pastikan semua pengguna HashTable lainnya melakukan hal yang sama—
penguncian berbasis klien:
disinkronkan (peta) {
if (! map.conainsKey (key))
map.put (kunci, nilai);
}

• Bungkus HashTable di objeknya sendiri dan gunakan API lain — penguncian berbasis server
menggunakan A DAPTER :

kelas publik WrappedHashtable <K, V> {


Peta pribadi <K, V> map = Hashtable baru <K, V> ();

public disinkronkan putIfAbsent (kunci K, nilai V) {


if (map.containsKey (key))
map.put (kunci, nilai);
}
}

• Gunakan koleksi thread-safe:

ConcurrentHashMap <Integer, String> map = new ConcurrentHashMap <Integer,


String> ();
map.putIfAbsent (kunci, nilai);

Koleksi di java.util.concurrent memiliki operasi seperti putIfAbsent () ke accommo-


tanggal operasi tersebut.

Ketergantungan Antara Metode s


Dapat Memecah Kode Serentak
Berikut ini adalah contoh sepele cara untuk memperkenalkan dependensi antar metode:
IntegerIterator kelas publik mengimplementasikan Iterator <Integer>
next Integer pribadiNilai = 0;

hasNext boolean yang disinkronkan publik () {


kembalikan nilai berikutnya <100000;
}
Integer tersinkronisasi publik next () {
if (nextValue == 100000)
melempar IteratorPastEndException baru ();
return nextValue ++;
}
Integer tersinkronisasi publik getNextValue () {
kembalikan Nilai berikutnya;
}
}

Berikut adalah beberapa kode untuk menggunakan IntegerIterator ini :


IntegerIterator iterator = integerIterator baru ();
while (iterator.hasNext ()) {

www.it-ebooks.info

Halaman 361

330 Lampiran A: Concurrency II

int nextValue = iterator.next ();


// lakukan sesuatu dengan nextValue
}

Jika satu utas mengeksekusi kode ini, tidak akan ada masalah. Tapi apa yang terjadi jika dua utas
berupaya membagikan satu instance IngeterIterator dengan maksud bahwa setiap utas akan melakukannya
memproses nilai yang didapatnya, tetapi setiap elemen dari daftar diproses hanya sekali? Kebanyakan
waktu, tidak ada hal buruk yang terjadi; utas berbagi daftar dengan senang hati, memproses elemen
https://translate.googleusercontent.com/translate_f 284/365
3/12/2020 www.it-ebooks.info
mereka diberikan oleh iterator dan berhenti ketika iterator selesai. Namun ada
kemungkinan kecil bahwa, pada akhir iterasi, kedua utas akan saling mengganggu
lainnya dan menyebabkan satu utas melampaui ujung iterator dan melemparkan pengecualian.
Inilah masalahnya: Thread 1 menanyakan pertanyaan hasNext () , yang mengembalikan true . Benang
1 didahului dan kemudian Thread 2 menanyakan pertanyaan yang sama, yang masih benar . Utas 2
kemudian memanggil next () , yang mengembalikan nilai seperti yang diharapkan tetapi memiliki efek samping dari pembuatan
hasNext () mengembalikan false . Thread 1 memulai kembali, berpikir hasNext () masih benar , lalu
panggilan selanjutnya () . Meskipun metode individual disinkronkan, klien menggunakan dua
metode.
Ini adalah masalah nyata dan contoh dari jenis masalah yang muncul dalam
kode saat ini. Dalam situasi khusus ini masalah ini sangat halus karena satu-satunya
waktu di mana ini menyebabkan kesalahan adalah ketika itu terjadi selama iterasi akhir iterator.
Jika utas kebetulan benar, maka salah satu utas bisa melampaui akhir
dari iterator. Ini adalah jenis bug yang terjadi lama setelah sebuah sistem di pro-
Duction, dan sulit untuk dilacak.
Anda memiliki tiga opsi:

• Toleransi kegagalan.
• Memecahkan masalah dengan mengubah klien: penguncian berbasis klien
• Memecahkan masalah dengan mengubah server, yang juga mengubah klien:
penguncian berbasis server

Toleransi Kegagalan
Kadang-kadang Anda dapat mengatur segala sesuatunya sehingga kegagalan tidak membahayakan. Misalnya,
klien di atas bisa menangkap pengecualian dan membersihkan. Terus terang, ini agak ceroboh. Itu lebih tepatnya
seperti membersihkan kebocoran memori dengan menyalakan kembali di tengah malam.

Penguncian Berbasis Klien


Untuk membuat IntegerIterator berfungsi dengan benar dengan beberapa utas, ubah klien ini (dan
setiap klien lain) sebagai berikut:
IntegerIterator iterator = integerIterator baru ();

while (true) {
int nextValue;

www.it-ebooks.info

Halaman 362

Ketergantungan Antara Metode Dapat Memecah Kode Serentak 331

disinkronkan (iterator) {
if (! iterator.hasNext ())
istirahat;
nextValue = iterator.next ();
}
doSometingWith (nextValue);
}

Setiap klien memperkenalkan kunci melalui kata kunci yang disinkronkan . Duplikasi ini melanggar
Prinsip KERING, tetapi mungkin diperlukan jika kode menggunakan alat pihak ketiga yang tidak aman.
Strategi ini berisiko karena semua programmer yang menggunakan server harus ingat
kunci sebelum menggunakannya dan buka kuncinya setelah selesai. Banyak (banyak!) Tahun yang lalu saya mengerjakan sebuah
sistem yang menggunakan penguncian berbasis klien pada sumber daya bersama. Sumber daya digunakan di
ratusan tempat berbeda di seluruh kode. Salah satu programmer miskin lupa mengunci
sumber daya di salah satu tempat itu.
Sistem ini adalah sistem pembagian waktu multi-terminal yang menjalankan perangkat lunak akuntansi
untuk Lokal 705 serikat pengemudi truk. Komputer berada di lantai atas, lingkungan-
kamar yang dikendalikan 50 mil di utara markas 705 Lokal. Di markas mereka
memiliki lusinan petugas entri data yang mengetik posting iuran serikat ke terminal. Termi-
Nals terhubung ke komputer menggunakan saluran telepon khusus dan 600bps setengah dupleks
modem. (Ini adalah waktu yang sangat, sangat lama.)
Kira-kira sekali sehari, salah satu terminal akan "terkunci." Tidak ada sajak atau reaksi
anak itu. Penguncian tidak menunjukkan preferensi untuk terminal atau waktu tertentu. Itu
seolah-olah ada seseorang yang melempar dadu memilih waktu dan terminal untuk dikunci.
Terkadang lebih dari satu terminal akan terkunci. Terkadang hari akan berlalu tanpa

https://translate.googleusercontent.com/translate_f 285/365
3/12/2020 www.it-ebooks.info
setiap penguncian.
Awalnya satu-satunya solusi adalah reboot. Tetapi reboot sulit untuk dikoordinasikan. Kita dulu punya
untuk memanggil kantor pusat dan meminta semua orang untuk menyelesaikan apa yang mereka lakukan pada semua termi-
nals. Lalu kita bisa mematikan dan memulai kembali. Jika seseorang melakukan sesuatu yang penting
yang membutuhkan satu atau dua jam, terminal yang terkunci hanya perlu tetap terkunci.
Setelah beberapa minggu debugging kami menemukan bahwa penyebabnya adalah penghitung cincin-penyangga itu
telah keluar dari sinkronisasi dengan penunjuknya. Buffer ini mengontrol output ke terminal. Itu
nilai pointer menunjukkan bahwa buffer kosong, tetapi penghitung mengatakan itu penuh. Karena
itu kosong, tidak ada yang bisa ditampilkan; tetapi karena itu juga penuh, tidak ada yang bisa
ditambahkan ke buffer untuk ditampilkan di layar.
Jadi kami tahu mengapa terminal terkunci, tetapi kami tidak tahu mengapa buffer cincin
sudah tidak sinkron. Jadi kami menambahkan retasan untuk mengatasi masalah tersebut. Itu mungkin
baca sakelar panel depan pada komputer. (Ini adalah waktu yang sangat, sangat, sangat lama.)
Kami menulis fungsi perangkap kecil yang terdeteksi ketika salah satu dari sakelar ini dilempar dan
kemudian mencari buffer cincin yang kosong dan penuh. Jika ditemukan, setel ulang itu
buffer kosong. Voila! Terminal yang terkunci mulai ditampilkan kembali.
Jadi sekarang kita tidak perlu me-reboot sistem ketika terminal terkunci. Lingkungan setempat
hanya akan memanggil kami dan memberi tahu kami bahwa kami memiliki penguncian, dan kemudian kami berjalan ke telepon
ruang komputer dan menjentikkan saklar.

www.it-ebooks.info

Halaman 363

332 Lampiran A: Concurrency II

Tentu saja kadang-kadang mereka bekerja di akhir pekan, dan kami tidak melakukannya. Jadi kami menambahkan
berfungsi untuk scheduler yang memeriksa semua buffer cincin sekali per menit dan mengatur ulang
keduanya kosong dan penuh. Hal ini menyebabkan tampilan terhenti sebelum Local dapat
bahkan di telepon.
Itu beberapa minggu lagi meneliti halaman demi halaman bahasa majelis monolitik
beri kode sebelum kita menemukan pelakunya. Kami telah melakukan perhitungan dan menghitung bahwa
jumlah penguncian konsisten dengan penggunaan buffer ring tanpa perlindungan. Begitu
yang harus kami lakukan adalah menemukan bahwa penggunaan salah. Sayangnya, ini sudah sangat lama
bahwa kami tidak memiliki alat pencarian atau referensi silang atau jenis bantuan otomatis lainnya.
Kami hanya perlu meneliti daftar.
Saya mendapat pelajaran penting bahwa musim dingin Chicago yang dingin pada tahun 1971. Penguncian berbasis klien
benar-benar pukulan.

Penguncian Berbasis Server


Duplikasi dapat dihapus dengan membuat perubahan berikut ke IntegerIterator :
IntegerIteratorServerLocked kelas publik {
next Integer pribadiNilai = 0;
Integer tersinkronisasi publik getNextOrNull () {
if (nextValue <100000)
return nextValue ++;
lain
kembali nol;
}
}

Dan kode klien juga berubah:


while (true) {
Integer nextValue = iterator.getNextOrNull ();
if (next == null)
istirahat;
// lakukan sesuatu dengan nextValue
}

Dalam hal ini, kami benar-benar mengubah API kelas kami menjadi multithread aware. 3 Klien
perlu melakukan pemeriksaan nol alih-alih memeriksa hasNext () .
Secara umum Anda sebaiknya memilih penguncian berbasis server karena alasan berikut:

• Ini mengurangi kode berulang — Penguncian berbasis klien memaksa setiap klien untuk mengunci server
tepat. Dengan memasukkan kode penguncian ke server, klien bebas menggunakan objek
dan tidak khawatir tentang menulis kode penguncian tambahan.

https://translate.googleusercontent.com/translate_f 286/365
3/12/2020 www.it-ebooks.info

3. Sebenarnya, antarmuka Iterator secara inheren tidak aman untuk thread. Itu tidak pernah dirancang untuk digunakan oleh banyak utas, jadi ini
Seharusnya tidak mengejutkan.

www.it-ebooks.info

Halaman 364

Meningkatkan Throughput 333

• Ini memungkinkan untuk kinerja yang lebih baik — Anda dapat menukar server yang aman dengan yang bukan
satu thread aman dalam hal penyebaran single-threaded, sehingga menghindari semua
atas.
• Ini mengurangi kemungkinan kesalahan — Yang diperlukan hanyalah satu programmer lupa untuk mengunci
tepat.
• Ini memberlakukan satu kebijakan — Kebijakan ini ada di satu tempat, server, bukan banyak
tempat, masing-masing klien.
• Ini mengurangi ruang lingkup variabel bersama — Klien tidak menyadarinya atau bagaimana
mereka dikunci. Semua itu disembunyikan di server. Ketika hal-hal pecah, jumlah
tempat untuk melihat lebih kecil.

Bagaimana jika Anda tidak memiliki kode server?

• Gunakan A DAPTER untuk mengubah API dan menambahkan penguncian


ThreadSafeIntegerIterator kelas publik {
private IntegerIterator iterator = integerIterator baru ();

Integer tersinkronisasi publik getNextOrNull () {


if (iterator.hasNext ())
kembalikan iterator.next ();
kembali nol;
}
}

• ATAU lebih baik lagi, gunakan koleksi thread-safe dengan antarmuka yang diperluas

Meningkatkan Throughput
Mari kita asumsikan bahwa kita ingin keluar di internet dan membaca isi dari satu set halaman
daftar URL. Saat setiap halaman dibaca, kami akan menguraikannya untuk mengumpulkan beberapa statistik. Sekali
semua halaman dibaca, kami akan mencetak ringkasan laporan.
Kelas berikut mengembalikan konten dari satu halaman, diberikan URL.
PageReader kelas publik {
// ...
public String getPageFor (String url) {
Metode HttpMethod = GetMethod baru (url);

coba {
httpClient.executeMethod (metode);
String response = method.getResponseBodyAsString ();
mengembalikan respons;
} catch (Exception e) {
menangani (e);
} akhirnya {
method.releaseConnection ();
}
}
}

www.it-ebooks.info

Halaman 365

https://translate.googleusercontent.com/translate_f 287/365
3/12/2020 www.it-ebooks.info

334 Lampiran A: Concurrency II

Kelas berikutnya adalah iterator yang menyediakan konten halaman berdasarkan iterator
URL:
PageIterator kelas publik {
pembaca PageReader pribadi;
url URLIterator pribadi;

PageIterator publik (pembaca PageReader, url URLIterator) {


this.urls = url;
this.reader = reader;
}

String getNextPageOrNull () yang disinkronkan publik () {


if (urls.hasNext ())
getPageFor (urls.next ());
lain
kembali nol;
}

public String getPageFor (String url) {


return reader.getPageFor (url);
}
}

Sebuah instance dari PageIterator dapat dibagi di antara banyak utas berbeda, masing-masing
satu menggunakan itu sendiri contoh dari PageReader untuk membaca dan mengurai halaman yang didapatnya dari
iterator.
Perhatikan bahwa kami menyimpan blok yang disinkronkan sangat kecil. Ini hanya berisi yang kritis
bagian jauh di dalam PageIterator . Itu selalu lebih baik untuk menyinkronkan sesedikit mungkin
sebagai lawan sinkronisasi sebanyak mungkin.

Perhitungan Single-Thread dari Throughput


Sekarang mari kita lakukan beberapa perhitungan sederhana. Untuk tujuan argumen, asumsikan yang berikut:

• Waktu I / O untuk mengambil halaman (rata-rata): 1 detik


• Waktu pemrosesan untuk mem-parsing halaman (rata-rata): .5 detik
• I / O membutuhkan 0 persen dari CPU sementara pemrosesan membutuhkan 100 persen.

Untuk N halaman sedang diproses oleh utas tunggal, total waktu eksekusi adalah 1,5 detik
OnD * N . Gambar A-1 menunjukkan snapshot 13 halaman atau sekitar 19,5 detik.

Gambar A-1
Utas tunggal

www.it-ebooks.info

Halaman 366

Jalan buntu 335

Perhitungan Throughput Multithread


Jika mungkin untuk mengambil halaman dalam urutan apa pun dan memproses halaman secara mandiri, maka itu
dimungkinkan untuk menggunakan banyak utas untuk meningkatkan throughput. Apa yang terjadi jika kita menggunakan tiga
utas? Berapa banyak halaman yang dapat kita peroleh dalam waktu yang bersamaan?
Seperti yang Anda lihat pada Gambar A-2, solusi multithreaded memungkinkan proses-terikat
parsing halaman untuk tumpang tindih dengan pembacaan halaman I / O terikat. Secara ideal
Dunia ini berarti prosesor sepenuhnya digunakan. Setiap halaman membaca satu detik adalah over-
disadap dengan dua parses. Dengan demikian, kita dapat memproses dua halaman per detik, yaitu tiga kali
throughput dari solusi single-threaded.

https://translate.googleusercontent.com/translate_f 288/365
3/12/2020 www.it-ebooks.info

Gambar A-2
Tiga utas bersamaan

Jalan buntu
Bayangkan aplikasi Web dengan dua kumpulan sumber daya bersama dari beberapa ukuran terbatas:

• Kumpulan koneksi database untuk pekerjaan lokal dalam penyimpanan proses


• Kumpulan koneksi MQ ke repositori master

Asumsikan ada dua operasi dalam aplikasi ini, buat dan perbarui:

• Buat — Memperoleh koneksi ke repositori induk dan basis data. Bicaralah dengan master layanan
repositori dan kemudian menyimpan pekerjaan dalam pekerjaan lokal dalam database proses.

www.it-ebooks.info

Halaman 367

336 Lampiran A: Concurrency II

• Perbarui — Memperoleh koneksi ke database dan kemudian menguasai repositori. Baca dari kantor
dalam proses database dan kemudian kirim ke repositori master

Apa yang terjadi ketika ada lebih banyak pengguna daripada ukuran kumpulan? Pertimbangkan setiap kolam memiliki
ukuran sepuluh.
• Sepuluh pengguna mencoba menggunakan create, sehingga kesepuluh koneksi basis data diperoleh, dan masing-masing
utas terputus setelah memperoleh koneksi basis data tetapi sebelum memperoleh koneksi
nection ke repositori master.
• Sepuluh pengguna mencoba menggunakan pembaruan, sehingga semua sepuluh koneksi repositori master diperoleh,
dan setiap utas terputus setelah mendapatkan repositori master tetapi sebelum mengakuisisi
koneksi database.
• Sekarang sepuluh utas "buat" harus menunggu untuk mendapatkan koneksi repositori master, tetapi
sepuluh utas "pembaruan" harus menunggu untuk mendapatkan koneksi basis data.
• Jalan buntu. Sistem tidak pernah pulih.

Ini mungkin terdengar seperti situasi yang tidak mungkin, tetapi siapa yang menginginkan sistem yang membeku
setiap minggu? Siapa yang ingin men-debug sistem dengan gejala yang sangat sulit
mereproduksi? Ini adalah jenis masalah yang terjadi di lapangan, kemudian membutuhkan waktu berminggu-minggu untuk dipecahkan.
"Solusi" yang khas adalah untuk memperkenalkan pernyataan debugging untuk mencari tahu apa yang terjadi-
ing. Tentu saja, pernyataan debug mengubah kode cukup sehingga kebuntuan terjadi
dalam situasi yang berbeda dan membutuhkan berbulan-bulan untuk kembali terjadi. 4
Untuk benar-benar menyelesaikan masalah kebuntuan, kita perlu memahami apa penyebabnya. Sana
empat kondisi yang diperlukan untuk kebuntuan terjadi:

• Pengucilan bersama
• Kunci & tunggu

https://translate.googleusercontent.com/translate_f 289/365
3/12/2020 www.it-ebooks.info
• Tidak ada preemption
• Penantian melingkar

Pengecualian Saling
Pengecualian bersama terjadi ketika beberapa utas perlu menggunakan sumber daya yang sama dan yang lainnya
sumber daya

• Tidak dapat digunakan oleh banyak utas secara bersamaan.


• Terbatas jumlahnya.

Contoh umum dari sumber daya tersebut adalah koneksi database, file yang terbuka untuk ditulis, a
rekam kunci, atau semafor.

4. Misalnya, seseorang menambahkan beberapa hasil debug dan masalahnya “menghilang.” Kode debugging “memperbaiki” masalah
jadi tetap di sistem.

www.it-ebooks.info

Halaman 368

Jalan buntu 337

Kunci & Tunggu


Setelah sebuah utas memperoleh sumber daya, utas tersebut tidak akan melepaskan sumber daya sampai semuanya diperoleh
dari sumber daya lain yang dibutuhkan dan telah menyelesaikan pekerjaannya.

Tanpa Preemption
Satu utas tidak dapat mengambil sumber daya dari utas lainnya. Setelah sebuah utas memegang a
sumber daya, satu-satunya cara bagi utas lain untuk mendapatkannya adalah bagi utas utas untuk melepaskannya.

Tunggu Edaran
Ini juga disebut sebagai pelukan maut. Bayangkan dua utas, T1 dan T2, dan dua
sumber daya, R1 dan R2. T1 memiliki R1, T2 memiliki R2. T1 juga membutuhkan R2, dan T2 juga membutuhkan R1.
Ini memberikan sesuatu seperti Gambar A-3:

Gambar A-3

Keempat kondisi ini harus memungkinkan terjadinya kebuntuan. Hancurkan salah satu dari ini
kondisi dan jalan buntu tidak memungkinkan.

Memutus Pengecualian Saling


Salah satu strategi untuk menghindari kebuntuan adalah dengan menghindari kondisi saling pengecualian. Kamu
mungkin bisa melakukan ini dengan

• Menggunakan sumber daya yang memungkinkan penggunaan secara bersamaan, misalnya, AtomicInteger .
• Meningkatkan jumlah sumber daya sehingga sama dengan atau melebihi jumlah perusahaan.
benang peting.
• Memeriksa bahwa semua sumber daya Anda bebas sebelum mengambil apa pun.

Sayangnya, sebagian besar sumber daya terbatas jumlahnya dan tidak memungkinkan secara bersamaan
menggunakan. Dan itu tidak biasa untuk identitas sumber kedua dipredikatkan pada
hasil operasi pada yang pertama. Tapi jangan berkecil hati; ada tiga kondisi yang tersisa.

https://translate.googleusercontent.com/translate_f 290/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 369

338 Lampiran A: Concurrency II

Breaking Lock & Tunggu


Anda juga dapat menghilangkan kebuntuan jika Anda menolak untuk menunggu. Periksa setiap sumber sebelum Anda
ambil itu, dan lepaskan semua sumber daya dan mulai lagi jika Anda mengalami salah satu yang sibuk.
Pendekatan ini memperkenalkan beberapa masalah potensial:

• Kelaparan — Satu utas terus tidak dapat memperoleh sumber daya yang dibutuhkan (mungkin itu
memiliki kombinasi unik sumber daya yang jarang semuanya tersedia).
• Livelock — Beberapa utas mungkin masuk dan semua memperoleh satu sumber daya dan
kemudian lepaskan satu sumber daya, berulang-ulang. Ini sangat mungkin dengan sederhana
Algoritma penjadwalan CPU (pikirkan perangkat tertanam atau tulisan tangan sederhana)
algoritma penyeimbangan ulir).

Kedua hal ini dapat menyebabkan hasil yang buruk. Hasil pertama dalam pemanfaatan CPU rendah,
sedangkan yang kedua menghasilkan pemanfaatan CPU yang tinggi dan tidak berguna.
Tidak efisien seperti strategi ini, itu lebih baik daripada tidak sama sekali. Ada manfaatnya
hampir selalu dapat diimplementasikan jika semuanya gagal.

Melanggar Preemption
Strategi lain untuk menghindari kebuntuan adalah membiarkan utas mengambil sumber daya
utas lainnya. Ini biasanya dilakukan melalui mekanisme permintaan sederhana. Saat utas
menemukan bahwa sumber daya sedang sibuk, ia meminta pemilik untuk melepaskannya. Jika pemilik juga menunggu
untuk beberapa sumber lain, ia melepaskan semuanya dan memulai kembali.
Ini mirip dengan pendekatan sebelumnya tetapi memiliki manfaat bahwa utas diizinkan
menunggu sumber daya. Ini mengurangi jumlah pemula. Namun, berhati-hatilah
mengelola semua permintaan itu bisa rumit.

Breaking Circular Wait


Ini adalah pendekatan yang paling umum untuk mencegah kebuntuan. Untuk sebagian besar sistem yang dibutuhkan
tidak lebih dari sebuah konvensi sederhana yang disepakati oleh semua pihak.
Dalam contoh di atas dengan Thread 1 yang menginginkan Resource 1 dan Resource 2 dan
Thread 2 menginginkan kedua Resource 2 dan kemudian Resource 1, cukup memaksa Thread 1 dan
Thread 2 untuk mengalokasikan sumber daya dalam urutan yang sama membuat menunggu melingkar menjadi tidak mungkin.
Lebih umum, jika semua utas dapat menyetujui pemesanan sumber daya global dan jika mereka
semua mengalokasikan sumber daya dalam urutan itu, maka kebuntuan tidak mungkin. Seperti semua strategi lainnya-
gies, ini dapat menyebabkan masalah:

• Urutan akuisisi mungkin tidak sesuai dengan urutan penggunaan; dengan demikian sumber daya
diperoleh pada awal mungkin tidak digunakan sampai akhir. Ini dapat menyebabkan sumber daya menjadi
terkunci lebih lama dari yang diperlukan.

www.it-ebooks.info

Halaman 370

Menguji Kode Multithreaded 339

https://translate.googleusercontent.com/translate_f 291/365
3/12/2020 www.it-ebooks.info

• Terkadang Anda tidak dapat memaksakan pesanan pada perolehan sumber daya. Jika ID dari
sumber daya kedua berasal dari operasi yang dilakukan pada yang pertama, maka pemesanan adalah
tidak layak.

Jadi ada banyak cara untuk menghindari kebuntuan. Beberapa menyebabkan kelaparan, sedangkan yang lainnya
banyak menggunakan CPU dan mengurangi daya tanggap. TANSTAAFL! 5
Mengisolasi bagian terkait benang dari solusi Anda untuk memungkinkan penyetelan dan eksperimen-
tion adalah cara yang ampuh untuk mendapatkan wawasan yang dibutuhkan untuk menentukan strategi terbaik.

Menguji Kode Multithreaded


Bagaimana kita bisa menulis tes untuk menunjukkan kode berikut ini rusak?
01: kelas publik ClassWithThreadingProblem {
02: int nextId;
03:
04: public int takeNextId () {
05: kembali nextId ++;
06:}
07:}

Berikut ini deskripsi tes yang akan membuktikan bahwa kode tersebut rusak:

• Ingat nilai nextId saat ini .


• Buat dua utas, keduanya panggil takeNextId () sekali.
• Pastikan nextId dua lebih dari apa yang kita mulai.
• Jalankan ini sampai kami menunjukkan bahwa nextId hanya bertambah satu saja
dari dua.

Listing A-2 menunjukkan tes seperti itu:

Listing A-2
ClassWithThreadingProblemTest.java
01: contoh paket;
02:
03: import static.junit.Assert.fail statis;
04:
05: import org.junit.Test;
06:
07: kelas publik ClassWithThreadingProblemTest {
08: @Test
09: public void twoThreadsShouldFailEventually () melempar Exception {
10: final ClassWithThreadingProblem classWithThreadingProblem
= new ClassWithThreadingProblem ();
11:

5. Tidak ada yang namanya makan siang gratis.

www.it-ebooks.info

Halaman 371

340 Lampiran A: Concurrency II

Listing A-2 (lanjutan)


ClassWithThreadingProblemTest.java
12: Runnable runnable = Runnable baru () {
13: public void run () {
14: classWithThreadingProblem.takeNextId ();
15: }
16: };
17:
18: untuk (int i = 0; i <50000; ++ i) {
19: int startingId = classWithThreadingProblem.lastId;
20: int expectedResult = 2 + startingId;
21:
22: Utas t1 = Utas baru (runnable);
23: Thread t2 = Thread baru (runnable);
24: t1.start ();
25: t2.start ();
26: t1.join ();
27: t2.join ();
28:

https://translate.googleusercontent.com/translate_f 292/365
3/12/2020 www.it-ebooks.info
29: int endingId = classWithThreadingProblem.lastId;
30:
31: if (endingId! = expectedResult)
32: kembali;
33: }
34:
35: gagal ("Seharusnya terkena masalah threading tetapi ternyata tidak.");
36:}
37:}

Baris Deskripsi
10 Buat satu instance dari ClassWithThreadingProblem . Catatan, kita harus menggunakan
kata kunci terakhir karena kami menggunakannya di bawah ini dalam kelas dalam anonim.
12-16 Buat kelas dalam anonim yang menggunakan instance tunggal
ClassWithThreadingProblem .
18 Jalankan kode ini "cukup" kali untuk menunjukkan bahwa kode gagal, tetapi tidak
sangat banyak sehingga tes "memakan waktu terlalu lama." Ini adalah tindakan penyeimbangan; kami tidak
ingin menunggu terlalu lama untuk menunjukkan kegagalan. Memilih nomor ini sulit—
meskipun nanti kita akan melihat bahwa kita dapat sangat mengurangi jumlah ini.
19 Ingat nilai awal. Tes ini mencoba membuktikan bahwa kode tersebut masuk
Masalah ClassWithThreading rusak. Jika tes ini lolos, itu membuktikan bahwa
kode rusak. Jika tes ini gagal, tes tidak dapat membuktikan kode itu
rusak.
20 Kami berharap nilai akhir menjadi dua lebih dari nilai saat ini.
22–23 Buat dua utas, yang keduanya menggunakan objek yang kami buat di baris 12-16.
Ini memberi kita potensi dua utas yang mencoba menggunakan contoh tunggal kita
dari ClassWithThreadingProblem dan mengganggu satu sama lain.

www.it-ebooks.info

Halaman 372

Menguji Kode Multithreaded 341

Baris Deskripsi
24–25 Jadikan dua utas kami layak dijalankan.
26–27 Tunggu hingga kedua utas selesai sebelum kami memeriksa hasilnya.
29 Catat nilai akhir aktual.
31–32 Apakah endingId kami berbeda dari yang kami harapkan? Jika demikian, kembalikan akhir tes—
kami telah membuktikan bahwa kodenya rusak. Jika tidak, coba lagi.
35 Jika kami sampai di sini, pengujian kami tidak dapat membuktikan bahwa kode produksi adalah
ken dalam jumlah waktu yang "masuk akal"; kode kami gagal. Entah kodenya
tidak rusak atau kami tidak menjalankan iterasi yang cukup untuk mendapatkan kondisi kegagalan
terjadi.

Tes ini tentu mengatur kondisi untuk masalah pembaruan bersamaan. Namun,
masalahnya terjadi sangat jarang sehingga sebagian besar kali tes ini tidak akan mendeteksi itu.
Memang, untuk benar-benar mendeteksi masalah kita perlu mengatur jumlah iterasi menjadi lebih dari satu
juta. Bahkan kemudian, dalam sepuluh eksekusi dengan jumlah loop 1.000.000, masalah terjadi
hanya sekali. Itu berarti kita mungkin harus mengatur jumlah iterasi menjadi lebih dari satu hun-
dred juta untuk mendapatkan kegagalan yang andal. Berapa lama kita siap untuk menunggu?
Bahkan jika kita menyetel tes untuk mendapatkan kegagalan yang andal pada satu mesin, kita mungkin akan melakukannya
untuk membatalkan tes dengan nilai yang berbeda untuk menunjukkan kegagalan pada komputer lain,
sistem operasi, atau versi JVM.
Dan ini adalah masalah sederhana . Jika kita tidak dapat mendemonstrasikan kode yang rusak dengan mudah dengan ini
masalah, bagaimana kita akan mendeteksi masalah yang benar-benar kompleks?
Jadi pendekatan apa yang bisa kita ambil untuk menunjukkan kegagalan sederhana ini? Dan, lebih penting-
penting, bagaimana kita bisa menulis tes yang akan menunjukkan kegagalan dalam kode yang lebih kompleks? Bagaimana
Akankah kita dapat menemukan jika kode kita mengalami kegagalan ketika kita tidak tahu harus mencari ke mana?
Berikut ini beberapa ide:

• Pengujian Monte Carlo. Jadikan tes fleksibel, sehingga bisa disetel. Kemudian jalankan tes
dan lebih — katakanlah pada server uji — secara acak mengubah nilai tuning. Jika tes pernah
gagal, kodenya rusak. Pastikan untuk mulai menulis tes-tes tersebut lebih awal sehingga berkesinambungan

https://translate.googleusercontent.com/translate_f 293/365
3/12/2020 www.it-ebooks.info
server integrasi segera mulai menjalankannya. Ngomong-ngomong, pastikan kamu login dengan cermat
kondisi di mana tes gagal.

• Jalankan tes pada setiap platform penyebaran target. Berkali-kali. Terus menerus
ously Semakin lama tes berjalan tanpa kegagalan, semakin besar kemungkinan itu
- Kode produksi sudah benar atau
- Tes tidak memadai untuk mengungkapkan masalah.
• Jalankan tes pada mesin dengan beban yang berbeda-beda. Jika Anda dapat mensimulasikan beban yang dekat dengan a
lingkungan produksi, lakukanlah.

www.it-ebooks.info

Halaman 373

342 Lampiran A: Concurrency II

Namun, bahkan jika Anda melakukan semua hal ini, Anda masih tidak memiliki peluang yang sangat baik untuk menemukan-
Masalah threading dengan kode Anda. Masalah yang paling berbahaya adalah yang
memiliki penampang kecil sehingga hanya terjadi sekali dalam satu miliar peluang. Seperti itu
masalah adalah teror sistem yang kompleks.

Alat Dukungan untuk Pengujian Kode Berbasis Thread


IBM telah menciptakan alat yang disebut ConTest. 6 Ini instrumen kelas untuk membuatnya lebih mungkin
kode non-thread-safe gagal.
Kami tidak memiliki hubungan langsung dengan IBM atau tim yang mengembangkan ConTest.
Seorang kolega kami menunjuk kami ke sana. Kami memperhatikan peningkatan besar dalam kemampuan kami untuk menemukan
masalah threading setelah beberapa menit menggunakannya.
Inilah garis besar cara menggunakan ConTest:

• Tulis tes dan kode produksi, pastikan ada tes yang dirancang khusus untuk
mensimulasikan banyak pengguna di bawah beragam muatan, seperti yang disebutkan di atas.
• Uji instrumen dan kode produksi dengan ConTest.
• Jalankan tes.

Ketika kami memasukkan kode dengan ConTest, tingkat keberhasilan kami berubah dari kira-kira satu kegagalan-
ure dalam sepuluh juta iterasi untuk kira-kira satu kegagalan dalam tiga puluh iterasi. Berikut adalah nilai-nilai loop
untuk beberapa tes setelah instrumentasi: 13, 23, 0, 54, 16, 14, 6, 69, 107, 49, 2. Jadi
jelas kelas instrumen gagal jauh lebih awal dan dengan keandalan yang jauh lebih besar.

Kesimpulan
Bab ini telah menjadi perjalanan yang sangat singkat melalui wilayah yang besar dan berbahaya di Jakarta
pemrograman bersamaan. Kami baru saja menggaruk permukaan. Penekanan kami di sini adalah pada dis-
ciplines untuk membantu menjaga kode bersamaan tetap bersih, tetapi ada banyak lagi yang harus Anda pelajari jika
Anda akan menulis sistem bersamaan. Kami sarankan Anda mulai dengan Doug Lea's
buku yang luar biasa Pemrograman Bersamaan di Jawa: Prinsip dan Pola Desain. 7
Dalam bab ini kita berbicara tentang pembaruan serentak, dan disiplin sinkronisasi bersih.
kronisasi dan penguncian yang bisa mencegahnya. Kami berbicara tentang bagaimana utas dapat meningkatkan
throughput dari sistem I / O-terikat dan menunjukkan teknik bersih untuk mencapai itu
perbaikan. Kami berbicara tentang kebuntuan dan disiplin untuk mencegahnya

6. http://www.haifa.ibm.com/projects/verification/contest/index.html
7. Lihat [Lea99] hlm. 191.

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 294/365
3/12/2020 www.it-ebooks.info

Halaman 374

Tutorial: Contoh Kode Lengkap 343

cara. Akhirnya, kami berbicara tentang strategi untuk mengungkap masalah bersamaan dengan menginstruksikan
kode Anda.

Tutorial: Contoh Kode Lengkap

Klien / Server Tidak Dibaca

Listing A-3
Server.java
package com.objectmentor.clientserver.nonthreaded;

impor java.io.IOException;
impor java.net.ServerSocket;
impor java.net. Liontin;
impor java.net.SocketException;

impor umum. PesanUtils;

Server kelas publik mengimplementasikan Runnable {


ServerSocket serverSocket;
volatile boolean keepProcessing = true;

Server publik (int port, int millisecondsTimeout) melempar IOException {


serverSocket = ServerSocket baru (port);
serverSocket.setSoTimeout (milidetikTimeout);
}

public void run () {


System.out.printf ("Server Starting \ n");

while (keepProcessing) {
coba {
System.out.printf ("accepting client \ n");
Soket soket = serverSocket.accept ();
System.out.printf ("mendapat klien \ n");
proses (soket);
} catch (Exception e) {
menangani (e);
}
}
}

private void handle (Exception e) {


if (! (contoh SocketException)) {
e.printStackTrace ();
}
}

public void stopProcessing () {


keepProcessing = false;
closeIgnoringException (serverSocket);
}

www.it-ebooks.info

Halaman 375

344 Lampiran A: Concurrency II

Listing A-3 (lanjutan)


Server.java

https://translate.googleusercontent.com/translate_f 295/365
3/12/2020 www.it-ebooks.info
proses batal (soket soket) {
if (socket == null)
kembali;

coba {
System.out.printf ("Server: mendapatkan pesan \ n");
Pesan string = MessageUtils.getMessage (socket);
System.out.printf ("Server: mendapat pesan:% s \ n", pesan);
Thread.sleep (1000);
System.out.printf ("Server: mengirim balasan:% s \ n", pesan);
MessageUtils.sendMessage (soket, "Diproses:" + pesan);
System.out.printf ("Server: sent \ n");
closeIgnoringException (socket);
} catch (Exception e) {
e.printStackTrace ();
}

private void closeIgnoringException (Socket socket) {


if (socket! = null)
coba {
socket.close ();
} catch (abaikan IOException) {
}
}

private void closeIgnoringException (ServerSocket serverSocket) {


if (serverSocket! = null)
coba {
serverSocket.close ();
} catch (abaikan IOException) {
}
}
}

Listing A-4
ClientTest.java
package com.objectmentor.clientserver.nonthreaded;

impor java.io.IOException;
impor java.net.ServerSocket;
impor java.net. Liontin;
impor java.net.SocketException;

impor umum. PesanUtils;

Server kelas publik mengimplementasikan Runnable {


ServerSocket serverSocket;
volatile boolean keepProcessing = true;

www.it-ebooks.info

Halaman 376

Tutorial: Contoh Kode Lengkap 345

Listing A-4 (lanjutan)


ClientTest.java
Server publik (int port, int millisecondsTimeout) melempar IOException {
serverSocket = ServerSocket baru (port);
serverSocket.setSoTimeout (milidetikTimeout);
}

public void run () {


System.out.printf ("Server Starting \ n");

while (keepProcessing) {
coba {
System.out.printf ("accepting client \ n");
Soket soket = serverSocket.accept ();
System.out.printf ("mendapat klien \ n");
proses (soket);
} catch (Exception e) {
menangani (e);
}
}
}

private void handle (Exception e) {


if (! (contoh SocketException)) {
e.printStackTrace ();

https://translate.googleusercontent.com/translate_f 296/365
3/12/2020 www.it-ebooks.info
}
}

public void stopProcessing () {


keepProcessing = false;
closeIgnoringException (serverSocket);
}

proses batal (soket soket) {


if (socket == null)
kembali;

coba {
System.out.printf ("Server: mendapatkan pesan \ n");
Pesan string = MessageUtils.getMessage (socket);
System.out.printf ("Server: mendapat pesan:% s \ n", pesan);
Thread.sleep (1000);
System.out.printf ("Server: mengirim balasan:% s \ n", pesan);
MessageUtils.sendMessage (soket, "Diproses:" + pesan);
System.out.printf ("Server: sent \ n");
closeIgnoringException (socket);
} catch (Exception e) {
e.printStackTrace ();
}

private void closeIgnoringException (Socket socket) {


if (socket! = null)
coba {
socket.close ();

www.it-ebooks.info

Halaman 377

346 Lampiran A: Concurrency II

Listing A-4 (lanjutan)


ClientTest.java
} catch (abaikan IOException) {
}
}

private void closeIgnoringException (ServerSocket serverSocket) {


if (serverSocket! = null)
coba {
serverSocket.close ();
} catch (abaikan IOException) {
}
}
}

Listing A-5
MessageUtils.java
paket umum;

impor java.io.IOException;
impor java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
impor java.io.OutputStream;
impor java.net. Liontin;

MessageUtils kelas publik {


public static void sendMessage (Socket socket, String message)
melempar IOException {
OutputStream stream = socket.getOutputStream ();
ObjectOutputStream oos = ObjectOutputStream baru (stream);
oos.writeUTF (pesan);
oos.flush ();
}

public getMessage String statis (Socket socket) melempar IOException {


InputStream stream = socket.getInputStream ();
ObjectInputStream ois = ObjectInputStream baru (stream);
return ois.readUTF ();
}
}

Klien / Server Menggunakan Thread


Mengubah server untuk menggunakan utas hanya memerlukan perubahan pada pesan proses (baru

https://translate.googleusercontent.com/translate_f 297/365
3/12/2020 www.it-ebooks.info
garis ditekankan untuk menonjol):
proses batal (final Socket socket) {
if (socket == null)
kembali;

Runnable clientHandler = Runnable baru () {


public void run () {

www.it-ebooks.info

Halaman 378

Tutorial: Contoh Kode Lengkap 347

coba {
System.out.printf ("Server: mendapatkan pesan \ n");
Pesan string = MessageUtils.getMessage (socket);
System.out.printf ("Server: mendapat pesan:% s \ n", pesan);
Thread.sleep (1000);
System.out.printf ("Server: mengirim balasan:% s \ n", pesan);
MessageUtils.sendMessage (soket, "Diproses:" + pesan);
System.out.printf ("Server: sent \ n");
closeIgnoringException (socket);
} catch (Exception e) {
e.printStackTrace ();
}
}
};

ClientConnection = Utas baru (clientHandler);


clientConnection.start ();
}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 298/365
3/12/2020 www.it-ebooks.info

Halaman 379

halaman ini sengaja dibiarkan kosong

www.it-ebooks.info

Halaman 380

https://translate.googleusercontent.com/translate_f 299/365
3/12/2020 www.it-ebooks.info

Lampiran B
org.jfree.date.SerialDate

Listing B-1
SerialDate.Java
1 / * =============================================== =========================
2 * JCommon: perpustakaan tujuan umum gratis untuk platform Java (tm)
3 * ================================================ ========================
4*
5 * (C) Hak Cipta 2000-2005, oleh Object Refinery Limited dan Kontributor.
6*
7 * Info Proyek: http://www.jfree.org/jcommon/index.html
8*
9 * Perpustakaan ini adalah perangkat lunak bebas; Anda dapat mendistribusikan ulang dan / atau memodifikasinya
10 * berdasarkan ketentuan Lisensi Publik Umum GNU yang diterbitkan oleh
11 * Yayasan Perangkat Lunak Bebas; baik versi 2.1 dari Lisensi, atau
12 * (sesuai pilihan Anda) versi yang lebih baru.
13 *
14 * Perpustakaan ini didistribusikan dengan harapan akan bermanfaat, tetapi
15 * TANPA GARANSI APA PUN; bahkan tanpa jaminan tersirat dari DAGANGAN
16 * atau KESESUAIAN UNTUK TUJUAN TERTENTU. Lihat GNU Lesser General Public
17 * Lisensi untuk lebih jelasnya.
18 *
19 * Anda seharusnya telah menerima salinan GNU Lesser General Public
20 * Lisensi bersama dengan perpustakaan ini; jika tidak, tulis ke Perangkat Lunak Bebas
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22 * AS.
23 *
24 * [Java adalah merek dagang atau merek dagang terdaftar dari Sun Microsystems, Inc.
25 * di Amerika Serikat dan negara-negara lain.]
26 *
27 * ---------------
28 * SerialDate.java
29 * ---------------
30 * (C) Hak Cipta 2001-2005, oleh Object Refinery Limited.
31 *
32 * Penulis Asli: David Gilbert (untuk Object Refinery Limited);
33 * Kontributor: -;
34 *
35 * $ Id: SerialDate.java, v 1.7 2005/11/03 09:25:17 mungady Exp $
36 *
37 * Perubahan (dari 11-Oktober-2001)

349

www.it-ebooks.info

Halaman 381

350 Lampiran B: org.jfree.date.SerialDate

Listing B-1 (lanjutan)


SerialDate.Java
38 * --------------------------
39 * 11-Okt-2001: Mengatur ulang kelas dan memindahkannya ke paket baru
40 * com.jrefinery.date (DG);
41 * 05-Nov-2001: Menambahkan metode getDescription (), dan menghapus NotableDate
42 * kelas (DG);
43 * 12-Nov-2001: IBD memerlukan metode setDescription (), sekarang NotableDate
44 * kelas hilang (DG); Mengubah getPreviousDayOfWeek (),
45 * getFollowingDayOfWeek () dan getNearestDayOfWeek () untuk memperbaiki
46 * bug (DG);
47 * 05-Des-2001: Fixed bug di kelas SpreadsheetDate (DG);
48 * 29-Mei-2002: Memindahkan konstanta bulan ke antarmuka terpisah
49 * (MonthConstants) (DG);
50 * 27-Agustus-2002: Memperbaiki bug dalam metode addMonths (), terima kasih kepada N ??? levka Petr (DG);
51 * 03-Oct-2002: Memperbaiki kesalahan yang dilaporkan oleh Checkstyle (DG);
52 * 13-Mar-2003: Implemented Serializable (DG);
53 * 29-Mei-2003: Fixed bug dalam metode addMonths (DG);
54 * 04-Sep-2003: Diimplementasikan Sebanding. Memperbarui javadocs (DG) isInRange;
55 * 05-Jan-2005: Memperbaiki bug dalam metode addYears () (1096282) (DG);
56 *
57 * /
58
59 paket org.jfree.date;
60
61 import java.io.Serializable;
62 impor java.text.DateFormatSymbols;
63 impor java.text.SimpleDateFormat;
64 impor java.util.Calendar;
65 impor java.util.GregorianCalendar;
66
67 / **
68 * Kelas abstrak yang mendefinisikan persyaratan kami untuk memanipulasi tanggal,
69 * tanpa mengikat implementasi tertentu.
70 * <P>

https://translate.googleusercontent.com/translate_f 300/365
3/12/2020 www.it-ebooks.info
71 * Persyaratan 1: cocok dengan setidaknya apa yang Excel lakukan untuk tanggal;
72 * Persyaratan 2: kelas tidak dapat diubah;
73 * <P>
74 * Mengapa tidak menggunakan java.util.Date saja? Kami akan, ketika itu masuk akal. Kadang-kadang,
75 * java.util.Date dapat * terlalu * tepat - ini mewakili instan dalam waktu,
76 * akurat hingga 1/1000 detik (dengan tanggalnya sendiri tergantung pada
77 * zona waktu). Terkadang kita hanya ingin mewakili hari tertentu (mis. 21
78 * Januari 2015) tanpa memperhatikan diri sendiri tentang waktu, atau
79 * zona waktu, atau apa pun. Itulah yang kami tentukan untuk SerialDate.
80 * <P>
81 * Anda dapat memanggil getInstance () untuk mendapatkan subkelas beton dari SerialDate,
82 * tanpa khawatir tentang implementasi yang tepat.
83 *
84 * @author David Gilbert
85 * /
86 SerialDate abstrak publik kelas mengimplementasikan Sebanding,
87 Serializable,
88 MonthConstants {
89
90 / ** Untuk serialisasi. * /
91 serialVersionUID final statis panjang pribadi = -293716040467423637L;
92
93 / ** Simbol format tanggal. * /
94 DateFormatSymbols final statis publik
95 DATE_FORMAT_SYMBOLS = SimpleDateFormat baru (). GetDateFormatSymbols ();
96
97 / ** Nomor seri untuk 1 Januari 1900. * /
98 public int static final SERIAL_LOWER_BOUND = 2;
99
100 / ** Nomor seri untuk 31 Desember 9999. * /
101 public int static final SERIAL_UPPER_BOUND = 2958465;
102

www.it-ebooks.info

Halaman 382

Lampiran B: org.jfree.date.SerialDate 351

Listing B-1 (lanjutan)


SerialDate.Java
103 / ** Nilai tahun terendah yang didukung oleh format tanggal ini. * /
104 int akhir statis publik MINIMUM_YEAR_SUPPORTED = 1900;
105
106 / ** Nilai tahun tertinggi yang didukung oleh format tanggal ini. * /
107 int akhir statis publik MAXIMUM_YEAR_SUPPORTED = 9999;
108
109 / ** Konstanta yang berguna untuk hari Senin. Setara dengan java.util.Calendar.MONDAY. * /
110 public int static final SENIN = Kalender. SENIN;
111
112 / **
113 * Konstanta yang berguna untuk hari Selasa. Setara dengan java.util.Calendar.TUESDAY.
114 * /
115 public int static final TUESDAY = Calendar.TUESDAY;
116
117 / **
118 * Konstanta yang berguna untuk hari Rabu. Setara dengan
119 * java.util.Calendar.WEDNESDAY.
120 * /
121 public int final statis RABU = Kalender. RABU;
122
123 / **
124 * Konstanta yang berguna untuk Thrusday. Setara dengan java.util.Calendar.THURSDAY.
125 * /
126 int final statis publik KAMIS = Kalender. THURSDAY;
127
128 / ** Konstanta yang berguna untuk hari Jumat. Setara dengan java.util.Calendar.FRIDAY. * /
129 public int static final FRIDAY = Calendar.FRIDAY;
130
131 / **
132 * Konstanta yang berguna untuk hari Sabtu. Setara dengan java.util.Calendar.SATURDAY.
133 * /
134 public int final statis SATURDAY = Calendar.SATURDAY;
135
136 / ** Konstanta yang berguna untuk hari Minggu. Setara dengan java.util.Calendar.SUNDAY. * /
137 public int static final SUNDAY = Calendar.SUNDAY;
138
139 / ** Jumlah hari dalam setiap bulan dalam tahun non kabisat. * /
140 int akhir statis [] LAST_DAY_OF_MONTH =
141 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
142
143 / ** Jumlah hari dalam satu tahun (non-lompatan) hingga akhir setiap bulan. * /
144 int akhir statis [] AGGREGATE_DAYS_TO_END_OF_MONTH =
145 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
146
147 / ** Jumlah hari dalam setahun hingga akhir bulan sebelumnya. * /
148 int final statis [] AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH =
149 {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
150
151 / ** Jumlah hari dalam satu tahun kabisat hingga akhir setiap bulan. * /
152 int akhir statis [] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_MONTH =
153 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
154
155 / **
156 * Jumlah hari dalam satu tahun kabisat hingga akhir bulan sebelumnya.
157 * /
158 int akhir statis []
159 LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH =
160 {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
161

https://translate.googleusercontent.com/translate_f 301/365
3/12/2020 www.it-ebooks.info
162 / ** Konstanta yang berguna untuk merujuk pada minggu pertama dalam sebulan. * /
163 int akhir statis publik FIRST_WEEK_IN_MONTH = 1;
164

www.it-ebooks.info

Halaman 383

352 Lampiran B: org.jfree.date.SerialDate

Listing B-1 (lanjutan)


SerialDate.Java
165 / ** Konstanta yang berguna untuk merujuk pada minggu kedua dalam sebulan. * /
166 int akhir statis publik SECOND_WEEK_IN_MONTH = 2;
167
168 / ** Konstanta yang berguna untuk merujuk pada minggu ketiga dalam sebulan. * /
169 int akhir statis publik THIRD_WEEK_IN_MONTH = 3;
170
171 / ** Konstanta yang berguna untuk merujuk pada minggu keempat dalam sebulan. * /
172 int akhir statis publik FOURTH_WEEK_IN_MONTH = 4;
173
174 / ** Konstanta yang berguna untuk merujuk pada minggu terakhir dalam sebulan. * /
175 int akhir statis publik LAST_WEEK_IN_MONTH = 0;
176
177 / ** Konstanta rentang berguna. * /
178 int final statis publik INCLUDE_NONE = 0;
179
180 / ** Konstanta rentang berguna. * /
181 int final statis publik INCLUDE_FIRST = 1;
182
183 / ** Konstanta rentang berguna. * /
184 public int static final INCLUDE_SECOND = 2;
185
186 / ** Konstanta rentang berguna. * /
187 public int static final INCLUDE_BOTH = 3;
188
189 / **
190 * Konstanta yang berguna untuk menentukan hari dalam seminggu relatif terhadap yang tetap
191 * tanggal.
192 * /
193 public int final statis PRECEDING = -1;
194
195 / **
196 * Konstanta yang berguna untuk menentukan hari dalam seminggu relatif terhadap yang tetap
197 * tanggal.
198 * /
199 public int static final TERDEKAT = 0;
200
201 / **
202 * Konstanta yang berguna untuk menentukan hari dalam seminggu relatif terhadap yang tetap
203 * tanggal.
204 * /
205 public int final statis BERIKUT = 1;
206
207 / ** Deskripsi untuk tanggal tersebut. * /
208 deskripsi String pribadi;
209
210 / **
211 * Konstruktor default.
212 * /
213 dilindungi SerialDate () {
214}
215
216 / **
217 * Mengembalikan <code> true </code> jika kode integer yang disediakan mewakili a
218 * berlaku hari dalam seminggu, dan <code> false </code> jika tidak.
219 *
220 * @param mengkode kode yang sedang diperiksa validitasnya.
221 *
222 * @kembali <code> true </code> jika kode integer yang disediakan mewakili a
223 * berlaku hari dalam seminggu, dan <code> false </code> jika tidak.
224 * /
225 public static boolean isValidWeekdayCode (kode int final) {
226

www.it-ebooks.info

Halaman 384

https://translate.googleusercontent.com/translate_f 302/365
3/12/2020 www.it-ebooks.info

Lampiran B: org.jfree.date.SerialDate 353

Listing B-1 (lanjutan)


SerialDate.Java
227 beralih (kode) {
228 kasus MINGGU:
229 kasus SENIN:
230 kasus SELASA:
231 kasus RABU:
232 kasus KAMIS:
233 kasus JUMAT:
234 kasus SABTU:
235 kembali benar;
236 default:
237 return false;
238 }
239
240}
241
242 / **
243 * Mengonversi string yang disediakan ke hari dalam seminggu.
244 *
245 * @param sa string mewakili hari dalam seminggu.
246 *
247 * @kembali <code> -1 </code> jika string tidak dapat dikonversi, hari
248 * minggu sebaliknya.
249 * /
250 public int static stringToWeekdayCode (String s) {
251
252 final String [] shortWeekdayNames
253 = DATE_FORMAT_SYMBOLS.getShortWeekdays ();
254 string terakhir [] weekDayNames = DATE_FORMAT_SYMBOLS.getWeekdays ();
255
256 hasil int = -1;
257 s = s.trim ();
258 untuk (int i = 0; i <weekDayNames.length; i ++) {
259 if (s.equals (shortWeekdayNames [i]))) {
260 hasil = i;
261 istirahat;
262 }
263 if (s.equals (weekDayNames [i])) {
264 hasil = i;
265 istirahat;
266 }
267 }
268 hasil pengembalian;
269
270}
271
272 / **
273 * Mengembalikan string yang mewakili hari-minggu yang disediakan.
274 * <P>
275 * Perlu menemukan pendekatan yang lebih baik.
276 *
277 * @param hari kerja, hari dalam seminggu.
278 *
279 * @mengembalikan string yang mewakili hari-minggu yang disediakan.
280 * /
281 public static String weekdayCodeToString (akhir int weekday) {
282
283 String akhir [] hari kerja = DATE_FORMAT_SYMBOLS.getWeekdays ();
284 kembali hari kerja [hari kerja];
285
286}
287
288 / **

www.it-ebooks.info

Halaman 385

354 Lampiran B: org.jfree.date.SerialDate

Listing B-1 (lanjutan)


SerialDate.Java
289 * Mengembalikan array nama bulan.
290 *
291 * @mengembalikan susunan nama bulan.
292 * /
293 String statis publik [] getMonths () {
294
295 return getMonths (false);
296
297}
298
299 / **
300 * Mengembalikan array nama bulan.
301 *

https://translate.googleusercontent.com/translate_f 303/365
3/12/2020 www.it-ebooks.info
302 ** @param memendekkan sebuah bendera
303 yang menunjukkan bahwa nama bulan pendek harus
dikembalikan.
304 *
305 * @mengembalikan susunan nama bulan.
306 * /
307 public static String [] getMonths (boolean akhir disingkat) {
308
309 if (disingkat) {
310 kembalikan DATE_FORMAT_SYMBOLS.getShortMonths ();
311 }
312 lain {
313 kembalikan DATE_FORMAT_SYMBOLS.getMonths ();
314 }
315
316}
317
318 / **
319 * Mengembalikan nilai true jika kode integer yang disediakan menunjukkan bulan yang valid.
320 *
321 * @param mengkode kode yang sedang diperiksa validitasnya.
322 *
323 * @kembali <code> true </code> jika kode integer yang disediakan mewakili a
324 * bulan yang valid.
325 * /
326 public static boolean isValidMonthCode (kode int final) {
327
328 beralih (kode) {
329 kasus JANUARI:
330 case FEBRUARY:
331 kasus MARET:
332 kasus APRIL:
333 kasus MUNGKIN:
334 kasing JUNE:
335 kasus JULI:
336 kasus AGUSTUS:
337 kasus SEPTEMBER:
338 kasus OKTOBER:
339 kasus NOVEMBER:
340 kasus DESEMBER:
341 kembali benar;
342 default:
343 return false;
344 }
345
346}
347
348 / **
349 * Mengembalikan kuartal untuk bulan yang ditentukan.
350 *

www.it-ebooks.info

Halaman 386

Lampiran B: org.jfree.date.SerialDate 355

Listing B-1 (lanjutan)


SerialDate.Java
351 * @param kode kode bulan (1-12).
352 *
353 * @ kembalikan kuartal yang menjadi milik bulan.
354 * @mengapa java.lang.IllegalArgumentException
355 * /
356 public int static publicCodeToQuarter (kode int final) {
357
358 beralih (kode) {
359 kasus JANUARI:
360 case FEBRUARY:
361 kasus MARET: return 1;
362 kasus APRIL:
363 kasus MUNGKIN:
364 kasus JUNE: return 2;
365 kasus JULI:
366 kasus AGUSTUS:
367 kasus SEPTEMBER: return 3;
368 kasus OKTOBER:
369 kasus NOVEMBER:
370 case DESEMBER: return 4;
371 default: melempar IllegalArgumentException baru (
372 "SerialDate.monthCodeToQuarter: kode bulan tidak valid.");
373 }
374
375}
376
377 / **
378 * Mengembalikan string yang mewakili bulan yang disediakan.
379 * <P>
380 * String yang dikembalikan adalah bentuk panjang dari nama bulan yang diambil dari
381 * lokal default.
382 *
383 * @param bulan bulan.
384 *
385 * @mengembalikan string yang mewakili bulan yang disediakan.
386 * /
387 String publicCodeToString statis publik (bulan int final) {
388
389 return monthCodeToString (bulan, false);

https://translate.googleusercontent.com/translate_f 304/365
3/12/2020 www.it-ebooks.info
390
391}
392
393 / **
394 * Mengembalikan string yang mewakili bulan yang disediakan.
395 * <P>
396 * String yang dikembalikan adalah bentuk panjang atau pendek dari nama bulan yang diambil
397 * dari lokal default.
398 *
399 * @param bulan bulan.
400 * @param disingkat jika <code> true </code> mengembalikan singkatan dari
401 * bulan.
402 *
403 * @mengembalikan string yang mewakili bulan yang disediakan.
404 * @throws java.lang.IllegalArgumentException
405 * /
406 public String publicCodeToString statis (bulan int akhir,
407 boolean akhir dipersingkat) {
408
409 // periksa argumen ...
410 if (! isValidMonthCode (bulan)) {
411 melempar IllegalArgumentException baru (
412 "SerialDate.monthCodeToString: bulan di luar rentang yang valid.");

www.it-ebooks.info

Halaman 387

356 Lampiran B: org.jfree.date.SerialDate

Listing B-1 (lanjutan)


SerialDate.Java
413 }
414
415 String akhir [] bulan;
416
417 if (disingkat) {
418 bulan = DATE_FORMAT_SYMBOLS.getShortMonths ();
419 }
420 lain {
421 bulan = DATE_FORMAT_SYMBOLS.getMonths ();
422 }
423
424 bulan kembali [bulan - 1];
425
426}
427
428 / **
429 * Mengubah string menjadi kode bulan.
430 * <P>
431 * Metode ini akan mengembalikan salah satu konstanta JANUARI, FEBRUARI, ...,
432 * DESEMBER yang sesuai dengan string. Jika string tidak
433 * dikenali, metode ini mengembalikan -1.
434 *
435 * @param adalah string untuk menguraikan.
436 *
437 * @kembali <code> -1 </code> jika string tidak dapat diuraikan, bulan bulan
438 * tahun sebaliknya.
439 * /
440 public int int statisToMonthCode (String s) {
441
442 string terakhir [] shortMonthNames = DATE_FORMAT_SYMBOLS.getShortMonths ();
443 String akhir [] monthNames = DATE_FORMAT_SYMBOLS.getMonths ();
444
445 hasil int = -1;
446 s = s.trim ();
447
448 // pertama coba parsing string sebagai integer (1-12) ...
449 coba {
450 hasil = Integer.parseInt (s);
451 }
452 catch (NumberFormatException e) {
453 // tekan
454 }
455
456 // sekarang cari melalui nama bulan ...
457 if ((hasil <1) || (hasil> 12)) {
458 untuk (int i = 0; i <monthNames.length; i ++) {
459 if (s.equals (shortMonthNames [i])) {
460 hasil = i +1;
461 istirahat;
462 }
463 if (s.equals (monthNames [i])) {
464 hasil = i +1;
465 istirahat;
466 }
467 }
468 }
469
470 hasil pengembalian;
471
472}
473
474 / **

https://translate.googleusercontent.com/translate_f 305/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 388

Lampiran B: org.jfree.date.SerialDate 357

Listing B-1 (lanjutan)


SerialDate.Java
475 * Mengembalikan nilai true jika kode integer yang disediakan menunjukkan valid
476 * minggu-dalam-bulan, dan salah jika tidak.
477 *
478 * @param kode kode sedang diperiksa validitasnya.
479 * @kembali <code> true </code> jika kode integer yang disediakan mewakili a
480 * berlaku minggu-dalam-bulan.
481 * /
482 public static boolean isValidWeekInMonthCode (kode int final) {
483
484 beralih (kode) {
485 huruf besar FIRST_WEEK_IN_MONTH:
486 case SECOND_WEEK_IN_MONTH:
487 huruf THIRD_WEEK_IN_MONTH:
488 huruf besar FOURTH_WEEK_IN_MONTH:
489 case LAST_WEEK_IN_MONTH: return true;
490 default: return false;
491 }
492
493}
494
495 / **
496 * Menentukan apakah tahun yang ditentukan adalah tahun kabisat.
497 *
498 * @param yyyy tahun (dalam kisaran 1900 hingga 9999).
499 *
500 * @kembalikan <code> true </code> jika tahun yang ditentukan adalah tahun kabisat.
501 * /
502 public static boolean isLeapYear (akhir int yyyy) {
503
504 if ((yyyy% 4)! = 0) {
505 return false;
506 }
507 lain jika ((yyyy% 400) == 0) {
508 kembali benar;
509 }
510 lain jika ((yyyy% 100) == 0) {
511 return false;
512 }
513 lain {
514 kembali benar;
515 }
516
517}
518
519 / **
520 * Mengembalikan jumlah tahun kabisat dari tahun 1900 ke tahun yang ditentukan
521 * TERMASUK.
522 * <P>
523 * Perhatikan bahwa 1900 bukan tahun kabisat.
524 *
525 * @param yyyy tahun (dalam kisaran 1900 hingga 9999).
526 *
527 * @mengembalikan jumlah tahun kabisat dari tahun 1900 ke tahun yang ditentukan.
528 * /
529 public static int leapYearCount (final int yyyy) {
530
531 final int leap4 = (yyyy - 1896) / 4;
532 final int leap100 = (yyyy - 1800) / 100;
533 le int final 400 = (yyyy - 1600) / 400;
534 return leap4 - leap100 + leap400;
535
536}

www.it-ebooks.info

Halaman 389

358 Lampiran B: org.jfree.date.SerialDate

https://translate.googleusercontent.com/translate_f 306/365
3/12/2020 www.it-ebooks.info

Listing B-1 (lanjutan)


SerialDate.Java
537
538 / **
539 * Mengembalikan nomor hari terakhir bulan itu, dengan mempertimbangkan
540 * tahun kabisat.
541 *
542 * @param bulan bulan.
543 * @param yyyy tahun (dalam kisaran 1900 hingga 9999).
544 *
545 * @mengembalikan nomor hari terakhir dalam sebulan.
546 * /
547 public static int lastDayOfMonth (akhir bulan int, akhir yyyy) {
548
549 hasil akhir int = LAST_DAY_OF_MONTH [bulan];
550 if (bulan! = FEBRUARI) {
551 hasil pengembalian;
552 }
553 lain jika (isLeapYear (yyyy)) {
554 hasil pengembalian +1;
555 }
556 lain {
557 hasil pengembalian;
558 }
559
560}
561
562 / **
563 * Membuat tanggal baru dengan menambahkan jumlah hari yang ditentukan ke pangkalan
564 * tanggal.
565 *
566 * @param hari jumlah hari yang ditambahkan (bisa negatif).
567 * @param mendasarkan tanggal dasar.
568 *
569 * @ kembalikan tanggal baru.
570 * /
571 publik SerialDate statis (hari int akhir, basis SerialDate akhir) {
572
573 serialDayNumber int akhir = base.toSerial () + hari;
574 kembalikan SerialDate.createInstance (serialDayNumber);
575
576}
577
578 / **
579 * Membuat tanggal baru dengan menambahkan jumlah bulan yang ditentukan ke pangkalan
580 * tanggal.
581 * <P>
582 * Jika tanggal dasar dekat dengan akhir bulan, hari pada hasilnya
583 * dapat disesuaikan sedikit: 31 Mei + 1 bulan = 30 Juni.
584 *
585 * @param bulan jumlah bulan untuk ditambahkan (bisa negatif).
586 * @param mendasarkan tanggal dasar.
587 *
588 * @ kembalikan tanggal baru.
589 * /
590 public static SerialDate addMonths (bulan int akhir,
591 basis SerialDate akhir) {
592
593 int akhir yy = (12 * base.getYYYY () + base.getMonth () + bulan - 1)
594 / 12;
595 int akhir mm = (12 * base.getYYYY () + base.getMonth () + bulan - 1)
596 % 12 + 1;
597 int final dd = Math.min (
598 base.getDayOfMonth (), SerialDate.lastDayOfMonth (mm, yy)

www.it-ebooks.info

Halaman 390

Lampiran B: org.jfree.date.SerialDate 359

Listing B-1 (lanjutan)


SerialDate.Java
599 );
600 return SerialDate.createInstance (dd, mm, yy);
601
602}
603
604 / **
605 * Membuat tanggal baru dengan menambahkan jumlah tahun yang ditentukan ke pangkalan
606 * tanggal.
607 *
608 * @param tahun jumlah tahun yang ditambahkan (bisa negatif).
609 * @param mendasarkan tanggal dasar.
610 *
611 * @return Tanggal baru.
612 * /
613 publik SerialDate statis (tahun int akhir, basis SerialDate akhir) {
614
615 base int finalY = base.getYYYY ();
616 base int finalM = base.getMonth ();
617 base int finalD = base.getDayOfMonth ();
618

https://translate.googleusercontent.com/translate_f 307/365
3/12/2020 www.it-ebooks.info
619 target int akhirY = baseY + tahun;
620 target int finalD = Math.min (
621 baseD, SerialDate.lastDayOfMonth (baseM, targetY)
622 );
623
624 return SerialDate.createInstance (targetD, baseM, targetY);
625
626}
627
628 / **
629 * Mengembalikan tanggal terbaru yang jatuh pada hari tertentu dan
630 * adalah SEBELUM tanggal dasar.
631 *
632 * @param targetSiap hari kode untuk target hari-of-the-week.
633 * @param mendasarkan tanggal dasar.
634 *
635 * @ kembalikan tanggal terakhir yang jatuh pada hari dan minggu yang ditentukan
636 * SEBELUM tanggal dasar.
637 * /
638 public static SerialDate getPreviousDayOfWeek (target int finalWeekday,
639 basis SerialDate akhir) {
640
641 // periksa argumen ...
642 if (! SerialDate.isValidWeekdayCode (targetWeekday)) {
643 melempar IllegalArgumentException baru (
644 "Kode hari-minggu tidak valid."
645 );
646 }
647
648 // temukan tanggal ...
649 penyesuaian int akhir;
650 base int finalDOW = base.getDayOfWeek ();
651 if (baseDOW> targetWeekday) {
652 sesuaikan = Math.min (0, targetWeekday - baseDOW);
653 }
654 lain {
655 sesuaikan = -7 + Math.max (0, targetWeekday - baseDOW);
656 }
657
658 mengembalikan SerialDate.addDays (sesuaikan, pangkalan);
659
660}

www.it-ebooks.info

Halaman 391

360 Lampiran B: org.jfree.date.SerialDate

Listing B-1 (lanjutan)


SerialDate.Java
661
662 / **
663 * Mengembalikan tanggal paling awal yang jatuh pada hari-of-the-minggu yang ditentukan
664 * dan SETELAH tanggal dasar.
665 *
666 * @param targetSiap hari kode untuk target hari-of-the-week.
667 * @param mendasarkan tanggal dasar.
668 *
669 * @ kembalikan tanggal paling awal yang jatuh pada hari-hari-minggu yang ditentukan
670 * dan SETELAH tanggal dasar.
671 * /
672 public static SerialDate getFollowingDayOfWeek (target int finalWeekday,
673 basis SerialDate akhir) {
674
675 // periksa argumen ...
676 if (! SerialDate.isValidWeekdayCode (targetWeekday)) {
677 melempar IllegalArgumentException baru (
678 "Kode hari-minggu tidak valid."
679 );
680 }
681
682 // temukan tanggal ...
683 penyesuaian int akhir;
684 base int finalDOW = base.getDayOfWeek ();
685 if (baseDOW> targetWeekday) {
686 sesuaikan = 7 + Math.min (0, targetWeekday - baseDOW);
687 }
688 lain {
689 sesuaikan = Math.max (0, targetWeekday - baseDOW);
690 }
691
692 mengembalikan SerialDate.addDays (sesuaikan, pangkalan);
693}
694
695 / **
696 * Mengembalikan tanggal yang jatuh pada hari-of-the-minggu yang ditentukan dan
697 * TERTUTUP hingga tanggal dasar.
698 *
699 * @param targetDOW kode untuk target hari-of-the-week.
700 * @param mendasarkan tanggal awal.
701 *
702 * @ kembalikan tanggal yang jatuh pada hari-of-the-minggu yang ditentukan dan
703 * TERTUTUP hingga tanggal dasar.
704 * /
705 SerialDate public static getNearestDayOfWeek (target int finalDOW,
706 basis SerialDate akhir) {

https://translate.googleusercontent.com/translate_f 308/365
3/12/2020 www.it-ebooks.info
707
708 // periksa argumen ...
709 if (! SerialDate.isValidWeekdayCode (targetDOW)) {
710 melempar IllegalArgumentException baru (
711 "Kode hari-minggu tidak valid."
712 );
713 }
714
715 // temukan tanggal ...
716 base int finalDOW = base.getDayOfWeek ();
717 int sesuaikan = -Math.abs (targetDOW - baseDOW);
718 jika (sesuaikan> = 4) {
719 sesuaikan = 7 - sesuaikan;
720 }
721 if (sesuaikan <= -4) {
722 sesuaikan = 7 + sesuaikan;

www.it-ebooks.info

Halaman 392

Lampiran B: org.jfree.date.SerialDate 361

Listing B-1 (lanjutan)


SerialDate.Java
723 }
724 mengembalikan SerialDate.addDays (sesuaikan, pangkalan);
725
726}
727
728 / **
729 * Gulung tanggal ke depan hingga hari terakhir bulan itu.
730 *
731 * @param mendasarkan tanggal dasar.
732 *
733 * @mengembalikan tanggal seri yang baru.
734 * /
735 SerialDate publik getEndOfCurrentMonth (basis SerialDate akhir) {
736 final int last = SerialDate.lastDayOfMonth (
737 base.getMonth (), base.getYYYY ()
738 );
739 kembalikan SerialDate.createInstance (terakhir, base.getMonth (), base.getYYYY ());
740}
741
742 / **
743 * Mengembalikan string yang sesuai dengan kode minggu-dalam-bulan.
744 * <P>
745 * Perlu menemukan pendekatan yang lebih baik.
746 *
747 * @param menghitung kode integer yang mewakili minggu-dalam-bulan.
748 *
749 * @mengembalikan string yang sesuai dengan kode minggu-dalam-bulan.
750 * /
751 public String statis mingguInMonthToString (jumlah int final) {
752
753 beralih (hitung) {
754 case SerialDate.FIRST_WEEK_IN_MONTH: return "First";
755 case SerialDate.SECOND_WEEK_IN_MONTH: return "Second";
756 case SerialDate.THIRD_WEEK_IN_MONTH: return "Third";
757 case SerialDate.FOURTH_WEEK_IN_MONTH: return "Fourth";
758 case SerialDate.LAST_WEEK_IN_MONTH: return "Last";
759 default:
760 return "SerialDate.weekInMonthToString (): kode tidak valid.";
761 }
762
763}
764
765 / **
766 * Mengembalikan string yang mewakili 'relatif' yang disediakan.
767 * <P>
768 * Perlu menemukan pendekatan yang lebih baik.
769 *
770 * @param relatif merupakan konstanta yang mewakili 'relatif'.
771 *
772 * @mengembalikan string yang mewakili 'relatif' yang disediakan.
773 * /
774 String publicToString statis publik (relatif int relatif) {
775
776 beralih (relatif) {
777 case SerialDate.PRECEDING: return "Preceding";
778 case SerialDate.NEAREST: return "Nearest";
779 case SerialDate.FOLLOWING: return "following";
780 default: return "ERROR: Relative To String";
781 }
782
783}
784

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 309/365
3/12/2020 www.it-ebooks.info

Halaman 393

362 Lampiran B: org.jfree.date.SerialDate

Listing B-1 (lanjutan)


SerialDate.Java
785 / **
786 * Metode pabrik yang mengembalikan turunan dari beberapa subkelas beton
787 * {@link SerialDate}.
788 *
789 * @param hari hari (1-31).
790 * @param bulan bulan (1-12).
791 * @param yyyy tahun (dalam kisaran 1900 hingga 9999).
792 *
793 * @return Contoh {@link SerialDate}.
794 * /
795 public static SerialDate createInstance (hari int akhir, bulan int akhir,
796 final int yyyy) {
797 kembalikan SpreadsheetDate baru (hari, bulan, tttt);
798}
799
800 / **
801 * Metode pabrik yang mengembalikan turunan dari beberapa subkelas beton
802 * {@link SerialDate}.
803 *
804 * @param membuat serial nomor seri untuk hari itu (1 Januari 1900 = 2).
805 *
806 * @return instance dari SerialDate.
807 * /
808 public static SerialDate createInstance (serial int final) {
809 kembalikan SpreadsheetDate baru (serial);
810}
811
812 / **
813 * Metode pabrik yang mengembalikan turunan dari subkelas SerialDate.
814 *
815 * @param date Objek tanggal Java.
816 *
817 * @ kembalikan instance SerialDate.
818 * /
819 public static SerialDate createInstance (tanggal java.util.Date akhir) {
820
821 kalender GregorianCalendar akhir = GregorianCalendar baru ();
822 calendar.setTime (tanggal);
823 kembalikan SpreadsheetDate baru (calendar.get (Calendar.DATE),
824 calendar.get (Calendar.MONTH) +1,
825 calendar.get (Calendar.YEAR));
826
827}
828
829 / **
830 * Mengembalikan nomor seri untuk tanggal, di mana 1 Januari 1900 = 2 (ini
831 * hampir sama dengan sistem penomoran yang digunakan dalam Microsoft Excel untuk
832 * Windows dan Lotus 1-2-3).
833 *
834 * @ kembalikan nomor seri untuk tanggal tersebut.
835 * /
836 abstrak publik int ke Serial ();
837
838 / **
839 * Mengembalikan java.util.Date. Sejak java.util.Date lebih presisi daripada
840 * SerialDate, kita perlu mendefinisikan konvensi untuk 'waktu hari'.
841 *
842 * @kembalikan ini sebagai <code> java.util.Date </code>.
843 * /
844 java.util.Date toDate abstrak publik ();
845
846 / **

www.it-ebooks.info

Halaman 394

Lampiran B: org.jfree.date.SerialDate 363

Listing B-1 (lanjutan)


SerialDate.Java
847 * Mengembalikan deskripsi tanggal.

https://translate.googleusercontent.com/translate_f 310/365
3/12/2020 www.it-ebooks.info
848 *
849 * @ kembalikan deskripsi tanggal.
850 * /
851 getDescription String publik () {
852 kembalikan this.description;
853}
854
855 / **
856 * Menetapkan deskripsi untuk tanggal.
857 *
858 * @param deskripsi deskripsi baru untuk tanggal.
859 * /
860 setDescription public void (deskripsi String akhir) {
861 this.description = deskripsi;
862}
863
864 / **
865 * Mengubah tanggal menjadi string.
866 *
867 * @ kembalikan representasi string dari tanggal.
868 * /
869 public String toString () {
870 return getDayOfMonth () + "-" + SerialDate.monthCodeToString (getMonth ())
871 + "-" + getYYYY ();
872}
873
874 / **
875 * Mengembalikan tahun (asumsikan rentang 1900 hingga 9999 yang valid).
876 *
877 * @ kembalikan tahun ini.
878 * /
879 int abstrak publik getYYYY ();
880
881 / **
882 * Mengembalikan bulan (Januari = 1, Februari = 2, Maret = 3).
883 *
884 * @ kembalikan bulan dalam setahun.
885 * /
886 abstrak publik int getMonth ();
887
888 / **
889 * Mengembalikan hari dalam sebulan.
890 *
891 * @ kembalikan hari dalam sebulan.
892 * /
893 int abstrak publik getDayOfMonth ();
894
895 / **
896 * Mengembalikan hari dalam seminggu.
897 *
898 * @ kembalikan hari dalam seminggu.
899 * /
900 int publik abstrak getDayOfWeek ();
901
902 / **
903 * Mengembalikan perbedaan (dalam hari) antara tanggal ini dan yang ditentukan
904 * tanggal 'lainnya'.
905 * <P>
906 * Hasilnya positif jika tanggal ini setelah tanggal 'lain' dan
907 * negatif jika sebelum tanggal 'lain'.
908 *

www.it-ebooks.info

Halaman 395

364 Lampiran B: org.jfree.date.SerialDate

Listing B-1 (lanjutan)


SerialDate.Java
909 * @param selain tanggal yang dibandingkan dengan.
910 *
911 * @ kembalikan perbedaan antara ini dan tanggal lainnya.
912 * /
913 public int abstrak membandingkan (SerialDate lainnya);
914
915 / **
916 * Mengembalikan nilai true jika SerialDate ini mewakili tanggal yang sama dengan
917 * ditentukan SerialDate.
918 *
919 * @param selain tanggal yang dibandingkan dengan.
920 *
921 * @return <code> true </code> jika SerialDate ini menunjukkan tanggal yang sama dengan
922 * SerialDate yang ditentukan.
923 * /
924 boolean abstrak publik isOn (SerialDate other);
925
926 / **
927 * Mengembalikan nilai true jika SerialDate ini menunjukkan tanggal yang lebih awal dibandingkan dengan
928 * SerialDate yang ditentukan.
929 *
930 * @param lainnya Tanggal dibandingkan dengan.
931 *
932 * @return <code> true </code> jika SerialDate ini menunjukkan tanggal yang lebih awal
933 * dibandingkan dengan SerialDate yang ditentukan.
934 * /
935 boolean abstrak publik isBefore (SerialDate other);

https://translate.googleusercontent.com/translate_f 311/365
3/12/2020 www.it-ebooks.info
936
937 / **
938 * Mengembalikan nilai true jika SerialDate ini mewakili tanggal yang sama dengan
939 * ditentukan SerialDate.
940 *
941 * @param selain tanggal yang dibandingkan dengan.
942 *
943 * @return <code> true <code> jika SerialDate ini menunjukkan tanggal yang sama
944 * sebagai SerialDate yang ditentukan.
945 * /
946 boolean abstrak publik isOnOrBefore (SerialDate other);
947
948 / **
949 * Mengembalikan nilai true jika SerialDate ini menunjukkan tanggal yang sama dengan tanggal
950 * ditentukan SerialDate.
951 *
952 * @param selain tanggal yang dibandingkan dengan.
953 *
954 * @return <code> true </code> jika SerialDate ini menunjukkan tanggal yang sama
955 * sebagai SerialDate yang ditentukan.
956 * /
957 abstrak publik boolean isAfter (SerialDate other);
958
959 / **
960 * Mengembalikan nilai true jika SerialDate ini mewakili tanggal yang sama dengan
961 * ditentukan SerialDate.
962 *
963 * @param selain tanggal yang dibandingkan dengan.
964 *
965 * @kembalikan <code> true </code> jika SerialDate ini menunjukkan tanggal yang sama
966 * sebagai SerialDate yang ditentukan.
967 * /
968 boolean abstrak publik isOnOrAfter (SerialDate other);
969
970 / **
971 * Mengembalikan <code> true </code> jika {@link SerialDate} ini berada di dalam

www.it-ebooks.info

Halaman 396

Lampiran B: org.jfree.date.SerialDate 365

Listing B-1 (lanjutan)


SerialDate.Java
972 * rentang yang ditentukan (TERMASUK). Urutan tanggal d1 dan d2 tidak
973 * penting.
974 *
975 * @param d1 tanggal batas untuk kisaran.
976 * @param d2 tanggal batas lainnya untuk kisaran.
977 *
978 * @return A boolean.
979 * /
980 boolean abstrak publik isInRange (SerialDate d1, SerialDate d2);
981
982 / **
983 * Mengembalikan <code> true </code> jika {@link SerialDate} ini berada di dalam
984 * rentang yang ditentukan (penelepon menentukan apakah titik akhirnya atau tidak
985 * termasuk). Urutan tanggal d1 dan d2 tidak penting.
986 *
987 * @param d1 tanggal batas untuk kisaran.
988 * @param d2 tanggal batas lainnya untuk kisaran.
989 * @param menyertakan kode yang mengontrol awal dan akhir
990 * tanggal termasuk dalam kisaran.
991 *
992 * @return A boolean.
993 * /
994 abstrak publik boolean isInRange (SerialDate d1, SerialDate d2,
995 int meliputi);
996
997 / **
998 * Mengembalikan tanggal terbaru yang jatuh pada hari dan minggu yang ditentukan
999 * adalah SEBELUM tanggal ini.
1000 *
1001 * @param targetDOW kode untuk target hari-of-the-week.
1002 *
1003 * @ kembalikan tanggal terakhir yang jatuh pada hari dan minggu yang ditentukan
1004 * SEBELUM tanggal ini.
1005 * /
1006 SerialDate publik getPreviousDayOfWeek (target int finalDOW) {
1007 return getPreviousDayOfWeek (targetDOW, ini);
1008}
1009
1010 / **
1011 * Mengembalikan tanggal paling awal yang jatuh pada hari tertentu dalam seminggu
1012 * dan SETELAH tanggal ini.
1013 *
1014 * @param targetDOW kode untuk target hari-of-the-week.
1015 *
1016 * @ kembalikan tanggal paling awal yang jatuh pada hari tertentu dalam seminggu
1017 * dan SETELAH tanggal ini.
1018 * /
1019 SerialDate publik getFollowingDayOfWeek (target int finalDOW) {
1020 return getFollowingDayOfWeek (targetDOW, ini);
1021}
1022
1023 / **
1024 * Mengembalikan tanggal terdekat yang jatuh pada hari tertentu dalam seminggu.

https://translate.googleusercontent.com/translate_f 312/365
3/12/2020 www.it-ebooks.info
1025 *
1026 * @param targetDOW kode untuk target hari-of-the-week.
1027 *
1028 * @ kembalikan tanggal terdekat yang jatuh pada hari tertentu dalam seminggu.
1029 * /
1030 SerialDate publik getNearestDayOfWeek (target int finalDOW) {
1031 return getNearestDayOfWeek (targetDOW, ini);
1032}
1033
1034}

www.it-ebooks.info

Halaman 397

366 Lampiran B: org.jfree.date.SerialDate

Daftar B-2
SerialDateTest.java
1 / * =============================================== =========================
2 * JCommon: perpustakaan tujuan umum gratis untuk platform Java (tm)
3 * ================================================ ========================
4*
5 * (C) Hak Cipta 2000-2005, oleh Object Refinery Limited dan Kontributor.
6*
7 * Info Proyek: http://www.jfree.org/jcommon/index.html
8*
9 * Perpustakaan ini adalah perangkat lunak bebas; Anda dapat mendistribusikan ulang dan / atau memodifikasinya
10 * berdasarkan ketentuan Lisensi Publik Umum GNU yang diterbitkan oleh
11 * Yayasan Perangkat Lunak Bebas; baik versi 2.1 dari Lisensi, atau
12 * (sesuai pilihan Anda) versi yang lebih baru.
13 *
14 * Perpustakaan ini didistribusikan dengan harapan akan bermanfaat, tetapi
15 * TANPA GARANSI APA PUN; bahkan tanpa jaminan tersirat dari DAGANGAN
16 * atau KESESUAIAN UNTUK TUJUAN TERTENTU. Lihat GNU Lesser General Public
17 * Lisensi untuk lebih jelasnya.
18 *
19 * Anda seharusnya telah menerima salinan GNU Lesser General Public
20 * Lisensi bersama dengan perpustakaan ini; jika tidak, tulis ke Perangkat Lunak Bebas
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22 * AS.
23 *
24 * [Java adalah merek dagang atau merek dagang terdaftar dari Sun Microsystems, Inc.
25 * di Amerika Serikat dan negara-negara lain.]
26 *
27 * --------------------
28 * SerialDateTests.java
29 * --------------------
30 * (C) Hak Cipta 2001-2005, oleh Object Refinery Limited.
31 *
32 * Penulis Asli: David Gilbert (untuk Object Refinery Limited);
33 * Kontributor: -;
34 *
35 * $ Id: SerialDateTests.java, v 1.6 2005/11/16 15:58:40 taqua Exp $
36 *
37 * Perubahan
38 * -------
39 * 15-Nov-2001: Versi 1 (DG);
40 * 25-Jun-2002: Dihapus impor yang tidak perlu (DG);
41 * 24-Okt-2002: Memperbaiki kesalahan yang dilaporkan oleh Checkstyle (DG);
42 * 13-Mar-2003: Uji serialisasi tambahan (DG);
43 * 05-Jan-2005: Uji tambah untuk laporan bug 1096282 (DG);
44 *
45 * /
46
47 paket org.jfree.date.junit;
48
49 impor java.io.ByteArrayInputStream;
50 impor java.io.ByteArrayOutputStream;
51 impor java.io.ObjectInput;
52 impor java.io.ObjectInputStream;
53 impor java.io.ObjectOutput;
54 impor java.io.ObjectOutputStream;
55
56 import junit.framework.Test;
57 import junit.framework.TestCase;
58 import junit.framework.TestSuite;
59
60 import org.jfree.date.MonthConstants;
61 import org.jfree.date.SerialDate;
62

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 313/365
3/12/2020 www.it-ebooks.info
Halaman 398

Lampiran B: org.jfree.date.SerialDate 367

Listing B-2 (lanjutan)


SerialDateTest.java
63 / **
64 * Beberapa tes JUnit untuk kelas {@link SerialDate}.
65 * /
66 SerialDateTests kelas publik memperpanjang TestCase {
67
68 / ** Tanggal yang mewakili 9 November * * /
69 nov9Y2001 SerialDate pribadi;
70
71 / **
72 * Membuat test case baru.
73 *
74 * @param beri nama namanya.
75 * /
76 SerialDateTests publik (nama String akhir) {
77 super (nama);
78}
79
80 / **
81 * Mengembalikan suite uji untuk pelari uji JUnit.
82 *
83 * @return Suite uji.
84 * /
85 Test suite statis publik () {
86 kembalikan TestSuite baru (SerialDateTests.class);
87}
88
89 / **
90 * Masalah diatur.
91 * /
92 setup void terlindungi () {
93 this.nov9Y2001 = SerialDate.createInstance (9, MonthConstants.NOVEMBER, 2001);
94}
95
96 / **
97 * 9 Nov 2001 ditambah dua bulan seharusnya 9 Jan 2002.
98 * /
99 public void testAddMonthsTo9Nov2001 () {
100 final SerialDate jan9Y2002 = SerialDate.addMonths (2, this.nov9Y2001);
101 SerialDate answer final = SerialDate.createInstance (9, 1, 2002);
102 assertEquals (jawaban, jan9Y2002);
103}
104
105 / **
106 * Kasing uji untuk bug yang dilaporkan, sekarang diperbaiki.
107 * /
108 public void testAddMonthsTo5Oct2003 () {
109 final SerialDate d1 = SerialDate.createInstance (5, MonthConstants.OCTOBER, 2003);
110 final SerialDate d2 = SerialDate.addMonths (2, d1);
111 assertEquals (d2, SerialDate.createInstance (5, MonthConstants.DECEMBER, 2003));
112}
113
114 / **
115 * Kasing uji untuk bug yang dilaporkan, sekarang diperbaiki.
116 * /
117 public void testAddMonthsTo1Jan2003 () {
118 final SerialDate d1 = SerialDate.createInstance (1, MonthConstants.JANUARY, 2003);
119 final SerialDate d2 = SerialDate.addMonths (0, d1);
120 assertEquals (d2, d1);
121}
122
123 / **
124 * Senin sebelum Jumat, 9 November 2001 adalah 5 November.

www.it-ebooks.info

Halaman 399

368 Lampiran B: org.jfree.date.SerialDate

Listing B-2 (lanjutan)


SerialDateTest.java
125 * /
126 public void testMondayPrecedingFriday9Nov2001 () {
127 SerialDate mondayBefore = SerialDate.getPreviousDayOfWeek (
128 SerialDate.MONDAY, this.nov9Y2001
129 );
130 assertEquals (5, mondayBefore.getDayOfMonth ());
131}

https://translate.googleusercontent.com/translate_f 314/365
3/12/2020 www.it-ebooks.info
132
133 / **
134 * Senin setelah Jumat 9 November 2001 harus 12 November.
135 * /
136 public void testMondayFollowingFriday9Nov2001 () {
137 SerialDate mondayAfter = SerialDate.getFollowingDayOfWeek (
138 SerialDate.MONDAY, this.nov9Y2001
139 );
140 assertEquals (12, mondayAfter.getDayOfMonth ());
141}
142
143 / **
144 * Senin terdekat Jumat 9 November 2001 adalah 12 November.
145 * /
146 public void testMondayNearestFriday9Nov2001 () {
147 SerialDate mondayNearest = SerialDate.getNearestDayOfWeek (
148 SerialDate.MONDAY, this.nov9Y2001
149 );
150 assertEquals (12, mondayNearest.getDayOfMonth ());
151}
152
153 / **
154 * Hari Senin terdekat dengan 22 Januari 1970 jatuh pada tanggal 19.
155 * /
156 public void testMondayNearest22Jan1970 () {
157 SerialDate jan22Y1970 = SerialDate.createInstance (22, MonthConstants.JANUARY, 1970);
158 SerialDate mondayNearest = SerialDate.getNearestDayOfWeek (SerialDate.MONDAY, jan22Y1970);
159 assertEquals (19, mondayNearest.getDayOfMonth ());
160}
161
162 / **
163 * Masalah bahwa konversi hari ke string mengembalikan hasil yang tepat. Sebenarnya ini
164 * hasil tergantung pada Lokal sehingga tes ini perlu dimodifikasi.
165 * /
166 public void testWeekdayCodeToString () {
167
168 final String test = SerialDate.weekdayCodeToString (SerialDate.SATURDAY);
169 assertEquals ("Saturday", test);
170
171}
172
173 / **
174 * Tes konversi string ke hari kerja. Perhatikan bahwa tes ini akan gagal jika
175 * lokal default tidak menggunakan nama hari kerja bahasa Inggris ... merancang tes yang lebih baik!
176 * /
177 public void testStringToWeekday () {
178
179 int weekday = SerialDate.stringToWeekdayCode ("Wednesday");
180 assertEquals (SerialDate.WEDNESDAY, weekday);
181
182 weekday = SerialDate.stringToWeekdayCode ("Wednesday");
183 assertEquals (SerialDate.WEDNESDAY, weekday);
184

www.it-ebooks.info

Halaman 400

Lampiran B: org.jfree.date.SerialDate 369

Listing B-2 (lanjutan)


SerialDateTest.java
185 weekday = SerialDate.stringToWeekdayCode ("Wed");
186 assertEquals (SerialDate.WEDNESDAY, weekday);
187
188}
189
190 / **
191 * Uji konversi string menjadi sebulan. Perhatikan bahwa tes ini akan gagal jika
192 * lokal default tidak menggunakan nama bulan Bahasa Inggris ... buat tes yang lebih baik!
193 * /
194 public void testStringToMonthCode () {
195
196 int m = SerialDate.stringToMonthCode ("Januari");
197 assertEquals (MonthConstants.JANUARI, m);
198
199 m = SerialDate.stringToMonthCode ("Januari");
200 assertEquals (MonthConstants.JANUARI, m);
201
202 m = SerialDate.stringToMonthCode ("Jan");
203 assertEquals (MonthConstants.JANUARI, m);
204
205}
206
207 / **
208 * Menguji konversi kode bulan ke string.
209 * /
210 public void testMonthCodeToStringCode () {
211
212 final String test = SerialDate.monthCodeToString (MonthConstants.DECEMBER);
213 assertEquals ("December", test);
214
215}
216
217 / **

https://translate.googleusercontent.com/translate_f 315/365
3/12/2020 www.it-ebooks.info
218 * 1900 bukan tahun kabisat.
219 * /
220 public void testIsNotLeapYear1900 () {
221 assertTrue (! SerialDate.isLeapYear (1900));
222}
223
224 / **
225 * 2000 adalah tahun kabisat.
226 * /
227 public void testIsLeapYear2000 () {
228 assertTrue (SerialDate.isLeapYear (2000));
229}
230
231 / **
232 * Jumlah tahun kabisat dari tahun 1900 hingga 1899 adalah 0.
233 * /
234 public void testLeapYearCount1899 () {
235 assertEquals (SerialDate.leapYearCount (1899), 0);
236}
237
238 / **
239 * Jumlah tahun kabisat dari tahun 1900 hingga tahun 1903 adalah 0.
240 * /
241 public void testLeapYearCount1903 () {
242 assertEquals (SerialDate.leapYearCount (1903), 0);
243}
244
245 / **
246 * Jumlah tahun kabisat dari tahun 1900 hingga tahun 1904 adalah 1.
247 * /

www.it-ebooks.info

Halaman 401

370 Lampiran B: org.jfree.date.SerialDate

Listing B-2 (lanjutan)


SerialDateTest.java
248 public void testLeapYearCount1904 () {
249 assertEquals (SerialDate.leapYearCount (1904), 1);
250}
251
252 / **
253 * Jumlah tahun kabisat dari tahun 1900 hingga tahun 1999 adalah 24.
254 * /
255 public void testLeapYearCount1999 () {
256 assertEquals (SerialDate.leapYearCount (1999), 24);
257}
258
259 / **
260 * Jumlah tahun kabisat dari tahun 1900 hingga tahun 2000 adalah 25.
261 * /
262 public void testLeapYearCount2000 () {
263 assertEquals (SerialDate.leapYearCount (2000), 25);
264}
265
266 / **
267 * Serialize sebuah instance, pulihkan, dan periksa untuk kesetaraan.
268 * /
269 public void testSerialization () {
270
271 SerialDate d1 = SerialDate.createInstance (15, 4, 2000);
272 SerialDate d2 = null;
273
274 coba {
275 ByteArrayOutputStream buffer = new ByteArrayOutputStream ();
276 ObjectOutput out = ObjectOutputStream (buffer) baru;
277 out.writeObject (d1);
278 out.close ();
279
280 ObjectInput in = ObjectInputStream baru (
ByteArrayInputStream baru (buffer.toByteArray ()));
281 d2 = (SerialDate) in.readObject ();
282 melampirkan();
283 }
284 catch (Exception e) {
285 System.out.println (e.toString ());
286 }
287 assertEquals (d1, d2);
288
289}
290
291 / **
292 * Tes untuk laporan bug 1096282 (sekarang sudah diperbaiki).
293 * /
294 public void test1096282 () {
295 SerialDate d = SerialDate.createInstance (29, 2, 2004);
296 d = SerialDate.addYears (1, d);
297 SerialDate diharapkan = SerialDate.createInstance (28, 2, 2005);
298 assertTrue (d.isOn (diharapkan));
299}
300
301 / **
302 * Tes lain-lain untuk metode addMonths ().
303 * /
304 public void testAddMonths () {
305 SerialDate d1 = SerialDate.createInstance (31, 5, 2004);

https://translate.googleusercontent.com/translate_f 316/365
3/12/2020 www.it-ebooks.info
306

www.it-ebooks.info

Halaman 402

Lampiran B: org.jfree.date.SerialDate 371

Listing B-2 (lanjutan)


SerialDateTest.java
307 SerialDate d2 = SerialDate.addMonths (1, d1);
308 assertEquals (30, d2.getDayOfMonth ());
309 assertEquals (6, d2.getMonth ());
310 assertEquals (2004, d2.getYYYY ());
311
312 SerialDate d3 = SerialDate.addMonths (2, d1);
313 assertEquals (31, d3.getDayOfMonth ());
314 assertEquals (7, d3.getMonth ());
315 assertEquals (2004, d3.getYYYY ());
316
317 SerialDate d4 = SerialDate.addMonths (1, SerialDate.addMonths (1, d1));
318 assertEquals (30, d4.getDayOfMonth ());
319 assertEquals (7, d4.getMonth ());
320 assertEquals (2004, d4.getYYYY ());
321}
322}

www.it-ebooks.info

Halaman 403

https://translate.googleusercontent.com/translate_f 317/365
3/12/2020 www.it-ebooks.info

372 Lampiran B: org.jfree.date.SerialDate

Listing B-3
MonthConstants.java
1 / * =============================================== =========================
2 * JCommon: perpustakaan tujuan umum gratis untuk platform Java (tm)
3 * ================================================ ========================
4*
5 * (C) Hak Cipta 2000-2005, oleh Object Refinery Limited dan Kontributor.
6*
7 * Info Proyek: http://www.jfree.org/jcommon/index.html
8*
9 * Perpustakaan ini adalah perangkat lunak bebas; Anda dapat mendistribusikan ulang dan / atau memodifikasinya
10 * berdasarkan ketentuan Lisensi Publik Umum GNU yang diterbitkan oleh
11 * Yayasan Perangkat Lunak Bebas; baik versi 2.1 dari Lisensi, atau
12 * (sesuai pilihan Anda) versi yang lebih baru.
13 *
14 * Perpustakaan ini didistribusikan dengan harapan akan bermanfaat, tetapi
15 * TANPA GARANSI APA PUN; bahkan tanpa jaminan tersirat dari DAGANGAN
16 * atau KESESUAIAN UNTUK TUJUAN TERTENTU. Lihat GNU Lesser General Public
17 * Lisensi untuk lebih jelasnya.
18 *
19 * Anda seharusnya telah menerima salinan GNU Lesser General Public
20 * Lisensi bersama dengan perpustakaan ini; jika tidak, tulis ke Perangkat Lunak Bebas
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22 * AS.
23 *
24 * [Java adalah merek dagang atau merek dagang terdaftar dari Sun Microsystems, Inc.
25 * di Amerika Serikat dan negara-negara lain.]
26 *
27 * -------------------
28 * MonthConstants.java
29 * -------------------
30 * (C) Hak Cipta 2002, 2003, oleh Object Refinery Limited.
31 *
32 * Penulis Asli: David Gilbert (untuk Object Refinery Limited);
33 * Kontributor: -;
34 *
35 * $ Id: MonthConstants.java, v 1.4 2005/11/16 15:58:40 taqua Exp $
36 *
37 * Perubahan
38 * -------
39 * 29-Mei-2002: Versi 1 (kode dipindahkan dari kelas SerialDate) (DG);
40 *
41 * /
42
43 paket org.jfree.date;
44
45 / **
46 * Konstanta yang berguna selama berbulan-bulan. Perhatikan bahwa ini TIDAK setara dengan
47 * konstanta didefinisikan oleh java.util.Calendar (di mana JANUARY = 0 dan DESEMBER = 11).
48 * <P>
49 * Digunakan oleh kelas SerialDate dan RegularTimePeriod.
50 *
51 * @author David Gilbert
52 * /
53 antarmuka publik MonthConstants {
54
55 / ** Konstan untuk Januari. * /
56 public int static final JANUARI = 1;
57
58 / ** Konstan untuk bulan Februari. * /
59 FEBRUARI public int static final = 2;
60

www.it-ebooks.info

Halaman 404

Lampiran B: org.jfree.date.SerialDate 373

Listing B-3 (lanjutan)


MonthConstants.java
61 / ** Konstan untuk bulan Maret. * /
62 int akhir public static MARET = 3;
63
64 / ** Konstan untuk bulan April. * /
65 public int static final APRIL = 4;
66
67 / ** Konstan untuk bulan Mei. * /
68 int akhir public static MUNGKIN = 5;
69
70 / ** Konstan untuk bulan Juni. * /
71 JUNI public int static final = 6;
72
73 / ** Konstan untuk bulan Juli. * /
74 int akhir public static JULI = 7;

https://translate.googleusercontent.com/translate_f 318/365
3/12/2020 www.it-ebooks.info
75
76 / ** Konstan untuk Agustus. * /
77 int akhir public static AGUSTUS = 8;
78
79 / ** Konstan untuk bulan September. * /
80 SEPTEMBER public int static final = 9;
81
82 / ** Konstan untuk Oktober. * /
83 int final public static OKTOBER = 10;
84
85 / ** Konstan untuk November. * /
86 public int static final NOVEMBER = 11;
87
88 / ** Konstan untuk Desember. * /
89 DES int final public static = 12;
90
91}

www.it-ebooks.info

Halaman 405

374 Lampiran B: org.jfree.date.SerialDate

Listing B-4
BobsSerialDateTest.java
1 paket org.jfree.date.junit;
2
3 impor junit.framework.TestCase;
4 import org.jfree.date. *;
5 impor org.jfree.date.SerialDate statis. *;
6
7 impor java.util. *;
8
9 BobsSerialDateTest kelas publik memperluas TestCase {
10
11 public void testIsValidWeekdayCode () melempar Exception {
12 untuk (int day = 1; hari <= 7; hari ++)
13 assertTrue (isValidWeekdayCode (day));
14 assertFalse (isValidWeekdayCode (0));
15 assertFalse (isValidWeekdayCode (8));
16}
17
18 public void testStringToWeekdayCode () melempar Exception {
19
20 assertEquals (-1, stringToWeekdayCode ("Hello"));
21 assertEquals (SENIN, stringToWeekdayCode ("Monday"));
22 assertEquals (SENIN, stringToWeekdayCode ("Sen"));
23 // todo assertEquals (SENIN, stringToWeekdayCode ("Senin"));
24 // assertEquals (SENIN, stringToWeekdayCode ("SENIN"));
25 // assertEquals (SENIN, stringToWeekdayCode ("mon"));
26
27 assertEquals (TUESDAY, stringToWeekdayCode ("Tuesday"));
28 assertEquals (TUESDAY, stringToWeekdayCode ("Tue"));
29 // assertEquals (TUESDAY, stringToWeekdayCode ("tuesday"));
30 // assertEquals (TUESDAY, stringToWeekdayCode ("TUESDAY"));
31 // assertEquals (SELESDAY, stringToWeekdayCode ("tue"));
32 // assertEquals (TUESDAY, stringToWeekdayCode ("tues"));
33
34 assertEquals (WEDNESDAY, stringToWeekdayCode ("Wednesday"));
35 assertEquals (WEDNESDAY, stringToWeekdayCode ("Wed"));
36 // assertEquals (WEDNESDAY, stringToWeekdayCode ("wednesday"));
37 // assertEquals (WEDNESDAY, stringToWeekdayCode ("WEDNESDAY"));
38 // assertEquals (WEDNESDAY, stringToWeekdayCode ("wed"));
39
40 assertEquals (KAMIS, stringToWeekdayCode ("Kamis"));

https://translate.googleusercontent.com/translate_f 319/365
3/12/2020 www.it-ebooks.info
41 assertEquals (KAMIS, stringToWeekdayCode ("Thu"));
42 // assertEquals (KAMIS, stringToWeekdayCode ("kamis"));
43 // assertEquals (THURSDAY, stringToWeekdayCode ("THURSDAY"));
44 // assertEquals (KAMIS, stringToWeekdayCode ("thu"));
45 // assertEquals (THURSDAY, stringToWeekdayCode ("thurs"));
46
47 assertEquals (JUMAT, stringToWeekdayCode ("Jumat"));
48 assertEquals (JUMAT, stringToWeekdayCode ("Fri"));
49 // assertEquals (JUMAT, stringToWeekdayCode ("friday"));
50 // assertEquals (JUMAT, stringToWeekdayCode ("JUMAT"));
51 // assertEquals (JUMAT, stringToWeekdayCode ("fri"));
52
53 assertEquals (SATURDAY, stringToWeekdayCode ("Saturday"));
54 assertEquals (SATURDAY, stringToWeekdayCode ("Sat"));
55 // assertEquals (SATURDAY, stringToWeekdayCode ("saturday"));
56 // assertEquals (SATURDAY, stringToWeekdayCode ("SATURDAY"));
57 // assertEquals (SATURDAY, stringToWeekdayCode ("sat"));
58
59 assertEquals (MINGGU, stringToWeekdayCode ("Sunday"));
60 assertEquals (MINGGU, stringToWeekdayCode ("Sun"));
61 // assertEquals (MINGGU, stringToWeekdayCode ("sunday"));
62 // assertEquals (MINGGU, stringToWeekdayCode ("MINGGU"));
63 // assertEquals (MINGGU, stringToWeekdayCode ("sun"));
64}
65

www.it-ebooks.info

Halaman 406

Lampiran B: org.jfree.date.SerialDate 375

Listing B-4 (lanjutan)


BobsSerialDateTest.java
66 public void testWeekdayCodeToString () melempar Exception {
67 assertEquals ("Sunday", weekdayCodeToString (SUNDAY));
68 assertEquals ("Monday", weekdayCodeToString (SENIN));
69 assertEquals ("Tuesday", weekdayCodeToString (TUESDAY));
70 assertEquals ("Wednesday", weekdayCodeToString (WEDNESDAY));
71 assertEquals ("Kamis", weekdayCodeToString (KAMIS));
72 assertEquals ("Friday", weekdayCodeToString (JUMAT));
73 assertEquals ("Saturday", weekdayCodeToString (SATURDAY));
74}
75
76 public void testIsValidMonthCode () melempar Exception {
77 untuk (int i = 1; i <= 12; i ++)
78 assertTrue (isValidMonthCode (i));
79 assertFalse (isValidMonthCode (0));
80 assertFalse (isValidMonthCode (13));
81}
82
83 public void testMonthToQuarter () melempar Exception {
84 assertEquals (1, monthCodeToQuarter (JANUARY));
85 assertEquals (1, monthCodeToQuarter (FEBRUARY));
86 assertEquals (1, monthCodeToQuarter (MARCH));
87 assertEquals (2, monthCodeToQuarter (APRIL));
88 assertEquals (2, monthCodeToQuarter (MAY));
89 assertEquals (2, monthCodeToQuarter (JUNE));
90 assertEquals (3, monthCodeToQuarter (JULY));
91 assertEquals (3, monthCodeToQuarter (AGUSTUS));
92 assertEquals (3, monthCodeToQuarter (SEPTEMBER));
93 assertEquals (4, monthCodeToQuarter (OKTOBER));
94 assertEquals (4, monthCodeToQuarter (NOVEMBER));
95 assertEquals (4, monthCodeToQuarter (DESEMBER));
96
97 coba {
98 monthCodeToQuarter (-1);
99 gagal ("Kode Bulan Tidak Valid harus membuang pengecualian");
100} tangkapan (IllegalArgumentException e) {
101}
102}
103
104 public void testMonthCodeToString () melempar Exception {
105 assertEquals ("Januari", monthCodeToString (JANUARY));
106 assertEquals ("Februari", monthCodeToString (FEBRUARY));
107 assertEquals ("March", monthCodeToString (MARCH));
108 assertEquals ("April", monthCodeToString (APRIL));
109 assertEquals ("May", monthCodeToString (MAY));
110 assertEquals ("June", monthCodeToString (JUNE));
111 assertEquals ("July", monthCodeToString (JULY));
112 assertEquals ("Agustus", monthCodeToString (AGUSTUS));
113 assertEquals ("September", monthCodeToString (SEPTEMBER));
114 assertEquals ("Oktober", monthCodeToString (OKTOBER));
115 assertEquals ("November", monthCodeToString (NOVEMBER));
116 assertEquals ("December", monthCodeToString (DESEMBER));
117
118 assertEquals ("Jan", monthCodeToString (JANUARY, true));
119 assertEquals ("Feb", monthCodeToString (FEBRUARY, true));
120 assertEquals ("Mar", monthCodeToString (MARET, true));
121 assertEquals ("Apr", monthCodeToString (APRIL, true));
122 assertEquals ("May", monthCodeToString (MUNGKIN, benar));
123 assertEquals ("Jun", monthCodeToString (JUNE, true));
124 assertEquals ("Jul", monthCodeToString (JULI, benar));
125 assertEquals ("Aug", monthCodeToString (AGUSTUS, true));
126 assertEquals ("Sep", monthCodeToString (SEPTEMBER, true));
127 assertEquals ("Oct", monthCodeToString (OKTOBER, true));

https://translate.googleusercontent.com/translate_f 320/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 407

376 Lampiran B: org.jfree.date.SerialDate

Listing B-4 (lanjutan)


BobsSerialDateTest.java
128 assertEquals ("Nov", monthCodeToString (NOVEMBER, true));
129 assertEquals ("Dec", monthCodeToString (DESEMBER, true));
130
131 coba {
132 monthCodeToString (-1);
133 gagal ("Kode bulan tidak valid harus membuang pengecualian");
134} catch (IllegalArgumentException e) {
135}
136
137}
138
139 public void testStringToMonthCode () melempar Exception {
140 assertEquals (JANUARY, stringToMonthCode ("1"));
141 assertEquals (FEBRUARY, stringToMonthCode ("2"));
142 assertEquals (MARCH, stringToMonthCode ("3"));
143 assertEquals (APRIL, stringToMonthCode ("4"));
144 assertEquals (MEI, stringToMonthCode ("5"));
145 assertEquals (JUNE, stringToMonthCode ("6"));
146 assertEquals (JULY, stringToMonthCode ("7"));
147 assertEquals (AUGUST, stringToMonthCode ("8"));
148 assertEquals (SEPTEMBER, stringToMonthCode ("9"));
149 assertEquals (OKTOBER, stringToMonthCode ("10"));
150 assertEquals (NOVEMBER, stringToMonthCode ("11"));
151 assertEquals (DESEMBER, stringToMonthCode ("12"));
152
153 // todo assertEquals (-1, stringToMonthCode ("0"));
154 // assertEquals (-1, stringToMonthCode ("13"));
155
156 assertEquals (-1, stringToMonthCode ("Hello"));
157
158 untuk (int m = 1; m <= 12; m ++) {
159 assertEquals (m, stringToMonthCode (monthCodeToString (m, false)));
160 assertEquals (m, stringToMonthCode (monthCodeToString (m, true)));
161}
162
163 // assertEquals (1, stringToMonthCode ("jan"));
164 // assertEquals (2, stringToMonthCode ("feb"));
165 // assertEquals (3, stringToMonthCode ("mar"));
166 // assertEquals (4, stringToMonthCode ("apr"));
167 // assertEquals (5, stringToMonthCode ("may"));
168 // assertEquals (6, stringToMonthCode ("jun"));
169 // assertEquals (7, stringToMonthCode ("jul"));
170 // assertEquals (8, stringToMonthCode ("aug"));
171 // assertEquals (9, stringToMonthCode ("sep"));
172 // assertEquals (10, stringToMonthCode ("oct"));
173 // assertEquals (11, stringToMonthCode ("nov"));
174 // assertEquals (12, stringToMonthCode ("dec"));
175
176 // assertEquals (1, stringToMonthCode ("JAN"));
177 // assertEquals (2, stringToMonthCode ("FEB"));
178 // assertEquals (3, stringToMonthCode ("MAR"));
179 // assertEquals (4, stringToMonthCode ("APR"));
180 // assertEquals (5, stringToMonthCode ("MEI"));
181 // assertEquals (6, stringToMonthCode ("JUN"));
182 // assertEquals (7, stringToMonthCode ("JUL"));
183 // assertEquals (8, stringToMonthCode ("AUG"));
184 // assertEquals (9, stringToMonthCode ("SEP"));
185 // assertEquals (10, stringToMonthCode ("OCT"));
186 // assertEquals (11, stringToMonthCode ("NOV"));
187 // assertEquals (12, stringToMonthCode ("DEC"));
188
189 // assertEquals (1, stringToMonthCode ("january"));
190 // assertEquals (2, stringToMonthCode ("february"));

www.it-ebooks.info

Halaman 408

Lampiran B: org.jfree.date.SerialDate 377

https://translate.googleusercontent.com/translate_f 321/365
3/12/2020 www.it-ebooks.info

Listing B-4 (lanjutan)


BobsSerialDateTest.java
191 // assertEquals (3, stringToMonthCode ("march"));
192 // assertEquals (4, stringToMonthCode ("april"));
193 // assertEquals (5, stringToMonthCode ("may"));
194 // assertEquals (6, stringToMonthCode ("june"));
195 // assertEquals (7, stringToMonthCode ("juli"));
196 // assertEquals (8, stringToMonthCode ("august"));
197 // assertEquals (9, stringToMonthCode ("september"));
198 // assertEquals (10, stringToMonthCode ("oktober"));
199 // assertEquals (11, stringToMonthCode ("november"));
200 // assertEquals (12, stringToMonthCode ("desember"));
201
202 // assertEquals (1, stringToMonthCode ("JANUARY"));
203 // assertEquals (2, stringToMonthCode ("FEBRUARY"));
204 // assertEquals (3, stringToMonthCode ("MAR"));
205 // assertEquals (4, stringToMonthCode ("APRIL"));
206 // assertEquals (5, stringToMonthCode ("MEI"));
207 // assertEquals (6, stringToMonthCode ("JUNE"));
208 // assertEquals (7, stringToMonthCode ("JULY"));
209 // assertEquals (8, stringToMonthCode ("AGUSTUS"));
210 // assertEquals (9, stringToMonthCode ("SEPTEMBER"));
211 // assertEquals (10, stringToMonthCode ("OKTOBER"));
212 // assertEquals (11, stringToMonthCode ("NOVEMBER"));
213 // assertEquals (12, stringToMonthCode ("DESEMBER"));
214}
215
216 public void testIsValidWeekInMonthCode () melempar Exception {
217 untuk (int w = 0; w <= 4; w ++) {
218 assertTrue (isValidWeekInMonthCode (w));
219}
220 assertFalse (isValidWeekInMonthCode (5));
221}
222
223 public void testIsLeapYear () melempar Exception {
224 assertFalse (isLeapYear (1900));
225 assertFalse (isLeapYear (1901));
226 assertFalse (isLeapYear (1902));
227 assertFalse (isLeapYear (1903));
228 assertTrue (isLeapYear (1904));
229 assertTrue (isLeapYear (1908));
230 assertFalse (isLeapYear (1955));
231 assertTrue (isLeapYear (1964));
232 assertTrue (isLeapYear (1980));
233 assertTrue (isLeapYear (2000));
234 assertFalse (isLeapYear (2001));
235 assertFalse (isLeapYear (2100));
236}
237
238 public void testLeapYearCount () melempar Exception {
239 assertEquals (0, leapYearCount (1900));
240 assertEquals (0, leapYearCount (1901));
241 assertEquals (0, leapYearCount (1902));
242 assertEquals (0, leapYearCount (1903));
243 assertEquals (1, leapYearCount (1904));
244 assertEquals (1, leapYearCount (1905));
245 assertEquals (1, leapYearCount (1906));
246 assertEquals (1, leapYearCount (1907));
247 assertEquals (2, leapYearCount (1908));
248 assertEquals (24, leapYearCount (1999));
249 assertEquals (25, leapYearCount (2001));
250 assertEquals (49, leapYearCount (2101));
251 assertEquals (73, leapYearCount (2201));

www.it-ebooks.info

Halaman 409

378 Lampiran B: org.jfree.date.SerialDate

Listing B-4 (lanjutan)


BobsSerialDateTest.java
252 assertEquals (97, leapYearCount (2301));
253 assertEquals (122, leapYearCount (2401));
254}
255
256 public void testLastDayOfMonth () melempar Exception {
257 assertEquals (31, lastDayOfMonth (JANUARY, 1901));
258 assertEquals (28, lastDayOfMonth (FEBRUARY, 1901));
259 assertEquals (31, lastDayOfMonth (MARCH, 1901));
260 assertEquals (30, lastDayOfMonth (APRIL, 1901));
261 assertEquals (31, lastDayOfMonth (MEI, 1901));
262 assertEquals (30, lastDayOfMonth (JUNE, 1901));
263 assertEquals (31, lastDayOfMonth (JULY, 1901));
264 assertEquals (31, lastDayOfMonth (AUGUST, 1901));
265 assertEquals (30, lastDayOfMonth (SEPTEMBER, 1901));
266 assertEquals (31, lastDayOfMonth (OCTOBER, 1901));
267 assertEquals (30, lastDayOfMonth (NOVEMBER, 1901));
268 assertEquals (31, lastDayOfMonth (DESEMBER, 1901));
269 assertEquals (29, lastDayOfMonth (FEBRUARY, 1904));
270}
271

https://translate.googleusercontent.com/translate_f 322/365
3/12/2020 www.it-ebooks.info
272 SerialDate
273 public voidnewYears
testAddDays () melempar
= d (1, JANUARY, Exception
1900); {
274 assertEquals (d (2, JANUARY, 1900), addDays (1, newYears));
275 assertEquals (d (1, FEBRUARY, 1900), addDays (31, newYears));
276 assertEquals (d (1, JANUARY, 1901), addDays (365, newYears));
277 assertEquals (d (31, DESEMBER, 1904), addDays (5 * 365, newYears));
278}
279
280 private static SpreadsheetDate d (hari int, bulan int, tahun int) {return new
SpreadsheetDate (hari, bulan, tahun);}
281
282 public void testAddMonths () melempar Exception {
283 assertEquals (d (1, FEBRUARY, 1900), addMonths (1, d (1, JANUARY, 1900)));
284 assertEquals (d (28, FEBRUARY, 1900), addMonths (1, d (31, JANUARY, 1900)));
285 assertEquals (d (28, FEBRUARY, 1900), addMonths (1, d (30, JANUARY, 1900)));
286 assertEquals (d (28, FEBRUARY, 1900), addMonths (1, d (29, JANUARY, 1900)));
287 assertEquals (d (28, FEBRUARY, 1900), addMonths (1, d (28, JANUARY, 1900)));
288 assertEquals (d (27, FEBRUARY, 1900), addMonths (1, d (27, JANUARY, 1900)));
289
290 assertEquals (d (30, JUNE, 1900), addMonths (5, d (31, JANUARY, 1900)));
291 assertEquals (d (30, JUNE, 1901), addMonths (17, d (31, JANUARY, 1900)));
292
293 assertEquals (d (29, FEBRUARY, 1904), addMonths (49, d (31, JANUARY, 1900)));
294
295}
296
297 public void testAddYears () melempar Exception {
298 assertEquals (d (1, JANUARY, 1901), addYears (1, d (1, JANUARY, 1900)));
299 assertEquals (d (28, FEBRUARY, 1905), addYears (1, d (29, FEBRUARY, 1904)));
300 assertEquals (d (28, FEBRUARY, 1905), addYears (1, d (28, FEBRUARY, 1904)));
301 assertEquals (d (28, FEBRUARY, 1904), addYears (1, d (28, FEBRUARY, 1903)));
302}
303
304 public void testGetPreviousDayOfWeek () melempar Exception {
305 assertEquals (d (24, FEBRUARY, 2006), getPreviousDayOfWeek (JUMAT, d (1, MARET, 2006)));
306 assertEquals (d (22, FEBRUARY, 2006), getPreviousDayOfWeek (WEDNESDAY, d (1, MARET, 2006)));
307 assertEquals (d (29, FEBRUARY, 2004), getPreviousDayOfWeek (MINGGU, d (3, MARET, 2004)));
308 assertEquals (d (29, DESEMBER, 2004), getPreviousDayOfWeek (WEDNESDAY, d (5, JANUARY, 2005)));
309
310 coba {
311 getPreviousDayOfWeek (-1, d (1, JANUARY, 2006));
312 gagal ("Kode hari tidak valid harus memberikan pengecualian");

www.it-ebooks.info

Halaman 410

Lampiran B: org.jfree.date.SerialDate 379

Listing B-4 (lanjutan)


BobsSerialDateTest.java
313} tangkapan (IllegalArgumentException e) {
314}
315}
316
317 public void testGetFollowingDayOfWeek () melempar Exception {
318 // assertEquals (d (1, JANUARY, 2005), getFollowingDayOfWeek (SATURDAY, d (25, DESEMBER, 2004)));
319 assertEquals (d (1, JANUARY, 2005), getFollowingDayOfWeek (SATURDAY, d (26, DESEMBER, 2004)));
320 assertEquals (d (3, MARET, 2004), getFollowingDayOfWeek (WEDNESDAY, d (28, FEBRUARY, 2004)));
321
322 coba {
323 getFollowingDayOfWeek (-1, d (1, JANUARY, 2006));
324 gagal ("Kode hari tidak valid harus memberikan pengecualian");
325} catch (IllegalArgumentException e) {
326}
327}
328
329 public void testGetNearestDayOfWeek () melempar Exception {
330 assertEquals (d (16, APRIL, 2006), getNearestDayOfWeek (MINGGU, d (16, APRIL, 2006)));
331 assertEquals (d (16, APRIL, 2006), getNearestDayOfWeek (MINGGU, d (17, APRIL, 2006)));
332 assertEquals (d (16, APRIL, 2006), getNearestDayOfWeek (MINGGU, d (18, APRIL, 2006)));
333 assertEquals (d (16, APRIL, 2006), getNearestDayOfWeek (MINGGU, d (19, APRIL, 2006)));
334 assertEquals (d (23, APRIL, 2006), getNearestDayOfWeek (MINGGU, d (20, APRIL, 2006)));
335 assertEquals (d (23, APRIL, 2006), getNearestDayOfWeek (MINGGU, d (21, APRIL, 2006)));
336 assertEquals (d (23, APRIL, 2006), getNearestDayOfWeek (MINGGU, d (22, APRIL, 2006)));
337
338 // todo assertEquals (d (17, APRIL, 2006), getNearestDayOfWeek (SENIN, d (16, APRIL, 2006)));
339 assertEquals (d (17, APRIL, 2006), getNearestDayOfWeek (SENIN, d (17, APRIL, 2006)));
340 assertEquals (d (17, APRIL, 2006), getNearestDayOfWeek (SENIN, d (18, APRIL, 2006)));
341 assertEquals (d (17, APRIL, 2006), getNearestDayOfWeek (SENIN, d (19, APRIL, 2006)));
342 assertEquals (d (17, APRIL, 2006), getNearestDayOfWeek (SENIN, d (20, APRIL, 2006)));
343 assertEquals (d (24, APRIL, 2006), getNearestDayOfWeek (SENIN, d (21, APRIL, 2006)));
344 assertEquals (d (24, APRIL, 2006), getNearestDayOfWeek (SENIN, d (22, APRIL, 2006)));
345
346 // assertEquals (d (18, APRIL, 2006), getNearestDayOfWeek (SELASA, d (16, APRIL, 2006)));
347 // assertEquals (d (18, APRIL, 2006), getNearestDayOfWeek (SELASA, d (17, APRIL, 2006)));
348 assertEquals (d (18, APRIL, 2006), getNearestDayOfWeek (SELASA, d (18, APRIL, 2006)));
349 assertEquals (d (18, APRIL, 2006), getNearestDayOfWeek (SELASA, d (19, APRIL, 2006)));
350 assertEquals (d (18, APRIL, 2006), getNearestDayOfWeek (SELASA, d (20, APRIL, 2006)));
351 assertEquals (d (18, APRIL, 2006), getNearestDayOfWeek (SELASA, d (21, APRIL, 2006)));
352 assertEquals (d (25, APRIL, 2006), getNearestDayOfWeek (SELASA, d (22, APRIL, 2006)));
353
354 // assertEquals (d (19, APRIL, 2006), getNearestDayOfWeek (WEDNESDAY, d (16, APRIL, 2006)));
355 // assertEquals (d (19, APRIL, 2006), getNearestDayOfWeek (WEDNESDAY, d (17, APRIL, 2006)));
356 // assertEquals (d (19, APRIL, 2006), getNearestDayOfWeek (WEDNESDAY, d (18, APRIL, 2006)));
357 assertEquals (d (19, APRIL, 2006), getNearestDayOfWeek (WEDNESDAY, d (19, APRIL, 2006)));
358 assertEquals (d (19, APRIL, 2006), getNearestDayOfWeek (WEDNESDAY, d (20, APRIL, 2006)));
359 assertEquals (d (19, APRIL, 2006), getNearestDayOfWeek (WEDNESDAY, d (21, APRIL, 2006)));

https://translate.googleusercontent.com/translate_f 323/365
3/12/2020 www.it-ebooks.info
360 assertEquals (d (19, APRIL, 2006), getNearestDayOfWeek (WEDNESDAY, d (22, APRIL, 2006)));
361
362 // assertEquals (d (13, APRIL, 2006), getNearestDayOfWeek (KAMIS, d (16, APRIL, 2006)));
363 // assertEquals (d (20, APRIL, 2006), getNearestDayOfWeek (KAMIS, d (17, APRIL, 2006)));
364 // assertEquals (d (20, APRIL, 2006), getNearestDayOfWeek (KAMIS, d (18, APRIL, 2006)));
365 // assertEquals (d (20, APRIL, 2006), getNearestDayOfWeek (THURSDAY, d (19, APRIL, 2006)));
366 assertEquals (d (20, APRIL, 2006), getNearestDayOfWeek (KAMIS, d (20, APRIL, 2006)));
367 assertEquals (d (20, APRIL, 2006), getNearestDayOfWeek (KAMIS, d (21, APRIL, 2006)));
368 assertEquals (d (20, APRIL, 2006), getNearestDayOfWeek (KAMIS, d (22, APRIL, 2006)));
369
370 // assertEquals (d (14, APRIL, 2006), getNearestDayOfWeek (FRIDAY, d (16, APRIL, 2006)));
371 // assertEquals (d (14, APRIL, 2006), getNearestDayOfWeek (FRIDAY, d (17, APRIL, 2006)));
372 // assertEquals (d (21, APRIL, 2006), getNearestDayOfWeek (FRIDAY, d (18, APRIL, 2006)));
373 // assertEquals (d (21, APRIL, 2006), getNearestDayOfWeek (FRIDAY, d (19, APRIL, 2006)));
374 // assertEquals (d (21, APRIL, 2006), getNearestDayOfWeek (FRIDAY, d (20, APRIL, 2006)));

www.it-ebooks.info

Halaman 411

380 Lampiran B: org.jfree.date.SerialDate

Listing B-4 (lanjutan)


BobsSerialDateTest.java
375 assertEquals (d (21, APRIL, 2006), getNearestDayOfWeek (FRIDAY, d (21, APRIL, 2006)));
376 assertEquals (d (21, APRIL, 2006), getNearestDayOfWeek (FRIDAY, d (22, APRIL, 2006)));
377
378 // assertEquals (d (15, APRIL, 2006), getNearestDayOfWeek (SATURDAY, d (16, APRIL, 2006)));
379 // assertEquals (d (15, APRIL, 2006), getNearestDayOfWeek (SATURDAY, d (17, APRIL, 2006)));
380 // assertEquals (d (15, APRIL, 2006), getNearestDayOfWeek (SATURDAY, d (18, APRIL, 2006)));
381 // assertEquals (d (22, APRIL, 2006), getNearestDayOfWeek (SATURDAY, d (19, APRIL, 2006)));
382 // assertEquals (d (22, APRIL, 2006), getNearestDayOfWeek (SATURDAY, d (20, APRIL, 2006)));
383 // assertEquals (d (22, APRIL, 2006), getNearestDayOfWeek (SATURDAY, d (21, APRIL, 2006)));
384 assertEquals (d (22, APRIL, 2006), getNearestDayOfWeek (SATURDAY, d (22, APRIL, 2006)));
385
386 coba {
387 getNearestDayOfWeek (-1, d (1, JANUARY, 2006));
388 gagal ("Kode hari tidak valid harus memberikan pengecualian");
389} tangkapan (IllegalArgumentException e) {
390}
391}
392
393 public void testEndOfCurrentMonth () melempar Exception {
394 SerialDate d = SerialDate.createInstance (2);
395 assertEquals (d (31, JANUARY, 2006), d.getEndOfCurrentMonth (d (1, JANUARY, 2006)));
396 assertEquals (d (28, FEBRUARY, 2006), d.getEndOfCurrentMonth (d (1, FEBRUARY, 2006)));
397 assertEquals (d (31, MARCH, 2006), d.getEndOfCurrentMonth (d (1, MARCH, 2006)));
398 assertEquals (d (30, APRIL, 2006), d.getEndOfCurrentMonth (d (1, APRIL, 2006)));
399 assertEquals (d (31, MEI, 2006), d.getEndOfCurrentMonth (d (1, MEI, 2006))));
400 assertEquals (d (30, JUNE, 2006), d.getEndOfCurrentMonth (d (1, JUNE, 2006)));
401 assertEquals (d (31, JULY, 2006), d.getEndOfCurrentMonth (d (1, JULY, 2006)));
402 assertEquals (d (31, AUGUST, 2006), d.getEndOfCurrentMonth (d (1, AUGUST, 2006)));
403 assertEquals (d (30, SEPTEMBER, 2006), d.getEndOfCurrentMonth (d (1, SEPTEMBER, 2006)));
404 assertEquals (d (31, OKTOBER, 2006), d.getEndOfCurrentMonth (d (1, OKTOBER, 2006)));
405 assertEquals (d (30, NOVEMBER, 2006), d.getEndOfCurrentMonth (d (1, NOVEMBER, 2006)));
406 assertEquals (d (31, DESEMBER, 2006), d.getEndOfCurrentMonth (d (1, DESEMBER, 2006)));
407 assertEquals (d (29, FEBRUARY, 2008), d.getEndOfCurrentMonth (d (1, FEBRUARY, 2008)));
408}
409
410 public void testWeekInMonthToString () melempar Exception {
411 assertEquals ("First", weekInMonthToString (FIRST_WEEK_IN_MONTH));
412 assertEquals ("Second", weekInMonthToString (SECOND_WEEK_IN_MONTH));
413 assertEquals ("Third", weekInMonthToString (THIRD_WEEK_IN_MONTH));
414 assertEquals ("Keempat", weekInMonthToString (FOURTH_WEEK_IN_MONTH));
415 assertEquals ("Last", weekInMonthToString (LAST_WEEK_IN_MONTH));
416
417 // todo try {
418 // weekInMonthToString (-1);
419 // gagal ("Kode minggu tidak valid harus memberikan pengecualian");
420 //} catch (IllegalArgumentException e) {
421 //}
422}
423
424 public void testRelativeToString () melempar Exception {
425 assertEquals ("Sebelumnya", relativeToString (PRECEDING));
426 assertEquals ("Nearest", relativeToString (NEAREST));
427 assertEquals ("Mengikuti", relativeToString (BERIKUT));
428
429 // todo try {
430 // relatifToString (-1000);
431 // gagal ("Kode relatif tidak valid harus membuang pengecualian");
432 //} catch (IllegalArgumentException e) {
433 //}
434}
435

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 324/365
3/12/2020 www.it-ebooks.info

Halaman 412

Lampiran B: org.jfree.date.SerialDate 381

Listing B-4 (lanjutan)


BobsSerialDateTest.java
436 public void testCreateInstanceFromDDMMYYY () melempar Pengecualian {
437 SerialDate date = createInstance (1, JANUARY, 1900);
438 assertEquals (1, date.getDayOfMonth ());
439 assertEquals (JANUARY, date.getMonth ());
440 assertEquals (1900, date.getYYYY ());
441 assertEquals (2, date.toSerial ());
442}
443
444 public void testCreateInstanceFromSerial () melempar Exception {
445 assertEquals (d (1, JANUARY, 1900), createInstance (2));
446 assertEquals (d (1, JANUARY, 1901), createInstance (367));
447}
448
449 public void testCreateInstanceFromJavaDate () melempar Exception {
450 assertEquals (d (1, JANUARY, 1900),
createInstance (GregorianCalendar baru (1900,0,1) .getTime ()));
451 assertEquals (d (1, JANUARY, 2006),
createInstance (GregorianCalendar baru (2006,0,1) .getTime ()));
452}
453
454 public static void main (String [] args) {
455 junit.textui.TestRunner.run (BobsSerialDateTest.class);
456}
457}

www.it-ebooks.info

Halaman 413

382 Lampiran B: org.jfree.date.SerialDate

Listing B-5
SpreadsheetDate.java
1 / * =============================================== =========================

https://translate.googleusercontent.com/translate_f 325/365
3/12/2020 www.it-ebooks.info
2 * JCommon: perpustakaan tujuan umum gratis untuk platform Java (tm)
3 * ================================================ ========================
4*
5 * (C) Hak Cipta 2000-2005, oleh Object Refinery Limited dan Kontributor.
6*
7 * Info Proyek: http://www.jfree.org/jcommon/index.html
8*
9 * Perpustakaan ini adalah perangkat lunak bebas; Anda dapat mendistribusikan ulang dan / atau memodifikasinya
10 * berdasarkan ketentuan Lisensi Publik Umum GNU yang diterbitkan oleh
11 * Yayasan Perangkat Lunak Bebas; baik versi 2.1 dari Lisensi, atau
12 * (sesuai pilihan Anda) versi yang lebih baru.
13 *
14 * Perpustakaan ini didistribusikan dengan harapan akan bermanfaat, tetapi
15 * TANPA GARANSI APA PUN; bahkan tanpa jaminan tersirat dari DAGANGAN
16 * atau KESESUAIAN UNTUK TUJUAN TERTENTU. Lihat GNU Lesser General Public
17 * Lisensi untuk lebih jelasnya.
18 *
19 * Anda seharusnya telah menerima salinan GNU Lesser General Public
20 * Lisensi bersama dengan perpustakaan ini; jika tidak, tulis ke Perangkat Lunak Bebas
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22 * AS.
23 *
24 * [Java adalah merek dagang atau merek dagang terdaftar dari Sun Microsystems, Inc.
25 * di Amerika Serikat dan negara-negara lain.]
26 *
27 * --------------------
28 * SpreadsheetDate.java
29 * --------------------
30 * (C) Hak Cipta 2000-2005, oleh Object Refinery Limited dan Kontributor.
31 *
32 * Penulis Asli: David Gilbert (untuk Object Refinery Limited);
33 * Kontributor: -;
34 *
35 * $ Id: SpreadsheetDate.java, v 1.8 2005/11/03 09:25:39 mungady Exp $
36 *
37 * Perubahan
38 * -------
39 * 11-Okt-2001: Versi 1 (DG);
40 * 05-Nov-2001: Menambahkan metode getDescription () dan setDescription () (DG);
41 * 12-Nov-2001: Mengubah nama dari ExcelDate.java menjadi SpreadsheetDate.java (DG);
42 * Memperbaiki bug dalam menghitung hari, bulan dan tahun dari serial
43 * nomor (DG);
44 * 24-Jan-2002: Memperbaiki bug dalam menghitung nomor seri dari hari itu,
45 * bulan dan tahun. Terima kasih kepada Trevor Hills untuk laporannya (Ditjen);
46 * 29-Mei-2002: Menambahkan equals (Object) method (SourceForge ID 558850) (DG);
47 * 03-Oktober-2002: Memperbaiki kesalahan yang dilaporkan oleh Checkstyle (DG);
48 * 13-Mar-2003: Implemented Serializable (DG);
49 * 04-Sep-2003: Completed isInRange () methods (DG);
50 * 05-Sep-2003: Implemented Comparable (DG);
51 * 21-Oct-2003: Menambahkan metode hashCode () (DG);
52 *
53 * /
54
55 paket org.jfree.date;
56
57 impor java.util.Calendar;
58 import java.util.Date;
59
60 / **
61 * Merupakan tanggal menggunakan integer, dengan cara yang mirip dengan tanggal
62 * implementasi di Microsoft Excel. Kisaran tanggal yang didukung adalah

www.it-ebooks.info

Halaman 414

Lampiran B: org.jfree.date.SerialDate 383

Listing B-5 (lanjutan)


SpreadsheetDate.java
63 * 1-Jan-1900 hingga 31-Des-9999.
64 * <P>
65 * Perlu diketahui bahwa ada bug yang disengaja di Excel yang mengenali tahun
66 * 1900 sebagai tahun kabisat padahal sebenarnya bukan tahun kabisat. Anda dapat menemukan lebih banyak
67 * informasi di situs web Microsoft dalam artikel Q181370:
68 * <P>
69 * http://support.microsoft.com/support/kb/articles/Q181/3/70.asp
70 * <P>
71 * Excel menggunakan konvensi bahwa 1-Jan-1900 = 1. Kelas ini menggunakan
72 * konvensi 1-Jan-1900 = 2.
73 * Hasilnya adalah bahwa jumlah hari di kelas ini akan berbeda dengan
74 * Angka Excel untuk Januari dan Februari 1900 ... tapi kemudian Excel menambahkan ekstra
75 * hari (29-Feb-1900 yang sebenarnya tidak ada!) Dan sejak saat itu
76 * angka hari akan cocok.
77 *
78 * @author David Gilbert
79 * /
80 SpreadsheetDate kelas publik meluas SerialDate {
81
82 / ** Untuk serialisasi. * /
83 serialVersionUID final statis panjang pribadi = -2039586705374454461L;
84
85 / **
86 * Nomor hari (1-Jan-1900 = 2, 2-Jan-1900 = 3, ..., 31-Des-9999 =
87 * 2958465).
88 * /
89 serial int pribadi;

https://translate.googleusercontent.com/translate_f 326/365
3/12/2020 www.it-ebooks.info
90
91 / ** Hari dalam sebulan (1 hingga 28, 29, 30 atau 31 tergantung pada bulan). * /
92 hari pribadi int;
93
94 / ** Bulan dalam setahun (1 hingga 12). * /
95 bulan pribadi int;
96
97 / ** Tahun (1900 hingga 9999). * /
98 tahun pribadi int;
99
100 / ** Deskripsi opsional untuk tanggal tersebut. * /
101 deskripsi String pribadi;
102
103 / **
104 * Membuat instance tanggal baru.
105 *
106 * @param hari hari (dalam kisaran 1 hingga 28/29/30/31).
107 * @param bulan bulan (dalam kisaran 1 hingga 12).
108 * @param tahun tahun (dalam kisaran 1900 hingga 9999).
109 * /
110 publik SpreadsheetDate (hari int akhir, bulan int akhir, tahun int akhir) {
111
112 if ((tahun> = 1900) && (tahun <= 9999)) {
113 this.year = year;
114 }
115 lain {
116 melempar IllegalArgumentException baru (
117 "Argumen 'tahun' harus dalam kisaran 1900 hingga 9999."
118 );
119 }
120
121 if ((bulan> = MonthConstants.JANUARI)
122 && (bulan <= MonthConstants.DECEMBER)) {
123 bulan ini = bulan;
124 }

www.it-ebooks.info

Halaman 415

384 Lampiran B: org.jfree.date.SerialDate

Listing B-5 (lanjutan)


SpreadsheetDate.java
125 lain {
126 melempar IllegalArgumentException baru (
127 "Argumen 'bulan' harus dalam kisaran 1 hingga 12."
128 );
129 }
130
131 if ((hari> = 1) && (hari <= SerialDate.lastDayOfMonth (bulan, tahun)))) {
132 this.day = hari;
133 }
134 lain {
135 melemparkan IllegalArgumentException baru ("Argumen 'hari' tidak valid.");
136 }
137
138 // nomor seri harus disinkronkan dengan hari-bulan-tahun ...
139 this.serial = calcSerial (hari, bulan, tahun);
140
141 this.description = null;
142
143}
144
145 / **
146 * Konstruktor standar - membuat objek tanggal baru yang mewakili
147 * nomor hari yang ditentukan (yang harus berada dalam kisaran 2 hingga 2958465.
148 *
149 * @param membuat serial nomor seri untuk hari itu (kisaran: 2 hingga 2958465).
150 * /
151 publik SpreadsheetDate (serial int final) {
152
153 if ((serial> = SERIAL_LOWER_BOUND) && (serial <= SERIAL_UPPER_BOUND)) {
154 this.serial = serial;
155 }
156 lain {
157 melempar IllegalArgumentException baru (
158 "SpreadsheetDate: Serial harus dalam kisaran 2 hingga 2958465.");
159 }
160
161 // hari-bulan-tahun perlu disinkronkan dengan nomor seri ...
162 calcDayMonthYear ();
163
164}
165
166 / **
167 * Mengembalikan deskripsi yang terlampir pada tanggal. Bukan itu
* Diperlukan bahwa tanggal memiliki deskripsi, tetapi untuk beberapa aplikasi itu
169 * berguna.
170 *
171 * @return Deskripsi yang terlampir pada tanggal.
172 * /
173 String publik getDescription () {
174 kembalikan this.description;
175}
176
177 / **

https://translate.googleusercontent.com/translate_f 327/365
3/12/2020 www.it-ebooks.info
178 * Mengatur deskripsi untuk tanggal.
179 *
180 * @param deskripsi deskripsi untuk tanggal ini (<code> null </code>
181 * diizinkan).
182 * /
183 setDescription public void (deskripsi String akhir) {
184 this.description = deskripsi;
185}
186

www.it-ebooks.info

Halaman 416

Lampiran B: org.jfree.date.SerialDate 385

Listing B-5 (lanjutan)


SpreadsheetDate.java
187 / **
188 * Mengembalikan nomor seri untuk tanggal, di mana 1 Januari 1900 = 2
189 * (ini hampir sama dengan sistem penomoran yang digunakan di Microsoft
190 * Excel untuk Windows dan Lotus 1-2-3).
191 *
192 * @return Nomor seri tanggal ini.
193 * /
194 public int toSerial () {
195 kembalikan ini. serial;
196}
197
198 / **
199 * Mengembalikan <code> java.util.Date </code> yang setara dengan tanggal ini.
200 *
201 * @return Tanggalnya.
202 * /
203 Tanggal publik untukTanggal () {
204 Kalender akhir kalender = Calendar.getInstance ();
205 calendar.set (getYYYY (), getMonth () - 1, getDayOfMonth (), 0, 0, 0);
206 return calendar.getTime ();
207}
208
209 / **
210 * Mengembalikan tahun (asumsikan rentang 1900 hingga 9999 yang valid).
211 *
212 * @return Tahun ini.
213 * /
214 public int getYYYY () {
215 kembalikan ini. tahun;
216}
217
218 / **
219 * Mengembalikan bulan (Januari = 1, Februari = 2, Maret = 3).
220 *
221 * @return Bulan dalam setahun.
222 * /
223 public int getMonth () {
224 kembalikan ini. bulan;
225}
226
227 / **
228 * Mengembalikan hari dalam sebulan.
229 *
230 * @return Hari pada bulan itu.
231 * /
232 public int getDayOfMonth () {
233 kembalikan ini.hari;
234}
235
236 / **
237 * Mengembalikan kode yang mewakili hari dalam seminggu.
238 * <P>
239 * Kode-kode didefinisikan dalam kelas {@link SerialDate} sebagai:
240 * <code> SUNDAY </code>, <code> MONDAY </code>, <code> TUESDAY </code>,
241 * <code> WEDNESDAY </code>, <code> THURSDAY </code>, <code> FRIDAY </code>, dan
242 * <code> SATURDAY </code>.
243 *
244 * @return Kode yang mewakili hari dalam seminggu.
245 * /
246 public int getDayOfWeek () {
247 return (this.serial + 6)% 7 +1;
248}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 328/365
3/12/2020 www.it-ebooks.info
Halaman 417

386 Lampiran B: org.jfree.date.SerialDate

Listing B-5 (lanjutan)


SpreadsheetDate.java
249
250 / **
251 * Menguji kesetaraan tanggal ini dengan objek yang arbitrer.
252 * <P>
253 * Metode ini akan mengembalikan true ONLY jika objek adalah turunan dari
254 * {@link SerialDate} kelas dasar, dan ini mewakili hari yang sama dengan ini
255 * {@link SpreadsheetDate}.
256 *
257 * @param objek objek untuk membandingkan (<code> null </code> diizinkan).
258 *
259 * @return A boolean.
260 * /
261 sama dengan boolean publik (objek Objek akhir) {
262
263 if (objek instance of SerialDate) {
264 objek final SerialDate s = (SerialDate);
265 return (s.toSerial () == this.toSerial ());
266 }
267 lain {
268 return false;
269 }
270
271}
272
273 / **
274 * Mengembalikan kode hash untuk instance objek ini.
275 *
276 * @return A kode hash.
277 * /
278 kode int publik () {)
279 kembali ke Serial ();
280}
281
282 / **
283 * Mengembalikan perbedaan (dalam hari) antara tanggal ini dan yang ditentukan
284 * tanggal 'lain'.
285 *
286 * @param selain tanggal yang dibandingkan dengan.
287 *
288 * @return Perbedaan (dalam hari) antara tanggal ini dan yang ditentukan
289 * tanggal 'lain'.
290 * /
291 perbandingan int publik (final SerialDate lainnya) {
292 mengembalikan this.serial - other.toSerial ();
293}
294
295 / **
296 * Menerapkan metode yang diperlukan oleh antarmuka Sebanding.
297 *
298 * @param lainnya objek lain (biasanya SerialDate lain).
299 *
300 * @return Bilangan bulat negatif, nol, atau bilangan bulat positif sebagai objek ini
301 * kurang dari, sama dengan, atau lebih besar dari objek yang ditentukan.
302 * /
303 public int compareTo (Obyek final lainnya) {
304 return bandingkan ((SerialDate) lainnya);
305}
306
307 / **
308 * Mengembalikan nilai true jika SerialDate ini menunjukkan tanggal yang sama dengan tanggal
309 * ditentukan SerialDate.
310 *

www.it-ebooks.info

Halaman 418

Lampiran B: org.jfree.date.SerialDate 387

Listing B-5 (lanjutan)


SpreadsheetDate.java
311 * @param selain tanggal yang dibandingkan dengan.
312 *
313 * @kembalikan <code> true </code> jika SerialDate ini menunjukkan tanggal yang sama dengan
314 * SerialDate yang ditentukan.
315 * /
316 public boolean isOn (final SerialDate lainnya) {
317 return (this.serial == other.toSerial ());
318}

https://translate.googleusercontent.com/translate_f 329/365
3/12/2020 www.it-ebooks.info
319
320 / **
321 * Mengembalikan nilai true jika SerialDate ini menunjukkan tanggal yang lebih awal dibandingkan dengan
322 * SerialDate yang ditentukan.
323 *
324 * @param selain tanggal yang dibandingkan dengan.
325 *
326 * @kembali <code> true </code> jika SerialDate ini menunjukkan tanggal yang lebih awal
327 * dibandingkan dengan SerialDate yang ditentukan.
328 * /
329 public boolean isBefore (SerialDate final lainnya) {
330 return (this.serial <other.toSerial ());
331}
332
333 / **
334 * Mengembalikan nilai true jika SerialDate ini menunjukkan tanggal yang sama dengan tanggal
335 * ditentukan SerialDate.
336 *
337 * @param selain tanggal yang dibandingkan dengan.
338 *
339 * @kembali <code> true </code> jika SerialDate ini menunjukkan tanggal yang sama
340 * sebagai SerialDate yang ditentukan.
341 * /
342 boolean publik isOnOrBefore (final SerialDate lainnya) {
343 return (this.serial <= other.toSerial ());
344}
345
346 / **
347 * Mengembalikan nilai true jika SerialDate ini mewakili tanggal yang sama dengan
348 * ditentukan SerialDate.
349 *
350 * @param selain tanggal yang dibandingkan dengan.
351 *
352 * @return <code> true </code> jika SerialDate ini menunjukkan tanggal yang sama
353 * sebagai SerialDate yang ditentukan.
354 * /
355 public boolean isAfter (final SerialDate other) {
356 return (this.serial> other.toSerial ());
357}
358
359 / **
360 * Mengembalikan nilai true jika SerialDate ini mewakili tanggal yang sama dengan
361 * ditentukan SerialDate.
362 *
363 * @param selain tanggal yang dibandingkan dengan.
364 *
365 * @kembali <code> true </code> jika SerialDate ini menunjukkan tanggal yang sama dengan
366 * SerialDate yang ditentukan.
367 * /
368 public boolean isOnOrAfter (final SerialDate lainnya) {
369 return (this.serial> = other.toSerial ());
370}
371
372 / **
373 * Mengembalikan <code> true </code> jika {@link SerialDate} ini berada di dalam

www.it-ebooks.info

Halaman 419

388 Lampiran B: org.jfree.date.SerialDate

Listing B-5 (lanjutan)


SpreadsheetDate.java
374 * rentang yang ditentukan (TERMASUK). Urutan tanggal d1 dan d2 tidak
375 * penting.
376 *
377 * @param d1 tanggal batas untuk kisaran.
378 * @param d2 tanggal batas lain untuk rentang.
379 *
380 * @return A boolean.
381 * /
382 boolean publik isInRange (final SerialDate d1, final SerialDate d2) {
383 return isInRange (d1, d2, SerialDate.INCLUDE_BOTH);
384}
385
386 / **
387 * Mengembalikan nilai true jika SerialDate ini berada dalam kisaran yang ditentukan (pemanggil
388 * menentukan apakah titik akhir disertakan atau tidak). Urutan d1
389 * dan d2 tidak penting.
390 *
391 * @param d1 satu tanggal batas untuk kisaran.
392 * @param d2 tanggal batas kedua untuk rentang.
393 * @param menyertakan kode yang mengontrol awal dan akhir
394 * tanggal termasuk dalam kisaran.
395 *
396 * @return <code> true </code> jika SerialDate ini dalam yang ditentukan
397 * jarak.
398 * /
399 boolean publik isInRange (final SerialDate d1, final SerialDate d2,
400 int final meliputi) {
401 int akhir s1 = d1.toSerial ();
402 int final s2 = d2.toSerial ();
403 int int final = Math.min (s1, s2);
404 int akhir = Math.max (s1, s2);
405
406 int akhir = toSerial ();
407 if (include == SerialDate.INCLUDE_BOTH) {

https://translate.googleusercontent.com/translate_f 330/365
3/12/2020 www.it-ebooks.info
408 return (s> = start && s <= end);
409 }
410 lain jika (termasuk == SerialDate.INCLUDE_FIRST) {
411 return (s> = start && s <end);
412 }
413 lain jika (termasuk == SerialDate.INCLUDE_SECOND) {
414 return (s> start && s <= end);
415 }
416 lain {
417 return (s> start && s <end);
418 }
419}
420
421 / **
422 * Hitung nomor seri dari hari, bulan dan tahun.
423 * <P>
424 * 1-Jan-1900 = 2.
425 *
426 * @param d hari.
427 * @param m bulan ini.
428 * @param y tahun ini.
429 *
430 * kembalikan nomor seri dari hari, bulan dan tahun.
431 * /
432 private int calcSerial (int d final, int final m, final int y) {
433 int final yy = ((y - 1900) * 365) + SerialDate.leapYearCount (y - 1);
434 int mm = SerialDate.AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH [m];
435 if (m> MonthConstants.FEBRUARY) {

www.it-ebooks.info

Halaman 420

Lampiran B: org.jfree.date.SerialDate 389

Listing B-5 (lanjutan)


SpreadsheetDate.java
436 if (SerialDate.isLeapYear (y)) {
437 mm = mm + 1;
438 }
439 }
440 int final dd = d;
441 kembalikan yy + mm + hh + 1;
442}
443
444 / **
445 * Hitung hari, bulan dan tahun dari nomor seri.
446 * /
447 void pribadi calcDayMonthYear () {
448
449 // dapatkan tahun dari tanggal seri
450 hari int final = this.serial - SERIAL_LOWER_BOUND;
451 // berlebihan karena kami mengabaikan hari kabisat
452 final int overestimatedYYYY = 1900 + (hari / 365);
453 lompatan int final = SerialDate.leapYearCount (overestimatedYYYY);
454 int final nonleapdays = hari - lompatan;
455 // diremehkan karena kita melebih-lebihkan tahun
456 int underestimatedYYYY = 1900 + (nonleapdays / 365);
457
458 if (underestimatedYYYY == overestimatedYYYY) {
459 this.year = diremehkanYYYYY;
460 }
461 lain {
462 int ss1 = calcSerial (1, 1, underestimatedYYYY);
463 while (ss1 <= this.serial) {
464 underestimatedYYYY = underestimatedYYYY + 1;
465 ss1 = calcSerial (1, 1, underestimatedYYYY);
466 }
467 this.year = underestimatedYYYY - 1;
468 }
469
470 int final SS2 = calcSerial (1, 1, this.year);
471
472 int [] daysToEndOfPrecedingMonth
473 = AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH;
474
475 if (isLeapYear (this.year)) {
476 hariKeDanDariPenerakhiranMonth
477 = LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH;
478 }
479
480 // dapatkan bulan dari tanggal seri
481 int mm = 1;
482 int sss = ss2 + daysToEndOfPrecedingMonth [mm] - 1;
483 while (sss <this.serial) {
484 mm = mm + 1;
485 sss = ss2 + daysToEndOfPrecedingMonth [mm] - 1;
486 }
487 this.month = mm - 1;
488
489 // yang tersisa adalah d (+1);
490 this.day = this.serial - ss2
491 - daysToEndOfPrecedingMonth [this.month] +1;
492
493}
494
495}

https://translate.googleusercontent.com/translate_f 331/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 421

390 Lampiran B: org.jfree.date.SerialDate

Listing B-6
RelativeDayOfWeekRule.java
1 / * =============================================== =========================
2 * JCommon: perpustakaan tujuan umum gratis untuk platform Java (tm)
3 * ================================================ ========================
4*
5 * (C) Hak Cipta 2000-2005, oleh Object Refinery Limited dan Kontributor.
6*
7 * Info Proyek: http://www.jfree.org/jcommon/index.html
8*
9 * Perpustakaan ini adalah perangkat lunak bebas; Anda dapat mendistribusikan ulang dan / atau memodifikasinya
10 * berdasarkan ketentuan Lisensi Publik Umum GNU yang diterbitkan oleh
11 * Yayasan Perangkat Lunak Bebas; baik versi 2.1 dari Lisensi, atau
12 * (sesuai pilihan Anda) versi yang lebih baru.
13 *
14 * Perpustakaan ini didistribusikan dengan harapan akan bermanfaat, tetapi
15 * TANPA GARANSI APA PUN; bahkan tanpa jaminan tersirat dari DAGANGAN
16 * atau KESESUAIAN UNTUK TUJUAN TERTENTU. Lihat GNU Lesser General Public
17 * Lisensi untuk lebih jelasnya.
18 *
19 * Anda seharusnya telah menerima salinan GNU Lesser General Public
20 * Lisensi bersama dengan perpustakaan ini; jika tidak, tulis ke Perangkat Lunak Bebas
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22 * AS.
23 *
24 * [Java adalah merek dagang atau merek dagang terdaftar dari Sun Microsystems, Inc.
25 * di Amerika Serikat dan negara-negara lain.]
26 *
27 * --------------------------
28 * RelativeDayOfWeekRule.java
29 * --------------------------
30 * (C) Hak Cipta 2000-2003, oleh Object Refinery Limited dan Kontributor.
31 *
32 * Penulis Asli: David Gilbert (untuk Object Refinery Limited);
33 * Kontributor: -;
34 *
35 * $ Id: RelativeDayOfWeekRule.java, v 1.6 2005/11/16 15:58:40 taqua Exp $
36 *
37 * Perubahan (dari 26-Okt-2001)
38 * --------------------------
39 * 26-Oct-2001: Paket yang diubah ke com.jrefinery.date. *;
40 * 03-Oct-2002: Memperbaiki kesalahan yang dilaporkan oleh Checkstyle (DG);
41 *
42 * /
43
44 paket org.jfree.date;
45
46 / **
47 * Aturan tanggal tahunan yang mengembalikan tanggal untuk setiap tahun berdasarkan (a) a
48 * aturan referensi; (B) hari dalam seminggu; dan (c) parameter pemilihan
49 * (SerialTanggal.HARGA, SerialTanggal.NEAREST, SerialTanggal.Lanjutkan)
50 * <P>
51 * Misalnya, Jumat Agung dapat ditentukan sebagai 'Jumat PRECEDING Jumat
52 * Minggu '.
53 *
54 * @author David Gilbert
55 * /
56 kelas publik RelativeDayOfWeekRule memperpanjang AnnualDateRule {
57
58 / ** Referensi ke aturan tanggal tahunan di mana aturan ini didasarkan. * /
59 subrule AnnualDateRule pribadi;
60
61 / **
62 * Hari dalam seminggu (SerialDate.MONDAY, SerialDate.TUESDAY, dan sebagainya).

www.it-ebooks.info

Halaman 422

https://translate.googleusercontent.com/translate_f 332/365
3/12/2020 www.it-ebooks.info

Lampiran B: org.jfree.date.SerialDate 391

Listing B-6 (lanjutan)


RelativeDayOfWeekRule.java
63 * /
64 private int dayOfWeek;
65
66 / ** Menentukan hari apa dalam seminggu (PRECEDING, NEAREST atau FOLLOWING). * /
67 rel pribadi int;
68
69 / **
70 * Konstruktor default - membuat aturan untuk hari Senin setelah 1 Januari.
71 * /
72 public RelativeDayOfWeekRule () {
73 ini (DayAndMonthRule () baru, SerialDate.MONDAY, SerialDate.FOLLOWING);
74}
75
76 / **
77 * Konstruktor standar - membangun aturan berdasarkan sub-aturan yang disediakan.
78 *
79 * @param mengesampingkan aturan yang menentukan tanggal referensi.
80 * @param dayOfWeek hari relatif relatif terhadap tanggal referensi.
81 * @param relatif menunjukkan * yang * hari dalam seminggu (sebelumnya, terdekat
82 * atau mengikuti).
83 * /
84 public RelativeDayOfWeekRule (subrule AnnualDate final,
85 final int dayOfWeek, final int relative) {
86 this.subrule = subrule;
87 this.dayOfWeek = dayOfWeek;
88 this.relative = relatif;
89}
90
91 / **
92 * Mengembalikan sub-aturan (juga disebut aturan referensi).
93 *
94 * @return Aturan tanggal tahunan yang menentukan tanggal referensi untuk ini
95 * aturan.
96 * /
97 AnnualDateRule publik getSubrule () {
98 kembalikan peraturan ini;
99}
100
101 / **
102 * Mengatur sub-aturan.
103 *
104 * @param mengesampingkan aturan tanggal tahunan yang menentukan tanggal referensi
105 * untuk aturan ini.
106 * /
107 setSubrule publik (subrule AnnualDateRule akhir) {
108 this.subrule = subrule;
109}
110
111 / **
112 * Mengembalikan hari-of-the-minggu untuk aturan ini.
113 *
114 * @ kembalikan hari-of-the-minggu untuk aturan ini.
115 * /
116 public int getDayOfWeek () {
117 kembalikan this.dayOfWeek;
118}
119
120 / **
121 * Mengatur hari-of-the-minggu untuk aturan ini.
122 *
123 * @param dayOfWeek the week-of-the-week (SerialDate.MONDAY,
124 * SerialDate.TUESDAY, dan sebagainya).

www.it-ebooks.info

Halaman 423

392 Lampiran B: org.jfree.date.SerialDate

Listing B-6 (lanjutan)


RelativeDayOfWeekRule.java
125 * /
126 public void setDayOfWeek (hari int finalOfWeek) {
127 this.dayOfWeek = dayOfWeek;
128}
129
130 / **
131 * Mengembalikan atribut 'relatif', yang menentukan * mana *
132 * hari-of-the-minggu kami tertarik (SerialDate.PRECEDING,
133 * SerialDate.NEAREST atau SerialDate.FOLLOWING).
134 *
135 * @return Atribut 'relatif'.
136 * /
137 public int getRelative () {
138 kembalikan ini. relatif;

https://translate.googleusercontent.com/translate_f 333/365
3/12/2020 www.it-ebooks.info
139}
140
141 / **
142 * Menetapkan atribut 'relatif' (SerialDate.PRECEDING, SerialDate.NEAREST,
143 * SerialDate.FOLLOWING).
144 *
145 * @param relatif menentukan * yang * hari dalam seminggu dipilih oleh ini
146 * aturan.
147 * /
148 kekosongan publik setRelative (relatif int akhir) {
149 this.relative = relatif;
150}
151
152 / **
153 * Membuat klon dari aturan ini.
154 *
155 * @ kembalikan klon aturan ini.
156 *
157 * @throws CloneNotSupportedException ini seharusnya tidak pernah terjadi.
158 * /
159 objek publik clone () melempar CloneNotSupportedException {
160 duplikat RelativeDayOfWeekRule akhir
161 = (RelativeDayOfWeekRule) super.clone ();
162 duplicate.subrule = (AnnualDateRule) dupate.getSubrule (). clone ();
163 kembalikan duplikat;
164}
165
166 / **
167 * Mengembalikan tanggal yang dihasilkan oleh aturan ini, untuk tahun yang ditentukan.
168 *
169 * @param tahun tahun (1900 & lt; = tahun & lt; = 9999).
170 *
171 * @return Tanggal yang dihasilkan oleh aturan untuk tahun tertentu (mungkin
172 * <code> null </code>).
173 * /
174 publik SerialDate getDate (tahun int final) {
175
176 // periksa argumen ...
177 if ((tahun <SerialDate.MINIMUM_YEAR_SUPPORTED)
178 || (tahun> SerialDate.MAXIMUM_YEAR_SUPPORTED)) {
179 melempar IllegalArgumentException baru (
180 "RelativeDayOfWeekRule.getDate (): tahun di luar rentang yang valid.");
181 }
182
183 // hitung tanggal ...
184 Hasil SerialDate = null;
185 final SerialDate base = this.subrule.getDate (tahun);
186

www.it-ebooks.info

Halaman 424

Lampiran B: org.jfree.date.SerialDate 393

Listing B-6 (lanjutan)


RelativeDayOfWeekRule.java
187 if (base! = null) {
188 switch (this.relative) {
189 case (SerialDate.PRECEDING):
190 hasil = SerialDate.getPreviousDayOfWeek (this.dayOfWeek,
191 mendasarkan);
192 istirahat;
193 case (SerialDate.NEAREST):
194 hasil = SerialDate.getNearestDayOfWeek (this.dayOfWeek,
195 mendasarkan);
196 istirahat;
197 case (SerialDate.FOLLOWING):
198 hasil = SerialDate.getFollowingDayOfWeek (this.dayOfWeek,
199 mendasarkan);
200 istirahat;
201 default:
202 istirahat;
203 }
204 }
205 hasil pengembalian;
206
207}
208
209}

https://translate.googleusercontent.com/translate_f 334/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 425

394 Lampiran B: org.jfree.date.SerialDate

Listing B-7
DayDate.java (Final)
1 / * =============================================== =========================
2 * JCommon: perpustakaan tujuan umum gratis untuk platform Java (tm)
3 * ================================================ ========================
4*
5 * (C) Hak Cipta 2000-2005, oleh Object Refinery Limited dan Kontributor.
...
36 * /
37 paket org.jfree.date;
38
39 impor java.io.Serializable;
40 impor java.util. *;
41
42 / **
43 * Kelas abstrak yang mewakili tanggal yang tidak dapat diubah dengan presisi
44 * suatu hari. Implementasinya akan memetakan setiap tanggal ke integer itu
45 * mewakili jumlah hari ordinal dari beberapa asal tetap.
46 *
47 * Mengapa tidak menggunakan java.util.Date saja? Kami akan, ketika itu masuk akal. Kadang-kadang,
48 * java.util.Date dapat * terlalu * tepat - ini merepresentasikan instan dalam waktu,
49 * akurat hingga 1/1000 detik (dengan tanggalnya sendiri tergantung pada
50 * zona waktu). Terkadang kita hanya ingin mewakili hari tertentu (mis. 21
51 * Januari 2015) tanpa memperhatikan diri sendiri tentang waktu, atau
52 * zona waktu, atau apa pun. Itulah yang kami tentukan untuk DayDate.
53 *
54 * Gunakan DayDateFactory.makeDate untuk membuat instance.
55 *
56 * @author David Gilbert
57 * @author Robert C. Martin melakukan banyak refactoring.
58 * /
59
60 DayDate kelas abstrak publik mengimplementasikan Sebanding, Serializable {
61 int publik abstrak getOrdinalDay ();
62 int publik abstrak getYear ();
63 Bulan abstrak publik getMonth ();
64 int abstrak publik getDayOfMonth ();
65
66 Hari abstrak terlindungi getDayOfWeekForOrdinalZero ();
67
68 DayDate publik plusDays (int days) {
69 return DayDateFactory.makeDate (getOrdinalDay () + hari);
70}
71
72 DayDate publik plusMonth (bulan int) {
73 int thisMonthAsOrdinal = getMonth (). ToInt () - Month.JANUARY.toInt ();
74 int thisMonthAndYearAsOrdinal = 12 * getYear () + thisMonthAsOrdinal;
75 int resultMonthAndYearAsOrdinal = thisMonthAndYearAsOrdinal + bulan;
76 int resultYear = resultMonthAndYearAsOrdinal / 12;
77 int resultMonthAsOrdinal = resultMonthAndYearAsOrdinal% 12 + Month.JANUARY.toInt ();
78 Month resultMonth = Month.fromInt (resultMonthAsOrdinal);
79 int resultDay = correctLastDayOfMonth (getDayOfMonth (), resultMonth, resultYear);
80 return DayDateFactory.makeDate (resultDay, resultMonth, resultYear);
81}
82
83 DayDate publik plusYears (int years) {
84 int resultYear = getYear () + tahun;
85 int resultDay = correctLastDayOfMonth (getDayOfMonth (), getMonth (), resultYear);
86 return DayDateFactory.makeDate (resultDay, getMonth (), resultYear);
87}
88
89 private int correctLastDayOfMonth (hari int, Bulan bulan, tahun int) {
90 int lastDayOfMonth = DateUtil.lastDayOfMonth (bulan, tahun);
91 if (hari> lastDayOfMonth)

https://translate.googleusercontent.com/translate_f 335/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 426

Lampiran B: org.jfree.date.SerialDate 395

Listing B-7 (lanjutan)


DayDate.java (Final)
92 hari = lastDayOfMonth;
93 hari kembali;
94}
95
96 DayDate publik getPreviousDayOfWeek (Hari targetDayOfWeek) {
97 int offsetToTarget = targetDayOfWeek.toInt () - getDayOfWeek (). ToInt ();
98 if (offsetToTarget> = 0)
99 offsetToTarget - = 7;
100 plus plusDays (offsetToTarget);
101}
102
103 public DayDate getFollowingDayOfWeek (Hari targetDayOfWeek) {
104 int offsetToTarget = targetDayOfWeek.toInt () - getDayOfWeek (). ToInt ();
105 if (offsetToTarget <= 0)
106 offsetToTarget + = 7;
107 return plusDays (offsetToTarget);
108}
109
110 public DayDate getNearestDayOfWeek (Hari targetDayOfWeek) {
111 int offsetToThisWeeksTarget = targetDayOfWeek.toInt () - getDayOfWeek (). ToInt ();
112 int offsetToFutureTarget = (offsetToThisWeeksTarget + 7)% 7;
113 int offsetToPreviousTarget = offsetToFutureTarget - 7;
114
115 if (offsetToFutureTarget> 3)
116 return plusDays (offsetToPreviousTarget);
117 yang lain
118 return plusDays (offsetToFutureTarget);
119}
120
121 DayDate publik getEndOfMonth () {
122 Bulan bulan = getMonth ();
123 int year = getYear ();
124 int lastDay = DateUtil.lastDayOfMonth (bulan, tahun);
125 return DayDateFactory.makeDate (lastDay, bulan, tahun);
126}
127
128 Tanggal publik untuk Tanggal () {
129 Kalender akhir kalender = Calendar.getInstance ();
130 int ordinalMonth = getMonth (). ToInt () - Month.JANUARY.toInt ();
131 calendar.set (getYear (), ordinalMonth, getDayOfMonth (), 0, 0, 0);
132 mengembalikan kalender.getTime ();
133}
134
135 public String toString () {
136 mengembalikan String.format ("% 02d-% s-% d", getDayOfMonth (), getMonth (), getYear ());
137}
138
139 Hari publik getDayOfWeek () {
140 Hari startingDay = getDayOfWeekForOrdinalZero ();
141 int startingOffset = startingDay.toInt () - Day.SUNDAY.toInt ();
142 int ordinalOfDayOfWeek = (getOrdinalDay () + startingOffset)% 7;
143 return Day.fromInt (ordinalOfDayOfWeek + Day.SUNDAY.toInt ());
144}
145
146 hari int publik sejak (tanggal DayDate) {
147 return getOrdinalDay () - date.getOrdinalDay ();
148}
149
150 public boolean isOn (DayDate other) {
151 kembalikan getOrdinalDay () == other.getOrdinalDay ();
152}
153

www.it-ebooks.info

Halaman 427

396 Lampiran B: org.jfree.date.SerialDate

https://translate.googleusercontent.com/translate_f 336/365
3/12/2020 www.it-ebooks.info

Listing B-7 (lanjutan)


DayDate.java (Final)
154 public boolean isBefore (DayDate other) {
155 kembalikan getOrdinalDay () <other.getOrdinalDay ();
156}
157
158 boolean publik isOnOrBefore (DayDate other) {
159 kembalikan getOrdinalDay () <= other.getOrdinalDay ();
160}
161
162 boolean publik isAfter (DayDate other) {
163 kembalikan getOrdinalDay ()> other.getOrdinalDay ();
164}
165
166 boolean publik isOnOrAfter (DayDate other) {
167 kembalikan getOrdinalDay ()> = other.getOrdinalDay ();
168}
169
170 boolean publik isInRange (DayDate d1, DayDate d2) {
171 return isInRange (d1, d2, DateInterval.CLOSED);
172}
173
174 boolean publik isInRange (DayDate d1, DayDate d2, DateInterval interval) {
175 int left = Math.min (d1.getOrdinalDay (), d2.getOrdinalDay ());
176 int right = Math.max (d1.getOrdinalDay (), d2.getOrdinalDay ());
177 return interval.isIn (getOrdinalDay (), kiri, kanan);
178}
179}

www.it-ebooks.info

Halaman 428

Lampiran B: org.jfree.date.SerialDate 397

Listing B-8
Month.java (Final)
1 paket org.jfree.date;
2
3 impor java.text.DateFormatSymbols;
4
5 bulan publik enum {
6 JANUARI (1), FEBRUARI (2), MARET (3),
7 APRIL (4), MEI (5), JUNI (6),
8 JULI (7), AGUSTUS (8), SEPTEMBER (9),
9 OKTOBER (10), NOVEMBER (11), DESEMBER (12);
10 DateFormatSymbols statis pribadi dateFormatSymbols = DateFormatSymbols baru ();
11 int final statis pribadi [] LAST_DAY_OF_MONTH =
12 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
13
14 indeks int pribadi;
15
16 Bulan (indeks int) {
17 this.index = indeks;
18}
19
20 Bulan statis publik dariInt (int monthIndex) {
21 untuk (Bulan m: Month.values ()) {

https://translate.googleusercontent.com/translate_f 337/365
3/12/2020 www.it-ebooks.info
22 if (m.index == monthIndex)
23 mengembalikan m;
24}
25 melempar IllegalArgumentException baru ("Indeks bulan tidak valid" + monthIndex);
26}
27
28 public int lastDay () {
29 kembalikan LAST_DAY_OF_MONTH [indeks];
30}
31
32 public int quarter () {
33 pengembalian 1 + (indeks - 1) / 3;
34}
35
36 String publik toString () {
37 return dateFormatSymbols.getMonths () [index - 1];
38}
39
40 public string toShortString () {
41 return dateFormatSymbols.getShortMonths () [index - 1];
42}
43
44 parse Bulan statis publik (String s) {
45 s = s.trim ();
46 untuk (Bulan m: Month.values ())
47 if (m.matches)
48 mengembalikan m;
49
50 coba {
51 kembali dariInt (Integer.parseInt (s));
52}
53 catch (NumberFormatException e) {}
54 melempar IllegalArgumentException baru ("Bulan tidak valid" +);
55}
56
57 pertandingan boolean pribadi (String s) {
58 mengembalikan s.equalsIgnoreCase (toString ()) ||
59 s.equalsIgnoreCase (toShortString ());
60}
61
62 public int toInt () {
63 indeks pengembalian;
64}
65}

www.it-ebooks.info

Halaman 429

398 Lampiran B: org.jfree.date.SerialDate

Listing B-9
Day.java (Final)
1 paket org.jfree.date;
2
3 impor java.util.Calendar;
4 impor java.text.DateFormatSymbols;
5
6 enum publik hari {
7 SENIN (Kalender. Senin),
8 SELASA (Kalender. SELASA),
9 WEDNESDAY (Calendar.WEDNESDAY),
10 KAMIS (Kalender. KAMIS),
11 JUMAT (Kalender.FRIDAY),
12 SATURDAY (Calendar.SATURDAY),
13 MINGGU (Kalender. MINGGU);
14
15 indeks int final swasta;
16 DateFormatSymbols statis pribadi dateSymbols = DateFormatSymbols baru ();
17
18 Hari (int hari) {
19 indeks = hari;
20}
21
22 Hari statis publik dariInt (int index) melempar IllegalArgumentException {
23 untuk (Hari d: Day.values ())
24 if (d.index == indeks)
25 kembali d;
26 membuang IllegalArgumentException baru (
27 String.format ("Indeks hari ilegal:% d.", Indeks));
28}
29
30 parse Hari statis publik (String s) melempar IllegalArgumentException {
31 String [] shortWeekdayNames =
32 dateSymbols.getShortWeekdays ();
33 String [] weekDayNames =
34 dateSymbols.getWeekdays ();
35
36 s = s.trim ();
37 untuk (Hari: Nilai hari) ()) {
38 if (s.equalsIgnoreCase (shortWeekdayNames [day.index]) ||
39 s.equalsIgnoreCase (weekDayNames [day.index])) {
40 hari kembali;
41}
42}
43 melempar IllegalArgumentException baru (
44 String.format ("% s bukan string hari kerja yang valid", s));
45}
46
47 String publik toString () {

https://translate.googleusercontent.com/translate_f 338/365
3/12/2020 www.it-ebooks.info
48 return dateSymbols.getWeekdays () [index];
49}
50
51 public int toInt () {
52 indeks pengembalian;
53}
54}

www.it-ebooks.info

Halaman 430

Lampiran B: org.jfree.date.SerialDate 399

Listing B-10
DateInterval.java (Final)
1 paket org.jfree.date;
2
3 publik enum DateInterval {
4 BUKA {
5 boolean publik isIn (int d, int left, int right) {
6 kembali d> kiri && d <kanan;
7}
8},
9 CLOSED_LEFT {
10 public boolean isIn (int d, int left, int right) {
11 kembali d> = kiri && d <kanan;
12}
13},
14 CLOSED_RIGHT {
15 boolean publik isIn (int d, int left, int right) {
16 kembali d> kiri && d <= kanan;
17}
18},
19 TUTUP {
20 boolean publik isIn (int d, int left, int right) {
21 mengembalikan d> = kiri && d <= kanan;
22}
23};
24
25 boolean abstrak publik adalah In (int d, int left, int right);
26}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 339/365
3/12/2020 www.it-ebooks.info

Halaman 431

400 Lampiran B: org.jfree.date.SerialDate

Listing B-11
WeekInMonth.java (Final)
1 paket org.jfree.date;
2
3 publik enum WeekInMonth {
4 PERTAMA (1), KEDUA (2), KETIGA (3), EMPAT (4), TERAKHIR (0);
5 indeks int final pribadi;
6
7 WeekInMonth (indeks int) {
8 this.index = indeks;
9}
10
11 public int toInt () {
12 indeks pengembalian;
13}
14}

www.it-ebooks.info

Halaman 432

Lampiran B: org.jfree.date.SerialDate 401

Listing B-12
WeekdayRange.java (Final)
1 paket org.jfree.date;
2

https://translate.googleusercontent.com/translate_f 340/365
3/12/2020 www.it-ebooks.info
3 publik enum WeekdayRange {
4 TERAKHIR, TERDEKAT, BERIKUTNYA
5}

www.it-ebooks.info

Halaman 433

402 Lampiran B: org.jfree.date.SerialDate

Listing B-13
DateUtil.java (Final)
1 paket org.jfree.date;
2
3 impor java.text.DateFormatSymbols;
4
5 DateUtil kelas publik {
6 private DateFormatSymbols statis dateFormatSymbols = DateFormatSymbols baru ();
7
8 String statis publik [] getMonthNames () {
9 return dateFormatSymbols.getMonths ();
10}
11
12 boolean statis publik isLeapYear (int year) {
13 boolean keempat = tahun% 4 == 0;
14 boolean keseratus = tahun% 100 == 0;
15 boolean fourHundredth = tahun% 400 == 0;
16 mengembalikan keempat && (! Keseratus || empat Keseratus);
17}
18
19 public static int lastDayOfMonth (Bulan bulan, tahun int) {
20 if (bulan == Month.FEBRUARY && isLeapYear (tahun))
21 return month.lastDay () + 1;
22 lainnya
23 return month.lastDay ();
24}
25
26 le int static publik publik (tahun int) {
27 int leap4 = (tahun - 1896) / 4;
28 int leap100 = (tahun - 1800) / 100;

https://translate.googleusercontent.com/translate_f 341/365
3/12/2020 www.it-ebooks.info
29 int leap400 = (tahun - 1600) / 400;
30 return leap4 - leap100 + leap400;
31}
32}

www.it-ebooks.info

Halaman 434

Lampiran B: org.jfree.date.SerialDate 403

Listing B-14
DayDateFactory.java (Final)
1 paket org.jfree.date;
2
3 kelas abstrak publik DayDateFactory {
4 pabrik DayDateFactory statis statis = SpreadsheetDateFactory baru ();
5 public static batal setInstance (pabrik DayDateFactory) {
6 DayDateFactory.factory = pabrik;
7}
8
9 DayDate abstrak yang dilindungi _makeDate (int ordinal);
10 DayDate abstrak yang dilindungi _makeDate (int day, Month month, int year);
11 DayDate abstrak yang dilindungi _makeDate (hari int, bulan int, tahun int);
12 DayDate abstrak yang dilindungi _makeDate (tanggal java.util.Date);
13 int abstrak get _getMinimumYear ();
14 int abstrak get _getMaximumYear ();
15
16 makeDate DayDate statis publik (int ordinal) {
17 return factory._makeDate (ordinal);
18}
19
20 tanggal statis DayDate publik (hari int, Bulan bulan, tahun int) {
21 return factory._makeDate (hari, bulan, tahun);
22}
23
24 DayDate statis publik makeDate (int day, int month, int year) {
25 return factory._makeDate (hari, bulan, tahun);
26}
27
28 makeDate statis publik DayDate (tanggal java.util.Date) {
29 return factory._makeDate (date);
30}
31
32 int statis publik getMinimumYear () {
33 mengembalikan pabrik._getMinimumYear ();
34}
35
36 public static int getMaximumYear () {
37 kembalikan pabrik._getMaximumYear ();
38}
39}

https://translate.googleusercontent.com/translate_f 342/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 435

404 Lampiran B: org.jfree.date.SerialDate

Listing B-15
SpreadsheetDateFactory.java (Final)
1 paket org.jfree.date;
2
3 impor java.util. *;
4
5 SpreadsheetDateFactory kelas publik memperpanjang DayDateFactory {
6 DayDate publik _makeDate (int ordinal) {
7 kembalikan SpreadsheetDate baru (ordinal);
8}
9
10 DayDate publik _makeDate (int day, Month month, int year) {
11 mengembalikan SpreadsheetDate baru (hari, bulan, tahun);
12}
13
14 DayDate publik _makeDate (hari int, bulan int, tahun int) {
15 mengembalikan SpreadsheetDate baru (hari, bulan, tahun);
16}
17
18 DayDate publik _makeDate (Tanggal tanggal) {
19 kalender GregorianCalendar akhir = new GregorianCalendar ();
20 calendar.setTime (tanggal);
21 mengembalikan SpreadsheetDate baru (
22 calendar.get (Calendar.DATE),
23 Bulan. Dari (kalender. Dapatkan (Kalender. BULAN) +1),
24 calendar.get (Calendar.YEAR));
25}
26
27 protected int _getMinimumYear () {
28 kembalikan SpreadsheetDate.MINIMUM_YEAR_SUPPORTED;
29}
30
31 dilindungi int _getMaximumYear () {
32 mengembalikan SpreadsheetDate.MAXIMUM_YEAR_SUPPORTED;
33}
34}

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 343/365
3/12/2020 www.it-ebooks.info
Halaman 436

Lampiran B: org.jfree.date.SerialDate 405

Listing B-16
SpreadsheetDate.java (Final)
1 / * =============================================== =========================
2 * JCommon: perpustakaan tujuan umum gratis untuk platform Java (tm)
3 * ================================================ ========================
4*
5 * (C) Hak Cipta 2000-2005, oleh Object Refinery Limited dan Kontributor.
6*
...
52 *
53 * /
54
55 paket org.jfree.date;
56
57 impor static.jfree.date.Month.FEBRUARY;
58
59 impor java.util. *;
60
61 / **
62 * Merupakan tanggal menggunakan integer, dengan cara yang mirip dengan tanggal
63 * implementasi di Microsoft Excel. Kisaran tanggal yang didukung adalah
64 * 1-Jan-1900 hingga 31-Des-9999.
65 * <p />
66 * Sadarilah bahwa ada bug yang disengaja di Excel yang mengenali tahun
67 * 1900 sebagai tahun kabisat padahal sebenarnya bukan tahun kabisat. Anda dapat menemukan lebih banyak
68 * informasi di situs web Microsoft dalam artikel Q181370:
69 * <p />
70 * http://support.microsoft.com/support/kb/articles/Q181/3/70.asp
71 * <p />
72 * Excel menggunakan konvensi bahwa 1-Jan-1900 = 1. Kelas ini menggunakan
73 * konvensi 1-Jan-1900 = 2.
74 * Hasilnya adalah bahwa jumlah hari di kelas ini akan berbeda dengan
75 * Angka Excel untuk Januari dan Februari 1900 ... tapi kemudian Excel menambahkan ekstra
76 * hari (29-Feb-1900 yang sebenarnya tidak ada!) Dan sejak saat itu
77 * angka hari akan cocok.
78 *
79 * @ penulis David Gilbert
80 * /
81 SpreadsheetDate kelas publik memperpanjang DayDate {
82 int final statis publik EARLIEST_DATE_ORDINAL = 2; // 1/1/1900
83 LATEST_DATE_ORDINAL public int static final = 2958465; // 12/31/9999
84 public int static final MINIMUM_YEAR_SUPPORTED = 1900;
85 int final statis publik MAXIMUM_YEAR_SUPPORTED = 9999;
86 int final statis [] AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH =
87 {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
88 int final statis [] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH =
89 {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
90
91 private int ordinalDay;
92 hari pribadi int;
93 bulan Bulan pribadi;
94 tahun pribadi int;
95
96 publik SpreadsheetDate (hari, bulan, tahun int) {
97 if (tahun <MINIMUM_YEAR_SUPPORTED || tahun> MAXIMUM_YEAR_SUPPORTED)
98 melempar IllegalArgumentException baru (
99 "Argumen 'tahun' harus dalam kisaran" +
100 MINIMUM_YEAR_SUPPORTED + "to" + MAXIMUM_YEAR_SUPPORTED + ".");
101 if (hari <1 || hari> DateUtil.lastDayOfMonth (bulan, tahun))
102 melempar IllegalArgumentException baru ("Argumen 'hari' tidak valid.");
103
104 ini. Tahun = tahun;
105 this.month = bulan;

www.it-ebooks.info

Halaman 437

406 Lampiran B: org.jfree.date.SerialDate

Listing B-16 (lanjutan)


SpreadsheetDate.java (Final)
106 this.day = hari;
107 ordinalDay = calcOrdinal (hari, bulan, tahun);
108}
109
110 publik SpreadsheetDate (hari int, bulan int, tahun int) {
111 ini (hari, Bulan. Sejak (bulan), tahun);
112}
113

https://translate.googleusercontent.com/translate_f 344/365
3/12/2020 www.it-ebooks.info
114 publik SpreadsheetDate (int serial) {
115 if (serial <EARLIEST_DATE_ORDINAL || serial> LATEST_DATE_ORDINAL)
116 melempar IllegalArgumentException baru (
117 "SpreadsheetDate: Serial harus dalam kisaran 2 hingga 2958465.");
118
119 ordinalDay = serial;
120 calcDayMonthYear ();
121}
122
123 public int getOrdinalDay () {
124 mengembalikan ordinalDay;
125}
126
127 public int getYear () {
128 tahun kembali;
129}
130
131 Bulan publik getMonth () {
132 bulan kembali;
133}
134
135 public int getDayOfMonth () {
136 hari kembali;
137}
138
139 Hari yang dilindungi getDayOfWeekForOrdinalZero () {return Day.SATURDAY;}
140
141 sama dengan boolean publik (Objek objek) {
142 if (! (Objek instance dari DayDate))
143 kembali salah;
144
145 DayDate date = (DayDate) objek;
146 return date.getOrdinalDay () == getOrdinalDay ();
147}
148
149 kode int publik () {)
150 return getOrdinalDay ();
151}
152
153 public int compareTo (Object other) {
154 kembali daysSince ((DayDate) lainnya);
155}
156
157 private int calcOrdinal (int day, Bulan bulan, int year) {
158 int leapDaysForYear = DateUtil.leapYearCount (tahun - 1);
159 int daysUpToYear = (tahun - MINIMUM_YEAR_SUPPORTED) * 365 + leapDaysForYear;
160 int daysUpToMonth = AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH [month.toInt ()];
161 if (DateUtil.isLeapYear (year) && month.toInt ()> FEBRUARY.toInt ())
162 hariUpTomonth ++;
163 int daysInMonth = hari - 1;
164 hari kembaliUpToYear + daysUpToMonth + daysInMonth + EARLIEST_DATE_ORDINAL;
165}
166

www.it-ebooks.info

Halaman 438

Lampiran B: org.jfree.date.SerialDate 407

Listing B-16 (lanjutan)


SpreadsheetDate.java (Final)
167 private void calcDayMonthYear () {
168 int days = ordinalDay - EARLIEST_DATE_ORDINAL;
169 int overestimatedYear = MINIMUM_YEAR_SUPPORTED + days / 365;
170 int nonleapdays = days - DateUtil.leapYearCount (overestimatedYear);
171 int underestimatedYear = MINIMUM_YEAR_SUPPORTED + nonleapdays / 365;
172
173 tahun = huntForYearContaining (ordinalDay, underestimatedYear);
174 int firstOrdinalOfYear = firstOrdinalOfYear (tahun);
175 bulan = huntForMonthContaining (ordinalDay, firstOrdinalOfYear);
176 hari = ordinalDay - firstOrdinalOfYear - daysBefore ThisMonth (month.toInt ());
177}
178
179 bulan pribadi huntForMonthContaining (int anOrdinal, int firstOrdinalOfYear) {
180 int daysIntoThisYear = anOrdinal - firstOrdinalOfYear;
181 int aMonth = 1;
182 while (daysBeforeThisMonth (aMonth) <daysIntoThisYear)
183 aMonth ++;
184
185 kembali Month.fromInt (aMonth - 1);
186}
187
188 hari pribadi intSebelum IniBulan (int aMonth) {
189 if (DateUtil.isLeapYear (tahun))
190 kembalikan LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH [aMonth] - 1;
191 lainnya
192 return AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH [aMonth] - 1;
193}
194
195 private int huntForYearContaining (int anOrdinalDay, int startingYear) {
196 int aYear = startingYear;
197 saat (firstOrdinalOfYear (aYear) <= anOrdinalDay)
198 tahun ++;
199
200 pengembalian setahun - 1;

https://translate.googleusercontent.com/translate_f 345/365
3/12/2020 www.it-ebooks.info
201}
202
203 private int firstOrdinalOfYear (int year) {
204 return calcOrdinal (1, Bulan. Januari, tahun);
205}
206
207 publik DayDate static createInstance (Tanggal tanggal) {
208 Kalender GregorianCalendar = GregorianCalendar baru ();
209 calendar.setTime (tanggal);
210 mengembalikan SpreadsheetDate baru (calendar.get (Calendar.DATE),
211 Month.fromInt (calendar.get (Calendar.MONTH) +1),
212 calendar.get (Calendar.YEAR));
213
214}
215}

www.it-ebooks.info

Halaman 439

halaman ini sengaja dibiarkan kosong

https://translate.googleusercontent.com/translate_f 346/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 440

Lampiran C
Referensi Silang Heuristik

Referensi silang Bau dan Heuristik. Semua referensi silang lainnya dapat dihapus.

C1 ................................................. ...................... 16-276, 16-279, 17-292


C2 ................................................. ........ 16-279, 16-285, 16-295, 17-292
C3 ................................................. ........ 16-283, 16-285, 16-288, 17-293
C4 ................................................. ................................................ 17- 293
C5 ................................................. ................................................ 17- 293
E1 ................................................. ................................................ 17- 294
E2 ................................................. ................................................ 17- 294
F1 ................................................. ................................... 14-239, 17-295
F2 ................................................. ................................................ 17- 295
F3 ................................................. ................................................ 17- 295
F4 ............................... 14-289, 16-273, 16-285, 16-287, 16- 288, 17-295
G1 ................................................. ................................... 16-276, 17-295
G2 ................................................. ...................... 16-273, 16-274, 17-296
G3 ................................................. ................................... 16-274, 17-296
G4 ................................................ 9 -31, 16-279, 16-286, 16-291, 17-297
G5 ................................... 9-31, 16-279, 16-286, 16- 291, 16-296, 17-297
G6 ................................ 6-106, 16-280, 16-283, 16-284, 16 -289, 16-293,
16-294, 16-296, 17-299
G7 ................................................. ...................... 16-281, 16-283, 17-300
G8 ................................................. ................................... 16-283, 17-301
G9 ............................................ 16-283, 16 -285, 16-286, 16-287, 17-302
G10 .................................................. 5-86 , 15-264, 16-276, 16-284, 17-302
G11 .......................................... 15-264, 16-284 , 16-288, 16-292, 17-302
G12 ............... 16-284, 16-285, 16-286, 16-287, 16-288, 16-295, 17-303
G13 ................................................. .................... 16-286, 16-288, 17-303
G14 ................................................. .................... 16-288, 16-292, 17-304

409

www.it-ebooks.info

Halaman 441

https://translate.googleusercontent.com/translate_f 347/365
3/12/2020 www.it-ebooks.info

410 Lampiran C: Referensi Silang Heuristik

G15 ................................................. ................................. 16-288, 17-305


G16 ................................................. ................................. 16-289, 17-306
G17 ................................................. .................... 16-289, 17-307, 17-312
G18 ................................................. ...... 16-289, 16-290, 16-291, 17-308
G19 ................................................. ...... 16-290, 16-291, 16-292, 17-309
G20 ................................................. ................................. 16-290, 17-309
G21 ................................................. ................................. 16-291, 17-310
G22 ................................................. ................................. 16-294, 17-322
G23 ................................................. ......... ?? - 44, 14-239, 16-295, 17-313
G24 ................................................. ................................. 16-296, 17-313
G25 ................................................. ................................. 16-296, 17-314
G26 ................................................. .............................................. 17-316
G27 ................................................. .............................................. 17-316
G28 ................................................. ................................. 15-262, 17-317
G29 ................................................. ................................. 15-262, 17-317
G30 ................................................. ................................. 15-263, 17-317
G31 ................................................. ................................. 15-264, 17-318
G32 ................................................. ................................. 15-265, 17-319
G33 ................................................. .................... 15-265, 15-266, 17-320
G34 ................................................. .......................... 1-40, 6-106, 17-321
G35 ................................................. ..................................... 5-90, 17-323
G36 ................................................. ................................... 6-103, 17-324
J1 ................................................. .................................... 16-276, 17-325
J2 ................................................. ....................... 16-278, 16-285, 17-326
J3 ................................................. ....................... 16-283, 16-285, 17-327
N1 .............................. 15-264, 16-277, 16-279, 16-282, 16-287 , 16-288,
16-289, 16-290, 16-294, 16-296, 17-328
N2 ................................................. ................................... 16-277, 17-330
N3 ................................................. ...................... 16-284, 16-288, 17-331
N4 ................................................. ...................... 15-263, 16-291, 17-332
N5 ................................................. ............ 2-26, 14-221, 15-262, 17-332
N6 ................................................. ................................... 15-261, 17-333
N7 ................................................. ................................... 15-263, 17-333
T1 ................................................. ...................... 16-273, 16-274, 17-334
T2 ................................................. ................................... 16-273, 17-334
T3 ................................................. ................................... 16-274, 17-334
T4 ................................................. ................................................ 17- 334
T5 ................................................. ...................... 16-274, 16-275, 17-335
T6 ................................................. ................................... 16-275, 17-335
T7 ................................................. ................................... 16-275, 17-335
T8 ................................................. ................................... 16-275, 17-335
T9 ................................................. ................................................ 17- 336

www.it-ebooks.info

Halaman 442

Epilog
Pada 2005, saat menghadiri konferensi Agile di Denver, Elisabeth Hedrickson 1 menyerahkan saya
gelang hijau mirip dengan yang dibuat Lance Armstrong begitu populer. Yang ini

https://translate.googleusercontent.com/translate_f 348/365
3/12/2020 www.it-ebooks.info
mengatakan "Tes Terobsesi" di atasnya. Saya dengan senang hati memakainya dan mengenakannya dengan bangga. Sejak belajar TDD dari
Kent Beck pada tahun 1999, saya memang terobsesi dengan pengembangan yang digerakkan oleh tes.
Tapi kemudian sesuatu yang aneh terjadi. Saya menemukan saya tidak bisa melepas band. Tidak
karena secara fisik macet, tetapi karena secara moral macet. Band ini membuat terbuka
pernyataan tentang etika profesional saya. Itu merupakan indikasi nyata dari komitmen saya untuk
menulis kode terbaik yang bisa saya tulis. Melepaskannya tampak seperti pengkhianatan terhadap etika itu dan
dari komitmen itu.
Jadi masih di pergelangan tangan saya. Ketika saya menulis kode, saya melihatnya di sana di penglihatan tepi saya. ini
pengingat akan janji yang kubuat untuk diriku sendiri untuk menulis kode bersih.

1. http://www.qualitytree.com/

411

www.it-ebooks.info

Halaman 443

halaman ini sengaja dibiarkan kosong

https://translate.googleusercontent.com/translate_f 349/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 444

Indeks
Deteksi ##, 237 –238 afinitas, 84
++ (sebelum atau sesudah kenaikan) operator, Pengembangan Perangkat Lunak Agile: Prinsip,
325, 326 Pola, Praktek (PPP) , 15
algoritma
mengoreksi, 269– 270
SEBUAH mengulangi, 48
perhitungan dibatalkan, 109 memahami, 297- 298
kelas abstrak, 149 , 271, 290 ambiguitas
ABSTRAK PABRIK pola, 38 , 156, dalam kode, 301
273, 274 tes diabaikan sebagai, 313
antarmuka abstrak, 94 komentar amplifikasi, 59
metode abstrak fungsi analisis, 265
menambah ArgumentMarshaler , 234- 235 "Formulir anotasi", dari AspectJ, 166
memodifikasi, 282 Proyek semut, 76, 77
istilah abstrak, 95 AOP (pemrograman berorientasi aspek),
abstraksi 160, 163
kelas tergantung pada, 150 Lebah. Lihat juga API publik
Kode di tingkat salah, 290 -291 memanggil metode pengembalian- null
turun satu tingkat pada satu waktu, 37 dari, 110
fungsi turun hanya satu tingkat khusus untuk tes, 127
dari, 304- 306 membungkus pihak ketiga, 108
tingkat pencampuran, 36 –37 aplikasi
nama pada tingkat yang sesuai, 311 dipisahkan dari Spring, 164
tingkat pemisahan, 305 decoupling dari konstruksi
membungkus suatu implementasi, 11 detail, 156
tingkat abstraksi infrastruktur, 163
meningkatkan, 290 menjaga kode terkait konkurensi
memisahkan, 305 terpisah, 181
fungsi pengakses, Hukum Demeter struktur sewenang-wenang , 303–304
dan, 98 args array, mengkonversi menjadi daftar , 231- 232
pengakses, penamaan, 25 Kelas args
Rekaman Aktif, 101 membangun, 194
server yang diadaptasi, 185 pelaksanaan, 194- 200
konsep kasar , 201 –212, 226–231

413

https://translate.googleusercontent.com/translate_f 350/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 445

414 Indeks

Kelas ArgsException Misalnya, 71- 72


daftar , 198–200 pengalaman membersihkan, 250
menggabungkan pengecualian menjadi, 239 –242 tidak menebus, 55
argumen komentar buruk, 59- 74
bendera, 41 spanduk, fungsi berkumpul di bawah, 67
untuk suatu fungsi, 40 kelas dasar, 290, 291
dalam fungsi, 288 BDUF (Big Design Up Front), 167
bentuk monadik, 41 kacang, variabel pribadi dimanipulasi,
mengurangi, 43 100- 101
daftar argumen, 43 Beck, Kent, 3, 34, 71, 171, 252,
objek argumen, 43 289, 296
jenis argumen perilaku, 288- 289
menambahkan, 200, 237 Big Design Up Front (BDUF), 167
dampak negatif dari, 208 baris kosong, dalam kode, 78- 79
Kelas ArgumentMarshaler blok, fungsi panggilan di dalam, 35
menambahkan kerangka, 213 –214 Booch, Grady, 8- 9
kelahiran, 212 boolean, beralih ke fungsi, 41
ArgumentMarshaler antarmuka, 197 -198 argumen boolean, 194, 288
array, bergerak, 279 peta boolean , menghapus, 224
seni, kode bersih, 6- 7 keluaran boolean, pengujian, 132
penggandengan buatan, 293 sumber daya terikat, 183, 184
Bahasa AspectJ, 166 batas-batas
pemrograman berorientasi aspek (AOP), bersih, 120
160, 163 mengeksplorasi dan belajar, 116
aspek perilaku salah pada, 289
dalam AOP, 160– 161 memisahkan diketahui dari tidak diketahui,
Dukungan "kelas satu" untuk, 166 118– 119
pernyataan tegas , 130–131 kesalahan kondisi batas, 269
assertEquals , 42 kondisi batas
pernyataan, menggunakan satu set, 111 merangkum, 304
tugas, unaligned, 87- 88 pengujian, 314
operasi atom, 323 –324 tes batas, memudahkan migrasi, 118
atribut, 68 “Game Bowling”, 312
penulis Aturan Pramuka, 14-15 , 257
dari JUnit, 252 berikut, 284
programmer sebagai, 13- 14 memuaskan, 265
pernyataan kepenulisan, 55 metafora windows rusak, 8
instrumentasi kode otomatis, 189 –190 brigade ember, 303
suite otomatis, unit test, 124 BUILD-OPERATE-CHECK pattern, 127
membangun, 287
logika bisnis, memisahkan dari kesalahan
B penanganan, 109
kode yang buruk, 3-4 . Lihat juga kode kotor; bylines, 68
kode berantakan perpustakaan byte-manipulation, 161 ,
efek penurunan, 250 162– 163

www.it-ebooks.info

Halaman 446

Indeks 415

https://translate.googleusercontent.com/translate_f 351/365
3/12/2020 www.it-ebooks.info

C batas bersih, 120


kode bersih
Bahasa Pemrograman C ++ , 7 seni, 6- 7
perhitungan, memecah menjadi perantara dijelaskan, 7- 12
nilai, 296 menulis, 6- 7
tumpukan panggilan, 324 tes bersih, 124 -127
Antarmuka Callable , 326 kebersihan
penelepon, berantakan, 104 diperoleh rasa, 6- 7
hierarki panggilan, 106 terkait dengan tes, 9
panggilan, menghindari rantai, 98 pembersihan, kode, 14- 15
peduli, untuk kode, 10 nama pintar, 26
Poin Cartesian, 42 klien, menggunakan dua metode, 330
Operasi CAS, sebagai atom, 328 kode klien, terhubung ke server, 318
perubahan penguncian berbasis klien, 185 , 329, 330–332
mengisolasi dari, 149 –150 clientScheduler , 320
sejumlah besar sangat kecil, 213 aplikasi klien / server, konkurensi dalam,
pengorganisasian untuk, 147- 150 317- 321
tes memungkinkan, 124 Klien / Server tidak dibaca, kode untuk,
mengubah riwayat, menghapus, 270 343- 346
periksa pengecualian, di Jawa, 106 client-server menggunakan utas, perubahan kode,
bundar menunggu, 337 , 338-339 346- 347
klarifikasi, komentar sebagai, 57 ClientTest.java , 318 , 344–346
kejelasan, 25, 26 menutup kawat gigi, komentar pada, 67- 68
nama kelas, 25 Clover, 268, 269
kelas kekacauan
kohesi, 140- 141 Javadocs as, 276
menciptakan untuk konsep yang lebih besar, 28- 29 bebas dari, 293
mendeklarasikan variabel instan, 81 kode, 2
menegakkan desain dan bisnis buruk, 3- 4
aturan, 115 Aturan Beck, 10
mengekspos internal of, 294 dikomentari, 68–69 , 287
menginstruksikan ke ConTest, 342 mati, 292
menjaga kecil, 136, 175 menjelaskan dirimu,, 55
meminimalkan jumlah, 176 mengekspresikan diri, 54
penamaan, 25, 138 pemformatan, 76
nonthread-aman, 328- 329 implicity dari, 18- 19
sebagai kata benda dari suatu bahasa, 49 instrumenting, 188, 342
organisasi, 136 jiggling, 190
pengorganisasian untuk mengurangi risiko membuat dapat dibaca, 311
berubah, 147 kebutuhan, 2
mendukung konkurensi lanjutan membaca dari atas ke bawah, 37
desain, 183 kesederhanaan, 18, 19
klasifikasi, kesalahan, 107 teknik untuk kain kafan, 20

www.it-ebooks.info

Halaman 447

416 Indeks

kode, terus redundan, 60–62 , 272, 275, 286–287


pihak ketiga, 114- 115 ulangan yang jelas, 64
lebar garis di, 85- 90 dipisahkan dari kode, 54
pada tingkat abstraksi yang salah , 290–291 TODO , 58- 59
basis kode, didominasi oleh kesalahan terlalu banyak informasi dalam, 70
penanganan, 103 ventilasi, 65
perubahan kode, komentar tidak selalu menulis, 287
berikut, 54 "Kesenjangan komunikasi", meminimalkan, 168
penyelesaian kode, otomatis, 20 Bandingkan dan Tukar operasi (CAS),
kode cakupan analisis, 254- 256 327- 328
kode instrumentasi, 188- 190 Modul ComparisonCompactor , 252– 265
"Pengertian kode", 6, 7 defactored, 256- 261
kode bau, daftar , 285 –314 akhir, 263- 265
standar pengkodean, 299 interim, 261- 263
kohesi kode asli, 254– 256
kelas, 140- 141 peringatan kompiler, mematikan, 289
mempertahankan, 141- 146 kode yang rumit, menunjukkan kegagalan

https://translate.googleusercontent.com/translate_f 352/365
3/12/2020 www.it-ebooks.info
argumen baris perintah, 193- 194 dalam, 341
perintah, memisahkan dari query, 45- 46 kompleksitas, pengelolaan, 139– 140
standar tajuk komentar, 55- 56 istilah ilmu komputer (CS), gunakan untuk
tajuk komentar, ganti, 70 nama, 27
kode komentar, 68–69 , 287 konsep
gaya berkomentar, contoh buruk, 71- 72 tetap dekat satu sama lain, 80
komentar penamaan, 19
memperkuat pentingnya satu kata per, 26
sesuatu, 59 memisahkan pada level yang berbeda, 290
buruk, 59- 74 pengejaan serupa dengan cara yang sama, 20
menghapus, 282 keterbukaan vertikal antara, 78- 79
sebagai kegagalan, 54 afinitas konseptual, kode, 84
baik, 55- 59 keprihatinan
heuristik aktif, 286 –287 lintas sektoral, 160– 161
HTML, 69 memisahkan, 154, 166, 178, 250
tidak akurat, 54 kelas konkret, 149
informatif, 56 detail nyata, 149
jurnal, 63- 64 istilah konkret, 94
legal, 55- 56 konkurensi
diamanatkan, 63 prinsip pertahanan, 180 –182
menyesatkan, 63 masalah, 190
bergumam, 59- 60 motif untuk mengadopsi, 178- 179
sebagai kejahatan yang diperlukan, 53- 59 mitos dan kesalahpahaman tentang,
kebisingan, 64- 66 179– 180
tidak menebus kode buruk, 55 kode konkurensi
usang, 286 dibandingkan dengan yang tidak terkait dengan mata uang
ditulis dengan buruk, 287 kode, 181
penggunaan yang tepat, 54 fokus, 321

www.it-ebooks.info

Halaman 448

Indeks 417

algoritma bersamaan, 179 pembaca terus menerus, 184


aplikasi bersamaan, partisi variabel kontrol, dalam pernyataan loop,
perilaku, 183 80- 81
kode bersamaan idiom yang nyaman, 155
melanggar, 329- 333 konvensi
bertahan dari masalah, 180 mengikuti standar, 299- 300
kelemahan bersembunyi di, 188 lebih dari konfigurasi, 164
pemrograman bersamaan, 180 struktur berakhir, 301
Pemrograman Serentak di Jawa: Desain menggunakan konsisten, 259
Prinsip dan Pola , 182, 342 kode berbelit-belit, 175
program bersamaan, 178 pernyataan hak cipta, 55
masalah pembaruan bersamaan, 341 sinar kosmik. Lihat sekali saja
Implementasi ConcurrentHashMap , 183 Kelas CountDownLatch , 183
persyaratan kopel. Lihat juga decoupling; sementara
menghindari negatif, 302 kopel; kopling ketat
merangkum, 257-258 , 301 buatan, 293
data yang dapat dikonfigurasi, 306 tersembunyi temporal, 302- 303
konstanta konfigurasi, 306 kekurangan, 150
konsekuensi, peringatan, 58 pola cakupan, pengujian, 314
konsistensi alat cakupan, 313
dalam kode, 292 “Abstraksi renyah”, 8- 9
enums, 278 keprihatinan lintas sektoral, 160
dalam nama, 40 Cunningham, Ward, 11- 12
konvensi yang konsisten, 259 kelucuan, dalam kode, 26
konstanta
dibandingkan enum , 308- 309
bersembunyi, 308 D
mewarisi, 271 , 307–308 menggantung argumen salah , 294
menjaga pada level yang sesuai, 83 data
meninggalkan sebagai angka mentah, 300 abstraksi, 93- 95
tidak mewarisi, 307 -308 salinan, 181- 182
lewat sebagai simbol, 276 enkapsulasi, 181
berubah menjadi enum, 275- 276 membatasi ruang lingkup, 181
konstruksi set diproses secara paralel, 179

https://translate.googleusercontent.com/translate_f 353/365
3/12/2020 www.it-ebooks.info
pindah semua ke utama , 155, 156 jenis, 97, 101
berpisah dengan pabrik, 156 struktur data. Lihat juga struktur
dari suatu sistem, 154 dibandingkan dengan objek, 95, 97
argumen konstruktor, 157 didefinisikan, 95
konstruktor, kelebihan beban, 25 antarmuka yang mewakili, 94
utas konsumen, 184 memperlakukan Rekaman Aktif sebagai, 101
Alat ConTest, 190, 342 objek transfer data (DTO),
konteks 100–110 , 160
menambahkan bermakna, 27- 29 bentuk normal basis data, 48
tidak menambahkan serampangan, 29- 30
DateInterval enum, 282- 283
memberikan pengecualian, 107
Enumerasi HARI , 277

www.it-ebooks.info

Halaman 449

418 Indeks

Kelas DayDate , menjalankan SerialDate deskripsi


as, 271 sebuah kelas, 138
DayDateFactory , 273- 274 membebani struktur kode
kode mati, 288, 292 menjadi, 310
fungsi mati, 288 nama deskriptif
kebuntuan, 183 , 335–339 memilih, 309– 310
pelukan mematikan. Lihat menunggu melingkar menggunakan, 39- 40
debugging, menemukan deadlock, 336 desain
pengambilan keputusan, mengoptimalkan, 167 -168 algoritma bersamaan, 179
keputusan, menunda, 168 minimal digabungkan, 167
deklarasi, tidak selaras, 87 –88 prinsip, 15
Objek DECORATOR, 164 pola desain, 290
Pola DECORATOR, 274 detail, memperhatikan, 8
arsitektur dipisahkan, 167 DI (Injeksi Ketergantungan), 157
decoupling, dari konstruksi Dijkstra, Edsger, 48
detail, 156 model eksekusi filsuf makan,
strategi decoupling, konkurensi 184– 185
sebagai, 178 DIP (Dependency Inversion Principle),
konstruktor default, menghapus, 276 15, 150
degradasi, pencegahan, 14 kode kotor. Lihat juga kode buruk;
penghapusan, karena mayoritas kode berantakan
perubahan, 250 kode kotor, pembersihan, 200
kepadatan, vertikal dalam kode, 79 –80 tes kotor, 123
ketergantungan disinformasi, menghindari, 19- 20
menemukan dan menghancurkan, 250 jarak, kode vertikal, 80 –84
menyuntikkan, 157 perbedaan, membuat bermakna, 20- 21
logis, 282 bahasa khusus domain (DSL),
membuat logis fisik, 298 –299 168– 169
antara metode, 329- 333 bahasa pengujian khusus domain, 127
antara disinkronkan Kelas DoubleArgumentMarshaler , 238
metode, 185 Prinsip KERING (Jangan Ulangi Diri Sendiri),
Dependency Injection (DI), 157 181, 289
Prinsip Ketergantungan Inversi (DIP), DTO (objek transfer data), 100–110 , 160
15, 150 lingkup boneka, 90
magnet ketergantungan, 47 duplikat jika pernyataan, 276
tergantung fungsi, format, 82- 83 duplikasi
turunannya kode, 48
kelas dasar tergantung pada, 291 dalam kode, 289 –290
kelas dasar mengetahui tentang, 273 menghilangkan, 173 –175
dari kelas pengecualian, 48 fokus pada, 10
memindahkan fungsi set ke, 232 , bentuk, 173, 290
233– 235 pengurangan, 48
mendorong fungsionalitas ke, 217 strategi untuk menghilangkan, 48

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 354/365
3/12/2020 www.it-ebooks.info

Halaman 450

Indeks 419

argumen diad, 40 pesan kesalahan, 107, 250


fungsi diad, 42 pemrosesan kesalahan, pengujian , 238 –239
proxy yang dinamis, 161 metode errorMessage , 250
kesalahan. Lihat juga kesalahan kondisi batas;
kesalahan pengejaan; perbandingan string
E kesalahan
mengklasifikasikan, 107
e , sebagai nama variabel, 22
Eclipse, 26 Evans, Eric, 311
mengedit sesi, memutar, 13 -14 acara, 41
efisiensi, kode, 7 klasifikasi pengecualian, 107
Arsitektur EJB, sejak awal direkayasa berlebihan, klausul pengecualian, 107 -108
167 kode manajemen pengecualian, 223
Standar EJB, perombakan total, 164 pengecualian
EJB2, 160 alih-alih mengembalikan kode , 103 –105
EJB3, objek Bank ditulis ulang, 165 –166 mempersempit jenis, 105 –106
Kode "elegan", 7 lebih suka kode kesalahan, 46
desain yang muncul, 171 –176 menyediakan konteks, 107
enkapsulasi, 136 memisahkan dari Args , 242- 250
kondisi batas, 304 melempar, 104–105 , 194
melanggar, 106- 107 dicentang, 106- 107
kondisional, 301 eksekusi, kemungkinan jalur , 321 –326
pengkodean, menghindari, 23 -24, 312-313 model eksekusi, 183- 185
kacang entitas, 158 –160 Pelaksana kerangka, 326- 327

enum (s) ExecutorClientScheduler.java , 321


mengubah MonthConstants ke, 272 penjelasan, tentang niat, 56 –57
menggunakan, 308- 309 variabel penjelas, 296 -297
enumerasi, bergerak, 277 ketegasan, kode, 19
lingkungan, heuristik aktif, 287 kode ekspresif, 295
sistem kontrol lingkungan, 128 -129 ekspresi
iri, ruang lingkup kelas, 293 dalam kode, 10- 11
periksa kesalahan, sembunyikan efek samping, 258 memastikan, 175 –176
Metode Ekstrak refactoring, 11
Kesalahan kelas, 47- 48
konstanta kode kesalahan, 198 -200 Petualangan Pemrograman Ekstrim
kode kesalahan dalam C # , 10
menyiratkan kelas atau enum, 47- 48 Pemrograman Ekstrim Diinstal , 10
lebih suka pengecualian untuk, 46 "Penuh mata", pemasangan kode , 79 –80
kembali, 103 –104
menggunakan kembali tua, 48
F
memisahkan dari modul Args ,
242- 250 pabrik , 155 –156
deteksi kesalahan, mendorong ke tepi, 109 kelas pabrik, 273 –275
bendera kesalahan , 103 –104 kegagalan
penanganan kesalahan, 8 , 47-48 untuk mengekspresikan diri kita dalam kode, 54

www.it-ebooks.info

Halaman 451

420 Indeks

kegagalan, terus turun satu tingkat abstraksi,


pola, 314 304- 306
mentolerir tanpa membahayakan, 330 melakukan satu hal, 35–36 , 302
argumen salah , 294 diad, 42

https://translate.googleusercontent.com/translate_f 355/365
3/12/2020 www.it-ebooks.info
tes cepat, 132 menghilangkan pernyataan jika tidak ada ,
utas yang berjalan cepat, kelaparan lebih lama 262
berlari, 183 membangun sifat temporal
ketakutan, penggantian nama, 30 dari, 260
Feathers, Michael, 10 format tergantung, 82- 83
iri fitur berkumpul di bawah spanduk, 67
menghilangkan, 293 –294 heuristik aktif, 288
berbau, 278 niat mengungkap, 19
ukuran file, di Jawa, 76 tetap kecil, 175
kata kunci terakhir , 276 panjangnya, 34 –35
Akronim PERTAMA, 132 -133 bergerak, 279
Hukum Pertama, dari TDD, 122 penamaan, 39, 297
Proyek FitNesse jumlah argumen dalam, 288
gaya pengkodean untuk, 90 satu tingkat dari per abstraksi, 36 -37
ukuran file, 76 , 77 menggantikan komentar, 67
fungsi dalam, 32 -33 mengganti nama untuk kejelasan, 258
menjalankan semua tes, 224 menulis ulang untuk kejelasan , 258 –259
argumen bendera, 41, 288 bagian dalam, 36
kode yang difokuskan, 8 sekecil mungkin, 34
kode asing. Lihat kode pihak ketiga pemrograman terstruktur dengan, 49
pemformatan memahami, 297 -298
horisontal, 85- 90 sebagai kata kerja suatu bahasa, 49
tujuan, 76 menulis, 49
Peraturan Paman Bob, 90- 92 berjangka, 326
vertikal, 76 –85
gaya format, untuk tim
pengembang, 90 G
Fortran, memaksa penyandian, 23
Gamma, Eric, 252
Fowler, Martin, 285, 293
heuristik umum , 288–307
bingkai, 324
kode byte yang dihasilkan, 180
argumen fungsi, 40- 45
generik, meningkatkan keterbacaan kode, 115
fungsi panggilan dependensi, 84- 85
header fungsi, 70 dapatkan fungsi, 218

tanda tangan fungsi, 45 Fungsi getBoolean , 224


fungsi, penempatan , 295–296 Instruksi GETFIELD , 325 , 326
fungsi Metode getNextId , 326
membobol kecil, 141 -146 fungsi getState , 129
Gilbert, David, 267, 268
menelepon dalam satu blok, 35
konvensi diberikan-kapan-kemudian, 130
mati, 288
gangguan. Lihat sekali saja
mendefinisikan pribadi, 292

www.it-ebooks.info

Halaman 452

Indeks 421

strategi pengaturan global, 155 mengekspos, 94


“Kelas Tuhan”, 136– 137 bersembunyi, 94
komentar yang baik, 55- 59 membungkus abstraksi, 11
pernyataan kebagian , menghindari, 48, 49 Pola Implementasi , 3 , 296
redesign besar, 5 implikasi, dari kode, 18
konteks serampangan, 29- 30 daftar impor
menghindari panjang, 307
pemendekan di SerialDate , 270
H impor, seperti halnya ketergantungan, 307
ketidaktepatan, dalam kode, 301
instrumentasi kode tangan, 189
komentar yang tidak akurat, 54
HashTable , 328- 329
informasi yang tidak pantas, dalam
header. Lihat tajuk komentar; fungsi
komentar, 286
header
metode statis yang tidak tepat, 296
heuristik
termasuk metode, 48
referensi silang, 286, 409
inkonsistensi, dalam kode, 292
umum, 288- 307
ejaan yang tidak konsisten, 20
daftar , 285 –314
incrementalism, 212– 214
kopling sementara yang tersembunyi, 259 , 302–303
level indent, dari suatu fungsi, 35
hal-hal tersembunyi, dalam suatu fungsi, 44
lekukan, kode, 88- 89
bersembunyi
aturan lekukan, 89
implementasi, 94

https://translate.googleusercontent.com/translate_f 356/365
3/12/2020 www.it-ebooks.info
struktur, 99 tes independen, 132
informasi
hierarki cakupan, 88
tidak pantas, 286
HN. Lihat Notasi Hongaria
terlalu banyak, 70, 291–292
keselarasan horisontal, kode, 87- 88
komentar informatif, 56
format horisontal, 85- 90
hierarki warisan, 308
ruang putih horisontal, 86
koneksi tidak jelas, di antara komentar
HTML, dalam kode sumber, 69
dan kode, 70
Notasi Hongaria (HN), 23-24 , 295
masukan argumen, 41
Hunt, Andy, 8, 289
variabel contoh
struktur hibrida, 99
di kelas, 140
mendeklarasikan, 81
saya menyembunyikan deklarasi, 81- 82
lewat sebagai fungsi
jika pernyataan argumen, 231
duplikat, 276 proliferasi, 140
menghilangkan, 262 kelas instrumen, 342
jika- rantai lain tes tidak mencukupi, 313
muncul lagi dan lagi, 290 argumen integer
menghilangkan, 233 mendefinisikan, 194
tes yang diabaikan, 313 mengintegrasikan, 224 –225
penerapan fungsi argumen integer ,
duplikasi, 173 pindah ke ArgumentMarshaler ,
penyandian, 24 215– 216

www.it-ebooks.info

Halaman 453

422 Indeks

tipe argumen integer, menambahkan Pemrogram Java, penyandian tidak


untuk Args , 212 dibutuhkan, 24
bilangan bulat, pola perubahan untuk, 220 Proxy Java, 161 –163
IntelliJ, 26 File sumber Java, 76 –77
maksud javadocs
menjelaskan dalam kode, 55 sebagai kekacauan, 276
penjelasan tentang, 56– 57 dalam kode nonpublik, 71
dikaburkan, 295 melestarikan pemformatan, 270
fungsi mengungkap niat, 19 dalam API publik, 59
Nama niat-mengungkapkan, 18- 19 membutuhkan untuk setiap fungsi, 63
antarmuka paket java.util.concurrent , koleksi
mendefinisikan lokal atau jarak jauh, 158 –160 di, 182 –183
penyandian, 24 JBoss AOP, proksi dalam, 163
mengimplementasikan, 149- 150 Perpustakaan JCommon, 267
mewakili keprihatinan abstrak, 150 Tes unit JCommon , 270
mengubah ArgumentMarshaler menjadi, 237 Proyek JDepend, 76, 77
didefinisikan dengan baik, 291- 292 Proxy JDK, memberikan dukungan kegigihan,
menulis, 119 161– 163
struktur internal, objek bersembunyi, 97 Jeffries, Ron, 10-11 , 289
persimpangan, domain, 160 strategi jiggling, 190
intuisi, tidak mengandalkan, 289 Pencarian JNDI, 157
penemu C ++, 7 jurnal komentar, 63- 64
Inversion of Control (IoC), 157 JUnit , 34
Objek InvocationHandler , 162 Kerangka kerja JUnit, 252– 265
I / O terikat, 318 Proyek Junit, 76, 77
mengisolasi, dari perubahan, 149 -150 Kompilator Just-In-Time, 180
isxxxArg metode, 221- 222
proses berulang, refactoring sebagai, 265
K
J bentuk kata kunci, dari nama fungsi, 43

file jar, menyebarkan turunan dan pangkalan


dalam, 291 L.
Jawa
L , huruf kecil dalam nama variabel, 20
aspek atau mekanisme seperti aspek,
desain bahasa, seni pemrograman seperti, 49
161– 166
bahasa
heuristik aktif , 307–309
tampak sederhana, 12
sebagai bahasa bertele-tele, 200
tingkat abstraksi, 2

https://translate.googleusercontent.com/translate_f 357/365
3/12/2020 www.it-ebooks.info
Java 5, perbaikan untuk bersamaan
pengembangan, 182- 183 multipel dalam satu file sumber, 288
kelipatan dalam komentar, 270
Java 5 Pelaksana kerangka kerja, 320- 321
struktur data last-in, first-out (LIFO),
Java 5 VM, solusi nonblocking di,
operan stack as, 324
327- 328
Law of Demeter, 97–98 , 306
Java AOP frameworks, 163- 166

www.it-ebooks.info

Halaman 454

Indeks 423

INISIALISASI LAZY / manajer, peran, 6


Ungkapan EVALUASI, 154 komentar yang diamanatkan, 63
INISIALISASI LAZY, 157 kontrol manual, lebih dari ID seri, 272
Lea, Doug, 182, 342 Peta
tes belajar, 116, 118 menambahkan untuk ArgumentMarshaler , 221
Hukum LeBlanc, 4 metode, 114
kode lama, 307 peta, melanggar penggunaan, 222 –223
komentar hukum, 55- 56 implementasi marshalling,
tingkat abstraksi, 36- 37 214– 215
tingkat kerincian, 99 konteks yang bermakna, 27- 29
leksikon, memiliki yang konsisten, 26 variabel anggota
baris kode f awalan untuk, 257
duplikat, 173 awalan, 24
lebar, 85 mengganti nama untuk kejelasan, 259
daftar pemetaan mental, menghindari, 25
argumen, 43 kode berantakan. Lihat juga kode buruk; kode kotor
artinya khusus untuk programmer, 19 total biaya kepemilikan, 4- 12
mengembalikan kekekalan yang telah ditentukan,doa
110metode, 324
kode baca tulis, 9 nama metode, 25
pemrograman melek huruf, 9 metode
Pemrograman Literate , 141 mempengaruhi urutan eksekusi, 188
livelock, 183, 338 memanggil saudara kembar dengan bendera, 278
komentar lokal, 69- 70 berubah dari statis ke instance, 280
variabel lokal, 324 kelas, 140
mendeklarasikan, 292 ketergantungan antara, 329- 333
di bagian atas setiap fungsi, 80 menghilangkan duplikasi antara,
kunci & tunggu, 337, 338 173– 174
mengunci, memperkenalkan, 185 meminimalkan pernyataan tegas dalam, 176
log4j paket, 116- 118 penamaan, 25
dependensi logis, 282 , 298–299 tes mengekspos bug dalam, 269
Bahasa LOGO, 36 kode minimal, 9
nama deskriptif panjang, 39 komentar yang menyesatkan, 63
nama panjang, untuk cakupan panjang, 312 tanggung jawab salah tempat, 295–296 , 299
penghitung putaran, nama huruf tunggal untuk, 25 OBYEK MOCK, menugaskan, 155
argumen monadik, 40
bentuk monadik, argumen, 41
M. Monad, mengubah angka dua menjadi, 42
Pengujian Monte Carlo, 341
angka ajaib
Bulan enum, 278
mengaburkan niat, 295
Kelas MonthConstants , 271
mengganti dengan konstanta bernama,
sadar multithread, 332
300- 301
perhitungan multithread, dari throughput,
fungsi utama , memindahkan konstruksi ke, 335
155, 156

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 358/365
3/12/2020 www.it-ebooks.info
Halaman 455

424 Indeks

kode multithreaded, 188 , 339-342 Newkirk, Jim, 116


bergumam, 59- 60 koran metafora, 77- 78
mutators, penamaan, 25 argumen nilad, 40
saling pengecualian, 183, 336, 337 tanpa preemption, 337
kebisingan
komentar, 64- 66
N menakutkan, 66
bernama konstanta, menggantikan sihir kata-kata, 21
angka, 300- 301 nomenklatur, menggunakan standar, 311- 312
bahasa dengan nama panjang, 23 solusi nonblocking, 327- 328
nama kode yang tidak berhubungan dengan mata uang, 181
abstraksi, level yang sesuai, 311 nama tidak informatif, 21
berubah, 40 Informasi nonlokal, 69- 70
memilih, 175, 309–310 kode nonpublik, javadocs dalam, 71
kelas, 270- 271 metode tidak statis, lebih disukai daripada statis, 296
pintar, 26 kode nonthreaded, mulai bekerja
deskriptif, 39- 40 pertama, 187
fungsi, 297 kelas nonthread-aman, 328- 329
heuristik pada, 309 -313 aliran normal, 109
pentingnya, 309– 310 batal

niat-mengungkapkan, 18- 19 tidak melewati ke metode, 111- 112


panjang sesuai dengan ruang lingkup, tidak kembali, 109 -110
22- 23 melewati penelepon tanpa sengaja, 111
nama panjang untuk cakupan panjang, 312 logika deteksi nol, untuk ArgumentMarshaler ,
membuat jelas, 258 214
domain masalah, 27 NullPointerException , 110, 111
diucapkan, 21- 22 penamaan seri-nomor, 21
aturan untuk menciptakan, 18- 30
dicari, 22- 23
HAI
lebih pendek umumnya lebih baik daripada lebih lama, 30
domain solusi, 27 Analisis dan Desain Berorientasi Objek dengan
dengan perbedaan halus, 20 Aplikasi , 8
jelas, 312 desain berorientasi objek, 15
pada tingkat abstraksi yang salah, 271 benda
penamaan, kelas, 138 dibandingkan dengan struktur data, 95, 97
konvensi penamaan, lebih rendah dari dibandingkan dengan tipe data dan prosedur-
struktur, 301 Dures, 101
metode navigasi, secara Aktif menyalin hanya baca, 181
Catatan, 101 didefinisikan, 95
dekat bug, pengujian, 314 niat tidak jelas, 295
persyaratan negatif, menghindari, 302 komentar usang, 286
negatif, 258 perilaku yang jelas, 288- 289
struktur bersarang, 46 kode yang jelas, 12

www.it-ebooks.info

Halaman 456

Indeks 425

Prinsip "Sekali dan hanya sekali", 289 kinerja


Aturan "ONE SWITCH" , 299 dari pasangan klien / server, 318
satu hal, fungsi melakukan, 35 -36, 302 peningkatan concurrency, 179
satu kali, 180, 187, 191 penguncian berbasis server, 333
Kode OO, 97 permutasi, menghitung, 323
Desain OO, 139 kegigihan, 160, 161
Prinsip Terbuka Tertutup (OCP), 15, 38 penguncian pesimistis, 327
dengan pengecualian yang diperiksa, 106 fraseologi, dengan nama yang mirip, 40

https://translate.googleusercontent.com/translate_f 359/365
3/12/2020 www.it-ebooks.info
mendukung, 149 secara fisik, ketergantungan, 299
tumpukan operan, 324 Benda Jawa Biasa-Lama. Lihat POJO
sistem operasi, kebijakan threading, 188 platform, menjalankan kode berulir, 188
operator, diutamakan, 86 kode menyenangkan, 7
penguncian optimis, 327 kode berbasis thread pluggable, 187
optimasi, MALAS-EVALUASI Sistem POJO, kelincahan yang diberikan oleh, 168
sebagai, 157 POJOs (Benda Jawa Kuno)
optimalisasi, pengambilan keputusan, 167 –168 menciptakan, 187
pemesanan, menghitung kemungkinan , 322 –323 menerapkan logika bisnis, 162
organisasi memisahkan kode sadar-ulir, 190
untuk perubahan, 147 –150 di Spring, 163
kelas, 136 menulis logika domain aplikasi, 166
mengelola kompleksitas, 139– 140 argumen poladik, 40
tes keluar, menggunakan antarmuka, 118 perilaku polimorfik, dari fungsi, 296
argumen keluaran, 41, 288 perubahan polimorfik, 96- 97
menghindari, 45 polimorfisme, 37, 299
butuhkan untuk menghilang, 45 penanda posisi, 67
output, argumen sebagai, 45 positif
overhead, yang ditimbulkan oleh concurrency, 179 lebih mudah dimengerti, 258
overload, kode dengan deskripsi, 310 menyatakan persyaratan sebagai, 302
keputusan, keputusan
sebagai titik semua penamaan, 30
P predikat, penamaan, 25
model paperback, sebagai seorang akademisi preemption, breaking, 338
model, 27 awalan
parameter, diambil dengan instruksi, 324 untuk variabel anggota, 24
tidak berguna di lingkungan saat ini,
operasi parse , melempar
pengecualian, 220 312- 313
partisi, 250 operator pra-kenaikan, ++ , 324, 325, 326
jalur eksekusi, 321 –326 "Prequel", buku ini sebagai, 15
jalur, melalui bagian kritis, 188 prinsip paling tidak mengejutkan , 288 –299, 295
nama-nama pola, menggunakan standar, 175 prinsip, desain, 15
pola Program PrintPrimes , terjemahan ke dalam
kegagalan, 314 Jawa, 141
sebagai salah satu jenis standar, 311 perilaku pribadi, mengisolasi , 148-149

www.it-ebooks.info

Halaman 457

426 Indeks

fungsi pribadi, 292 Dave Thomas aktif, 9


perilaku metode pribadi, 147 meningkatkan penggunaan obat generik, 115
nama domain bermasalah, 27 perspektif keterbacaan, 8
kode prosedural, 97 pembaca
contoh bentuk prosedural, 95- 96 kode, 13- 14
prosedur, dibandingkan dengan objek, 101 terus menerus, 184
fungsi proses, partisi ulang , 319-320 model eksekusi pembaca-penulis, 184
metode proses , I / O terikat, 319 bacaan
proses, bersaing untuk sumber daya, 184 kode bersih, 8
terikat prosesor, kode sebagai, 318 kode dari atas ke bawah, 37
model eksekusi konsumen produsen, 184 versus tulisan, 14
utas produsen, 184 reboot, sebagai solusi penguncian, 331
lingkungan produksi, 127 –130 rekomendasi, dalam buku ini, 13
produktivitas, dikurangi dengan kode berantakan, 4 mendesain ulang, diminta oleh tim, 5
programmer profesional, 25 redundansi, dari kata-kata bising, 21
ulasan profesional, kode, 268 komentar yang berlebihan, 60–62 , 272, 275,
programmer 286- 287
sebagai penulis, 13 -14 Kelas ReentrantLock , 183
teka-teki yang dihadapi, 6 program refactored, lebih lama, 146
tanggung jawab untuk messes, 5- 6 refactoring
tidak profesional, 5- 6 Args , 212
pemrograman kode secara bertahap, 172
didefinisikan, 2 sebagai proses berulang, 265
terstruktur, 48 -49 menempatkan sesuatu untuk dibawa keluar, 233
program, membuat mereka bekerja, 201 kode tes, 127
nama diucapkan, 21- 22 Refactoring (Fowler), 285

https://translate.googleusercontent.com/translate_f 360/365
3/12/2020 www.it-ebooks.info
variabel yang dilindungi, menghindari, 80 mengganti nama, takut, 30
proksi, kekurangan dari, 163 pengulangan, bug konkurensi, 180
API publik, javadocs di, 59 tes berulang, 132
puns, menghindari, 26- 27 persyaratan, menentukan, 2
Instruksi PUTFIELD , sebagai atom, 325 resetId , kode byte yang dihasilkan untuk , 324 –325
sumber daya
terikat, 183
Q proses bersaing, 184
kueri, pisahkan dari perintah, 45 –46 utas menyetujui pemesanan global
dari, 338
tanggung jawab
R berhitung di kelas, 136
definisi, 138
goncangan acak, tes berjalan, 190
mengidentifikasi, 139
rentang, termasuk tanggal titik akhir di, 276
salah tempat, 295–296 , 299
keterbacaan
memecah program menjadi main, 146
tes bersih, 124
kembalikan kode, sebagai gantinya gunakan pengecualian,
kode, 76
103- 105

www.it-ebooks.info

Halaman 458

Indeks 427

digunakan kembali, 174 aplikasi server , 317 –318, 343–344


risiko perubahan, pengurangan, 147 kode server, tanggung jawab, 319
kode yang jelas, tulisan, 112 penguncian berbasis server, 329
draft kasar, tulisan, 200 seperti yang lebih disukai, 332 –333
antarmuka runnable , 326 dengan metode yang disinkronkan, 185
ekspresi run-on, 295 "Servlet" model, aplikasi Web, 178
run-on entri jurnal, 63- 64 Server kecil , masalah sinkronisasi, 182
logika runtime, memisahkan startup dari, 154 mengatur fungsi, pindah ke yang sesuai
turunannya, 232 , 233–235
setArgument , changing, 232– 233
S fungsi setBoolean , 217
mekanisme keselamatan, diganti, 289 metode penyetel, menyuntikkan dependensi,
meningkatkan, 157– 161 157
suara menakutkan, 66 strategi pengaturan, 155
skema, sebuah kelas, 194 Daftar SetupTeardownIncluder.java ,
sekolah pemikiran, tentang kode bersih, 50– 52
12- 13 kelas bentuk, 95- 96
aturan gunting, dalam C ++, 81 data bersama, membatasi akses, 181
ruang lingkup variabel bersama
didefinisikan oleh pengecualian, 105 memperbarui metode, 328
dummy, 90 mengurangi ruang lingkup, 333
iri, 293 pendekatan shotgun, instrumen kode tangan
memperluas dan indentasi, 89 sebagaimana, 189
hierarki dalam file sumber, 88 kode shut-down, 186
membatasi data, 181 shutdowns, anggun, 186
nama yang terkait dengan panjang, efek samping
22–23 , 312 tidak memiliki, 44
variabel bersama, 333 nama yang menjelaskan, 313
nama dicari, 22- 23 Simmons, Robert, 276
Hukum Kedua, dari TDD, 122 kode sederhana, 10, 12
bagian, dalam fungsi, 36 Desain Sederhana, aturan, 171 –176
argumen pemilih, menghindari, 294 –295 kesederhanaan, kode, 18, 19
tes validasi diri, 132 aturan tegas tunggal , 130 –131
konsep tunggal, di setiap fungsi tes,
Kelas semafor , 183
titik koma, membuat terlihat, 90 131– 132
"Nomor seri", menggunakan SerialDate , 271 Prinsip Tanggung Jawab Tunggal (SRP), 15 ,
138– 140
Kelas SerialDate
membuatnya benar, 270 –284 melamar, 321
penamaan dari, 270- 271 melanggar, 155
refactoring, 267 –284 sebagai prinsip pertahanan konkurensi,
181
Kelas SerialDateTests , 268
serialisasi, 272 mengakui pelanggaran, 174
server, utas dibuat oleh, 319 –321 server melanggar, 320

https://translate.googleusercontent.com/translate_f 361/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

Halaman 459

428 Indeks

Prinsip Tanggung Jawab Tunggal (SRP), kelaparan, 183, 184, 338


melanjutkan fungsi statis, 279
Sql melanggar kelas, 147 impor statis, 308
mendukung, 157 metode statis, tidak pantas, 296
di kelas tes sesuai dengan, 172 Aturan Langkah-ke Bawah , 37
melanggar, 38 cerita, hanya mengimplementasikan hari ini, 158
nilai tunggal, komponen yang dipesan, 42 Pola STRATEGI, 290
nama satu huruf, 22, 25 argumen string , 194 , 208–212, 214–225
perhitungan single-thread, dari throughput, kesalahan perbandingan string, 252
334 StringBuffers , 129
Pola SINGLETON, 274 Stroustrup, Bjarne, 7- 8
kelas kecil, 136 struktur. Lihat juga struktur data
Pola Praktek Terbaik Smalltalk , 296 bersembunyi, 99
programmer pintar, 25 hibrida, 99
proyek perangkat lunak, pemeliharaan, 175 membuat perubahan besar ke, 212
sistem perangkat lunak. Lihat juga sistem lebih dari konvensi, 301
dibandingkan dengan sistem fisik, 158 pemrograman terstruktur, 48- 49
Prinsip desain kelas SOLID, 150 Kelas SuperDashboard , 136 –137
nama domain solusi, 27 swapping, sebagai permutasi, 323
sistem kontrol kode sumber, 64, 68, 69 beralih pernyataan
file sumber mengubur, 37, 38
dibandingkan dengan artikel surat kabar, mempertimbangkan polimorfisme
77- 78 sebelumnya, 299
banyak bahasa dalam, 288 alasan untuk mentoleransi, 38 -39
Program Sparkle , 34 saklar / rantai case , 290
untaian benang, menemui jalan buntu, 186 masalah sinkronisasi, hindari dengan
objek kasus khusus, 110 Servlets , 182
POLA KASUS KHUSUS, 109 blok tersinkronisasi , 334
spesifikasi, tujuan, 2 kata kunci yang disinkronkan , 185
kesalahan pengejaan, koreksi, 20 menambahkan, 323
SpreadsheetDateFactory , 274– 275 selalu mendapatkan kunci, 328
Spring AOP, proxy di, 163 memperkenalkan kunci via, 331
Kerangka Spring, 157 melindungi bagian yang kritis
Model pegas, mengikuti EJB3, 165 dalam kode, 181
File konfigurasi Spring V2.5, 163 –164 metode yang disinkronkan, 185
kegagalan palsu, 187 sinkronisasi, menghindari, 182
Sql kelas, berubah, 147 –149 fungsi sintesis, 265
akar kuadrat, sebagai batas iterasi, 74 sistem Lihat juga sistem perangkat lunak
SRP. Lihat Prinsip Tanggung Jawab Tunggal ukuran file signifikan, 77
konvensi standar, 299 –300 tetap berjalan selama pengembangan,
nomenklatur standar, 175 , 311–312 213
standar, menggunakan dengan bijak, 168 membutuhkan spesifik domain, 168
proses startup, terpisah dari runtime arsitektur sistem, tes mengemudi,
logika, 154 166– 167

www.it-ebooks.info

Halaman 460

https://translate.googleusercontent.com/translate_f 362/365
3/12/2020 www.it-ebooks.info

Indeks 429

kegagalan sistem, tidak mengabaikan fungsi tes, konsep tunggal dalam , 131 –132
satu kali, 187 implementasi tes, dari sebuah antarmuka, 150
tingkat sistem, tetap bersih,, 154 test suite
informasi seluruh sistem, secara lokal otomatis, 213
komentar, 69- 70 tes unit, 124, 268
memverifikasi perilaku yang tepat, 146
sistem yang dapat diuji, 172
T pengembangan berbasis tes. Lihat TDD
tabel, bergerak, 275 pengujian
platform penyebaran target, menjalankan tes argumen membuat lebih sulit, 40
pada, 341 logika konstruksi dicampur dengan
bertukar tugas, memberi semangat, 188 runtime, 155
TDD (Test Driven Development), 213 menguji bahasa, khusus domain, 127
logika pembangunan, 106 proyek testNG, 76, 77
sebagai disiplin dasar, 9 tes
hukum, 122- 123 bersih, 124- 127
aturan tim, 90 kebersihan terkait dengan, 9
tim berkomentar untuk SerialDate ,
standar pengkodean untuk setiap, 299 –300 268- 270
diperlambat oleh kode berantakan, 4 kotor, 123
nama teknis, pemilihan, 27 mengaktifkan -ilities, 124
catatan teknis, pemesanan komentar cepat, 132
untuk, 286 cepat versus lambat, 314
Pola METODE TEMPLATE heuristik pada, 313- 314
menangani duplikasi, 290 diabaikan, 313
menghapus duplikasi tingkat yang lebih tinggi, mandiri, 132
174- 175 tidak cukup, 313
menggunakan, 130 menjaga bersih, 123- 124
kopling temporal. Lihat juga kopling meminimalkan pernyataan tegas dalam,
mengekspos, 259– 260 130- 131
tersembunyi, 302- 303 tidak berhenti sepele, 313
menciptakan efek samping, 44 refactoring, 126 –127
variabel sementara, menjelaskan, 279- 281 berulang, 132
kasus uji membutuhkan lebih dari satu langkah, 287
menambahkan untuk memeriksa argumen, 237 berlari, 341
dalam ComparisonCompactor , 252– 254 memvalidasi diri, 132
pola kegagalan, 269 , 314 semua desain sederhana berjalan, 172
mematikan, 58 suite otomatis, 213
kode tes, 124, 127 tepat waktu, 133
TEST DOUBLE, assigning, 155 menulis untuk kode multithreaded,
Pengembangan Berbasis Tes. Lihat TDD 339- 342
tes mengemudi, arsitektur , 166 –167 menulis untuk kode berulir, 186 –190
lingkungan pengujian, 127- 130 menulis yang baik, 122- 123

www.it-ebooks.info

Halaman 461

430 Indeks

Hukum Ketiga, dari TDD, 122 program pengatur waktu, pengujian, 121 –122
kode pihak ketiga Kata kunci "TO", 36
mengintegrasikan, 116 KE paragraf, 37
belajar, 116 TODO komentar, 58- 59
menggunakan, 114- 115 token, digunakan sebagai angka ajaib, 300
tes menulis untuk, 116 Proyek Tomcat, 76, 77
variabel ini , 324 alat
Thomas, Dave, 8 , 9, 289 Alat ConTest, 190, 342
utas cakupan, 313
menambah metode, 322 menangani pelat boiler proxy, 163
saling mengganggu, 330 menguji kode berbasis thread, 342
membuat independen seperti kereta rongsokan, 98- 99
mungkin, 182 transformasi, sebagai nilai balik, 41

https://translate.googleusercontent.com/translate_f 363/365
3/12/2020 www.it-ebooks.info
saling menginjak, 180, 326 navigasi transitif, menghindari , 306 –307
mengambil sumber daya dari yang lain argumen triadik, 40
utas, 338 triad, 42
strategi manajemen utas, 320 coba blok, 105
kolam ulir, 326 coba / tangkap balok, 46-47 , 65-66
kode berbasis thread, pengujian, 342 pernyataan try-catch-akhirnya , 105- 106
kode berulir merdu berulir berbasis kode, 187- 188
membuat pluggable, 187 jenis pengkodean, 24
membuat merdu, 187– 188
gejala bug di, 187
pengujian, 186 –190 U
menulis dalam Java 5, 182 –183 bahasa di mana-mana, 311- 312
threading nama jelas, 312
menambah aplikasi klien / server, pengecualian dicentang, 106- 107
319, 346–347 kondisional tanpa enkapsulasi, enkapsulasi,
masalah dalam sistem yang kompleks, 342 257
koleksi thread-safe, 182 –183, 329 pengujian unit, terisolasi karena sulit, 160
hasil unit test, 124 , 175, 268
menyebabkan kelaparan, 184 pemrograman tidak profesional, 5- 6
membaik, 319 huruf besar C , dalam nama variabel, 20
meningkat, 333- 335 kegunaan, dari surat kabar, 78
memvalidasi, 318 gunakan, dari suatu sistem, 154
klausa melempar , 106 pengguna, menangani secara bersamaan, 179
tim harimau, 5
kopling ketat, 172
waktu, mengambil untuk pergi cepat, 6 V
Proyek Waktu dan Uang, 76
validasi, dari throughput, 318
ukuran file, 77
nama variabel, satu huruf, 25
tes tepat waktu, 133

www.it-ebooks.info

Halaman 462

Indeks 431

variabel W
1 berbasis versus nol, 261
mendeklarasikan, 80, 81, 292 mengarungi, melalui kode yang buruk, 3
menjelaskan sementara, 279- 281 Wadah web, disediakan decoupling
jelas, 296- 297 oleh, 178
merahasiakannya, 93 apa, memisahkan dari kapan, 178
lokal, 292, 324 ruang putih, penggunaan horizontal, 86
pindah ke kelas yang berbeda, 273 wildcard, 307
menggantikan komentar, 67 Bekerja secara Efektif dengan Legacy
mempromosikan ke variabel instan dari Kode , 10
kelas, 141 Program "bekerja", 201
dengan konteks yang tidak jelas, 28 pengerjaan, 176
ventilasi, dalam komentar, 65 pembungkus, 108
kata kerja, kata kunci dan, 43 pembungkus, 108
Kelas versi , 139 penulis, kelaparan, 184
versi, tidak deserializing di seluruh, 272 "Menulis Kode Pemalu", 306
density vertikal, dalam kode, 79- 80
jarak vertikal, dalam kode, 80- 84
pemformatan vertikal, 76- 85
X
keterbukaan vertikal, antar konsep, XML
78- 79 deskriptor penyebaran, 160
pemesanan vertikal, dalam kode, 84- 85 Konfigurasi "kebijakan" yang ditentukan
pemisahan vertikal, 292 file, 164

https://translate.googleusercontent.com/translate_f 364/365
3/12/2020 www.it-ebooks.info

www.it-ebooks.info

https://translate.googleusercontent.com/translate_f 365/365

Anda mungkin juga menyukai