2.6 Pemrograman Berorientasi Objek (OOP) dalam Java
Dalam dunia pengembangan perangkat lunak, salah satu tantangan terbesar adalah menjaga agar sebuah aplikasi tetap terstruktur dengan baik seiring dengan bertambahnya fitur, kompleksitas logika, dan jumlah developer yang terlibat. Tanpa struktur yang tepat, aplikasi akan sulit dipelihara, rawan bug, dan berisiko tinggi mengalami kerusakan saat terjadi perubahan kecil sekalipun.
Di sinilah Object-Oriented Programming (OOP) berperan penting. OOP adalah paradigma pemrograman yang menyusun kode program berdasarkan objek, yaitu representasi dari entitas dunia nyata yang memiliki state (data/properti) dan behavior (fungsi/metode). Dengan menggunakan OOP, pengembang dapat menulis kode yang lebih modular, dapat digunakan ulang, dan mudah diuji.
Sebagai seorang QA Engineer, memahami dasar-dasar OOP sangatlah penting, bukan untuk menulis kode seperti developer, tetapi untuk memahami bagaimana aplikasi dibangun dan diorganisir secara internal. Pengetahuan ini akan membantu kita:
● Mengidentifikasi titik-titik kritis yang rawan bug,
● Menyusun test case berdasarkan class dan method yang saling bergantung,
● Membedakan mana bagian yang perlu diuji secara isolated (unit test) dan mana yang perlu diuji dalam integrasi,
● Memahami akar penyebab dari error atau defect berdasarkan struktur OOP yang tidak tepat,
● Berkomunikasi lebih efektif dengan developer saat mendiskusikan bugs, regression, atau refactor.
Contoh praktis: Ketika sebuah aplikasi web memiliki class PaymentProcessor yang menangani semua metode pembayaran (kartu kredit, e-wallet, virtual account), dan QA tahu bahwa class ini bergantung pada UserAccount, maka QA bisa fokus menyiapkan skenario pengujian saat UserAccount belum diverifikasi atau belum memiliki saldo. Tanpa memahami struktur OOP, test seperti ini mungkin akan terlewatkan.
Sebelum memulai pembelajaran pada topik ini, mari luangkan waktu sejenak untuk merefleksikan poin-poin berikut:
Sebagai QA, kita sering dihadapkan pada bug yang muncul di tempat yang tidak terduga. Misalnya, kamu sedang mengetes fitur checkout, tapi error justru muncul karena perubahan di bagian profil pengguna.
Apa yang bisa menyebabkan efek berantai seperti itu? Bagaimana jika kita bisa memahami struktur 'class' dan hubungan antar 'objek' dalam aplikasi, apakah itu akan membantu kita menemukan sumber masalah lebih cepat?
Digiers, sebelum memulai pembelajaran, mari kita pahami terlebih dahulu capaian yang ingin diraih dalam topik ini. Berikut adalah tujuan pembelajaran yang menjadi panduan dalam proses belajar:
● Menjelaskan konsep OOP seperti Inheritance, Polymorphism, Encapsulation, dan Abstraction.
● Menganalisis penerapan OOP dalam struktur kerangka kerja automated testing.
● Menerapkan prinsip OOP untuk mengembangkan kerangka kerja testing yang efisien.
2.6.1 Konsep OOP: Inheritance, Polymorphism, Encapsulation, dan Abstraction
Object-Oriented Programming (OOP) adalah pendekatan pemrograman yang memodelkan program sebagai kumpulan objek yang saling berinteraksi. Setiap objek mewakili entitas nyata atau abstrak, dengan data dan perilaku tersendiri. Konsep OOP membantu pengembang membangun program yang lebih modular, dapat digunakan ulang, dan mudah dipelihara.
Gambar 2.6.1 Pilar Utama OOP
Dalam pemrograman berorientasi objek (OOP), class dan object adalah konsep dasar yang sangat penting.
Class adalah sebuah cetak biru atau template yang digunakan untuk membuat objek. Class mendefinisikan atribut (data/properti) dan perilaku (metode/fungsi) yang dimiliki oleh objek-objek yang dibuat dari class tersebut.
Object adalah instance atau contoh nyata dari sebuah class. Setiap object memiliki nilai atributnya sendiri dan bisa menjalankan metode yang sudah didefinisikan dalam class.
Misalnya, jika kita memiliki class "Mobil", maka objeknya bisa berupa mobil-mobil tertentu seperti "Mobil merah dengan plat nomor X123YZ". Class menjelaskan apa yang dimiliki dan bisa dilakukan mobil, sedangkan object adalah mobil spesifik yang dibuat berdasarkan class tersebut.
⬛ Empat pilar utama OOP adalah:
- Encapsulation
Encapsulation adalah cara menyembunyikan data di dalam sebuah class supaya tidak bisa diakses langsung dari luar.
Jadi, data (atau variabel) di dalam class dilindungi, dan kalau mau mengubah atau mengambil data itu, harus lewat method khusus, yaitu getter (untuk mengambil data) dan setter (untuk mengubah data).
Tujuan:
● Melindungi data agar tidak rusak atau dimodifikasi sembarangan.
● Mengontrol perubahan data dengan aturan tertentu.
● Membuat penggunaan objek lebih sederhana: pengguna hanya tahu cara memakainya, tidak perlu tahu detail cara kerjanya di dalam.
Konsep Teknis:
● Atribut (variabel) dibuat private.
● Method getter akan mengambil nilai.
● Method setter akan mengubah nilai, bisa disertai validasi.
● Ini disebut data hiding.
Contoh Kode:
public class Student { |
Dalam contoh di atas, age adalah data sensitif. Kita tidak ingin ada sembarang kode dari luar yang bisa menulis student.age = -100. Dengan encapsulation, kita membatasi akses langsung dan hanya mengizinkan perubahan melalui method resmi setAge() yang memiliki validasi.
- Inheritance
Inheritance memungkinkan sebuah class (subclass) mewarisi properti dan method dari class lain (superclass). Konsep ini menggambarkan hubungan "adalah" (is-a). Misalnya, jika kita punya class Animal, maka Dog adalah Animal.
Tujuan:
● Mengurangi duplikasi kode (code reuse).
● Menciptakan hirarki logis antar class.
● Mempermudah pengelompokan objek berdasarkan sifat umum.
Konsep Teknis:
● Java menggunakan keyword extends untuk inheritance.
● Subclass bisa mengakses method dan atribut dari superclass.
● Subclass bisa menambahkan atau mengubah perilaku (method overriding).
Contoh Kode:
// Superclass |
Objek dari class Car bisa memanggil startEngine() karena method itu diwarisi dari Vehicle.
Car myCar = new Car(); |
- Polymorphism
Polymorphism memungkinkan satu interface digunakan oleh berbagai tipe objek. Artinya, method yang sama bisa memberikan hasil atau perilaku berbeda tergantung objeknya. Kata "polymorphism" sendiri berasal dari bahasa Yunani: poly (banyak) + morph (bentuk).
Tujuan:
● Membuat sistem lebih fleksibel dan scalable.
● Memungkinkan penggunaan generalisasi class (superclass) untuk objek yang lebih spesifik.
● Menyederhanakan penulisan kode dengan satu method untuk banyak objek.
Dua Jenis Polymorphism:
Gambar 2.6.2 Polymorphism in Java
- Compile-time Polymorphism (Method Overloading):
○ Method dengan nama sama tapi parameter berbeda dalam satu class.
○ Ditentukan saat kompilasi.
public class Calculator { |
- Runtime Polymorphism (Method Overriding):
○ Method yang sama ditulis ulang di subclass.
○ Ditentukan saat program berjalan.
class Animal { |
- Abstraction
Abstraction adalah prinsip untuk menyembunyikan detail implementasi suatu sistem dan hanya menampilkan fungsi penting kepada pengguna. Pengguna tidak perlu tahu bagaimana sesuatu bekerja, cukup tahu apa yang bisa dilakukan.
Gambar 2.6.3 Abstraction
Ada dua cara umum melakukan abstraksi:
● Interface Class → Menentukan aturan/perilaku yang wajib diikuti oleh kelas yang menggunakannya.
● Abstract Class → Kelas dasar yang bisa punya metode abstract (belum ada isinya) dan metode biasa. Turunannya bisa memilih untuk mengisi metode abstract tersebut.
Tujuan:
● Menyederhanakan penggunaan sistem kompleks.
● Meningkatkan keamanan dan fleksibilitas kode.
● Memastikan kelas tertentu mengikuti aturan atau kontrak tertentu melalui interface atau abstract class.
Cara Implementasi di Java:
● abstract class → bisa punya method konkret dan abstrak.
● interface → hanya mendefinisikan method tanpa implementasi (sebelum Java 8).
Contoh Kode:
abstract class Shape { |
Pemanggil cukup tahu bahwa semua Shape punya method getArea() — tanpa peduli bagaimana rumusnya dihitung.
Shape s = new Rectangle(5, 10); |
Kesimpulan
Pilar OOP |
Tujuan Utama |
Kelebihan Utama |
Encapsulation |
Menyembunyikan data internal |
Melindungi integritas data, membatasi akses langsung |
Inheritance |
Pewarisan sifat antar class |
Reusability, pengorganisasian class lebih baik |
Polymorphism |
Satu interface, banyak bentuk |
Fleksibel, satu method bisa bekerja di banyak tipe objek |
Abstraction |
Menyederhanakan kompleksitas |
Fokus hanya pada fungsionalitas, bukan detail implementasi |
Tabel 2.6.1 Pilar Utama OOP
Sebagai QA, memahami keempat prinsip ini akan membantumu menganalisis struktur kode dengan lebih baik, mengidentifikasi area risiko, dan menyusun test case yang mencakup berbagai skenario secara efektif. Bahkan dalam automation testing, prinsip OOP sangat relevan karena Page Object Model dan reusable test component dibangun di atas fondasi OOP.
Setelah memahami pilar-pilar utama OOP, sekarang kita akan membahas beberapa konsep penting yang sering digunakan dalam penerapan OOP sehari-hari. Konsep-konsep ini membantu kita mengatur bagaimana data diakses, bagaimana objek dibuat, bagaimana hubungan antar class dibangun, dan bagaimana membagi tanggung jawab di dalam kode.
Pemahaman yang baik tentang bagian ini akan membuat kode lebih aman, rapi, mudah dipelihara, dan fleksibel untuk dikembangkan. Berikut adalah penjelasannya:
1. Access Modifier
Access modifier digunakan untuk mengatur tingkat akses suatu atribut atau method di dalam class.
● private: Hanya bisa diakses di dalam class itu sendiri.
● public: Bisa diakses dari mana pun.
● protected: Bisa diakses oleh class turunan (subclass) dan dalam package yang sama.
● (default/no modifier): Hanya bisa diakses dari class dalam package yang sama.
2. Constructor
Constructor adalah method khusus yang dipanggil saat objek dibuat. Fungsinya untuk menginisialisasi nilai awal atribut pada saat objek diciptakan.
Contoh:
public class User { |
3. Composition vs Inheritance
● Inheritance: pewarisan perilaku dari superclass ke subclass. Misalnya: class MobilListrik mewarisi dari class Mobil.
● Composition: Class baru dibuat dengan “menggabungkan” object dari class lain sebagai bagian di dalamnya, bukan mewarisi. Contohnya: class Mobil punya object Engine di dalamnya, seperti di kode pada gambar.
Contoh Composition:
public class Engine { |
Kenapa penting?
Composition lebih fleksibel dan disarankan dalam beberapa desain karena menghindari keterikatan erat antar class (tight coupling).
4. Interface vs Abstract Class
Aspek |
Abstract Class |
Interface |
Keyword |
abstract |
interface |
Inheritance |
Bisa memiliki implementasi sebagian |
Tidak bisa (sebelum Java 8) |
Multiple inheritance |
Tidak bisa multiple inheritance |
Bisa multiple interface |
Use Case |
Untuk hubungan yang kuat dan berbagi logika dasar |
Untuk kontrak perilaku umum |
Tabel 2.6.2 Interface vs Abstract Class
5. Relevansi Praktis OOP dalam Page Object Model (POM)
Page Object Model adalah desain pattern yang sangat bergantung pada OOP.
● Dalam POM, setiap halaman web dimodelkan sebagai class.
● Elemen dan fungsionalitas halaman menjadi properti dan method class.
● Encapsulation menyembunyikan detail locators dan memberi method publik untuk tindakan (klik, isi form, dll).
● Inheritance dan abstraction memungkinkan kita membuat base class dengan fungsi bersama (misalnya: wait, scroll, getTitle).
⬛ Studi Kasus: Sistem Checkout di Aplikasi E-Commerce
Bayangkan kamu sedang membuat sistem checkout untuk aplikasi e-commerce seperti Tokopedia, Shopee, atau Bukalapak. Dalam sistem tersebut, terdapat berbagai jenis metode pembayaran, seperti transfer bank, e-wallet, dan kartu kredit. Masing-masing memiliki logika pemrosesan yang berbeda. Bagaimana kita bisa merancang sistem ini agar mudah dikembangkan dan dikelola?
1. Encapsulation
public class ShoppingCart { |
Penjelasan:
Pengguna kelas ShoppingCart tidak tahu cara totalPrice dihitung, dan tidak bisa sembarangan mengubah isinya. Mereka cukup menggunakan method addItem() atau getTotalPrice(). Ini meminimalkan risiko bug dan menjaga integritas data.
2. Inheritance
public abstract class PaymentMethod { |
public class CreditCardPayment extends PaymentMethod { |
Penjelasan:
Semua jenis pembayaran mewarisi dari PaymentMethod. Hal ini memungkinkan kita membuat struktur umum, namun tetap bisa di-extend sesuai kebutuhan masing-masing metode.
3. Polymorphism
public class CheckoutService { |
Penjelasan:
CheckoutService tidak peduli apakah user pakai e-wallet, kartu kredit, atau transfer bank, asalkan objek yang diberikan adalah turunan dari PaymentMethod. Inilah kekuatan polymorphism, di mana satu interface dapat bekerja untuk berbagai tipe objek yang berbeda.
4. Abstraction
public abstract class DiscountRule { |
Penjelasan:
DiscountRule mendefinisikan bahwa semua strategi diskon harus punya method applyDiscount(). Implementasinya bisa bermacam-macam: diskon khusus metode pembayaran, diskon seasonal, dll. Dengan abstraction, kita bisa membuat sistem diskon yang fleksibel dan interchangeable.
Ringkasan
Pilar OOP |
Contoh Implementasi |
Manfaat |
Encapsulation |
ShoppingCart menyembunyikan cara perhitungan total |
Mencegah manipulasi langsung dan melindungi data |
Inheritance |
CreditCardPayment mewarisi PaymentMethod |
Reusable, menghindari duplikasi kode |
Polymorphism |
checkout() menerima semua tipe PaymentMethod |
Fleksibel dan mendukung penambahan metode baru |
Abstraction |
DiscountRule sebagai kerangka aturan diskon |
Memudahkan pengembangan fitur tanpa ubah struktur lama |
Tabel 2.6.3 Ringkasan Contoh Implementasi Pilar OOP