JavaRush /Java Blog /Random-ID /Rehat kopi #85. Tiga pelajaran Java yang saya pelajari de...

Rehat kopi #85. Tiga pelajaran Java yang saya pelajari dengan susah payah. Cara menggunakan prinsip SOLID dalam kode

Dipublikasikan di grup Random-ID

Tiga Pelajaran Java yang Saya Pelajari dengan Cara yang Sulit

Sumber : Medium Belajar Java itu sulit. Saya belajar dari kesalahan saya. Sekarang Anda juga bisa belajar dari kesalahan dan pengalaman pahit saya, yang sebenarnya tidak perlu Anda alami. Rehat kopi #85.  Tiga pelajaran Java yang saya pelajari dengan susah payah.  Cara menggunakan prinsip SOLID dalam kode - 1

1. Lambdas dapat menimbulkan masalah.

Lambdas sering kali melebihi 4 baris kode dan lebih besar dari yang diharapkan. Ini membebani memori kerja. Apakah Anda perlu mengubah variabel dari lambda? Anda tidak bisa melakukan itu. Mengapa? Jika lambda dapat mengakses variabel situs panggilan, masalah threading mungkin timbul. Oleh karena itu Anda tidak dapat mengubah variabel dari lambda. Tapi jalur Happy di lambda berfungsi dengan baik. Setelah kegagalan runtime, Anda akan menerima respons ini:
at [CLASS].lambda$null$2([CLASS].java:85)
at [CLASS]$$Lambda$64/730559617.accept(Unknown Source)
Sulit untuk mengikuti jejak tumpukan lambda. Nama-namanya membingungkan dan sulit dilacak dan di-debug. Lebih banyak lambda = lebih banyak jejak tumpukan. Apa cara terbaik untuk men-debug lambda? Gunakan hasil antara.
map(elem -> {
 int result = elem.getResult();
 return result;
});
Cara bagus lainnya adalah dengan menggunakan teknik debugging IntelliJ tingkat lanjut. Gunakan TAB untuk memilih kode yang ingin Anda debug dan gabungkan dengan hasil antara. “Saat kita berhenti di baris yang berisi lambda, jika kita menekan F7 (melangkah ke), maka IntelliJ akan menyorot fragmen yang perlu di-debug. Kita dapat mengalihkan blok untuk melakukan debug menggunakan Tab, dan setelah kita memutuskannya, tekan F7 lagi.” Bagaimana cara mengakses variabel situs panggilan dari lambda? Anda hanya dapat mengakses variabel final atau sebenarnya final. Anda perlu membuat pembungkus di sekitar variabel lokasi panggilan. Baik menggunakan AtomicType atau menggunakan tipe Anda sendiri. Anda dapat mengubah pembungkus variabel yang dibuat dengan lambda. Bagaimana cara mengatasi masalah pelacakan tumpukan? Gunakan fungsi bernama. Dengan cara ini, Anda dapat dengan cepat menemukan kode yang bertanggung jawab, memeriksa logika, dan memecahkan masalah. Gunakan fungsi bernama untuk menghapus jejak tumpukan samar. Apakah lambda yang sama terulang? Tempatkan di fungsi bernama. Anda akan memiliki satu titik acuan. Setiap lambda mendapatkan fungsi yang dihasilkan, sehingga sulit untuk dilacak.
lambda$yourNamedFunction
lambda$0
Fungsi bernama memecahkan masalah yang berbeda. Lambda besar. Fungsi yang diberi nama memecah lambda besar, membuat potongan kode yang lebih kecil, dan membuat fungsi yang dapat dicolokkan.
.map(this::namedFunc1).filter(this::namedFilter1).map(this::namedFunc2)

2. Masalah dengan daftar

Anda perlu bekerja dengan daftar ( Lists ). Anda memerlukan HashMap untuk datanya. Untuk peran, Anda memerlukan TreeMap . Daftarnya terus bertambah. Dan tidak ada cara untuk menghindari bekerja dengan koleksi. Bagaimana cara membuat daftarnya? Daftar seperti apa yang Anda butuhkan? Haruskah itu tetap atau bisa diubah? Semua jawaban ini mempengaruhi masa depan kode Anda. Pilih daftar yang tepat terlebih dahulu agar Anda tidak menyesal di kemudian hari. Arrays::asList membuat daftar “end-to-end”. Apa yang tidak dapat Anda lakukan dengan daftar ini? Anda tidak dapat mengubah ukurannya. Dia tidak bisa diubah. Apa yang bisa kamu lakukan di sini? Tentukan elemen, pengurutan, atau operasi lain yang tidak memengaruhi ukuran. Gunakan Arrays::asList dengan hati-hati karena ukurannya tidak dapat diubah tetapi isinya tidak. new ArrayList() membuat daftar "mutable" baru. Operasi apa yang didukung oleh daftar yang dibuat? Itu saja, dan ini alasan untuk berhati-hati. Buat daftar yang bisa diubah dari daftar yang tidak bisa diubah menggunakan new ArrayList() . List::of membuat koleksi yang “tidak dapat diubah”. Ukuran dan isinya tidak berubah dalam kondisi tertentu. Jika kontennya berupa data primitif, seperti int , daftarnya tidak dapat diubah. Lihatlah 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());
}
### PENGUJIAN listOF dengan konten yang dapat diubah ### a123
Anda perlu membuat objek yang tidak dapat diubah dan memasukkannya ke dalam List::of . Namun, List::of tidak memberikan jaminan kekekalan. List::of memberikan kekekalan, keandalan, dan keterbacaan. Ketahui kapan harus menggunakan struktur yang bisa berubah dan kapan harus menggunakan struktur yang tidak bisa diubah. Daftar argumen yang tidak boleh diubah harus ada dalam daftar yang tidak dapat diubah. Daftar yang bisa berubah bisa menjadi daftar yang bisa berubah. Pahami koleksi apa yang Anda perlukan untuk membuat kode yang andal.

3. Anotasi memperlambat Anda

Apakah Anda menggunakan anotasi? Apakah Anda memahaminya? Tahukah Anda apa yang mereka lakukan? Jika Anda berpikir bahwa anotasi yang dicatat cocok untuk setiap metode, maka Anda salah. Saya menggunakan Log untuk mencatat argumen metode. Yang mengejutkan saya, itu tidak berhasil.
@Transaction
@Method("GET")
@PathElement("time")
@PathElement("date")
@Autowired
@Secure("ROLE_ADMIN")
public void manage(@Qualifier('time')int time) {
...
}
Apa yang salah dengan kode ini? Ada banyak intisari konfigurasi di sini. Anda akan mengalami hal ini berkali-kali. Konfigurasinya dicampur dengan kode biasa. Tidak buruk, tapi menarik perhatian Anda. Anotasi diperlukan untuk mengurangi kode boilerplate. Anda tidak perlu menulis logika logging untuk setiap titik akhir. Tidak perlu mengatur transaksi, gunakan @Transactional . Anotasi mengurangi pola dengan mengekstraksi kode. Tidak ada pemenang yang jelas di sini karena keduanya ada dalam permainan. Saya masih menggunakan XML dan anotasi. Saat Anda menemukan pola berulang, sebaiknya pindahkan logika ke dalam anotasi. Misalnya, logging adalah opsi anotasi yang bagus. Moral: jangan terlalu sering menggunakan anotasi dan jangan lupa XML.

Bonus: Anda mungkin mengalami masalah dengan Opsional

Anda akan menggunakan orElse dari Optional . Perilaku yang tidak diinginkan terjadi ketika Anda tidak meneruskan konstanta orElse . Anda harus mewaspadai hal ini untuk mencegah masalah di kemudian hari. Mari kita lihat beberapa contoh. Ketika getValue(x) mengembalikan nilai, getValue(y) dieksekusi . Metode di orElse dijalankan jika getValue(x) mengembalikan Optional value 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 . Itu tidak akan mengeksekusi kode untuk Optionals 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 itu sulit. Anda tidak bisa belajar Java dalam 24 jam. Asah keterampilan Anda. Luangkan waktu, pelajari, dan unggul dalam pekerjaan Anda.

Cara menggunakan prinsip SOLID dalam kode

Sumber: Cleanthecode Menulis kode yang andal memerlukan prinsip SOLID. Pada titik tertentu kita semua harus belajar bagaimana memprogram. Dan jujur ​​saja. Kami BODOH. Dan kode kami sama. Alhamdulillah kami punya SOLID. Rehat kopi #85.  Tiga pelajaran Java yang saya pelajari dengan susah payah.  Cara Menggunakan Prinsip SOLID dalam Kode - 2

Prinsip PADAT

Jadi bagaimana Anda menulis kode SOLID? Ini sebenarnya sederhana. Anda hanya perlu mengikuti lima aturan berikut:
  • Prinsip Tanggung Jawab Tunggal
  • Prinsip terbuka-tertutup
  • Prinsip penggantian Liskov
  • Prinsip pemisahan antarmuka
  • Prinsip Pembalikan Ketergantungan
Jangan khawatir! Prinsip-prinsip ini jauh lebih sederhana dari yang terlihat!

Prinsip Tanggung Jawab Tunggal

Dalam bukunya, Robert C. Martin menjelaskan prinsip ini sebagai berikut: “Sebuah kelas seharusnya hanya mempunyai satu alasan untuk berubah.” Mari kita lihat dua contoh bersama-sama.

1. Apa yang tidak boleh dilakukan

Kami memiliki kelas bernama Pengguna yang memungkinkan pengguna melakukan hal-hal berikut:
  • Daftar akun
  • Gabung
  • Terima pemberitahuan saat pertama kali Anda masuk
Kelas ini sekarang memiliki beberapa tanggung jawab. Jika proses pendaftaran berubah, kelas Pengguna akan berubah. Hal yang sama juga terjadi jika proses login atau proses notifikasi berubah. Artinya kelasnya kelebihan beban. Dia memiliki terlalu banyak tanggung jawab. Cara termudah untuk memperbaikinya adalah dengan memindahkan tanggung jawab ke kelas Anda sehingga kelas Pengguna hanya bertanggung jawab untuk menggabungkan kelas. Jika prosesnya kemudian berubah, Anda memiliki satu kelas yang jelas dan terpisah yang perlu diubah.

2. Apa yang harus dilakukan

Bayangkan sebuah kelas yang seharusnya menampilkan notifikasi kepada pengguna baru, FirstUseNotification . Ini akan terdiri dari tiga fungsi:
  • Periksa apakah notifikasi telah ditampilkan
  • Tunjukkan notifikasi
  • Tandai notifikasi seperti yang sudah ditampilkan
Apakah kelas ini memiliki banyak alasan untuk berubah? TIDAK. Kelas ini memiliki satu fungsi yang jelas - menampilkan notifikasi untuk pengguna baru. Artinya kelas mempunyai satu alasan untuk berubah. Yakni jika tujuan ini berubah. Jadi, golongan ini tidak melanggar prinsip tanggung jawab tunggal. Tentu saja, ada beberapa hal yang mungkin berubah: cara notifikasi ditandai sebagai telah dibaca mungkin berubah, atau cara notifikasi muncul. Namun, karena tujuan kelasnya jelas dan mendasar, hal ini tidak masalah.

Prinsip terbuka-tertutup

Prinsip buka-tutup diciptakan oleh Bertrand Meyer: “Objek perangkat lunak (kelas, modul, fungsi, dll.) harus terbuka untuk perluasan, tetapi tertutup untuk modifikasi.” Prinsip ini sebenarnya sangat sederhana. Anda harus menulis kode Anda agar fitur baru dapat ditambahkan ke dalamnya tanpa mengubah kode sumber. Ini membantu mencegah situasi di mana Anda perlu mengubah kelas yang bergantung pada kelas yang Anda modifikasi. Namun prinsip ini jauh lebih sulit untuk diterapkan. Meyer menyarankan menggunakan warisan. Tapi itu mengarah pada hubungan yang kuat. Kita akan membahasnya di Prinsip Pemisahan Antarmuka dan Prinsip Inversi Ketergantungan. Jadi Martin datang dengan pendekatan yang lebih baik: gunakan polimorfisme. Alih-alih pewarisan konvensional, pendekatan ini menggunakan kelas dasar abstrak. Dengan cara ini, spesifikasi warisan dapat digunakan kembali saat implementasi tidak diperlukan. Antarmuka dapat ditulis sekali dan kemudian ditutup untuk melakukan perubahan. Fungsi-fungsi baru kemudian harus mengimplementasikan antarmuka ini dan memperluasnya.

Prinsip penggantian Liskov

Prinsip ini ditemukan oleh Barbara Liskov, pemenang Turing Award atas kontribusinya pada bahasa pemrograman dan metodologi perangkat lunak. Dalam artikelnya, dia mendefinisikan prinsipnya sebagai berikut: “Objek dalam suatu program harus dapat diganti dengan instance dari subtipenya tanpa mempengaruhi pelaksanaan program yang benar.” Mari kita lihat prinsip ini sebagai seorang programmer. Bayangkan kita memiliki sebuah persegi. Bentuknya bisa berupa persegi panjang, kedengarannya logis karena persegi adalah bentuk khusus dari persegi panjang. Di sinilah prinsip penggantian Liskov membantu. Di mana pun Anda berharap melihat persegi panjang dalam kode Anda, persegi juga mungkin muncul. Sekarang bayangkan persegi panjang Anda memiliki metode SetWidth dan SetHeight . Artinya alun-alun juga membutuhkan cara-cara tersebut. Sayangnya, hal ini tidak masuk akal. Artinya prinsip penggantian Liskov dilanggar di sini.

Prinsip pemisahan antarmuka

Seperti semua prinsip, prinsip pemisahan antarmuka jauh lebih sederhana daripada yang terlihat: “Banyak antarmuka khusus klien lebih baik daripada satu antarmuka tujuan umum.” Sebagaimana prinsip tanggung jawab tunggal, tujuannya adalah untuk mengurangi efek samping dan jumlah perubahan yang diperlukan. Tentu saja, tidak ada yang menulis kode seperti itu dengan sengaja. Tapi itu mudah untuk ditemui. Ingat persegi dari prinsip sebelumnya? Sekarang bayangkan kita memutuskan untuk melaksanakan rencana kita: kita membuat persegi dari persegi panjang. Sekarang kami memaksa persegi untuk mengimplementasikan setWidth dan setHeight , yang mungkin tidak melakukan apa pun. Jika mereka melakukan itu, kami mungkin akan merusak sesuatu karena lebar dan tingginya tidak sesuai dengan yang kami harapkan. Beruntung bagi kami, ini berarti kami tidak lagi melanggar prinsip substitusi Liskov, karena kami sekarang mengizinkan penggunaan persegi di mana pun kami menggunakan persegi panjang. Namun, hal ini menimbulkan masalah baru: kami sekarang melanggar prinsip pemisahan antarmuka. Kami memaksa kelas turunan untuk mengimplementasikan fungsionalitas yang tidak digunakannya.

Prinsip Pembalikan Ketergantungan

Prinsip terakhir sederhana: modul tingkat tinggi harus dapat digunakan kembali dan tidak terpengaruh oleh perubahan pada modul tingkat rendah.
  • A. Modul tingkat tinggi tidak boleh bergantung pada modul tingkat rendah. Keduanya harus bergantung pada abstraksi (seperti antarmuka).
  • B. Abstraksi tidak boleh bergantung pada detail. Detailnya (implementasi konkret) harus bergantung pada abstraksi.
Hal ini dapat dicapai dengan menerapkan abstraksi yang memisahkan modul tingkat tinggi dan rendah. Nama prinsipnya menunjukkan bahwa arah ketergantungan berubah, namun kenyataannya tidak demikian. Itu hanya memisahkan ketergantungan dengan memperkenalkan abstraksi di antara keduanya. Hasilnya, Anda akan mendapatkan dua dependensi:
  • Modul tingkat tinggi, tergantung pada abstraksi
  • Modul tingkat rendah tergantung pada abstraksi yang sama
Hal ini mungkin tampak sulit, namun sebenarnya hal ini terjadi secara otomatis jika Anda menerapkan prinsip buka/tutup dan prinsip substitusi Liskov dengan benar. Itu saja! Anda sekarang telah mempelajari lima prinsip inti yang mendasari SOLID. Dengan lima prinsip ini, Anda dapat membuat kode Anda luar biasa!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION