7 days of WordPress plugins, themes & templates - for free!* Unlimited asset downloads! Start 7-Day Free Trial
Advertisement
  1. Code
  2. Designing

Cara Menulis Kode Itu Merangkul Perubahan

Read Time: 18 mins

Indonesian (Bahasa Indonesia) translation by Uady (you can also view the original English article)

Menulis kode, yang mudah diubah adalah Holy Grail of programming. Selamat datang di program nirwana! Tetapi hal-hal jauh lebih sulit dalam kenyataan: kode sumber sulit dimengerti, titik dependensi dalam arah yang tak terhitung jumlahnya, kopling menjengkelkan, dan Anda segera merasakan panasnya pemrograman neraka. Dalam tutorial ini, kita akan membahas beberapa prinsip, teknik, dan ide yang akan membantu Anda menulis kode yang mudah diubah.


Beberapa Konsep Berorientasi Objek

Pemrograman berorientasi objek (OOP) menjadi populer, karena janji organisasi kode dan penggunaan kembali; itu benar-benar gagal dalam usaha ini. Kita telah menggunakan konsep OOP selama bertahun-tahun sekarang, namun kita terus berulang menerapkan logika yang sama dalam proyek. OOP memperkenalkan serangkaian prinsip dasar yang baik yang jika digunakan dengan tepat, dapat mengarah pada kode yang lebih baik dan lebih bersih.

Cohesion

Hal-hal yang menjadi milik bersama harus disatukan; jika tidak, mereka harus dipindahkan ke tempat lain. Inilah yang disebut istilah, kohesi,. Contoh terbaik dari kohesi dapat ditunjukkan dengan kelas:

Contoh ini mendefinisikan kelas dengan bidang yang mewakili angka dan ukuran. Properti-properti ini, yang dinilai hanya dengan nama mereka, tidak saling memiliki. Kita kemudian memiliki dua metode, add() dan substract(), yang beroperasi hanya pada dua variabel nomor. Kita lebih lanjut memiliki metode area(), yang beroperasi pada bidang length dan width.

Jelas bahwa kelas ini bertanggung jawab untuk kelompok informasi yang terpisah. Ini memiliki kohesi yang sangat rendah. Mari kita refactor.

Ini adalah kelas yang sangat kohesif. Mengapa? Karena setiap bagian dari kelas ini saling memiliki satu sama lain. Anda harus berusaha untuk kohesi, tetapi berhati-hatilah, itu bisa sulit untuk dicapai.

Orthogonality

Secara sederhana, ortogonalitas mengacu pada isolasi atau penghapusan efek samping. Metode, kelas, atau modul yang mengubah status kelas atau modul yang tidak terkait lainnya tidak ortogonal. Misalnya, kotak hitam pesawat terbang adalah orthogonal. Ini memiliki fungsi dalam, sumber daya dalam, mikrofon dan sensor. Ini tidak berpengaruh pada pesawat yang ditempatinya, atau di dunia luar. Ini hanya menyediakan mekanisme untuk merekam dan mengambil data penerbangan.

Contoh salah satu sistem non-orthogonal adalah elektronik mobil Anda. Meningkatkan kecepatan kendaraan Anda memiliki beberapa efek samping, seperti meningkatkan volume radio (antara lain). Kecepatan tidak ortogonal terhadap mobil.

Dalam contoh ini, Calculator metode kelas add() menunjukkan perilaku tak terduga: ia menciptakan objek AlertMechanism dan memanggil salah satu metodenya. Ini adalah perilaku yang tidak terduga dan tidak diinginkan; konsumen perpustakaan tidak akan pernah mengharapkan pesan dicetak ke layar. Sebaliknya, mereka hanya mengharapkan jumlah dari angka yang diberikan.

Ini lebih baik. AlertMechanism tidak berpengaruh pada Calculator. Sebaliknya, AlertMechanism menggunakan apa pun yang dibutuhkan untuk menentukan apakah peringatan harus dikeluarkan.

Ketergantungan dan Kopling

Dalam banyak kasus, kedua kata ini dapat dipertukarkan; tetapi, dalam beberapa kasus, satu istilah lebih disukai daripada yang lain.

Jadi apa itu ketergantungan? Ketika objek A perlu menggunakan objek B, untuk melakukan perilaku yang ditentukan, kita mengatakan bahwa A bergantung pada B. Dalam OOP, dependensi sangat umum. Objek sering bekerja dengan dan bergantung satu sama lain. Jadi, sementara menghilangkan ketergantungan adalah pengejaran yang luhur, hampir tidak mungkin untuk melakukannya. Mengontrol ketergantungan dan mengurangi mereka, bagaimanapun, lebih baik.

Istilah, kopling berat dan kopling lepas, biasanya merujuk pada seberapa banyak objek bergantung pada objek lain.

Dalam sistem yang digabungkan secara longgar, perubahan dalam satu objek memiliki efek yang berkurang pada objek lain yang bergantung padanya. Dalam sistem seperti itu, kelas tergantung pada antarmuka dan bukannya implementasi konkret (kita akan membicarakannya nanti). Inilah sebabnya mengapa sistem yang digabungkan secara longgar lebih terbuka untuk modifikasi.

Coupling dalam Field

Mari kita pertimbangkan sebuah contoh:

Adalah hal yang umum untuk melihat kode jenis ini. Kelas, Display dalam kasus ini, tergantung pada kelas Calculator dengan langsung mereferensikan kelas itu. Dalam kode di atas, bidang Display $calculator adalah jenis Calculator. Objek yang berisi bidang adalah hasil dari panggilan langsung konstruktor Calculator.

Coupling dengan Mengakses Metode Kelas Lain

Tinjau kode berikut untuk demonstrasi kopling semacam ini:

Kelas Display memanggil metode add() dari objek Calculator. Ini adalah bentuk lain dari kopling, karena satu kelas mengakses metode yang lain.

Coupling oleh Referensi Metode

Anda juga dapat menggabungkan beberapa kelas dengan referensi metode. Sebagai contoh:

Penting untuk dicatat bahwa metode makeCalculator() mengembalikan objek Calculator. Ini adalah ketergantungan.

Coupling oleh Polymorphism

Inheritance mungkin bentuk terkuat dari ketergantungan:

Tidak hanya AdvancedCalculator tidak melakukan tugasnya tanpa Calculator, tetapi bahkan tidak bisa ada tanpa itu.

Mengurangi Coupling oleh Injeksi Ketergantungan

Satu dapat mengurangi kopling dengan menyuntikkan ketergantungan. Berikut salah satu contohnya:

Dengan menginjeksi objek Calculator melalui konstruktor Display, kami mengurangi ketergantungan Display pada kelas Calculator. Tetapi ini hanya setengah dari solusi.

Mengurangi Coupling dengan Interface

Kita selanjutnya dapat mengurangi kopling dengan menggunakan interface. Sebagai contoh:

Anda dapat menganggap ISP sebagai prinsip kohesi tingkat tinggi.

Kode ini memperkenalkan antarmuka CanCompute. Antarmuka adalah abstrak seperti yang Anda bisa dapatkan di OOP; itu mendefinisikan anggota yang harus diimplementasikan oleh kelas. Dalam kasus contoh di atas, Calculator mengimplementasikan antarmuka CanCompute.

Konstruktor Display mengharapkan sebuah objek yang mengimplementasikan CanCompute. Pada titik ini, ketergantungan Display dengan Calculator rusak secara efektif. Setiap saat, kita dapat membuat kelas lain yang mengimplementasikan CanCompute dan meneruskan objek kelas tersebut ke konstruktor Display. Display sekarang hanya bergantung pada antarmuka CanCompute, tetapi bahkan ketergantungan itu adalah opsional. Jika kita tidak melewati argumen ke konstruktor Display, itu hanya akan membuat objek Calculator klasik dengan memanggil makeCalculator(). Teknik ini sering digunakan, dan sangat membantu untuk pengembangan berbasis tes (TDD).


Prinsip-Prinsip Pokok

SOLID adalah seperangkat prinsip untuk menulis kode bersih, yang kemudian membuatnya lebih mudah untuk diubah, dipertahankan dan diperluas di masa depan. Mereka adalah rekomendasi yang, ketika diterapkan ke kode sumber, memiliki efek positif pada pemeliharaan.

Sedikit Sejarah

Prinsip-prinsip PADAT, juga dikenal sebagai prinsip Agile, awalnya didefinisikan oleh Robert C. Martin. Meskipun dia tidak menciptakan semua prinsip ini, dia adalah orang yang menempatkan mereka bersama. Anda dapat membaca lebih lanjut tentang mereka dalam bukunya: Pengembangan Perangkat Lunak Agile, Prinsip, Pola, dan Praktik. Prinsip-prinsip SOLID mencakup beragam topik, tetapi saya akan menyajikannya sesederhana mungkin. Jangan ragu untuk meminta detail tambahan di komentar, jika diperlukan.

Single Responsibility Principle (SRP)

Satu kelas memiliki satu tanggung jawab tunggal. Ini mungkin terdengar sederhana, tetapi terkadang sulit dipahami dan dipraktekkan.

Menurut Anda, siapa yang mendapat manfaat dari perilaku kelas ini? Nah, departemen akuntansi adalah pilihan (untuk keseimbangan), departemen keuangan mungkin lain (untuk laporan pendapatan/pembayaran), dan bahkan departemen pengarsipan bisa mencetak dan mengarsipkan laporan.

Ada empat alasan mengapa Anda mungkin harus mengubah kelas ini; masing-masing departemen mungkin ingin metode masing-masing disesuaikan dengan kebutuhan mereka.

SRP merekomendasikan untuk memecah kelas-kelas tersebut menjadi kelas yang lebih kecil, kelas khusus perilaku, masing-masing hanya memiliki satu alasan untuk berubah. Kelas-kelas seperti itu cenderung sangat kohesif dan digabungkan secara longgar. Dalam arti, SRP adalah kohesi yang didefinisikan dari sudut pandang pengguna.

Open-Closed Principle (OCP)

Kelas (dan modul) harus menyambut perpanjangan fungsionalitas mereka, serta menolak modifikasi fungsi mereka saat ini. Mari kita bermain dengan contoh klasik kipas angin listrik. Anda memiliki tombol dan Anda ingin mengontrol kipas. Jadi, Anda bisa menulis sesuatu di sepanjang baris:

Warisan mungkin merupakan bentuk ketergantungan yang paling kuat.

Kode ini mendefinisikan kelas Switch_ yang membuat dan mengontrol objek Fan. Harap perhatikan garis bawah setelah "Switch_". PHP tidak memungkinkan Anda untuk menentukan kelas dengan nama "Switch."

Bos Anda memutuskan bahwa ia ingin mengendalikan cahaya dengan saklar yang sama. Ini adalah masalah, karena Anda harus mengubah Switch_.

Setiap modifikasi untuk kode yang ada adalah risiko; Bagian lain dari sistem mungkin akan terpengaruh dan memerlukan modifikasi lebih jauh. Hal ini selalu lebih baik untuk meninggalkan fungsi yang ada sendirian, ketika menambahkan baru functionlaity.

Dalam terminologi OOP, Anda dapat melihat bahwa Switch_ memiliki ketergantungan yang kuat pada Fan. Di sinilah letak masalah kita, dan di mana kita harus membuat perubahan.

Solusi ini memperkenalkan antarmuka Switchable. Ini mendefinisikan metode yang semua objek switch-enabled perlu menerapkan. Fan mengimplementasikan Switchable, dan Switch_ menerima referensi ke objek Switchable di dalam konstruktornya.

Bagaimana ini membantu kita?

Pertama, solusi ini memecah ketergantungan antara Switch_ dan Fan. Switch_ tidak tahu bahwa ia memulai fan, juga tidak peduli. Kedua, memperkenalkan kelas Light tidak akan memengaruhi Switch_ atau Switchable. Apakah Anda ingin mengontrol objek Light dengan kelas Switch_ Anda? Cukup buat objek Light dan berikan ke Switch_, seperti ini:

Prinsip Pengganti Liskov (LSP)

LSP menyatakan bahwa kelas anak tidak boleh melanggar fungsi kelas induk. Ini sangat penting karena konsumen dari kelas induk mengharapkan kelas untuk berperilaku dengan cara tertentu. Melewatkan kelas anak ke konsumen hanya harus bekerja dan tidak mempengaruhi fungsi aslinya.

Ini membingungkan pada pandangan pertama, jadi mari kita lihat contoh klasik lain:

Contoh ini mendefinisikan kelas Rectangle sederhana. Kita dapat mengatur tinggi dan lebarnya, dan metode area() menyediakan area persegi panjang. Menggunakan kelas Rectangle bisa terlihat seperti berikut:

Metode rectArea() menerima objek Rectangle sebagai argumen, mengatur tinggi dan lebarnya, dan mengembalikan area bentuk.

Di sekolah, kita diajarkan bahwa persegi adalah persegi panjang. Ini mengisyaratkan bahwa jika kita memodelkan program kita ke objek geometri kita, kelas Square harus memperluas kelas Rectangle. Bagaimana kelas seperti itu?

Saya memiliki waktu yang sulit mencari tahu apa yang harus ditulis di kelas Square. Kita memiliki beberapa opsi. Kita bisa mengganti metode area() dan mengembalikan kuadrat dari $width:

Perhatikan bahwa saya mengubah bidang Rectangle untuk protected, memberikan akses Square ke bidang-bidang tersebut. Ini terlihat masuk akal dari sudut pandang geometri. Persegi memiliki sisi yang sama; mengembalikan kuadrat lebar adalah wajar.

Namun, kita memiliki masalah dari sudut pandang pemrograman. Jika Square adalah Rectangle, kita seharusnya tidak memiliki masalah untuk memasukkannya ke dalam kelas Geometry. Namun, dengan demikian, Anda dapat melihat bahwa kode Geometry tidak masuk akal; ia menetapkan dua nilai berbeda untuk tinggi dan lebar. Inilah sebabnya mengapa persegi bukan persegi panjang dalam pemrograman. LSP dilanggar.

Interface Segregation Principle (ISP)

Tes unit harus berjalan cepat - sangat cepat.

Prinsip ini berkonsentrasi pada memecah antarmuka besar menjadi antarmuka khusus yang kecil. Ide dasarnya adalah bahwa konsumen yang berbeda dari kelas yang sama tidak boleh tahu tentang berbagai antarmuka - hanya antarmuka yang perlu digunakan konsumen. Bahkan jika konsumen tidak secara langsung menggunakan semua metode publik pada suatu objek, itu masih tergantung pada semua metode. Jadi mengapa tidak menyediakan antarmuka dengan yang hanya menyatakan metode yang dibutuhkan setiap pengguna?

Ini sesuai erat bahwa antarmuka harus milik klien dan tidak untuk implementasi. Jika Anda menyesuaikan antarmuka Anda dengan kelas yang mengkonsumsi, mereka akan menghormati ISP. Implementasinya sendiri bisa unik, karena kelas dapat mengimplementasikan beberapa antarmuka.

Mari kita bayangkan bahwa kita menerapkan aplikasi pasar saham. Kita memiliki broker yang membeli dan menjual saham, dan dapat melaporkan pendapatan dan kerugian hariannya. Implementasi yang sangat sederhana akan mencakup sesuatu seperti antarmuka Broker, kelas NYSEBroker yang mengimplementasikan Broker dan beberapa kelas interface pengguna: satu untuk membuat transaksi (TransactionsUI) dan satu untuk pelaporan (DailyReporter). Kode untuk sistem semacam itu bisa serupa dengan yang berikut:

Meskipun kode ini dapat berfungsi, kode ini melanggar ISP. Baik DailyReporter dan TransactionUI bergantung pada antarmuka Broker. Namun, mereka masing-masing hanya menggunakan sebagian kecil dari interface. TransactionUI menggunakan metode buy() dan sell(), sementara DailyReporter menggunakan metode dailyEarnings() dan dailyLoss().

Anda mungkin berpendapat bahwa Broker tidak kohesif karena memiliki metode yang tidak terkait, dan dengan demikian tidak saling memiliki.

Ini mungkin benar, tetapi jawabannya tergantung pada implementasi Broker; jual beli mungkin sangat terkait dengan kerugian dan penghasilan saat ini. Misalnya, Anda mungkin tidak diizinkan membeli saham jika Anda kehilangan uang.

Anda mungkin juga berpendapat bahwa Broker juga melanggar SRP. Karena kita memiliki dua kelas yang menggunakannya dengan cara yang berbeda, mungkin ada dua pengguna yang berbeda. Yah, saya katakan tidak. Satu-satunya pengguna mungkin adalah broker yang sebenarnya. Dia ingin membeli, menjual, dan melihat dana mereka saat ini. Tetapi sekali lagi, jawaban yang sebenarnya tergantung pada keseluruhan sistem dan bisnis.

ISP pasti dilanggar. Kedua kelas UI bergantung pada seluruh Broker. Ini adalah masalah umum, jika Anda berpikir antarmuka termasuk ke dalam implementasinya. Namun, mengalihkan sudut pandang Anda dapat menyarankan desain berikut:

Ini sebenarnya masuk akal dan menghormati ISP. DailyReporter hanya bergantung pada BrokerStatistics; tidak peduli dan tidak harus tahu tentang operasi jual dan beli. TransactionsUI, di sisi lain, hanya tahu tentang membeli dan menjual. NYSEBroker identik dengan kelas kita sebelumnya, kecuali sekarang mengimplementasikan antarmuka BrokerTransactions dan BrokerStatistics.

Anda dapat menganggap ISP sebagai prinsip kohesi tingkat tinggi.

Ketika kedua kelas UI bergantung pada interface Broker, mereka mirip dengan dua kelas, masing-masing memiliki empat bidang, yang dua digunakan dalam metode dan dua lainnya dalam metode lain. Kelas tidak akan sangat kohesif.

Contoh yang lebih kompleks dari prinsip ini dapat ditemukan dalam salah satu makalah pertama Robert C. Martin tentang masalah: Prinsip Pemisahan Interface.

Dependency Inversion Principle (DIP)

Prinsip ini menyatakan bahwa modul tingkat tinggi tidak boleh bergantung pada modul tingkat rendah; keduanya harus bergantung pada abstraksi. Abstraksi tidak boleh bergantung pada detail; detail harus bergantung pada abstraksi. Sederhananya, Anda harus bergantung pada abstraksi sebanyak mungkin dan tidak pernah pada implementasi konkret.

Trik dengan DIP adalah bahwa Anda ingin membalikkan ketergantungan, tetapi selalu ingin menjaga aliran kontrol. Mari kita tinjau contoh kita dari OCP (Switch dan Kelas Light). Dalam implementasi awal, kita memiliki saklar yang langsung mengendalikan light.

Seperti yang Anda lihat, baik ketergantungan dan aliran kontrol dari Switch ke Light. Sementara ini yang kita inginkan, kita tidak ingin langsung bergantung pada Light. Jadi kita memperkenalkan antarmuka.

Sungguh menakjubkan bagaimana hanya memperkenalkan interface membuat kode kita menghargai DIP dan OCP. Seperti yang Anda lihat tidak, kelas tergantung pada implementasi konkret dari Light, dan baik Light and Switch bergantung pada interface Switchable. Kita membalikkan ketergantungan, dan aliran kontrol tidak berubah.


Desain Tingkat Tinggi

Aspek penting lainnya dari kode Anda adalah desain tingkat tinggi dan arsitektur umum Anda. Arsitektur yang terjerat menghasilkan kode yang sulit dimodifikasi. Menjaga arsitektur yang bersih sangat penting, dan langkah pertama adalah memahami cara memisahkan masalah berbeda kode Anda.

Dalam gambar ini, saya mencoba merangkum masalah utama. Di tengah skema adalah logika bisnis kita. Ia harus terisolasi dengan baik dari bagian dunia lainnya, dan dapat bekerja dan berperilaku seperti yang diharapkan tanpa adanya bagian-bagian lain. Melihatnya sebagai ortogonalitas pada tingkat yang lebih tinggi.

Mulai dari kanan, Anda memiliki "utama" Anda - titik masuk ke aplikasi - dan pabrik-pabrik yang membuat objek. Solusi ideal akan mendapatkan benda-benda dari pabrik khusus, tetapi kebanyakan tidak mungkin atau tidak praktis. Namun, Anda harus menggunakan pabrik ketika Anda memiliki kesempatan untuk melakukannya, dan menjaga mereka di luar logika bisnis Anda.

Kemudian, di bagian bawah (dalam warna oranye), kita memiliki ketekunan (basis data, akses file, komunikasi jaringan) untuk tujuan mempertahankan informasi. Tidak ada obyek dalam logika bisnis kita yang harus tahu cara kerja persistensi.

Di sebelah kiri adalah mekanisme pengiriman.

MVC, seperti Laravel atau CakePHP, seharusnya hanya menjadi mekanisme pengiriman, tidak lebih.

Ini memungkinkan Anda menukar satu mekanisme dengan mekanisme lainnya tanpa menyentuh logika bisnis Anda. Ini mungkin terdengar keterlaluan bagi beberapa dari Anda. Kita diberitahu bahwa logika bisnis yang harus ditempatkan dalam model. Yah, saya tidak setuju. Model kita harus "model permintaan", yaitu objek data bodoh yang digunakan untuk meneruskan informasi dari MVC ke logika bisnis. Opsional, saya tidak melihat ada masalah termasuk validasi input dalam model, tetapi tidak lebih. Logika bisnis tidak boleh dalam model.

Ketika Anda melihat arsitektur atau struktur direktori aplikasi Anda, Anda akan melihat struktur yang menunjukkan apa yang dilakukan program dibandingkan dengan kerangka atau basis data yang Anda gunakan.

Terakhir, pastikan bahwa semua titik ketergantungan mengarah ke logika bisnis kita. Interface pengguna, pabrik, basis data adalah implementasi yang sangat konkret, dan Anda tidak boleh bergantung padanya. Membalikkan ketergantungan untuk menunjuk ke arah logika bisnis kita memodulasi sistem kita, memungkinkan kita untuk mengubah dependensi tanpa memodifikasi logika bisnis.


Beberapa Pemikiran Tentang Pola Desain

Pola desain memainkan peran penting dalam membuat kode lebih mudah untuk dimodifikasi, dengan menawarkan solusi desain umum yang dapat dipahami oleh setiap programmer. Dari sudut pandang struktural, pola desain jelas menguntungkan. Mereka diuji dengan baik dan solusi pemikiran.

Jika Anda ingin mempelajari lebih lanjut tentang pola desain, saya membuat kursus Premium+ Tuts pada mereka!


Kekuatan Pengujian

Test-Driven Development mendorong penulisan kode yang mudah diuji. TDD memaksa Anda untuk menghormati sebagian besar prinsip di atas agar kode Anda mudah diuji. Menyuntikkan ketergantungan dan menulis kelas ortogonal sangat penting; jika tidak, Anda berakhir dengan metode uji besar. Tes unit harus berjalan cepat - sangat cepat, sebenarnya, dan semua yang tidak diuji harus diejek. Mengejek banyak kelas kompleks untuk ujian sederhana bisa sangat banyak. Jadi ketika Anda menemukan diri Anda mengejek sepuluh objek untuk menguji satu metode di kelas, Anda mungkin memiliki masalah dengan kode Anda... bukan tes Anda.


Pemikiran Akhir

Pada akhirnya, semuanya bermuara pada seberapa banyak Anda peduli dengan kode sumber Anda. Memiliki pengetahuan teknis tidak cukup; Anda perlu menerapkan pengetahuan itu lagi dan lagi, tidak pernah 100% puas dengan kode Anda. Anda harus membuat kode Anda mudah dipelihara, bersih, dan terbuka untuk diubah.

Terima kasih telah membaca dan jangan ragu untuk berkontribusi teknik Anda di komentar di bawah ini.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Scroll to top
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.