JavaRush /Blog Java /Random-MS /Panduan Gaya Pengaturcaraan Umum
pandaFromMinsk
Tahap
Минск

Panduan Gaya Pengaturcaraan Umum

Diterbitkan dalam kumpulan
Artikel ini adalah sebahagian daripada kursus akademik "Advanced Java." Kursus ini direka untuk membantu anda mempelajari cara menggunakan ciri Java dengan berkesan. Bahan ini merangkumi topik "lanjutan" seperti penciptaan objek, persaingan, siri, refleksi, dll. Kursus ini akan mengajar anda cara menguasai teknik Java dengan berkesan. Butiran di sini .
Kandungan
1. Pengenalan 2. Skop Pembolehubah 3. Medan Kelas dan Pembolehubah Tempatan 4. Argumen Kaedah dan Pembolehubah Tempatan 5. Tinju dan Nyah Tinju 6. Antara Muka 7. Rentetan 8. Konvensyen Penamaan 9. Perpustakaan Standard 10. Ketidakbolehubahan 11. Pengujian 12. Seterusnya. .. 13. Muat turun kod sumber
1. Pengenalan
Dalam bahagian tutorial ini, kami akan meneruskan perbincangan kami tentang prinsip umum gaya pengaturcaraan yang baik dan reka bentuk responsif dalam Java. Kami telah melihat beberapa prinsip ini dalam bab panduan sebelumnya, tetapi banyak petua praktikal akan diberikan dengan tujuan untuk meningkatkan kemahiran pembangun Java.
2. Skop boleh ubah
Dalam Bahagian Tiga ("Cara Reka Bentuk Kelas dan Antara Muka") kami membincangkan cara keterlihatan dan kebolehcapaian boleh digunakan pada ahli kelas dan antara muka, memandangkan kekangan skop. Walau bagaimanapun, kami masih belum membincangkan pembolehubah tempatan yang digunakan dalam pelaksanaan kaedah. Dalam bahasa Java, setiap pembolehubah tempatan, setelah diisytiharkan, mempunyai skop. Pembolehubah ini boleh dilihat dari tempat ia diisytiharkan ke titik di mana pelaksanaan kaedah (atau blok kod) selesai. Secara amnya, satu-satunya peraturan yang perlu diikuti ialah mengisytiharkan pembolehubah tempatan sedekat mungkin dengan tempat ia akan digunakan. Biar saya lihat contoh biasa: for( final Locale locale: Locale.getAvailableLocales() ) { // блок codeа } try( final InputStream in = new FileInputStream( "file.txt" ) ) { // блока codeа } Dalam kedua-dua serpihan kod, skop pembolehubah adalah terhad kepada blok pelaksanaan di mana pembolehubah ini diisytiharkan. Apabila blok selesai, skop tamat dan pembolehubah menjadi tidak kelihatan. Ini kelihatan lebih jelas, tetapi dengan keluaran Java 8 dan pengenalan lambdas, banyak simpulan bahasa yang terkenal menggunakan pembolehubah tempatan menjadi usang. Izinkan saya memberikan contoh daripada contoh sebelumnya menggunakan lambdas dan bukannya gelung: Arrays.stream( Locale.getAvailableLocales() ).forEach( ( locale ) -> { // блок codeа } ); Anda boleh melihat bahawa pembolehubah tempatan telah menjadi hujah kepada fungsi, yang seterusnya dihantar sebagai hujah kepada kaedah forEach .
3. Medan kelas dan pembolehubah tempatan
Setiap kaedah dalam Java tergolong dalam kelas tertentu (atau, dalam kes Java8, antara muka di mana kaedah diisytiharkan sebagai kaedah lalai). Antara pembolehubah tempatan yang merupakan medan kelas atau kaedah yang digunakan dalam pelaksanaan, terdapat, oleh itu, kemungkinan konflik nama. Pengkompil Java tahu cara memilih pembolehubah yang betul daripada yang tersedia, walaupun lebih daripada satu pembangun berhasrat untuk menggunakan pembolehubah itu. IDE Java moden melakukan tugas yang baik untuk memberitahu pembangun apabila konflik seperti itu akan berlaku, melalui amaran pengkompil dan penyerlahan berubah-ubah. Tetapi lebih baik untuk memikirkan perkara sedemikian semasa menulis kod. Saya cadangkan melihat contoh: public class LocalVariableAndClassMember { private long value; public long calculateValue( final long initial ) { long value = initial; value *= 10; value += value; return value; } } Contoh itu kelihatan agak mudah, tetapi ia adalah perangkap. Kaedah calculateValue memperkenalkan nilai pembolehubah tempatan dan, beroperasi padanya, menyembunyikan medan kelas dengan nama yang sama. Baris itu value += value; hendaklah jumlah nilai medan kelas dan pembolehubah tempatan, tetapi sebaliknya, sesuatu yang lain sedang dilakukan. Pelaksanaan yang betul akan kelihatan seperti ini (menggunakan kata kunci ini): public class LocalVariableAndClassMember { private long value; public long calculateValue( final long initial ) { long value = initial; value *= 10; value += this.value; return value; } } Walaupun contoh ini naif dalam beberapa cara, ia menunjukkan perkara penting yang dalam sesetengah kes ia boleh mengambil masa berjam-jam untuk nyahpepijat dan membetulkannya.
4. Hujah kaedah dan pembolehubah tempatan
Satu lagi perangkap yang sering dialami oleh pembangun Java yang tidak berpengalaman ialah menggunakan argumen kaedah sebagai pembolehubah tempatan. Java membenarkan anda untuk menetapkan semula nilai kepada argumen bukan malar (namun, ini tidak mempunyai kesan ke atas nilai asal): public String sanitize( String str ) { if( !str.isEmpty() ) { str = str.trim(); } str = str.toLowerCase(); return str; } Coretan kod di atas tidak elegan, tetapi ia berfungsi dengan baik untuk mendedahkan masalah: str diberikan yang berbeza nilai (dan pada asasnya digunakan sebagai pembolehubah tempatan) . Dalam semua kes (tanpa sebarang pengecualian), anda boleh dan harus melakukannya tanpa contoh ini (contohnya, dengan mengisytiharkan hujah sebagai pemalar). Contohnya: public String sanitize( final String str ) { String sanitized = str; if( !str.isEmpty() ) { sanitized = str.trim(); } sanitized = sanitized.toLowerCase(); return sanitized; } Dengan mengikuti peraturan mudah ini, lebih mudah untuk mengesan kod yang diberikan dan mencari punca masalah, walaupun semasa memperkenalkan pembolehubah tempatan.
5. Pembungkusan dan pembongkaran
Boxing dan unboxing ialah nama teknik yang digunakan dalam Java untuk menukar jenis primitif ( int, long, double, dll. ) kepada jenis pembalut yang sepadan ( Integer, Long, Double , dll.). Dalam Bahagian 4 tutorial Cara dan Bila Menggunakan Generik, anda sudah melihat ini dalam tindakan apabila saya bercakap tentang membungkus jenis primitif sebagai parameter jenis generik. Walaupun pengkompil Java cuba sedaya upaya untuk menyembunyikan penukaran sedemikian dengan melakukan autoboxing, kadangkala ini kurang daripada yang dijangkakan dan menghasilkan hasil yang tidak dijangka. Mari lihat contoh: public static void calculate( final long value ) { // блок codeа } final Long value = null; calculate( value ); Coretan kod di atas disusun dengan baik. Walau bagaimanapun, ia akan membuang NullPointerException pada baris // блок di mana ia menukar antara Long dan long . Nasihat untuk kes sedemikian adalah dinasihatkan untuk menggunakan jenis primitif (namun, kita sudah tahu bahawa ini tidak selalu mungkin).
6. Antara muka
Dalam Bahagian 3 tutorial, "Cara Reka Bentuk Kelas dan Antara Muka," kami membincangkan antara muka dan pengaturcaraan kontrak, menekankan bahawa antara muka harus diutamakan berbanding kelas konkrit apabila boleh. Tujuan bahagian ini adalah untuk menggalakkan anda mempertimbangkan antara muka terlebih dahulu dengan menunjukkan perkara ini dengan contoh kehidupan sebenar. Antara muka tidak terikat dengan pelaksanaan tertentu (kecuali kaedah lalai). Ia hanya kontrak dan, sebagai contoh, ia memberikan banyak kebebasan dan fleksibiliti dalam cara kontrak boleh dilaksanakan. Fleksibiliti ini menjadi lebih penting apabila pelaksanaannya melibatkan sistem atau perkhidmatan luaran. Mari lihat contoh antara muka mudah dan kemungkinan pelaksanaannya: public interface TimezoneService { TimeZone getTimeZone( final double lat, final double lon ) throws IOException; } public class TimezoneServiceImpl implements TimezoneService { @Override public TimeZone getTimeZone(final double lat, final double lon) throws IOException { final URL url = new URL( String.format( "http://api.geonames.org/timezone?lat=%.2f&lng=%.2f&username=demo", lat, lon ) ); final HttpURLConnection connection = ( HttpURLConnection )url.openConnection(); connection.setRequestMethod( "GET" ); connection.setConnectTimeout( 1000 ); connection.setReadTimeout( 1000 ); connection.connect(); int status = connection.getResponseCode(); if (status == 200) { // Do something here } return TimeZone.getDefault(); } } Coretan kod di atas menunjukkan corak antara muka biasa dan pelaksanaannya. Pelaksanaan ini menggunakan perkhidmatan HTTP luaran ( http://api.geonames.org/ ) untuk mendapatkan semula zon waktu lokasi tertentu. Namun, kerana kontrak bergantung pada antara muka, ia adalah sangat mudah untuk memperkenalkan satu lagi pelaksanaan antara muka, menggunakan, sebagai contoh, pangkalan data atau fail rata biasa. Dengan mereka, antara muka sangat membantu dalam mereka bentuk kod yang boleh diuji. Sebagai contoh, tidak selalu praktikal untuk memanggil perkhidmatan luaran pada setiap ujian, jadi masuk akal untuk melaksanakan alternatif, pelaksanaan paling mudah (seperti rintisan): public class TimezoneServiceTestImpl implements TimezoneService { @Override public TimeZone getTimeZone(final double lat, final double lon) throws IOException { return TimeZone.getDefault(); } } Pelaksanaan ini boleh digunakan di mana-mana sahaja di mana antara muka TimezoneService diperlukan, mengasingkan skrip ujian daripada pergantungan pada komponen luaran. Banyak contoh terbaik penggunaan berkesan antara muka sedemikian terkandung dalam perpustakaan standard Java. Koleksi, senarai, set - antara muka ini mempunyai pelbagai pelaksanaan yang boleh ditukar dengan lancar dan boleh ditukar ganti apabila kontrak mengambil kesempatan. Sebagai contoh: public static< T > void print( final Collection< T > collection ) { for( final T element: collection ) { System.out.println( element ); } } print( new HashSet< Object >( /* ... */ ) ); print( new ArrayList< Integer >( /* ... */ ) ); print( new TreeSet< String >( /* ... */ ) );
7. Rentetan
String adalah salah satu jenis yang paling banyak digunakan dalam kedua-dua Java dan bahasa pengaturcaraan lain. Bahasa Java memudahkan banyak manipulasi rentetan rutin dengan menyokong operasi penggabungan dan perbandingan terus dari kotak. Selain itu, perpustakaan standard mengandungi banyak kelas yang menjadikan operasi rentetan cekap. Inilah yang akan kita bincangkan dalam bahagian ini. Di Java, rentetan ialah objek tidak berubah yang diwakili dalam pengekodan UTF-16. Setiap kali anda menggabungkan rentetan (atau melakukan sebarang operasi yang mengubah suai rentetan asal), contoh baharu kelas String dicipta . Oleh kerana itu, operasi penggabungan boleh menjadi sangat tidak cekap, menyebabkan banyak contoh perantaraan kelas String dicipta (membuat sampah, secara umum). Tetapi perpustakaan standard Java mengandungi dua kelas yang sangat berguna yang tujuannya adalah untuk memudahkan manipulasi rentetan. Ini adalah StringBuilder dan StringBuffer (satu-satunya perbezaan di antara mereka ialah StringBuffer adalah thread selamat manakala StringBuilder adalah sebaliknya). Mari kita lihat beberapa contoh salah satu daripada kelas ini yang digunakan: final StringBuilder sb = new StringBuilder(); for( int i = 1; i <= 10; ++i ) { sb.append( " " ); sb.append( i ); } sb.deleteCharAt( 0 ); sb.insert( 0, "[" ); sb.replace( sb.length() - 3, sb.length(), "]" ); Semasa menggunakan StringBuilder/StringBuffer ialah cara yang disyorkan untuk memanipulasi rentetan, ia mungkin kelihatan berlebihan dalam senario paling mudah untuk menggabungkan dua atau tiga rentetan, supaya pengendali penambahan biasa ( ( "+"), sebagai contoh: String userId = "user:" + new Random().nextInt( 100 ); Selalunya alternatif terbaik untuk memudahkan penggabungan ialah menggunakan pemformatan rentetan serta Pustaka Standard Java untuk membantu menyediakan kaedah penolong String.format statik . Ini menyokong set penentu format yang kaya, termasuk nombor, simbol, tarikh/masa, dsb. (Rujuk dokumentasi rujukan untuk butiran lengkap) String.format( "%04d", 1 ); -> 0001 String.format( "%.2f", 12.324234d ); -> 12.32 String.format( "%tR", new Date() ); -> 21:11 String.format( "%tF", new Date() ); -> 2014-11-11 String.format( "%d%%", 12 ); -> 12% Kaedah String.format menyediakan pendekatan yang bersih dan ringan untuk menjana rentetan daripada pelbagai jenis data. Perlu diingat bahawa IDE Java moden boleh menghuraikan spesifikasi format daripada argumen yang dihantar kepada kaedah String.format dan memberi amaran kepada pembangun jika sebarang ketidakpadanan dikesan.
8. Konvensyen penamaan
Java ialah bahasa yang tidak memaksa pembangun untuk mengikut ketat mana-mana konvensyen penamaan, tetapi komuniti telah membangunkan satu set peraturan mudah yang menjadikan kod Java kelihatan konsisten dalam pustaka standard dan dalam mana-mana projek Java lain:
  • nama pakej adalah dalam huruf kecil: org.junit, com.fasterxml.jackson, javax.json
  • nama kelas, penghitungan, antara muka, anotasi ditulis dengan huruf besar: StringBuilder, Runnable, @Override
  • nama medan atau kaedah (kecuali akhir statik ) dinyatakan dalam tatatanda unta: isEmpty, format, addAll
  • medan akhir statik atau nama pemalar penghitungan adalah dalam huruf besar, dipisahkan dengan garis bawah ("_"): LOG, MIN_RADIX, INSTANCE.
  • pembolehubah tempatan atau argumen kaedah ditaip dalam notasi unta: str, newLength, minimumCapacity
  • nama jenis parameter untuk generik diwakili oleh satu huruf dalam huruf besar: T, U, E
Dengan mengikuti konvensyen mudah ini, kod yang anda tulis akan kelihatan ringkas dan tidak dapat dibezakan dalam gaya daripada perpustakaan atau rangka kerja lain, dan akan berasa seperti ia dibangunkan oleh orang yang sama (salah satu daripada masa yang jarang berlaku apabila konvensyen benar-benar berfungsi ).
9. Perpustakaan standard
Tidak kira apa jenis projek Java yang anda sedang kerjakan, perpustakaan standard Java ialah kawan terbaik anda. Ya, sukar untuk tidak bersetuju bahawa mereka mempunyai beberapa kelebihan kasar dan keputusan reka bentuk yang pelik, namun, 99% daripada masa itu adalah kod berkualiti tinggi yang ditulis oleh pakar. Ia berbaloi untuk diterokai. Setiap keluaran Java membawa banyak ciri baharu kepada perpustakaan sedia ada (dengan beberapa kemungkinan isu dengan ciri lama), dan juga menambah banyak perpustakaan baharu. Java 5 membawa perpustakaan concurrency baharu sebagai sebahagian daripada pakej java.util.concurrent . Java 6 menyediakan (kurang terkenal) sokongan untuk skrip ( pakej javax.script ) dan API pengkompil Java (sebagai sebahagian daripada pakej javax.tools ). Java 7 membawa banyak peningkatan kepada java.util.concurrent , memperkenalkan perpustakaan I/O baharu dalam pakej java.nio.file dan sokongan untuk bahasa dinamik dalam java.lang.invoke . Dan akhirnya, Java 8 menambah tarikh/masa yang ditunggu-tunggu dalam pakej java.time . Java sebagai platform sedang berkembang dan sangat penting baginya untuk maju bersama dengan perubahan di atas. Setiap kali anda mempertimbangkan untuk memasukkan perpustakaan atau rangka kerja pihak ketiga dalam projek anda, pastikan fungsi yang diperlukan belum terkandung dalam perpustakaan Java standard (sudah tentu, terdapat banyak pelaksanaan khusus dan berprestasi tinggi bagi algoritma yang mendahului algoritma dalam perpustakaan standard, tetapi dalam kebanyakan kes ia benar-benar Tidak diperlukan).
10. Keabadian
Ketidakbolehubahan sepanjang panduan dan bahagian ini kekal sebagai peringatan: sila ambil perhatian serius. Jika kelas yang anda reka atau kaedah yang anda laksanakan boleh memberikan jaminan kebolehubah, ia boleh digunakan dalam kebanyakan kes di mana-mana tanpa rasa takut untuk diubah suai pada masa yang sama. Ini akan menjadikan hidup anda sebagai pembangun (dan semoga kehidupan ahli pasukan anda) lebih mudah.
11. Pengujian
Amalan pembangunan dipacu ujian (TDD) sangat popular dalam komuniti Java, meningkatkan bar untuk kualiti kod. Dengan semua faedah yang disediakan oleh TDD, adalah menyedihkan untuk melihat bahawa perpustakaan standard Java hari ini tidak menyertakan sebarang rangka kerja ujian atau alat sokongan. Walau bagaimanapun, ujian telah menjadi bahagian penting dalam pembangunan Java moden dan dalam bahagian ini kita akan melihat beberapa teknik asas menggunakan rangka kerja JUnit . Dalam JUnit, pada asasnya, setiap ujian ialah satu set pernyataan tentang keadaan atau tingkah laku yang dijangkakan sesuatu objek. Rahsia untuk menulis ujian yang hebat adalah memastikannya ringkas dan pendek, menguji satu perkara pada satu masa. Sebagai latihan, mari tulis satu set ujian untuk mengesahkan bahawa String.format ialah fungsi daripada bahagian rentetan yang mengembalikan hasil yang diingini. package com.javacodegeeks.advanced.generic; import static org.junit.Assert.assertThat; import static org.hamcrest.CoreMatchers.equalTo; import org.junit.Test; public class StringFormatTestCase { @Test public void testNumberFormattingWithLeadingZeros() { final String formatted = String.format( "%04d", 1 ); assertThat( formatted, equalTo( "0001" ) ); } @Test public void testDoubleFormattingWithTwoDecimalPoints() { final String formatted = String.format( "%.2f", 12.324234d ); assertThat( formatted, equalTo( "12.32" ) ); } } Kedua-dua ujian kelihatan sangat mudah dibaca dan pelaksanaannya adalah contoh. Hari ini, purata projek Java mengandungi ratusan kes ujian, memberikan maklum balas pantas kepada pembangun semasa proses pembangunan tentang regresi atau ciri.
12. Seterusnya
Bahagian panduan ini melengkapkan siri perbincangan yang berkaitan dengan amalan pengaturcaraan dalam Java dan manual untuk bahasa pengaturcaraan ini. Lain kali kita akan kembali kepada ciri bahasa, meneroka dunia Java mengenai pengecualian, jenisnya, cara dan bila menggunakannya.
13. Muat turun kod sumber
Ini adalah pelajaran tentang prinsip pembangunan umum dari kursus Java Lanjutan. Kod sumber untuk pelajaran boleh dimuat turun di sini .
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION