Foto oleh Pixabay via Pexels

Object-Oriented Programming (OOP) adalah paradigma pemrograman yang mengorganisasikan kode ke dalam unit-unit bernama object, masing-masing menggabungkan data (property) dan perilaku (method) yang saling terkait dalam satu kesatuan yang disebut class.

Class berfungsi sebagai cetak biru (blueprint) untuk membuat object, mendefinisikan properti apa saja yang dimiliki dan apa saja yang bisa dilakukan object tersebut. Satu class bisa dipakai untuk membuat banyak object dengan nilai properti yang berbeda-beda, namun berbagi struktur dan perilaku yang sama.

Artikel ini membahas cara mendefinisikan class, membuat object darinya, serta konsep access modifier yang mengatur sejauh mana bagian dalam class bisa diakses dari luar, sebagai fondasi sebelum mempelajari pilar OOP lain seperti inheritance dan polymorphism.

Mendefinisikan Class dan Property

Class Mahasiswa dengan tiga property

public class Mahasiswa { public string Nama { get; set; } public int Nim { get; set; } public double Ipk { get; set; } }
Property pada class mendefinisikan data apa saja yang dimiliki setiap object yang dibuat darinya. Sintaks get dan set yang disebut auto-property ini secara otomatis menghasilkan mekanisme untuk membaca dan mengubah nilai property tersebut tanpa perlu menulis field privat dan method getter-setter secara manual satu per satu.

Membuat Object dari Class

Dua object independen dibuat dari class yang sama

Mahasiswa mhs1 = new Mahasiswa { Nama = "Siti", Nim = 1001, Ipk = 3.8 }; Mahasiswa mhs2 = new Mahasiswa { Nama = "Andi", Nim = 1002, Ipk = 3.5 }; Console.WriteLine($"{mhs1.Nama}: {mhs1.Ipk}"); -- Expected output: Siti: 3.8
Setiap pemanggilan kata kunci new akan membuat object baru yang independen di memori, meski keduanya berasal dari class yang sama. Mengubah nilai property pada mhs1 tidak akan memengaruhi mhs2 sama sekali, karena keduanya adalah instance yang benar-benar terpisah satu sama lain.

Method untuk Mendefinisikan Perilaku Object

Method mendefinisikan perilaku yang bisa dipanggil object

public class Mahasiswa { public string Nama { get; set; } public double Ipk { get; set; } public string CekStatusKelulusan() { return Ipk >= 3.0 ? "Cumlaude" : "Lulus"; } } Console.WriteLine(mhs1.CekStatusKelulusan()); -- Expected: Cumlaude
Selain menyimpan data lewat property, class juga bisa mendefinisikan method yang merepresentasikan perilaku atau logika yang berkaitan dengan data tersebut. Method ini bisa mengakses property lain milik object yang sama secara langsung tanpa perlu dikirim sebagai parameter, karena keduanya berada dalam konteks object yang sama.

Access Modifier public dan private

Private menyembunyikan detail internal dari akses luar

public class RekeningBank { private double saldo; public void Setor(double jumlah) { if (jumlah > 0) saldo += jumlah; } public double LihatSaldo() => saldo; } -- rekening.saldo tidak bisa diakses langsung dari luar class, -- hanya lewat method Setor() dan LihatSaldo()
Access modifier private membatasi akses sebuah field atau method hanya dari dalam class itu sendiri, menyembunyikan detail implementasi internal dari kode luar dan memaksa interaksi hanya lewat method public yang sudah disediakan. Pola ini, yang disebut encapsulation, mencegah data diubah sembarangan tanpa melalui validasi yang semestinya.

Access Modifier pada Class: Public, Private, Protected, dan Internal

Access modifier menentukan batas visibilitas anggota class

public class Karyawan { public string Nama; private decimal gajiPokok; protected string Departemen; internal string KodeKaryawan; public Karyawan(string nama, decimal gaji) { Nama = nama; gajiPokok = gaji; } public decimal LihatGaji() => gajiPokok; } var k = new Karyawan("Budi", 5000000); Console.WriteLine(k.LihatGaji()); // Output: 5000000 // k.gajiPokok tidak bisa diakses dari luar class -> compile error

Access modifier adalah kata kunci yang menentukan sejauh mana suatu member class (field, property, method) dapat diakses dari luar class tersebut. Public berarti dapat diakses dari mana saja tanpa batasan, sedangkan private, yang juga merupakan default jika tidak dituliskan modifier apapun, hanya dapat diakses dari dalam class yang sama, seperti field gajiPokok pada contoh di atas yang hanya bisa dibaca melalui method LihatGaji().

Protected memperbolehkan akses dari dalam class itu sendiri maupun dari class turunannya (inheritance), sehingga cocok digunakan untuk data yang perlu dibagikan ke subclass tetapi tetap disembunyikan dari kode eksternal. Sementara itu, internal membatasi akses hanya pada lingkup assembly yang sama, artinya kode pada project atau library lain yang mereferensikan assembly tersebut tidak bisa mengakses member internal meskipun class-nya bersifat public.

Penerapan access modifier yang tepat merupakan bagian inti dari prinsip encapsulation dalam OOP, karena tujuannya adalah membatasi akses langsung ke data internal sebuah object dan memaksa interaksi dilakukan melalui method atau property yang telah disediakan, sehingga validasi dan logika bisnis tetap terjaga konsistensinya di satu tempat.

Static Class dan Static Member untuk Logika yang Tidak Bergantung pada Instance

Static class hanya berisi static member dan tidak bisa diinstansiasi

static class KalkulatorPajak { public const decimal TarifPpn = 0.11m; public static decimal HitungPpn(decimal subtotal) => subtotal * TarifPpn; } decimal subtotal = 200000; decimal ppn = KalkulatorPajak.HitungPpn(subtotal); Console.WriteLine(ppn); // Output: 22000

Static class adalah class yang ditandai kata kunci static dan hanya boleh berisi member yang juga bersifat static, sehingga class tersebut tidak dapat diinstansiasi menggunakan kata kunci new sama sekali. Class seperti KalkulatorPajak pada contoh di atas cocok dipakai untuk mengelompokkan logika yang sifatnya stateless, artinya tidak bergantung pada data yang berbeda-beda antar instance, melainkan selalu menghasilkan output yang sama untuk input yang sama seperti perhitungan PPN berdasarkan tarif tetap.

Static member, baik field, property, maupun method, dimiliki bersama oleh seluruh instance dari suatu class, bukan dimiliki secara terpisah oleh masing-masing objek. Artinya, jika sebuah class biasa (bukan static) memiliki static field, maka perubahan nilai field tersebut melalui satu instance akan terlihat juga ketika diakses melalui instance lain dari class yang sama, karena static field disimpan satu kali saja di memori selama aplikasi berjalan, bukan per objek yang dibuat.

Class utility seperti Math dan Console pada .NET Base Class Library merupakan contoh nyata static class yang dipakai sehari-hari, karena method seperti Math.Sqrt atau Console.WriteLine tidak memerlukan state apapun dari suatu object tertentu untuk dapat berfungsi. Kesalahan umum pemula adalah memaksakan static class untuk menyimpan data yang semestinya berbeda-beda per transaksi atau per pengguna, yang justru dapat menimbulkan bug serius pada aplikasi yang diakses banyak pengguna secara bersamaan. Konstanta yang dideklarasikan dengan const, seperti TarifPpn di atas, juga bersifat implisit static dan nilainya ditentukan pada saat kompilasi sehingga tidak bisa diubah lagi sepanjang aplikasi berjalan, berbeda dengan static readonly yang nilainya masih bisa ditentukan sekali saat runtime melalui static constructor.

Object Initializer dan Collection Initializer untuk Inisialisasi Ringkas

public class Produk { public string Nama { get; set; } = ""; public decimal Harga { get; set; } } var produk = new Produk { Nama = "Keyboard", Harga = 150000 }; var daftarProduk = new List<Produk> { new Produk { Nama = "Mouse", Harga = 75000 }, new Produk { Nama = "Monitor", Harga = 1200000 } }; Console.WriteLine($"{produk.Nama}: {produk.Harga}"); Console.WriteLine(daftarProduk.Count); // Output: // Keyboard: 150000 // 2

Object initializer memungkinkan pengisian property sebuah object langsung di dalam kurung kurawal setelah pemanggilan constructor, tanpa harus membuat constructor khusus yang menerima semua parameter satu per satu seperti pada pendekatan lama. Cara ini membuat kode lebih ringkas terutama untuk class dengan banyak property opsional yang tidak selalu perlu diisi semuanya pada setiap pemakaian, serta membuat urutan pengisian nilai lebih fleksibel karena setiap property dipanggil dengan namanya langsung, bukan berdasarkan posisi parameter.

Collection initializer menerapkan konsep serupa untuk koleksi seperti List atau Dictionary, mengizinkan beberapa elemen dimasukkan sekaligus saat deklarasi tanpa memanggil method Add() berulang kali baris demi baris. Gabungan keduanya sering dipakai bersama saat membuat data uji, data konfigurasi awal, atau data seed yang strukturnya sudah diketahui sejak awal penulisan kode, sehingga seluruh data awal bisa dilihat dalam satu blok kode yang ringkas dan mudah dipahami tanpa harus menelusuri banyak baris terpisah.

Kesalahan Umum yang Sering Terjadi

Membuat Semua Property dan Field Bersifat Public

Menjadikan seluruh data pada class bisa diakses dan diubah bebas dari luar menghilangkan manfaat encapsulation, membuka risiko data berada dalam kondisi yang tidak valid karena diubah tanpa melalui validasi yang semestinya.

Bingung Antara Class dan Object

Class adalah cetakan atau definisi, sementara object adalah instance nyata yang dibuat dari cetakan tersebut; menyamakan keduanya bisa membuat developer pemula bingung saat harus membuat banyak object dari satu class yang sama.

Menaruh Terlalu Banyak Tanggung Jawab dalam Satu Class

Class yang menangani terlalu banyak hal sekaligus (melanggar Single Responsibility Principle) menjadi sulit dipahami, diuji, dan dipelihara seiring bertambahnya fitur yang harus ditambahkan ke dalamnya.

Kapan Pendekatan Ini Tidak Cocok Dipakai

Auto-property dengan get-set publik memberikan kemudahan dan kecepatan penulisan kode, cocok untuk data sederhana seperti DTO (Data Transfer Object) yang memang tidak membutuhkan validasi khusus.

Property dengan backing field privat dan logika validasi di dalam setter memberikan kontrol lebih ketat terhadap integritas data, namun membutuhkan lebih banyak kode untuk diimplementasikan, sehingga pilihan antara keduanya bergantung pada seberapa kritikal validasi data tersebut bagi aplikasi.

Ringkasan

Class dan object adalah fondasi OOP di C#, dengan class berperan sebagai cetakan dan object sebagai instance nyata yang dibuat darinya. Gunakan access modifier dengan bijak untuk menerapkan encapsulation, menyembunyikan detail implementasi yang tidak perlu diketahui kode luar, sebagai dasar sebelum mempelajari inheritance dan polymorphism pada artikel berikutnya.