JavaRush /Blog Java /Random-MS /Coffee break #85. Tiga pelajaran Java yang saya pelajari ...

Coffee break #85. Tiga pelajaran Java yang saya pelajari dengan cara yang sukar. Cara menggunakan prinsip SOLID dalam kod

Diterbitkan dalam kumpulan

Tiga Pelajaran Java yang Saya Pelajari Dengan Cara Sukar

Sumber: Pembelajaran Sederhana Java adalah sukar. Saya belajar dari kesilapan saya. Kini anda juga boleh belajar daripada kesilapan dan pengalaman pahit saya, yang tidak semestinya anda perlu miliki. Coffee break #85.  Tiga pelajaran Java yang saya pelajari dengan cara yang sukar.  Cara menggunakan prinsip SOLID dalam kod - 1

1. Lambdas boleh menyebabkan masalah.

Lambdas selalunya melebihi 4 baris kod dan lebih besar daripada yang dijangkakan. Ini membebankan ingatan kerja. Adakah anda perlu menukar pembolehubah daripada lambda? Anda tidak boleh berbuat demikian. kenapa? Jika lambda boleh mengakses pembolehubah tapak panggilan, isu threading mungkin timbul. Oleh itu anda tidak boleh menukar pembolehubah daripada lambda. Tetapi laluan Happy dalam lambda berfungsi dengan baik. Selepas kegagalan runtime, anda akan menerima respons ini:

at [CLASS].lambda$null$2([CLASS].java:85)
at [CLASS]$$Lambda$64/730559617.accept(Unknown Source)
Sukar untuk mengikuti jejak tindanan lambda. Nama-nama itu mengelirukan dan sukar untuk dikesan dan nyahpepijat. Lebih banyak lambda = lebih banyak kesan tindanan. Apakah cara terbaik untuk nyahpepijat lambdas? Gunakan keputusan pertengahan.

map(elem -> {
 int result = elem.getResult();
 return result;
});
Satu lagi cara yang baik ialah menggunakan teknik penyahpepijatan IntelliJ lanjutan. Gunakan TAB untuk memilih kod yang anda ingin nyahpepijat dan menggabungkannya dengan hasil perantaraan. “Apabila kita berhenti di barisan yang mengandungi lambda, jika kita menekan F7 (langkah masuk), maka IntelliJ menyerlahkan serpihan yang perlu dinyahpepijat. Kami boleh menukar blok kepada nyahpepijat menggunakan Tab, dan setelah kami memutuskannya, tekan F7 sekali lagi.” Bagaimana untuk mengakses pembolehubah tapak panggilan dari lambda? Anda hanya boleh mengakses pembolehubah akhir atau sebenarnya akhir. Anda perlu membuat pembalut di sekeliling pembolehubah lokasi panggilan. Sama ada menggunakan AtomicType atau menggunakan jenis anda sendiri. Anda boleh menukar pembungkus pembolehubah yang dibuat dengan lambda. Bagaimana untuk menyelesaikan masalah jejak tindanan? Gunakan fungsi bernama. Dengan cara ini, anda boleh mencari kod yang bertanggungjawab, menyemak logik dan menyelesaikan masalah dengan cepat. Gunakan fungsi bernama untuk menanggalkan surih tindanan samar. Adakah lambda yang sama berulang? Letakkannya dalam fungsi bernama. Anda akan mempunyai satu titik rujukan. Setiap lambda mendapat fungsi yang dijana, yang menjadikannya sukar untuk dijejaki.

lambda$yourNamedFunction
lambda$0
Fungsi bernama menyelesaikan masalah yang berbeza. Lambda besar. Fungsi yang dinamakan memecahkan lambda besar, mencipta kepingan kod yang lebih kecil dan mencipta fungsi boleh pasang.

.map(this::namedFunc1).filter(this::namedFilter1).map(this::namedFunc2)

2. Masalah dengan senarai

Anda perlu bekerja dengan senarai ( Senarai ). Anda memerlukan HashMap untuk data. Untuk peranan anda memerlukan TreeMap . Senarai itu diteruskan. Dan tidak ada cara anda boleh mengelak daripada bekerja dengan koleksi. Bagaimana untuk membuat senarai? Apakah jenis senarai yang anda perlukan? Adakah ia tidak boleh berubah atau boleh berubah? Semua jawapan ini mempengaruhi masa depan kod anda. Pilih senarai yang betul terlebih dahulu supaya anda tidak menyesal di kemudian hari. Arrays::asList mencipta senarai "end-to-end". Apa yang anda tidak boleh lakukan dengan senarai ini? Anda tidak boleh mengubah saiznya. Dia tidak boleh berubah. Apa yang boleh anda lakukan di sini? Tentukan elemen, pengisihan atau operasi lain yang tidak menjejaskan saiz. Gunakan Arrays::asList dengan berhati-hati kerana saiznya tidak berubah tetapi kandungannya tidak. new ArrayList() mencipta senarai "boleh berubah" baharu. Apakah operasi yang disokong oleh senarai yang dibuat? Itu sahaja, dan ini adalah sebab untuk berhati-hati. Buat senarai boleh ubah daripada senarai tidak boleh ubah menggunakan new ArrayList() . List::of mencipta koleksi "tidak berubah". Saiz dan kandungannya tidak berubah dalam keadaan tertentu. Jika kandungan adalah data primitif, seperti int , senarai itu tidak boleh diubah. Perhatikan contoh berikut.

@Test
public void testListOfBuilders() {
  System.out.println("### TESTING listOF with mutable content ###");

  StringBuilder one = new StringBuilder();
  one.append("a");

  StringBuilder two = new StringBuilder();
  two.append("a");

  List<StringBuilder> asList = List.of(one, two);

  asList.get(0).append("123");

  System.out.println(asList.get(0).toString());
}
### Senarai UJIAN dengan kandungan boleh ubah ### a123
Anda perlu mencipta objek tidak berubah dan memasukkannya ke dalam List::of . Walau bagaimanapun, List::of tidak memberikan sebarang jaminan kebolehubah. List::of menyediakan kebolehubah, kebolehpercayaan dan kebolehbacaan. Ketahui masa untuk menggunakan boleh ubah dan bila untuk menggunakan struktur tidak berubah. Senarai hujah yang tidak sepatutnya berubah hendaklah dalam senarai tidak boleh ubah. Senarai boleh ubah boleh menjadi senarai boleh ubah. Fahami koleksi yang anda perlukan untuk membuat kod yang boleh dipercayai.

3. Anotasi memperlahankan anda

Adakah anda menggunakan anotasi? Adakah anda memahami mereka? Adakah anda tahu apa yang mereka lakukan? Jika anda berpendapat bahawa anotasi Log sesuai untuk setiap kaedah, maka anda silap. Saya menggunakan Log untuk log hujah kaedah. Saya terkejut, ia tidak berfungsi.

@Transaction
@Method("GET")
@PathElement("time")
@PathElement("date")
@Autowired
@Secure("ROLE_ADMIN")
public void manage(@Qualifier('time')int time) {
...
}
Apa yang salah dengan kod ini? Terdapat banyak ringkasan konfigurasi di sini. Anda akan menghadapi ini berkali-kali. Konfigurasi bercampur dengan kod biasa. Tidak buruk dengan sendirinya, tetapi ia menarik perhatian anda. Anotasi diperlukan untuk mengurangkan kod boilerplate. Anda tidak perlu menulis logik pengelogan untuk setiap titik akhir. Tidak perlu menyediakan transaksi, gunakan @Transactional . Anotasi mengurangkan corak dengan mengekstrak kod. Tiada pemenang yang jelas di sini kerana kedua-duanya berada dalam permainan. Saya masih menggunakan XML dan anotasi. Apabila anda menemui corak yang berulang, sebaiknya alihkan logik ke dalam anotasi. Sebagai contoh, pembalakan ialah pilihan anotasi yang baik. Moral: jangan terlalu menggunakan anotasi dan jangan lupa XML.

Bonus: Anda mungkin menghadapi masalah dengan Pilihan

Anda akan menggunakan orElse daripada Pilihan . Tingkah laku yang tidak diingini berlaku apabila anda tidak melepasi pemalar orElse . Anda harus sedar perkara ini untuk mengelakkan masalah di masa hadapan. Mari lihat beberapa contoh. Apabila getValue(x) mengembalikan nilai, getValue(y) dilaksanakan . Kaedah dalam orElse dilaksanakan jika getValue(x) mengembalikan nilai Pilihan yang tidak kosong .

getValue(x).orElse(getValue(y)
                  .orElseThrow(() -> new NotFoundException("value not present")));

public Optional<Value> getValue(Source s)
{
  System.out.println("Source: " + s.getName());
  
  // returns value from s source
}

// when getValue(x) is present system will output
Source: x
Source: y
Gunakan orElseGet . Ia tidak akan melaksanakan kod untuk Pilihan yang tidak kosong .

getValue(x).orElseGet(() -> getValue(y)
                  .orElseThrow(() -> new NotFoundException("value not present")));

public Optional<Value> getValue(Source s)
{
  System.out.println("Source: " + s.getName());
  
  // returns value from s source
}

// when getValue(x) is present system will output
Source: x

Kesimpulan

Belajar Java adalah sukar. Anda tidak boleh belajar Java dalam masa 24 jam. Asah kemahiran anda. Luangkan masa, belajar dan cemerlang dalam pekerjaan anda.

Cara menggunakan prinsip SOLID dalam kod

Sumber: Cleanthecode Menulis kod yang boleh dipercayai memerlukan prinsip SOLID. Pada satu ketika kita semua terpaksa belajar cara memprogram. Dan mari kita jujur. Kami adalah BODOH. Dan kod kami adalah sama. Alhamdulillah kami dah SOLID. Coffee break #85.  Tiga pelajaran Java yang saya pelajari dengan cara yang sukar.  Cara Menggunakan Prinsip SOLID dalam Kod - 2

Prinsip PADAT

Jadi bagaimana anda menulis kod SOLID? Ia sebenarnya mudah. Anda hanya perlu mengikuti lima peraturan ini:
  • Prinsip Tanggungjawab Tunggal
  • Prinsip terbuka-tertutup
  • Prinsip penggantian Liskov
  • Prinsip pemisahan antara muka
  • Prinsip Inversi Ketergantungan
jangan risau! Prinsip-prinsip ini jauh lebih mudah daripada yang kelihatan!

Prinsip Tanggungjawab Tunggal

Dalam bukunya, Robert C. Martin menerangkan prinsip ini seperti berikut: "Sebuah kelas harus mempunyai satu sebab untuk berubah." Mari kita lihat dua contoh bersama-sama.

1. Apa yang tidak boleh dilakukan

Kami mempunyai kelas yang dipanggil Pengguna yang membolehkan pengguna melakukan perkara berikut:
  • Daftar akaun
  • Log masuk
  • Terima pemberitahuan pada kali pertama anda log masuk
Kelas ini kini mempunyai beberapa tanggungjawab. Jika proses pendaftaran berubah, kelas Pengguna akan berubah. Perkara yang sama akan berlaku jika proses log masuk atau proses pemberitahuan berubah. Ini bermakna kelas itu terlebih beban. Dia mempunyai terlalu banyak tanggungjawab. Cara paling mudah untuk membetulkannya adalah dengan mengalihkan tanggungjawab ke kelas anda supaya kelas Pengguna hanya bertanggungjawab untuk menggabungkan kelas. Jika proses kemudian berubah, anda mempunyai satu kelas yang jelas dan berasingan yang perlu diubah.

2. Apa yang perlu dilakukan

Bayangkan kelas yang sepatutnya menunjukkan pemberitahuan kepada pengguna baharu, FirstUseNotification . Ia akan terdiri daripada tiga fungsi:
  • Semak sama ada pemberitahuan telah dipaparkan
  • Tunjukkan pemberitahuan
  • Tandakan pemberitahuan seperti yang telah ditunjukkan
Adakah kelas ini mempunyai pelbagai sebab untuk berubah? Tidak. Kelas ini mempunyai satu fungsi yang jelas - memaparkan pemberitahuan untuk pengguna baharu. Ini bermakna kelas mempunyai satu sebab untuk berubah. Iaitu, jika matlamat ini berubah. Jadi, kelas ini tidak melanggar prinsip tanggungjawab tunggal. Sudah tentu, terdapat beberapa perkara yang mungkin berubah: cara pemberitahuan ditandai sebagai dibaca mungkin berubah, atau cara pemberitahuan itu muncul. Walau bagaimanapun, kerana tujuan kelas adalah jelas dan asas, ini tidak mengapa.

Prinsip terbuka-tertutup

Prinsip tertutup terbuka dicipta oleh Bertrand Meyer: "Objek perisian (kelas, modul, fungsi, dll.) harus dibuka untuk sambungan, tetapi ditutup untuk pengubahsuaian." Prinsip ini sebenarnya sangat mudah. Anda mesti menulis kod anda supaya ciri baharu boleh ditambah padanya tanpa mengubah kod sumber. Ini membantu mengelakkan situasi di mana anda perlu menukar kelas yang bergantung pada kelas anda yang diubah suai. Walau bagaimanapun, prinsip ini adalah lebih sukar untuk dilaksanakan. Meyer mencadangkan menggunakan warisan. Tetapi ia membawa kepada sambungan yang kuat. Kami akan membincangkan perkara ini dalam Prinsip Pemisahan Antara Muka dan Prinsip Penyongsangan Ketergantungan. Jadi Martin datang dengan pendekatan yang lebih baik: gunakan polimorfisme. Daripada warisan konvensional, pendekatan ini menggunakan kelas asas abstrak. Dengan cara ini, spesifikasi warisan boleh digunakan semula sementara pelaksanaan tidak diperlukan. Antara muka boleh ditulis sekali dan kemudian ditutup untuk membuat perubahan. Fungsi baharu mesti melaksanakan antara muka ini dan memanjangkannya.

Prinsip penggantian Liskov

Prinsip ini dicipta oleh Barbara Liskov, pemenang Anugerah Turing atas sumbangannya kepada bahasa pengaturcaraan dan metodologi perisian. Dalam artikelnya, dia mentakrifkan prinsipnya seperti berikut: "Objek dalam program harus diganti dengan contoh subjenisnya tanpa menjejaskan pelaksanaan program yang betul." Mari kita lihat prinsip ini sebagai pengaturcara. Bayangkan kita mempunyai segi empat sama. Ia boleh menjadi segi empat tepat, yang kedengaran logik kerana segi empat sama ialah bentuk khas segi empat tepat. Di sinilah prinsip penggantian Liskov datang untuk menyelamatkan. Di mana sahaja anda menjangkakan untuk melihat segi empat tepat dalam kod anda, segi empat sama juga mungkin muncul. Sekarang bayangkan segi empat tepat anda mempunyai kaedah SetWidth dan SetHeight . Ini bermakna bahawa persegi juga memerlukan kaedah ini. Malangnya, ini tidak masuk akal. Ini bermakna prinsip penggantian Liskov dilanggar di sini.

Prinsip pemisahan antara muka

Seperti semua prinsip, prinsip pemisahan antara muka adalah lebih mudah daripada yang kelihatan: "Banyak antara muka khusus pelanggan lebih baik daripada satu antara muka tujuan umum." Seperti prinsip tanggungjawab tunggal, matlamatnya adalah untuk mengurangkan kesan sampingan dan bilangan perubahan yang diperlukan. Sudah tentu, tiada siapa yang menulis kod sedemikian dengan sengaja. Tetapi ia mudah ditemui. Ingat petak dari prinsip sebelumnya? Sekarang bayangkan bahawa kami memutuskan untuk melaksanakan rancangan kami: kami menghasilkan segi empat sama dari segi empat tepat. Sekarang kami memaksa petak untuk melaksanakan setWidth dan setHeight , yang mungkin tidak melakukan apa-apa. Jika mereka berbuat demikian, kita mungkin akan memecahkan sesuatu kerana lebar dan tingginya tidak seperti yang kita harapkan. Nasib baik bagi kami, ini bermakna kami tidak lagi melanggar prinsip penggantian Liskov, kerana kami kini membenarkan penggunaan segi empat sama di mana sahaja kami menggunakan segi empat tepat. Walau bagaimanapun, ini menimbulkan masalah baharu: kami kini melanggar prinsip mengasingkan antara muka. Kami memaksa kelas terbitan untuk melaksanakan fungsi yang ia pilih untuk tidak digunakan.

Prinsip Inversi Ketergantungan

Prinsip terakhir adalah mudah: modul peringkat tinggi harus boleh diguna semula dan tidak boleh dipengaruhi oleh perubahan pada modul peringkat rendah.
  • A. Modul peringkat tinggi tidak boleh bergantung kepada modul tahap rendah. Kedua-duanya mesti bergantung pada abstraksi (seperti antara muka).
  • B. Abstraksi tidak boleh bergantung pada butiran. Butiran (pelaksanaan konkrit) mesti bergantung pada abstraksi.
Ini boleh dicapai dengan melaksanakan abstraksi yang memisahkan modul peringkat tinggi dan rendah. Nama prinsip menunjukkan bahawa arah pergantungan berubah, tetapi ini tidak berlaku. Ia hanya memisahkan pergantungan dengan memperkenalkan abstraksi antara mereka. Akibatnya, anda akan mendapat dua kebergantungan:
  • Modul peringkat tinggi, bergantung kepada abstraksi
  • Modul tahap rendah bergantung pada abstraksi yang sama
Ini mungkin kelihatan sukar, tetapi sebenarnya ia berlaku secara automatik jika anda menggunakan prinsip terbuka/tertutup dan prinsip penggantian Liskov dengan betul. Itu sahaja! Anda kini telah mempelajari lima prinsip teras yang menyokong SOLID. Dengan lima prinsip ini, anda boleh menjadikan kod anda hebat!
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION