Kode yang mulai rumit sering menyulitkan proses perawatan (maintenance). Jika kita sering menggunakan R, biasanya kita menulis banyak fungsi. Seiring bertambahnya kebutuhan, fungsi-fungsi tersebut bisa saling bergantung dan data yang digunakan harus dikirim berulang kali sebagai argumen.
Salah satu cara untuk merapikan pola seperti ini adalah menggunakan sistem kelas R6. R6 memungkinkan kita mengelompokkan data dan fungsi yang mengolah data tersebut dalam satu objek. Tutorial ini mengenalkan konsep dasarnya melalui contoh sederhana.
1. Memasang dan memuat paket
Contents
Jika paket R6 belum tersedia, pasang terlebih dahulu:
install.packages("R6")
Kemudian muat paketnya:
library(R6)
2. Membuat kelas pertama
Misalkan kita ingin membuat objek untuk menyimpan informasi seseorang. Objek ini memiliki data nama dan umur, serta metode untuk menyapa.
Orang <- R6Class(
"Orang",
public = list(
nama = NULL,
umur = NULL,
initialize = function(nama, umur) {
self$nama <- nama
self$umur <- umur
},
sapa = function() {
paste("Halo, nama saya", self$nama)
}
)
)
R6Class() mendefinisikan cetak biru kelas. Bagian public berisi anggota yang dapat diakses dari luar objek, termasuk data (sering disebut field) dan fungsi milik objek (disebut method). initialize() dijalankan saat objek dibuat. Kata self merujuk pada objek yang sedang digunakan.
3. Membuat dan menggunakan objek
Kelas adalah cetak birunya; untuk menggunakannya kita membuat sebuah objek atau instance:
andi <- Orang$new("Andi", 25)
andi$nama
#> [1] "Andi"
andi$sapa()
#> [1] "Halo, nama saya Andi"
Gunakan $new() untuk membuat objek. Setelah itu, akses field atau jalankan method melalui $, misalnya andi$nama atau andi$sapa().
4. Menambahkan perilaku yang mengubah data
Method juga dapat memperbarui field milik objek. Berikut contoh sederhana untuk menaikkan umur:
Orang <- R6Class(
"Orang",
public = list(
nama = NULL,
umur = NULL,
initialize = function(nama, umur) {
self$nama <- nama
self$umur <- umur
},
tambah_umur = function(tahun = 1) {
self$umur <- self$umur + tahun
invisible(self)
}
)
)
budi <- Orang$new("Budi", 30)
budi$tambah_umur(2)
budi$umur
#> [1] 32
invisible(self) membuat method mengembalikan objek tanpa mencetaknya. Ini berguna bila kita ingin memanggil beberapa method secara berantai, meskipun untuk contoh awal hal ini tidak wajib.
5. Membatasi akses dengan private
Tidak semua data perlu bisa diubah langsung dari luar objek. R6 menyediakan bagian private untuk anggota yang hanya digunakan di dalam objek.
Rekening <- R6Class(
"Rekening",
public = list(
initialize = function(saldo_awal = 0) {
private$.saldo <- saldo_awal
},
lihat_saldo = function() {
private$.saldo
},
setor = function(jumlah) {
if (jumlah <= 0) {
stop("Jumlah setoran harus lebih besar dari nol.")
}
private$.saldo <- private$.saldo + jumlah
invisible(self)
}
),
private = list(
.saldo = NULL
)
)
rekening <- Rekening$new(100)
rekening$setor(50)
rekening$lihat_saldo()
#> [1] 150
Saldo disimpan sebagai private$.saldo, sehingga pengguna objek tidak mengubahnya secara langsung. Perubahan dilakukan lewat method setor(), tempat kita juga dapat memeriksa bahwa nilai setoran masuk akal. Awalan titik pada .saldo adalah konvensi penamaan, bukan syarat khusus R6.
Kapan R6 berguna?
R6 cocok ketika data dan operasi atas data tersebut perlu disimpan bersama, terutama jika beberapa fungsi harus berbagi dan memperbarui keadaan objek. Contohnya bisa berupa sesi koneksi, keranjang belanja, atau proses yang menyimpan status selama beberapa langkah.
Untuk transformasi data yang sederhana dan tidak perlu menyimpan keadaan, fungsi biasa sering lebih ringkas dan mudah dipahami. R6 juga memakai semantik reference: ketika sebuah objek diberikan ke variabel lain, keduanya dapat menunjuk pada objek yang sama. Karena itu, perubahan melalui satu variabel dapat terlihat melalui variabel lainnya.
objek_a <- Orang$new("Citra", 20)
objek_b <- objek_a
objek_b$umur <- 21
objek_a$umur
#> [1] 21
Perilaku ini berbeda dari banyak objek R biasa yang menggunakan semantik copy-on-modify. Memahami perbedaan tersebut membantu menghindari perubahan data yang tidak disengaja.
Penutup
Dengan R6, kita dapat mengelompokkan data, inisialisasi, dan perilaku objek dalam satu tempat. Mulailah dari kelas kecil, tentukan data mana yang perlu dibuka ke pengguna, dan gunakan private ketika perubahan sebaiknya melalui method yang memiliki validasi. R6 bukan pengganti semua fungsi; ia adalah pilihan ketika objek dengan keadaan yang terus berubah membuat kumpulan fungsi mulai sulit dirawat.