Chapter sebelumnya menjelaskan mengapa kita membutuhkan OOP; sekarang kita menuliskan class-nya secara utuh. Semua class MATLAB dibangun dari satu struktur berkas yang sama — classdef untuk membuka definisi, properties untuk data, dan methods untuk perilaku — sehingga begitu satu class dikuasai, class lain hanya variasi isi. Pada chapter ini kita merakit class RekeningBank dari nol, menginstansiasi object-nya, dan membandingkannya dengan struct yang selama ini kita pakai. Cakupan materi:

  • struktur berkas classdef dan aturan penamaannya
  • instansiasi object, akses property, dan pemanggilan method
  • dua gaya pemanggilan method: dot notation dan function style
  • perbandingan class vs struct

Anatomi classdef #

Aturannya: satu class hidup dalam satu berkas .m yang namanya harus sama dengan nama class — class RekeningBank ditulis di RekeningBank.m. Nama class memakai PascalCase (RekeningBank, KalkulatorStatistik), berbeda dengan fungsi yang kita biasa tulis camelCase. Mekanisme di balik layar: saat MATLAB menemui ekspresi RekeningBank(...), MATLAB mencari berkas RekeningBank.m di path, memuat definisi class-nya sekali, lalu menjalankan constructor. Kerangka minimalnya:

matlab
classdef NamaClass
    %NAMACLASS Deskripsi singkat class.

    properties
        % deklarasi property + nilai default
    end

    methods
        % constructor + method lain
    end
end

Mari kita bedah baris-baris kode di atas:

  1. Baris 1 (classdef NamaClass): membuka definisi class; berkas harus bernama NamaClass.m — ketidakcocokan nama membuat class tidak bisa diinstansiasi.
  2. Baris 4-6 (properties ... end): wadah data; setiap property boleh diberi nilai default yang dipakai saat object baru dibuat.
  3. Baris 8-10 (methods ... end): wadah perilaku; seluruh function di dalam blok ini otomatis menjadi method milik class.
  4. Baris 11 (end): penutup classdef — definisi class berakhir di berkas ini juga.

Blok properties dan methods juga menerima atribut dalam kurung — misalnya properties (Constant = true) atau methods (Static = true) — yang mengubah perilakunya; kita bahas satu per satu pada chapter Properties dan Methods. Atribut level class seperti Sealed (melarang subclass) serta superclass < handle diperkenalkan pada chapter-chapter selanjutnya.

Instansiasi: Membuat Object #

Instansiasi dilakukan dengan memanggil nama class seperti memanggil function: r = RekeningBank('Budi', 1000000). Tiga hal terjadi di balik layar: MATLAB mengalokasikan object baru, mengisi property dengan nilai default, lalu menjalankan constructor untuk menimpa sebagian nilai tersebut. Object yang dihasilkan bisa diperiksa identitasnya:

matlab
>> r = RekeningBank('Budi', 1000000);
>> class(r)
>> whos
text
ans =

    'RekeningBank'


  Name      Size            Bytes  Class           Attributes

  r         1x1               112  RekeningBank

Kolom Class pada whos kini memuat RekeningBank — bukan double atau struct — sehingga type data buatan kita tampil setara dengan type bawaan MATLAB. Tanpa titik koma, object menampilkan ringkasan property-nya:

matlab
>> r = RekeningBank('Budi', 1000000)
text
r =

  RekeningBank with properties:

    pemilik: 'Budi'
    saldo: 1000000

Dua Gaya Memanggil Method #

Aturannya: setiap method menerima object sebagai argumen pertama, dan MATLAB menyediakan dua gaya pemanggilan yang setara — r.hitungBunga(0.03) (dot notation) dan hitungBunga(r, 0.03) (function style). Mekanismenya: dot notation berarti “mintalah object r menjalankan method hitungBunga”, sedangkan function style membuat MATLAB melihat class dari argumen pertama lalu mengirimkan r sebagai argumen pertama method tersebut. Keduanya menulis argumen sisanya dengan urutan yang sama. Gaya pertama lebih umum karena membaca alami dari kiri ke kanan; gaya kedua berguna ketika method di-function_handle-kan atau dipass sebagai argumen. Keduanya diuji pada uji_RekeningBank.m di bawah.

Class vs Struct: Kapan Class Menang #

Struct dan class sama-sama menyimpan data berlabel, jadi kapan class lebih baik? Perbedaannya ada pada penjagaan aturan: field struct terbuka untuk siapa pun, sedangkan property class melewati constructor dan method yang bisa memvalidasi. Tabel berikut merangkum pertimbangannya:

Aspekstructclass (classdef)
Validasi nilaitidak ada — field menerima apa sajadi constructor dan method
Perilaku menyatu dengan datatidak — fungsi terpisahya — method dalam classdef
Nilai default terkontrolmanual tiap kali membuatotomatis tiap instansiasi
Cocok untukdata sekali pakai, konfigurasientitas dengan aturan dan state

Perbandingan langsungnya tampak pada uji_RekeningBank.m: constructor RekeningBank menolak saldo awal negatif dengan pesan galat, sementara struct menerima assignment s.saldo = 'delapan juta' tanpa protes — field berubah tipe sekaligus kehilangan makna, dan class-nya pun bukan object (isobject(s) mengembalikan 0).

Contoh Lengkap: RekeningBank #

Class berikut menyimpan pemilik dan saldo, lalu menegakkan aturan “setoran harus positif” serta “penarikan tidak boleh melebihi saldo”:

matlab
classdef RekeningBank
    %REKENINGBANK Rekening tabungan sederhana (value class).
    properties
        pemilik = 'Tanpa Nama'
        saldo = 0
    end
    methods
        function obj = RekeningBank(pemilik, saldoAwal)
            if nargin < 2
                saldoAwal = 0;
            end
            if nargin < 1
                pemilik = 'Tanpa Nama';
            end
            if ~ischar(pemilik) || isempty(strtrim(pemilik))
                error('RekeningBank:pemilikTidakValid', ...
                    'pemilik harus teks non-kosong.');
            end
            if ~isscalar(saldoAwal) || ~isnumeric(saldoAwal) || saldoAwal < 0
                error('RekeningBank:saldoTidakValid', ...
                    'saldo awal harus skalar >= 0.');
            end
            obj.pemilik = pemilik;
            obj.saldo = saldoAwal;
        end
        function obj = setor(obj, jumlah)
            if ~isscalar(jumlah) || ~isnumeric(jumlah) || jumlah <= 0
                error('RekeningBank:jumlahTidakValid', ...
                    'jumlah setor harus skalar positif.');
            end
            obj.saldo = obj.saldo + jumlah;
        end
        function obj = tarik(obj, jumlah)
            if ~isscalar(jumlah) || ~isnumeric(jumlah) || jumlah <= 0
                error('RekeningBank:jumlahTidakValid', ...
                    'jumlah tarik harus skalar positif.');
            end
            if jumlah > obj.saldo
                error('RekeningBank:saldoKurang', ...
                    'saldo %g tidak cukup untuk tarik %g.', ...
                    obj.saldo, jumlah);
            end
            obj.saldo = obj.saldo - jumlah;
        end
        function b = hitungBunga(obj, tingkatTahunan)
            b = obj.saldo * tingkatTahunan;
        end
        function tampilkanInfo(obj)
            fprintf('%s | saldo Rp%.2f\n', obj.pemilik, obj.saldo);
        end
    end
end

Mari kita bedah baris-baris kode di atas:

  1. Baris 8-25 (function obj = RekeningBank(pemilik, saldoAwal)): constructor — argumen keduanya opsional (nargin), lalu dua pemeriksaan menolak pemilik kosong dan saldo negatif sebelum property diisi.
  2. Baris 26-32 (setor): method pengubah — validasi jumlah lebih dulu, kemudian obj.saldo ditambah. Perhatikan variabel outputnya obj: class ini value class, sehingga hasil perubahan harus di-return dan pemanggil menulis r = r.setor(...).
  3. Baris 33-44 (tarik): selain validasi jumlah, baris if jumlah > obj.saldo menjaga invariant saldo tak negatif — aturan yang pada versi prosedural Chapter Konsep OOP berhasil dilanggar.
  4. Baris 45-50 (hitungBunga, tampilkanInfo): dua method pembaca — yang pertama mengembalikan nilai (b), yang kedua langsung menampilkan ke Command Window.

Skrip uji uji_RekeningBank.m menjalankan seluruh alur tersebut:

matlab
% uji_RekeningBank.m - menguji class RekeningBank
r = RekeningBank('Budi', 1000000);
r.tampilkanInfo();

r = r.setor(250000);      % value class: objek baru di-bind ulang ke r
r.tampilkanInfo();

r = r.tarik(300000);
r.tampilkanInfo();

fprintf('bunga 3%% per tahun : Rp%.2f\n', hitungBunga(r, 0.03));  % function style
fprintf('class r            : %s\n', class(r));

d = RekeningBank();       % tanpa input -> nilai default
d.tampilkanInfo();

try
    RekeningBank('Siti', -100);
catch err
    fprintf('ditolak : %s\n', err.message);
end

% --- perbandingan dengan struct: aturan tidak dijaga ---
s = struct('pemilik', 'Siti', 'saldo', 1000000);
s.saldo = 'delapan juta';          % struct menerima apa saja
fprintf('struct s.saldo    : %s\n', s.saldo);
fprintf('class s           : %s\n', class(s));
fprintf('s adalah object   : %d\n', isobject(s));

Output yang Dihasilkan:

text
Budi | saldo Rp1000000.00
Budi | saldo Rp1250000.00
Budi | saldo Rp950000.00
bunga 3% per tahun : Rp28500.00
class r            : RekeningBank
Tanpa Nama | saldo Rp0.00
ditolak : saldo awal harus skalar >= 0.
struct s.saldo    : delapan juta
class s           : struct
s adalah object   : 0

Ringkasan #

Class ditulis dalam berkas bernama sama dengan class-nya: classdef ... end membungkus blok properties (data beserta nilai default) dan methods (perilaku). Instansiasi r = RekeningBank(...) menjalankan constructor dan menghasilkan object yang teridentifikasi lewat class(r) serta kolom Class pada whos. Method dipanggil dengan dot notation obj.metode(x) atau function style metode(obj, x) — keduanya setara. Dibanding struct, class menang ketika data perlu dijaga aturan: constructor dan method kita menjadi satu-satunya pintu perubahan. Berkas RekeningBank.m dan uji_RekeningBank.m di bagian bawah halaman memuat seluruh kode yang sudah teruji.