Menerapkan Prinsip SOLID Untuk Melakukan Refactor Pada React.js
Tujuan utama dari prinsip-prinsip SOLID adalah sebagai pedoman bagi para developer perangkat lunak yang peduli dengan hasil karya mereka. Orang-orang yang bangga dalam membangun basis kode yang dirancang dengan indah yang bertahan hingga waktu yang lama dan mudah untuk dikembangkan.
SOLID merupakan singkatan dari
Single-Responsibility
Open Closed
Liskov Substitution
Interface Segregation
Dependency Inversion
Pada artikel ini, saya akan membahas bagaimana kelompok kami melakukan refactor pada projek perangkat lunak yang sedang kami kembangkan dengan mengacu pada prinsip-prinsip SOLID.
Single-Responsibility Principle
single-responsibility principle adalah prinsip dimana setiap kelas atau komponen harus memiliki tujuan yang tunggal. Komponen harus melakukan hanya satu kepentingan dan melakukannya dengan baik.
Mari kita refactor bagian kode yang buruk tetapi berfungsi dan membuatnya lebih bersih dan lebih baik dengan menggunakan prinsip ini.
Contoh kode yang buruk dan melanggar prinsip Single-Responsibility:
Saya telah membuat contoh kasus diatas untuk penggambaran yang lebih baik. Ada banyak hal yang terjadi di sini:
1. Pengambilan data API
2. Penyaringan Data
3. Fungsionalitas UI yang kompleks
Jadi mari kita jelajahi bagaimana kita dapat meningkatkan desain kode dan membuatnya sederhana.
Pindahkan Logika Pemrosesan Data Keluar Komponen
Jangan pernah melakukan pemanggilan HTTP di dalam komponen. Ada beberapa strategi yang dapat Anda ikuti untuk menghapus kode-kode ini dari komponen.
Paling tidak yang harus Anda lakukan adalah membuat custom Hooks dan memindahkan logika pengambilan data ke sana. Misalnya, kita dapat membuat Hooks bernama useFetchList yang terlihat seperti ini:
sekarang komponen InfoHibah akan terlihat seperti ini:
Kami bisa menyederhanakan kode kami dari 83 baris menjadi 63 baris! Dan kami menciptakan satu file Hooks baru dengan tanggung jawab yang jelas dan tunggal.
Open Closed Principle
Dikutip dari Bertrand Meyer yang menuliskan tentang ini di bukunya yang berjudul “ Object-Oriented Software Construction” menjelaskan definisi dari Open/Closed principle sebagai ‘Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.’
Prinsip ini memiliki aturan untuk menulis kode sedemikian rupa sehingga Anda dapat menambahkan fungsionalitas tambahan tanpa mengubah kode yang ada.
Mari kita lihat di mana kita dapat menerapkan prinsip ini.
Contoh Kasus
Sebut saja kita perlu menampilkan UI berbentuk Card dari dua jenis Produk yaitu HKI dan Paten, sehingga kita memiliki dua komponen card yaitu HKICard dan PatenCard sebagai berikut:
- HKICard
- PatenCard
letak perbedaan kedua komponen tersebut adalah hanya pada properti jenis_paten dan jenis_hki. Bagaimana jika terdapat jenis Produk lain yaitu Jurnal, maka kita harus membuat komponen baru yaitu JurnalCard tentu saja ini menyalahi aturan Open-Closed principle. Pendekatan seperti ini tentu akan meningkatkan kompleksitas kodingan kita.
Solusinya?
Kita perlu men-desain kodingan kita agar komponen Card dapat menerima data baik itu Produk berjenis HKI maupun Paten. sehingga kita perlu membuat komponen tersebut bernama ProdukCard sebagai berikut:
sehingga pada komponen List masing-masing jenis produk akan menggunakan komponen ProdukCard yang sama seperti berikut:
- ListHKI
- ListPaten
sekarang kita dapat dengan leluasa membuat berbagai jenis Produk sesuai kebutuhan. kita tidak perlu membuat komponen Card baru hanya untuk satu jenis Produk tertentu.
Liskov Substitution Principle
prinsip ini secara sederhana berbunyi:
“Subclasses should be substitutable for their superclasses.”
Itu berarti subclass dari kelas tertentu harus dapat menggantikan superclass tanpa merusak fungsionalitas apa pun.
React.js bukanlah framework berorientasi objek karena pada dasarnya adalah JavaScript. Dalam konteks React, ide utama di balik prinsip ini adalah:
“Components should abide by some kind of contract.”
Pada intinya, ini berarti harus ada semacam kontrak antar komponen. Jadi, setiap kali komponen menggunakan komponen lain, komponen tersebut tidak boleh merusak fungsinya.
Contoh Kasus
kita memiliki komponen Paginator sebagai berikut:
komponen tersebut akan digunakan oleh komponen ListHKI seperti berikut:
// components/ListHKI.tsx...
<Paginator pageCount={Math.ceil(count / 10)} onPageChange={handlePageChange} page={page} />...
sejauh ini tidak akan terjadi masalah apapun karena Props pageCount dan page memiliki tipe data number. bagaimana jika pada Props pageCount dan page kita kirimi data bertipe string? maka akan terjadi runtime error karena data yang dikirimi bukan merupakan number.
Solusi?
solusi sederhana dari masalah ini adalah melakukan definisi kontrak pada tipe data Props yang akan di kirim dari luar komponen Paginator seperti berikut:
sehingga ketika kita mencoba untuk mengirim data bertipe string pada Props pageCount dan page akan menunjukkan compile error seperti berikut:
sehingga mencegah terjadinya runtime error.
Interface Segregation Principle
prinsip in mengatakan bahwa
“Clients should not be forced to depend upon interfaces that they do not use”
Di ReactJS tidak menggunakan interface apa pun, dan bukan pemrograman berorientasi objek. Jadi fokus utama untuk skenario ini adalah:
“Components should not depend on things they don’t need.”
atau dengan kata lain “Kita hanya perlu mengirim informasi yang relevan kepada anak komponen”.
Contoh Kasus
kita memiliki komponen FilterBox yang berfungsi menampilkan kotak Filter pada tampilan sebagai berikut:
dan komponen tersebut digunakan oleh komponen halaman HKI sebagai berikut:
// components/HKI.tsx...
<div data-testid="box-filter">
<FilterBox onSubmit={onSubmitFilter} onClear={onClearFilter} form={form} data={data} />
</div>
...
Jika diperhatikan pada Props data di komponen FilterBox, hanya menggunakan properti isLoading pada objek data sehingga ini menyalahi aturan interface segregation.
Solusi?
solusinya yaitu dengan mengganti Props data menjadi isLoading yang bertipekan boolean, sehingga kodingan tersebut menjadi:
// components/HKI.tsx...
<div data-testid="box-filter">
<FilterBox onSubmit={onSubmitFilter} onClear={onClearFilter} form={form} isLoading={data.isLoading} />
</div>
...
dan pada komponen FilterBox menjadi:
Dependency Inversion Principle
Dalam hal pemrograman berorientasi objek, ide utama di balik prinsip ini adalah untuk selalu memiliki interface kode tingkat tinggi dengan abstraksi daripada detail implementasinya.
secara sederhana pada React.js, prinmsip ini berarti:
“No component or function should care about how a particular thing is done.”
Contoh Kasus
Kita akan melakukan pemanggilan API dari komponen untuk mendapatkan data dari sumber internet. implementasinya seperti berikut:
Kita mendapatkan data list tahun HKI dari function fetchYears.
Tanggung jawab utama komponen HKI adalah merender data List Tahun. Seharusnya tidak peduli tentang bagaimana data tersebut diambil atau dari mana data itu berasal. Komponen ini tahu terlalu banyak — dan itu masalah.
Solusi?
kita dapat memisahkan logic untuk mendapatkan data dari API menggunakan Hook yaitu membuat suatu file yang fungsinya memanggil API dan mengembalikan data hasil pemanggilan API tersebut.
contoh isi file Hook useListYears.tsx:
dan contoh penggunaan Hook diatas adalah sebagai berikut:
Perhatikan tentang solusi ini:
useListYears Hook kini tidak peduli siapa yang memanggilnya dan hanya membutuhkan parameter model dan asItem sebagai input dan mengembalikan data list tahun.
Sekarang komponen lain dapat memanfaatkan Hook yang baru saja kita tulis. Dan komponen FilterComponent tidak lagi bergantung pada detail konkret tentang bagaimana data dikembalikan atau library mana yang kita digunakan.
Sekian artikel kali ini, semoga bermanfaat.
Sumber: