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:

  1. 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 {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int newAge) {
        if (newAge >= 0) {
            age = newAge;
        }
    }
}

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.

  1. 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
public class Vehicle {
    protected String brand = "Generic Brand";

    public void startEngine() {
        System.out.println("Engine started");
    }
}

// Subclass
public class Car extends Vehicle {
    public void playRadio() {
        System.out.println("Playing music...");
    }
}

Objek dari class Car bisa memanggil startEngine() karena method itu diwarisi dari Vehicle.

Car myCar = new Car();
myCar.startEngine();  // Output: Engine started
myCar.playRadio();    // Output: Playing music...

 

  1. 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

  1. Compile-time Polymorphism (Method Overloading):

      Method dengan nama sama tapi parameter berbeda dalam satu class.

      Ditentukan saat kompilasi.

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public double add(double a, double b) {
        return a + b;
    }
}

  1. Runtime Polymorphism (Method Overriding):

      Method yang sama ditulis ulang di subclass.

      Ditentukan saat program berjalan.

class Animal {
    public void sound() {
        System.out.println("Some animal sound");
    }
}

class Dog extends Animal {
    public void sound() {
        System.out.println("Bark");
    }
}

Animal myAnimal = new Dog();
myAnimal.sound(); // Output: Bark

 

  1. 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 ClassMenentukan aturan/perilaku yang wajib diikuti oleh kelas yang menggunakannya.

      Abstract ClassKelas 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 {
    abstract double getArea();
}

class Rectangle extends Shape {
    double width, height;

    Rectangle(double w, double h) {
        this.width = w;
        this.height = h;
    }

    double getArea() {
        return width * height;
    }
}

Pemanggil cukup tahu bahwa semua Shape punya method getArea() — tanpa peduli bagaimana rumusnya dihitung.

Shape s = new Rectangle(5, 10);
System.out.println(s.getArea()); // Output: 50.0

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 {
    private String name;

    public User(String name) {
        this.name = name;
    }
}

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 {
    public void start() {
        System.out.println("Engine started");
    }
}

public class Car {
    private Engine engine = new Engine();

    public void startCar() {
        engine.start();
    }
}

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 {
    private List<Item> items = new ArrayList<>();
    private double totalPrice = 0.0;

    public void addItem(Item item) {
        items.add(item);
        totalPrice += item.getPrice();
    }

    public double getTotalPrice() {
        return totalPrice;
    }
}

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 {
    protected String customerId;

    public PaymentMethod(String customerId) {
        this.customerId = customerId;
    }

    public abstract boolean processPayment(double amount);
}

 

public class CreditCardPayment extends PaymentMethod {
    private String cardNumber;

    public CreditCardPayment(String customerId, String cardNumber) {
        super(customerId);
        this.cardNumber = cardNumber;
    }

    @Override
    public boolean processPayment(double amount) {
        // Proses pembayaran kartu kredit
        System.out.println("Processing credit card...");
        return true;
    }
}

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 {
    public void checkout(PaymentMethod payment, double amount) {
        boolean success = payment.processPayment(amount);
        if (success) {
            System.out.println("Pembayaran berhasil!");
        } else {
            System.out.println("Pembayaran gagal!");
        }
    }
}

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 {
    public abstract double applyDiscount(double total);
}

public class ShopeePayDiscount extends DiscountRule {
    @Override
    public double applyDiscount(double total) {
        if (total >= 500000) {
            return total * 0.9; // diskon 10%
        }
        return total;
    }
}

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

Last modified: Thursday, 4 September 2025, 12:43 AM