CodeGym /Java Blog /Acak /Aturan Pengodean: Dari Membuat Sistem hingga Bekerja deng...
John Squirrels
Level 41
San Francisco

Aturan Pengodean: Dari Membuat Sistem hingga Bekerja dengan Objek

Dipublikasikan di grup Acak
Selamat siang semuanya! Hari ini kami ingin berbicara dengan Anda tentang menulis kode yang baik. Tentu saja, tidak semua orang ingin langsung mengunyah buku seperti Clean Code, karena berisi banyak sekali informasi tetapi tidak banyak yang jelas pada awalnya. Dan pada saat Anda selesai membaca, Anda dapat membunuh semua keinginan Anda untuk membuat kode. Mempertimbangkan semua itu, hari ini saya ingin memberi Anda panduan kecil (seperangkat kecil rekomendasi) untuk menulis kode yang lebih baik. Pada artikel ini, mari kita bahas aturan dan konsep dasar yang terkait dengan pembuatan sistem dan bekerja dengan antarmuka, kelas, dan objek. Membaca artikel ini tidak akan memakan banyak waktu dan, saya harap, tidak akan membuat Anda bosan. Saya akan bekerja dari atas ke bawah, yaitu dari struktur umum aplikasi hingga detailnya yang lebih sempit. Aturan Pengodean: Dari Membuat Sistem hingga Bekerja dengan Objek - 1

Sistem

Berikut ini adalah karakteristik umum yang diinginkan dari suatu sistem:
  • Kompleksitas minimal. Proyek yang terlalu rumit harus dihindari. Yang paling penting adalah kesederhanaan dan kejelasan (sederhana = lebih baik).
  • Kemudahan perawatan. Saat membuat aplikasi, Anda harus ingat bahwa itu perlu dipertahankan (bahkan jika Anda secara pribadi tidak bertanggung jawab untuk memeliharanya). Ini berarti kode harus jelas dan jelas.
  • Kopling longgar. Ini berarti kami meminimalkan jumlah ketergantungan antara berbagai bagian program (memaksimalkan kepatuhan kami terhadap prinsip OOP).
  • Dapat digunakan kembali. Kami merancang sistem kami dengan kemampuan untuk menggunakan kembali komponen di aplikasi lain.
  • Portabilitas. Seharusnya mudah untuk mengadaptasi sistem ke lingkungan lain.
  • Gaya seragam. Kami merancang sistem kami menggunakan gaya yang seragam dalam berbagai komponennya.
  • Ekstensibilitas (skalabilitas). Kita dapat meningkatkan sistem tanpa melanggar struktur dasarnya (menambah atau mengubah komponen tidak boleh mempengaruhi yang lainnya).
Secara praktis tidak mungkin membuat aplikasi yang tidak memerlukan modifikasi atau fungsionalitas baru. Kami terus-menerus perlu menambahkan bagian-bagian baru untuk membantu gagasan kami mengikuti perkembangan zaman. Di sinilah skalabilitas berperan. Skalabilitas pada dasarnya memperluas aplikasi, menambahkan fungsionalitas baru, dan bekerja dengan lebih banyak sumber daya (atau, dengan kata lain, dengan beban yang lebih besar). Dengan kata lain, untuk memudahkan menambahkan logika baru, kami tetap berpegang pada beberapa aturan, seperti mengurangi kopling sistem dengan meningkatkan modularitas.Aturan Pengkodean: Dari Membuat Sistem hingga Bekerja Dengan Objek - 2

Sumber Gambar

Tahapan mendesain sistem

  1. Sistem perangkat lunak. Desain aplikasi secara keseluruhan.
  2. Pembagian ke dalam subsistem/paket. Tentukan bagian yang berbeda secara logis dan tentukan aturan untuk interaksi di antara mereka.
  3. Pembagian subsistem ke dalam kelas-kelas. Bagilah bagian-bagian sistem menjadi kelas dan antarmuka tertentu, dan tentukan interaksi di antara mereka.
  4. Pembagian kelas menjadi metode. Buat definisi lengkap tentang metode yang diperlukan untuk kelas, berdasarkan tanggung jawab yang diberikan.
  5. Desain metode. Buat definisi mendetail tentang fungsionalitas metode individual.
Biasanya pengembang biasa menangani desain ini, sedangkan arsitek aplikasi menangani poin-poin yang dijelaskan di atas.

Prinsip dan konsep umum desain sistem

Inisialisasi malas. Dalam idiom pemrograman ini, aplikasi tidak membuang waktu membuat objek sampai benar-benar digunakan. Ini mempercepat proses inisialisasi dan mengurangi beban pada pengumpul sampah. Meskipun demikian, Anda tidak boleh bertindak terlalu jauh, karena dapat melanggar prinsip modularitas. Mungkin ada baiknya memindahkan semua instance konstruksi ke bagian tertentu, misalnya, metode utama atau ke kelas pabrik . Salah satu karakteristik dari kode yang baik adalah tidak adanya kode boilerplate yang berulang. Sebagai aturan, kode tersebut ditempatkan di kelas terpisah sehingga dapat dipanggil saat dibutuhkan.

AOP

Saya juga ingin mencatat pemrograman berorientasi aspek. Paradigma pemrograman ini adalah tentang memperkenalkan logika transparan. Yaitu, kode berulang dimasukkan ke dalam kelas (aspek) dan dipanggil ketika kondisi tertentu terpenuhi. Misalnya, saat memanggil metode dengan nama tertentu atau mengakses variabel dengan tipe tertentu. Kadang-kadang aspek dapat membingungkan, karena tidak segera jelas dari mana kode dipanggil, tetapi fungsi ini masih sangat berguna. Terutama saat melakukan caching atau logging. Kami menambahkan fungsionalitas ini tanpa menambahkan logika tambahan ke kelas biasa. Empat aturan Kent Beck untuk arsitektur sederhana:
  1. Expressiveness — Maksud dari sebuah kelas harus diungkapkan dengan jelas. Ini dicapai melalui penamaan yang tepat, ukuran kecil, dan kepatuhan pada prinsip tanggung jawab tunggal (yang akan kami pertimbangkan lebih detail di bawah).
  2. Jumlah minimum kelas dan metode — Dalam keinginan Anda untuk membuat kelas sekecil dan sefokus mungkin, Anda bisa melangkah terlalu jauh (mengakibatkan anti-pola operasi senapan). Prinsip ini menuntut agar sistem tetap kompak dan tidak melangkah terlalu jauh, membuat kelas terpisah untuk setiap kemungkinan tindakan.
  3. Tanpa duplikasi — Kode duplikat, yang menciptakan kebingungan dan merupakan indikasi desain sistem yang kurang optimal, diekstraksi dan dipindahkan ke lokasi terpisah.
  4. Menjalankan semua pengujian — Sistem yang lulus semua pengujian dapat dikelola. Setiap perubahan dapat menyebabkan pengujian gagal, mengungkapkan kepada kami bahwa perubahan kami dalam logika internal metode juga mengubah perilaku sistem dengan cara yang tidak terduga.

PADAT

Saat merancang sistem, prinsip SOLID yang terkenal patut dipertimbangkan:

S (tanggung jawab tunggal), O (terbuka-tertutup), L (substitusi Liskov), I (pemisahan antarmuka), D (inversi ketergantungan).

Kami tidak akan memikirkan setiap prinsip individu. Itu sedikit di luar cakupan artikel ini, tetapi Anda dapat membaca lebih lanjut di sini .

Antarmuka

Mungkin salah satu langkah terpenting dalam membuat kelas yang dirancang dengan baik adalah membuat antarmuka yang dirancang dengan baik yang merepresentasikan abstraksi yang baik, menyembunyikan detail implementasi kelas dan secara bersamaan menampilkan sekelompok metode yang jelas konsisten satu sama lain. Mari kita lihat lebih dekat salah satu prinsip SOLID — segregasi antarmuka: klien (kelas) tidak boleh mengimplementasikan metode yang tidak perlu yang tidak akan mereka gunakan. Dengan kata lain, jika kita berbicara tentang membuat antarmuka dengan jumlah metode paling sedikit yang ditujukan untuk melakukan satu-satunya pekerjaan antarmuka (yang menurut saya sangat mirip dengan prinsip tanggung jawab tunggal), lebih baik membuat beberapa yang lebih kecil sebagai gantinya. dari satu antarmuka kembung. Untungnya, sebuah kelas dapat mengimplementasikan lebih dari satu antarmuka. Ingatlah untuk menamai antarmuka Anda dengan benar: nama harus mencerminkan tugas yang diberikan seakurat mungkin. Dan, tentu saja, semakin pendek, semakin sedikit kebingungan yang ditimbulkannya. Komentar dokumentasi biasanya ditulis pada tingkat antarmuka. Komentar ini memberikan detail tentang apa yang harus dilakukan setiap metode, argumen apa yang dibutuhkan, dan apa yang akan dikembalikan.

Kelas

Aturan Pengodean: Dari Membuat Sistem hingga Bekerja dengan Objek - 3

Sumber Gambar

Mari kita lihat bagaimana kelas diatur secara internal. Atau lebih tepatnya, beberapa perspektif dan aturan yang harus diikuti saat menulis kelas. Sebagai aturan, kelas harus dimulai dengan daftar variabel dalam urutan tertentu:
  1. konstanta statis publik;
  2. konstanta statis pribadi;
  3. variabel instan pribadi.
Berikutnya adalah berbagai konstruktor, diurutkan dari yang memiliki argumen paling sedikit hingga yang paling banyak. Mereka diikuti oleh metode dari yang paling umum ke yang paling pribadi. Secara umum, metode privat yang menyembunyikan penerapan beberapa fungsi yang ingin kita batasi ada di bagian paling bawah.

Ukuran kelas

Sekarang saya ingin berbicara tentang ukuran kelas. Mari kita ingat salah satu prinsip SOLID — prinsip tanggung jawab tunggal. Ini menyatakan bahwa setiap objek hanya memiliki satu tujuan (tanggung jawab), dan logika dari semua metodenya bertujuan untuk mencapainya. Ini memberitahu kita untuk menghindari kelas yang besar dan kembung (yang sebenarnya adalah anti-pola objek Tuhan), dan jika kita memiliki banyak metode dengan segala macam logika berbeda yang dijejalkan ke dalam sebuah kelas, kita perlu memikirkan untuk memecahnya menjadi kelas beberapa bagian logis (kelas). Ini, pada gilirannya, akan meningkatkan keterbacaan kode, karena tidak butuh waktu lama untuk memahami tujuan dari setiap metode jika kita mengetahui perkiraan tujuan dari setiap kelas yang diberikan. Juga, awasi nama kelas, yang harus mencerminkan logika yang dikandungnya. Misalnya, jika kita memiliki kelas dengan 20+ kata dalam namanya, kita perlu berpikir tentang refactoring. Setiap kelas yang menghargai diri sendiri seharusnya tidak memiliki banyak variabel internal. Bahkan, setiap metode bekerja dengan satu atau beberapa dari mereka, menyebabkan banyak kohesi dalam kelas (yang persis seperti yang seharusnya, karena kelas harus menjadi kesatuan yang utuh). Akibatnya, peningkatan kohesi kelas mengarah pada pengurangan ukuran kelas, dan, tentu saja, jumlah kelas bertambah. Ini menjengkelkan bagi sebagian orang, karena Anda perlu lebih banyak membaca file kelas untuk melihat cara kerja tugas besar tertentu. Di atas semua itu, setiap kelas adalah modul kecil yang harus berhubungan minimal dengan yang lain. Isolasi ini mengurangi jumlah perubahan yang perlu kita buat saat menambahkan logika tambahan ke kelas. setiap metode bekerja dengan satu atau beberapa dari mereka, menyebabkan banyak kohesi di dalam kelas (yang persis seperti yang seharusnya, karena kelas harus menjadi satu kesatuan). Akibatnya, peningkatan kohesi kelas mengarah pada pengurangan ukuran kelas, dan, tentu saja, jumlah kelas bertambah. Ini menjengkelkan bagi sebagian orang, karena Anda perlu lebih banyak membaca file kelas untuk melihat cara kerja tugas besar tertentu. Di atas semua itu, setiap kelas adalah modul kecil yang harus berhubungan minimal dengan yang lain. Isolasi ini mengurangi jumlah perubahan yang perlu kita buat saat menambahkan logika tambahan ke kelas. setiap metode bekerja dengan satu atau beberapa dari mereka, menyebabkan banyak kohesi di dalam kelas (yang persis seperti yang seharusnya, karena kelas harus menjadi satu kesatuan). Akibatnya, peningkatan kohesi kelas mengarah pada pengurangan ukuran kelas, dan, tentu saja, jumlah kelas bertambah. Ini menjengkelkan bagi sebagian orang, karena Anda perlu lebih banyak membaca file kelas untuk melihat cara kerja tugas besar tertentu. Di atas semua itu, setiap kelas adalah modul kecil yang harus berhubungan minimal dengan yang lain. Isolasi ini mengurangi jumlah perubahan yang perlu kita buat saat menambahkan logika tambahan ke kelas. s kohesi mengarah pada pengurangan ukuran kelas, dan, tentu saja, jumlah kelas meningkat. Ini menjengkelkan bagi sebagian orang, karena Anda perlu lebih banyak membaca file kelas untuk melihat cara kerja tugas besar tertentu. Di atas semua itu, setiap kelas adalah modul kecil yang harus berhubungan minimal dengan yang lain. Isolasi ini mengurangi jumlah perubahan yang perlu kita buat saat menambahkan logika tambahan ke kelas. s kohesi mengarah pada pengurangan ukuran kelas, dan, tentu saja, jumlah kelas meningkat. Ini menjengkelkan bagi sebagian orang, karena Anda perlu lebih banyak membaca file kelas untuk melihat cara kerja tugas besar tertentu. Di atas semua itu, setiap kelas adalah modul kecil yang harus berhubungan minimal dengan yang lain. Isolasi ini mengurangi jumlah perubahan yang perlu kita buat saat menambahkan logika tambahan ke kelas.

Objek

Enkapsulasi

Di sini pertama-tama kita akan berbicara tentang prinsip OOP: enkapsulasi. Menyembunyikan implementasi tidak sama dengan membuat metode untuk mengisolasi variabel (membatasi akses secara sembarangan melalui metode individual, getter, dan setter, yang tidak baik, karena seluruh titik enkapsulasi hilang). Menyembunyikan akses ditujukan untuk membentuk abstraksi, yaitu kelas menyediakan metode konkret bersama yang kita gunakan untuk bekerja dengan data kita. Dan pengguna tidak perlu tahu persis bagaimana kami bekerja dengan data ini — ini berfungsi dan itu sudah cukup.

Hukum Demeter

Kita juga dapat mempertimbangkan Hukum Demeter: ini adalah sekumpulan kecil aturan yang membantu mengelola kompleksitas di tingkat kelas dan metode. Misalkan kita memiliki objek Car , dan memiliki metode move(Object arg1, Object arg2) . Menurut Hukum Demeter, metode ini terbatas pada pemanggilan:
  • metode objek Mobil itu sendiri (dengan kata lain, objek "ini");
  • metode objek yang dibuat dalam metode pemindahan ;
  • metode objek yang diteruskan sebagai argumen ( arg1 , arg2 );
  • metode objek Mobil internal (sekali lagi, "ini").
Dengan kata lain, Hukum Demeter adalah seperti apa yang mungkin dikatakan orang tua kepada seorang anak: "Anda dapat berbicara dengan teman Anda, tetapi tidak dengan orang asing".

Struktur data

Struktur data adalah kumpulan elemen terkait. Saat mempertimbangkan objek sebagai struktur data, ada sekumpulan elemen data tempat metode beroperasi. Keberadaan metode ini diasumsikan secara implisit. Artinya, struktur data adalah objek yang tujuannya untuk menyimpan dan bekerja dengan (memproses) data yang disimpan. Perbedaan utamanya dari objek biasa adalah bahwa objek biasa adalah kumpulan metode yang beroperasi pada elemen data yang secara implisit dianggap ada. Apakah kamu mengerti? Aspek utama dari objek biasa adalah metode. Variabel internal memfasilitasi operasi yang benar. Namun dalam struktur data, metode ada untuk mendukung pekerjaan Anda dengan elemen data yang disimpan, yang terpenting di sini. Salah satu jenis struktur data adalah objek transfer data (DTO). Ini adalah kelas dengan variabel publik dan tidak ada metode (atau hanya metode untuk membaca/menulis) yang digunakan untuk mentransfer data saat bekerja dengan database, mem-parsing pesan dari soket, dll. Data biasanya tidak disimpan dalam objek tersebut untuk waktu yang lama. Hampir segera dikonversi ke jenis entitas tempat aplikasi kita bekerja. Entitas, pada gilirannya, juga merupakan struktur data, tetapi tujuannya adalah untuk berpartisipasi dalam logika bisnis di berbagai level aplikasi. Tujuan dari DTO adalah untuk mengangkut data ke/dari aplikasi. Contoh DTO: juga merupakan struktur data, tetapi tujuannya adalah untuk berpartisipasi dalam logika bisnis di berbagai level aplikasi. Tujuan dari DTO adalah untuk mengangkut data ke/dari aplikasi. Contoh DTO: juga merupakan struktur data, tetapi tujuannya adalah untuk berpartisipasi dalam logika bisnis di berbagai level aplikasi. Tujuan dari DTO adalah untuk mengangkut data ke/dari aplikasi. Contoh DTO:

@Setter
@Getter
@NoArgsConstructor
public class UserDto {
    private long id;
    private String firstName;
    private String lastName;
    private String email;
    private String password;
}
Segalanya tampak cukup jelas, tetapi di sini kita belajar tentang keberadaan hibrida. Hibrida adalah objek yang memiliki metode untuk menangani logika penting, menyimpan elemen internal, dan juga menyertakan metode pengakses (get/set). Objek seperti itu berantakan dan menyulitkan untuk menambahkan metode baru. Anda harus menghindarinya, karena tidak jelas untuk apa mereka — menyimpan elemen atau menjalankan logika?

Prinsip membuat variabel

Mari kita renungkan sedikit tentang variabel. Lebih khusus lagi, mari pikirkan tentang prinsip apa yang berlaku saat membuatnya:
  1. Idealnya, Anda harus mendeklarasikan dan menginisialisasi variabel sebelum menggunakannya (jangan membuatnya dan melupakannya).
  2. Bila memungkinkan, nyatakan variabel sebagai final untuk mencegah nilainya berubah setelah inisialisasi.
  3. Jangan lupa tentang variabel penghitung, yang biasanya kita gunakan dalam beberapa jenis perulangan for . Artinya, jangan lupa untuk membidiknya. Kalau tidak, semua logika kita bisa rusak.
  4. Anda harus mencoba menginisialisasi variabel dalam konstruktor.
  5. Jika ada pilihan antara menggunakan objek dengan referensi atau tanpa ( new SomeObject() ), pilih tanpa, karena setelah objek digunakan, objek tersebut akan dihapus selama siklus pengumpulan sampah berikutnya dan sumber dayanya tidak akan terbuang sia-sia.
  6. Pertahankan masa pakai variabel (jarak antara pembuatan variabel dan terakhir kali direferensikan) sesingkat mungkin.
  7. Menginisialisasi variabel yang digunakan dalam sebuah loop tepat sebelum loop, bukan di awal metode yang berisi loop.
  8. Mulailah selalu dengan cakupan yang paling terbatas dan perluas hanya jika diperlukan (Anda harus mencoba membuat variabel selokal mungkin).
  9. Gunakan setiap variabel untuk satu tujuan saja.
  10. Hindari variabel dengan tujuan tersembunyi, misalnya pemisahan variabel antara dua tugas — ini berarti jenisnya tidak cocok untuk menyelesaikan salah satunya.

Metode

Aturan Pengkodean: Dari Membuat Sistem hingga Bekerja Dengan Objek - 4

dari film "Star Wars: Episode III - Revenge of the Sith" (2005)

Mari langsung ke penerapan logika kita, yaitu ke metode.
  1. Aturan #1 — Kekompakan. Idealnya, sebuah metode tidak boleh melebihi 20 baris. Ini berarti bahwa jika metode publik "membengkak" secara signifikan, Anda perlu memikirkan untuk memecah logika dan memindahkannya ke metode pribadi yang terpisah.

  2. Aturan #2 — if , else , while dan pernyataan lainnya tidak boleh memiliki banyak blok bersarang: banyak bersarang secara signifikan mengurangi keterbacaan kode. Idealnya, Anda tidak boleh memiliki lebih dari dua blok {} bersarang .

    Dan juga diinginkan untuk menjaga kode di blok ini tetap kompak dan sederhana.

  3. Aturan #3 — Sebuah metode harus melakukan hanya satu operasi. Artinya, jika suatu metode melakukan semua jenis logika kompleks, kita memecahnya menjadi submetode. Akibatnya, metode itu sendiri akan menjadi fasad yang tujuannya memanggil semua operasi lain dalam urutan yang benar.

    Tetapi bagaimana jika operasi tersebut tampaknya terlalu sederhana untuk dimasukkan ke dalam metode terpisah? Benar, terkadang rasanya seperti menembakkan meriam ke burung pipit, tetapi metode kecil memberikan sejumlah keuntungan:

    • Pemahaman kode yang lebih baik;
    • Metode cenderung menjadi lebih kompleks seiring kemajuan pembangunan. Jika suatu metode awalnya sederhana, maka akan sedikit lebih mudah untuk memperumit fungsinya;
    • Detail implementasi disembunyikan;
    • Penggunaan kembali kode yang lebih mudah;
    • Kode yang lebih andal.

  4. Aturan stepdown — Kode harus dibaca dari atas ke bawah: semakin rendah Anda membaca, semakin dalam Anda mempelajari logikanya. Dan sebaliknya, semakin tinggi Anda pergi, semakin abstrak metodenya. Misalnya, pernyataan peralihan agak tidak ringkas dan tidak diinginkan, tetapi jika Anda tidak dapat menghindari penggunaan sakelar, Anda harus mencoba memindahkannya serendah mungkin, ke metode tingkat terendah.

  5. Argumen metode — Berapa angka idealnya? Idealnya, tidak ada sama sekali :) Tapi apakah itu benar-benar terjadi? Yang mengatakan, Anda harus mencoba untuk memiliki argumen sesedikit mungkin, karena semakin sedikit argumen, semakin mudah menggunakan metode dan semakin mudah untuk mengujinya. Jika ragu, coba antisipasi semua skenario untuk menggunakan metode dengan sejumlah besar parameter masukan.

  6. Selain itu, akan baik untuk memisahkan metode yang memiliki flag boolean sebagai parameter input, karena ini semua dengan sendirinya menyiratkan bahwa metode melakukan lebih dari satu operasi (jika benar, lakukan satu hal; jika salah, lakukan yang lain). Seperti yang saya tulis di atas, ini tidak baik dan harus dihindari jika memungkinkan.

  7. Jika sebuah metode memiliki sejumlah besar parameter masukan (ekstrimnya adalah 7, tetapi Anda harus benar-benar mulai berpikir setelah 2-3), beberapa argumen harus dikelompokkan ke dalam objek terpisah.

  8. Jika ada beberapa metode yang serupa (kelebihan beban), maka parameter serupa harus diteruskan dalam urutan yang sama: ini meningkatkan keterbacaan dan kegunaan.

  9. Saat Anda meneruskan parameter ke suatu metode, Anda harus yakin bahwa semuanya digunakan, jika tidak, mengapa Anda membutuhkannya? Potong semua parameter yang tidak digunakan dari antarmuka dan selesaikan.

  10. try/catch pada dasarnya tidak terlihat bagus, jadi sebaiknya pindahkan ke metode perantara yang terpisah (metode untuk menangani pengecualian):

    
    public void exceptionHandling(SomeObject obj) {
        try {  
            someMethod(obj);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

Saya berbicara tentang kode duplikat di atas, tetapi saya ulangi sekali lagi: Jika kita memiliki beberapa metode dengan kode berulang, kita perlu memindahkannya ke metode terpisah. Ini akan membuat metode dan kelas lebih kompak. Jangan lupa tentang aturan yang mengatur nama: detail tentang cara memberi nama kelas, antarmuka, metode, dan variabel dengan benar akan dibahas di bagian selanjutnya dari artikel ini. Tapi hanya itu yang saya miliki untuk Anda hari ini.
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION