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.
Konsep class di R sangat jarang diajarkan oleh lembaga tutorial karena mereka fokus kepada functional programming saja, padahal kalau olah data yang enteprise udah sangat rumit bahkan permintaan penyajian datanya makin banyak, sehingga kalau buat function terlalu banyak malah merepotkan. Mengapa demikian? karena ada beberapa function itu sifatnya unik (hanya dipakai oleh hal tertentu saja) sehingga saya menyembunyikan nya didalam method private saja. Sebenarnya walaupun bisa disiasati dengan inner function tapi kok terlalu rumit buat merawat kode tersebut.
Library Class di R sangat banyak, namun saya lebih suka menggunakan R6 karena lebih mirip bahasa Java, C#, bahkan lebih canggih daripada python karena python tidak mengenal konsep private lho. Konsep Class tidak cocok bagi kalian yang hanya mengolah dataset yang kecil dan tidak saling terhubung apalagi kalau cuman dataset yang siap pakai. Jangan dibandingkan dengan dataset perusahaan besar yang kadang membutuhkan banyak pre pengolahan.
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.
Contoh lainnya
Berikut salah satu contoh penggunaan attribute private dan public
library(R6)
BankAccount <- R6Class(
classname = "BankAccount",
# Bagian Public (dapat diakses dari luar class)
public = list(
owner = NULL, # Public field
# Constructor (inisialisasi objek)
initialize = function(owner, initial_balance = 0) {
self$owner <- owner
private$balance <- initial_balance
cat("Akun berhasil dibuat untuk:", self$owner, "\n")
},
# Public method untuk menyetor uang
deposit = function(amount) {
if (amount > 0) {
private$balance <- private$balance + amount
private$log_activity("Deposit")
cat("Berhasil setor:", amount, "\n")
} else {
warning("Jumlah deposit harus lebih dari 0!")
}
invisible(self)
},
# Public method untuk menarik uang
withdraw = function(amount) {
if (amount > 0 && amount <= private$balance) {
private$balance <- private$balance - amount
private$log_activity("Tarik Tunai")
cat("Berhasil tarik:", amount, "\n")
} else {
warning("Saldo tidak mencukupi atau jumlah tidak valid!")
}
invisible(self)
},
# Public method untuk melihat saldo (mengakses private field)
get_balance = function() {
return(private$balance)
}
),
# Bagian Private (hanya dapat diakses dari dalam class/metode)
private = list(
balance = 0, # Private field
# Private method (helper internal yang tidak bisa dipanggil dari luar)
log_activity = function(action) {
cat("[LOG]", Sys.time(), "-", action, "dilakukan.\n")
}
)
)
Kita membuat object instance dari class diatas
# 1. Membuat objek baru (instansiasi) my_account <- BankAccount$new(owner = "Budi", initial_balance = 1000) # 2. Mengakses public field print(my_account$owner) # Output: "Budi" # 3. Memanggil public method my_account$deposit(500) my_account$withdraw(200) # 4. Mengecek saldo menggunakan public method my_account$get_balance() # Output: 1300 # 5. Mencoba mengakses private field/method dari luar (Akan menghasilkan error) # my_account$balance # Error: object 'balance' not found # my_account$log_activity() # Error: attempt to apply non-function
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.