Clean Code Terjemahan PDF
Clean Code Terjemahan PDF
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
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.
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.
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
www.it-ebooks.info
Halaman 6
https://translate.googleusercontent.com/translate_f 4/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 7
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
vii
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 6/365
3/12/2020 www.it-ebooks.info
Halaman 9
viii Isi
www.it-ebooks.info
Halaman 10
Isi ix
www.it-ebooks.info
Halaman 11
x Isi
www.it-ebooks.info
Halaman 12
Isi xi
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
www.it-ebooks.info
Halaman 14
https://translate.googleusercontent.com/translate_f 10/365
3/12/2020 www.it-ebooks.info
Isi xiii
www.it-ebooks.info
Halaman 15
xiv Isi
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
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
https://translate.googleusercontent.com/translate_f 13/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 18
Isi xvii
www.it-ebooks.info
Halaman 19
https://translate.googleusercontent.com/translate_f 14/365
3/12/2020 www.it-ebooks.info
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
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
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
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
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.
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
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.
xxix
www.it-ebooks.info
Halaman 31
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
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.
www.it-ebooks.info
Halaman 34
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
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 .
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
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
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.
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
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.
www.it-ebooks.info
Halaman 39
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.
3. http://www.pragmaticprogrammer.com/booksellers/2004-12.html
www.it-ebooks.info
Halaman 40
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.
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
Dalam beberapa tahun terakhir saya mulai, dan hampir berakhir, dengan Beck
aturan kode sederhana. Dalam urutan prioritas, kode sederhana:
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
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.
www.it-ebooks.info
Halaman 43
“. . . 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
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.
www.it-ebooks.info
Halaman 45
...
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.
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
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.
www.it-ebooks.info
Halaman 47
https://translate.googleusercontent.com/translate_f 36/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 48
2
https://translate.googleusercontent.com/translate_f 37/365
3/12/2020 www.it-ebooks.info
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
begitu banyak, lebih baik kita melakukannya dengan baik. Berikut ini adalah beberapa aturan sederhana untuk dibuat
nama baik.
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?
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:
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:
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
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
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:
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.
3. Paman Bob biasa melakukan ini dalam C ++ tetapi telah menghentikan praktik karena IDE modern membuatnya tidak perlu.
www.it-ebooks.info
Halaman 53
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?"
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 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
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;
}
}
_________________________________________________
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.
https://translate.googleusercontent.com/translate_f 43/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 56
Nama Metode 25
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
Ketika konstruktor kelebihan beban, gunakan metode pabrik statis dengan nama itu
jelaskan argumennya. Sebagai contoh,
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
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.
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
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.
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.
www.it-ebooks.info
Halaman 59
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
Listing 2-2
Variabel memiliki konteks.
GuessStatisticsMessage kelas publik {
nomor String pribadi;
kata kerja String pribadi;
private String pluralModifier;
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 47/365
3/12/2020 www.it-ebooks.info
Halaman 61
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");
}
www.it-ebooks.info
Halaman 64
Fungsi 33
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 ());
}
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
www.it-ebooks.info
Halaman 66
https://translate.googleusercontent.com/translate_f 51/365
3/12/2020 www.it-ebooks.info
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 ();
}
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].
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.
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.
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
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
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.
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 .
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);
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
Listing 3-6
UserValidator.java
UserValidator kelas publik {
cryptographer pribadi Cryptographer;
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
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.
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:
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");
...
}
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 ());
}
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
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
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
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. *;
www.it-ebooks.info
Halaman 82
SetupTeardownIncluder 51
https://translate.googleusercontent.com/translate_f 64/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 83
52 Bab 3: Fungsi
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.
https://translate.googleusercontent.com/translate_f 65/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 84
4
Komentar
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
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"
www.it-ebooks.info
Halaman 86
https://translate.googleusercontent.com/translate_f 67/365
3/12/2020 www.it-ebooks.info
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 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:
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
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.
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:
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.
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 ()));
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:
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.
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
/ **
* 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
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
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 () {
}
/ **
* 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
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 ()))
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.
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
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;
}
/*
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.
*/
/*
* 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
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. *;
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
// 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
}
}
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
www.it-ebooks.info
Halaman 105
74 Bab 4: Komentar
https://translate.googleusercontent.com/translate_f 82/365
3/12/2020 www.it-ebooks.info
hasil [j ++] = i;
}
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.
Listing 5-1
BoldWidget.java
paket fitnesse.wikitext.widgets;
import java.util.regex. *;
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> ();
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> ();
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.
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
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);
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
TestSuite publik () {
}
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;
www.it-ebooks.info
Halaman 114
Pemformatan Vertikal 83
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);
}
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
https://translate.googleusercontent.com/translate_f 90/365
3/12/2020 www.it-ebooks.info
}
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.
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);
}
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;
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
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 (); }}}
-----
www.it-ebooks.info
Halaman 120
Pemformatan Horizontal 89
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.
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)?";
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)?";
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.
www.it-ebooks.info
Halaman 122
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 ();
}
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
www.it-ebooks.info
Halaman 124
6
https://translate.googleusercontent.com/translate_f 97/365
3/12/2020 www.it-ebooks.info
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
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
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.
Listing 6-5
Bentuk Prosedural
Square kelas publik {
public Point topLeft;
sisi ganda publik;
}
https://translate.googleusercontent.com/translate_f 99/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 127
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;
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
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.
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
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 () .
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 ();
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,
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.
www.it-ebooks.info
Halaman 131
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.
Listing 6-7
address.java
Alamat kelas publik {
jalan pribadi String;
private String streetExtra;
kota String pribadi;
keadaan String pribadi;
zip string pribadi;
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 103/365
3/12/2020 www.it-ebooks.info
Halaman 132
Bibliografi 101
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
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
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 {
...
www.it-ebooks.info
Halaman 136
pauseDevice (handle);
clearDeviceWorkQueue (handle);
closeDevice (pegangan);
}
...
}
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.
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 107/365
3/12/2020 www.it-ebooks.info
Halaman 137
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.
www.it-ebooks.info
Halaman 138
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.
1. [Martin].
www.it-ebooks.info
Halaman 139
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;
www.it-ebooks.info
Halaman 140
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.
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
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:
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.
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
Dalam banyak kasus, objek kasus khusus adalah obat yang mudah. Bayangkan Anda memiliki kode
seperti ini:
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.
www.it-ebooks.info
Halaman 143
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
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
dengan kita sendiri. Dalam bab ini kita melihat praktik dan teknik untuk menjaga batas
perangkat lunak kami bersih.
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
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 ();
//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
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");
}
www.it-ebooks.info
Halaman 148
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
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.
https://translate.googleusercontent.com/translate_f 117/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 150
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.
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 118/365
3/12/2020 www.it-ebooks.info
Halaman 151
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.
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
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.
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
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.
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
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.
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
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 ();
request.setResource ("root");
request.addInput ("ketik", "halaman");
Responder responder = new SerializedPageResponder ();
Respons SimpleResponse =
(SimpleResponse) responder.makeResponse (
FitNesseContext baru (root), permintaan);
String xml = response.getContent ();
request.setResource ("TestPageOne");
request.addInput ("type", "data");
www.it-ebooks.info
Halaman 157
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);
}
Listing 9-2
SerializedPageResponderTest.java (refactored)
public void testGetPageHierarchyAsXml () melempar Exception {
makePages ("PageOne", "PageOne.ChildOne", "PageTwo");
assertResponseIsXML ();
assertResponseContains (
"<name> PageOne </name>", "<name> PageTwo </name>", "<name> ChildOne </name>"
);
}
assertResponseIsXML ();
assertResponseContains (
"<name> PageOne </name>", "<name> PageTwo </name>", "<name> ChildOne </name>"
);
assertResponseDoesNotContain ("SymPage");
}
www.it-ebooks.info
Halaman 158
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.
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
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
www.it-ebooks.info
Halaman 160
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
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.
Listing 9-7
SerializedPageResponderTest.java (Pernyataan Tunggal)
public void testGetPageHierarchyAsXml () melempar Exception {
diberikanPages ("PageOne", "PageOne.ChildOne", "PageTwo");
thenResponseShouldBeXML ();
}
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.
www.it-ebooks.info
Halaman 162
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.
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);
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
• 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.
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
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
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.
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
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
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.
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 133/365
3/12/2020 www.it-ebooks.info
Halaman 170
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
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> ();
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
keluar dari kelas yang lebih besar. Anda harus mencoba memisahkan variabel dan metode menjadi dua atau
lebih banyak kelas sehingga kelas baru lebih kohesif.
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;
3. [Knuth92].
www.it-ebooks.info
Halaman 173
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
Listing 10-6
PrimePrinter.java (refactored)
paket LiteratePrimes;
tablePrinter.print (primes);
}
Listing 10-7
RowColumnPagePrinter.java
paket LiteratePrimes;
impor java.io.PrintStream;
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 137/365
3/12/2020 www.it-ebooks.info
Halaman 175
www.it-ebooks.info
Halaman 176
Listing 10-8
PrimeGenerator.java
paket LiteratePrimes;
import java.util.ArrayList;
https://translate.googleusercontent.com/translate_f 138/365
3/12/2020 www.it-ebooks.info
private static ArrayList <Integer> multiplesOfPrimeFactors;
www.it-ebooks.info
Halaman 177
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
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
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 ();
}
www.it-ebooks.info
Halaman 180
https://translate.googleusercontent.com/translate_f 141/365
3/12/2020 www.it-ebooks.info
4. [PPP].
www.it-ebooks.info
Halaman 181
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.
https://translate.googleusercontent.com/translate_f 143/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 183
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
153
www.it-ebooks.info
Halaman 185
https://translate.googleusercontent.com/translate_f 145/365
3/12/2020 www.it-ebooks.info
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
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
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
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 148/365
3/12/2020 www.it-ebooks.info
Halaman 189
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. *;
www.it-ebooks.info
Halaman 190
Meningkatkan 159
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. *;
www.it-ebooks.info
Halaman 191
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.
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.
www.it-ebooks.info
Halaman 192
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. *;
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
// Di tempat lain...
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
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
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
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
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;
@Embedded
alamat Alamat pribadi;
https://translate.googleusercontent.com/translate_f 154/365
3/12/2020 www.it-ebooks.info
mappedBy =akun
Koleksi pribadi <Account> "bank")
= ArrayList baru <Account> ();
www.it-ebooks.info
Halaman 197
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.
https://translate.googleusercontent.com/translate_f 155/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 198
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.
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
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.
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.
[AspectJ]: http://eclipse.org/aspectj
[Colyer]: Adrian Colyer, Andy Clement, George Hurley, Mathew Webster, Eclipse
AspectJ, Person Education, Inc., Upper Saddle River, NJ, 2005.
www.it-ebooks.info
Halaman 201
[Goetz]: Brian Goetz, Teori dan Praktek Jawa: Dekorasi dengan Dynamic Proxie s,
http://www.ibm.com/developerworks/java/library/j-jtp08305.html
[Kolence]: Kenneth W. Kolence, Fisika perangkat lunak dan ukuran kinerja komputer-
, Prosiding konferensi tahunan ACM — Volume 2 , Boston, Massachusetts,
hlm. 1024-1040, 1972.
[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
1. [XPE].
171
https://translate.googleusercontent.com/translate_f 159/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 203
www.it-ebooks.info
Halaman 204
https://translate.googleusercontent.com/translate_f 160/365
3/12/2020 www.it-ebooks.info
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);
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
replaceImage (ImageUtilities.getScaledImage (
gambar, scalingFactor, scalingFactor));
}
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
// ...
}
2. [GOF].
www.it-ebooks.info
Halaman 206
Ekspresif 175
alterForLegalMinimums ();
applyToPayroll ();
}
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
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.
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
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
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.
https://translate.googleusercontent.com/translate_f 165/365
3/12/2020 www.it-ebooks.info
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
• 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;
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 .
www.it-ebooks.info
Halaman 212
• 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.
• 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.
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
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.
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
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.
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
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
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.
• 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.
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
www.it-ebooks.info
Halaman 218
https://translate.googleusercontent.com/translate_f 171/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 219
utas agar mudah disetel. Pertimbangkan untuk mengubahnya saat sistem berjalan.
Pertimbangkan untuk memungkinkan penyesuaian diri berdasarkan throughput dan pemanfaatan sistem.
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.
• 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
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
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.
www.it-ebooks.info
Halaman 223
https://translate.googleusercontent.com/translate_f 175/365
3/12/2020 www.it-ebooks.info
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
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;
www.it-ebooks.info
Halaman 226
parseSchema (skema);
parseArgumentStrings (Arrays.asList (args));
}
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 178/365
3/12/2020 www.it-ebooks.info
Halaman 227
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
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;
Listing 14-5
StringArgumentMarshaler.java
impor com.objectmentor.utilities.args.ArgsException.ErrorCode statis. *;
www.it-ebooks.info
Halaman 229
Listing 14-6
IntegerArgumentMarshaler.java
impor com.objectmentor.utilities.args.ArgsException.ErrorCode statis. *;
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. *;
ArgsException publik () {}
www.it-ebooks.info
Halaman 230
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
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.
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
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.
Listing 14-8
Args.java (konsep pertama)
impor java.text.ParseException;
import java.util. *;
www.it-ebooks.info
Halaman 233
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);
}
}
www.it-ebooks.info
Halaman 234
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;
}
www.it-ebooks.info
Halaman 235
kardinalitas publik () {
return argsFound.size ();
}
https://translate.googleusercontent.com/translate_f 185/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 236
www.it-ebooks.info
Halaman 237
https://translate.googleusercontent.com/translate_f 186/365
3/12/2020 www.it-ebooks.info
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. *;
www.it-ebooks.info
Halaman 238
https://translate.googleusercontent.com/translate_f 187/365
3/12/2020 www.it-ebooks.info
} }
kardinalitas publik () {
mengembalikan numberOfArguments;
}
www.it-ebooks.info
Halaman 239
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. *;
www.it-ebooks.info
Halaman 240
enum ErrorCode {
Oke, MISSING_STRING}
https://translate.googleusercontent.com/translate_f 189/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 241
set kembali;
}
www.it-ebooks.info
Halaman 242
https://translate.googleusercontent.com/translate_f 190/365
3/12/2020 www.it-ebooks.info
kardinalitas publik () {
return argsFound.size ();
}
www.it-ebooks.info
Halaman 243
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.
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
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;
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 .
www.it-ebooks.info
Halaman 245
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 ();
}
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
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.
www.it-ebooks.info
Halaman 247
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;
www.it-ebooks.info
Halaman 248
https://translate.googleusercontent.com/translate_f 195/365
3/12/2020 www.it-ebooks.info
}
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
Hanya untuk mendapatkan ini untuk dikompilasi, saya menambahkan fungsi get ke ArgumentMarshaler .
kelas abstrak pribadi ArgumentMarshaler {
...
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;
...
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
...
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;
www.it-ebooks.info
Halaman 251
https://translate.googleusercontent.com/translate_f 197/365
3/12/2020 www.it-ebooks.info
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 .
www.it-ebooks.info
Halaman 252
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);
}
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;
}
www.it-ebooks.info
Halaman 253
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;
}
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
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;
}
https://translate.googleusercontent.com/translate_f 200/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 255
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 :
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
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
Listing 14-12
Args.java (Setelah refactoring pertama)
package com.objectmentor.utilities.getopts;
impor java.text.ParseException;
import java.util. *;
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;
www.it-ebooks.info
Halaman 258
https://translate.googleusercontent.com/translate_f 203/365
3/12/2020 www.it-ebooks.info
parseElements (arg);
}
www.it-ebooks.info
Halaman 259
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
www.it-ebooks.info
Halaman 261
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;
}
}
www.it-ebooks.info
Halaman 262
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;
www.it-ebooks.info
Halaman 263
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;
}
}
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
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
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.
www.it-ebooks.info
Halaman 266
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 = "";
www.it-ebooks.info
Halaman 267
https://translate.googleusercontent.com/translate_f 210/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 268
Sekarang kita dapat menyingkirkan beberapa fungsi crufty di IntegerArgumentMarshaler dan membersihkannya
sedikit.
kelas privat IntegerArgumentMarshaler memperluas ArgumentMarshaler {
private int intValue = 0
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
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
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 () {}
www.it-ebooks.info
Halaman 271
https://translate.googleusercontent.com/translate_f 213/365
3/12/2020 www.it-ebooks.info
...
...
www.it-ebooks.info
Halaman 272
https://translate.googleusercontent.com/translate_f 214/365
3/12/2020 www.it-ebooks.info
Obyek publik dapatkan () {
mengembalikan nilai;
}
}
www.it-ebooks.info
Halaman 273
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;
https://translate.googleusercontent.com/translate_f 215/365
3/12/2020 www.it-ebooks.info
} catch (ArgsException e) {
www.it-ebooks.info
Halaman 274
www.it-ebooks.info
Halaman 275
https://translate.googleusercontent.com/translate_f 216/365
3/12/2020 www.it-ebooks.info
Listing 14-14
ArgsExceptionTest.java
ArgsExceptionTest kelas publik memperluas TestCase {
public void testUnexpectedMessage () melempar Exception {
ArgsException e =
www.it-ebooks.info
Halaman 276
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 ());
}
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 () {}
www.it-ebooks.info
Halaman 277
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
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;
https://translate.googleusercontent.com/translate_f 219/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 279
www.it-ebooks.info
Halaman 280
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 ();
}
www.it-ebooks.info
Halaman 281
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
Listing 15-1
ComparisonCompactorTest.java
paket junit.tests.framework;
import junit.framework.ComparisonCompactor;
import junit.framework.TestCase;
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 223/365
3/12/2020 www.it-ebooks.info
Halaman 284
www.it-ebooks.info
Halaman 285
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);
}
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;
www.it-ebooks.info
Halaman 286
findCommonPrefix ();
findCommonSuffix ();
String diharapkan = compactString (fExpected);
String aktual = compactString (fActual);
return Assert.format (pesan, diharapkan, aktual);
}
https://translate.googleusercontent.com/translate_f 225/365
3/12/2020 www.it-ebooks.info
if (fExpected.charAt
istirahat; (fPrefix)! = fActual.charAt (fPrefix))
}
}
www.it-ebooks.info
Halaman 287
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;
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
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;
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);
}
www.it-ebooks.info
Halaman 289
https://translate.googleusercontent.com/translate_f 227/365
3/12/2020 www.it-ebooks.info
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);
}
}
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;
...
www.it-ebooks.info
Halaman 290
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);
}
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
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);
}
https://translate.googleusercontent.com/translate_f 229/365
3/12/2020 www.it-ebooks.info
istirahat;
}
suffixIndex = expected.length () - expectedSuffix;
}
www.it-ebooks.info
Halaman 292
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;
}
}
...
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
...
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: "");
}
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
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 publik (
int contextLength, String diharapkan, String aktual
){
this.contextLength = contextLength;
this.expected = diharapkan;
this.actual = aktual;
}
www.it-ebooks.info
Halaman 295
suffixOverlapsPrefix pribadi () {)
return actual.length () - suffixLength <= prefixLength ||
expected.length () - suffixLength <= prefixLength;
}
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;
}
www.it-ebooks.info
Halaman 296
Kesimpulan 265
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
https://translate.googleusercontent.com/translate_f 234/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 298
16
SerialDate Refactoring
267
www.it-ebooks.info
Halaman 299
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.
www.it-ebooks.info
Halaman 300
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 .
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:
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
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."
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
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.
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 238/365
3/12/2020 www.it-ebooks.info
Halaman 303
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].
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
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.
3. [GOF].
www.it-ebooks.info
Halaman 305
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);
}
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
4. Ibid.
5. Ibid.
www.it-ebooks.info
Halaman 306
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
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
www.it-ebooks.info
Halaman 308
https://translate.googleusercontent.com/translate_f 242/365
3/12/2020 www.it-ebooks.info
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 .
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));
}
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
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].
coba {
return make (Integer.parseInt (s));
}
catch (NumberFormatException e) {}
throw baru IllegalArgumentException ("Bulan tidak valid");
}
7. [Refactoring].
www.it-ebooks.info
Halaman 310
The isLeapYear metode (baris 495-517) dapat dibuat sedikit lebih ekspresif [G16].
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].
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.
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].
8. [Beck97].
www.it-ebooks.info
Halaman 311
Fungsi addYears (baris 604-626) tidak memberikan kejutan di atas yang lain.
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
sedangkan yang berikut ini tidak cukup lancar membaca bagi pembaca untuk menerima begitu saja
objek tanggal berubah:
date.plusDays (5);
Analisis dan hasil yang persis sama terjadi untuk getFollowingDayOfWeek (baris 662-693).
https://translate.googleusercontent.com/translate_f 245/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 312
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.
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.
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 .
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
https://translate.googleusercontent.com/translate_f 247/365
3/12/2020 www.it-ebooks.info
}
};
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
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.
[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
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
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
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.
Lingkungan Hidup
www.it-ebooks.info
Halaman 319
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.)
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.
www.it-ebooks.info
Halaman 320
Umum 289
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.
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
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.
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
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.
www.it-ebooks.info
Halaman 323
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.
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.
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
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;
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.
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 ();
}
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.
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.
www.it-ebooks.info
Halaman 327
https://translate.googleusercontent.com/translate_f 257/365
3/12/2020 www.it-ebooks.info
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.
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.
www.it-ebooks.info
Halaman 328
Umum 297
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.
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.
www.it-ebooks.info
Halaman 329
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.
Listing 17-1
HourlyReporter.java
HourlyReporter kelas publik {
formatter HourlyReportFormatter pribadi;
halaman Daftar pribadi <LineItem>;
PAGE_SIZE int akhir pribadi = 55;
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 ();
}
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
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 .
https://translate.googleusercontent.com/translate_f 260/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 331
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.
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 ());
11. Atau lebih baik lagi, kelas Uang yang menggunakan bilangan bulat.
www.it-ebooks.info
Halaman 333
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);
}
Masing-masing fungsi melakukan satu hal. (Lihat “Do One Thing” di halaman 35.)
www.it-ebooks.info
Halaman 334
Umum 303
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;
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
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;
}
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 ();
}
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 .
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
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.
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.
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.
www.it-ebooks.info
Halaman 339
Tidak, tidak di sana. Tapi lalu dimana? Perhatikan baik-baik Karyawan kelas . Ini mengimplementasikan
Konstanta Penggajian .
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. *;
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
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;
}
};
Nama
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 268/365
3/12/2020 www.it-ebooks.info
Halaman 341
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;
}
www.it-ebooks.info
Halaman 342
Nama 311
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.
14. [DDD].
www.it-ebooks.info
Halaman 343
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.
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
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.
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.
Fungsi ini sedikit lebih banyak daripada “oos”; itu menciptakan "oos" jika belum
sudah makan. Dengan demikian, nama yang lebih baik adalah createOrReturnOos .
Tes
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
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.
[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.
[PPP]: Pengembangan Perangkat Lunak Agile: Prinsip, Pola, dan Praktik , Robert C. Martin,
Prentice Hall, 2002.
www.it-ebooks.info
Halaman 347
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.
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
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":
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:
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
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 ):
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?
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);
}
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
Daftar A-1
ExecutorClientScheduler.java
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
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.
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
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!
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
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.
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 279/365
3/12/2020 www.it-ebooks.info
Halaman 355
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:
www.it-ebooks.info
Halaman 356
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 .
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
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
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:
www.it-ebooks.info
Halaman 358
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);
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
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;
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.
• 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
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 :
www.it-ebooks.info
Halaman 361
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.
while (true) {
int nextValue;
www.it-ebooks.info
Halaman 362
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
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.
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
• 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.
• 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
Kelas berikutnya adalah iterator yang menyediakan konten halaman berdasarkan iterator
URL:
PageIterator kelas publik {
pembaca PageReader pribadi;
url URLIterator pribadi;
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.
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
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:
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
• 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
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
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.
• 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
• 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.
• 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
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.
Berikut ini deskripsi tes yang akan membuktikan bahwa kode tersebut rusak:
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:
www.it-ebooks.info
Halaman 371
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
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
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.
• 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
cara. Akhirnya, kami berbicara tentang strategi untuk mengungkap masalah bersamaan dengan menginstruksikan
kode Anda.
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;
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);
}
}
}
www.it-ebooks.info
Halaman 375
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 ();
}
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;
www.it-ebooks.info
Halaman 376
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);
}
}
}
https://translate.googleusercontent.com/translate_f 296/365
3/12/2020 www.it-ebooks.info
}
}
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 ();
}
www.it-ebooks.info
Halaman 377
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;
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;
www.it-ebooks.info
Halaman 378
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 ();
}
}
};
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 298/365
3/12/2020 www.it-ebooks.info
Halaman 379
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
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
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
www.it-ebooks.info
Halaman 384
https://translate.googleusercontent.com/translate_f 302/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 385
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
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
https://translate.googleusercontent.com/translate_f 305/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 388
www.it-ebooks.info
Halaman 389
https://translate.googleusercontent.com/translate_f 306/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 390
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
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
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 309/365
3/12/2020 www.it-ebooks.info
Halaman 393
www.it-ebooks.info
Halaman 394
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
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
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
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
www.it-ebooks.info
Halaman 399
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
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
https://translate.googleusercontent.com/translate_f 316/365
3/12/2020 www.it-ebooks.info
306
www.it-ebooks.info
Halaman 402
www.it-ebooks.info
Halaman 403
https://translate.googleusercontent.com/translate_f 317/365
3/12/2020 www.it-ebooks.info
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
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
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
https://translate.googleusercontent.com/translate_f 320/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 407
www.it-ebooks.info
Halaman 408
https://translate.googleusercontent.com/translate_f 321/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 409
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
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
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 324/365
3/12/2020 www.it-ebooks.info
Halaman 412
www.it-ebooks.info
Halaman 413
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
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
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
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 328/365
3/12/2020 www.it-ebooks.info
Halaman 417
www.it-ebooks.info
Halaman 418
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
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
https://translate.googleusercontent.com/translate_f 331/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 421
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
www.it-ebooks.info
Halaman 423
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
https://translate.googleusercontent.com/translate_f 334/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 425
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
www.it-ebooks.info
Halaman 427
https://translate.googleusercontent.com/translate_f 336/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 428
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
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
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
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
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
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
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
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
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
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
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
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.
409
www.it-ebooks.info
Halaman 441
https://translate.googleusercontent.com/translate_f 347/365
3/12/2020 www.it-ebooks.info
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
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
www.it-ebooks.info
Halaman 446
Indeks 415
https://translate.googleusercontent.com/translate_f 351/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 447
416 Indeks
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
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
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 354/365
3/12/2020 www.it-ebooks.info
Halaman 450
Indeks 419
www.it-ebooks.info
Halaman 451
420 Indeks
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
www.it-ebooks.info
Halaman 452
Indeks 421
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
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
www.it-ebooks.info
https://translate.googleusercontent.com/translate_f 358/365
3/12/2020 www.it-ebooks.info
Halaman 455
424 Indeks
www.it-ebooks.info
Halaman 456
Indeks 425
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
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
https://translate.googleusercontent.com/translate_f 361/365
3/12/2020 www.it-ebooks.info
www.it-ebooks.info
Halaman 459
428 Indeks
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