Belajar dengan membaca secara berurutan

Polimorfisme — Nama Method Sama, Perilaku Berbeda per Tipe

Pelajari polimorfisme di Python. Override method induk di subclass agar pemanggil tidak perlu memikirkan tipe, dan ganti cabang if type(...) dengan OOP yang bersih — semuanya dengan diagram.

Sebelumnya kita membahas multiple inheritance dan MRO. Untuk menutup seri OOP, artikel ini membahas pilar ketiga — polimorfisme.

Apa itu polimorfisme?

Polimorfisme adalah ide bahwa interface yang sama (nama method) bisa melakukan hal berbeda tergantung tipe. Misalnya kamu ingin satu operasi «hitung gaji», tetapi kamu ingin karyawan, manajer, dan engineer memakai rumus berbeda.

Definisikan calculate_salary di class induk Employee, lalu biarkan subclass Manager dan Engineer meng-override-nya dengan rumus mereka sendiri. Sekarang kode pemanggil cukup menulis employee.calculate_salary() tanpa peduli class apa, dan perhitungan yang tepat akan berjalan.

Tiga class meng-override calculate_salary
Employee(induk)base_salary(tetap)= 300kManager(anak override)base +team x 50k= 1,2MEngineer(anak override)base +skill x 20k= 380k
Induk Employee mendefinisikan calculate_salary. Subclass Manager / Engineer meng-override-nya dengan rumus mereka sendiri. Nama method sama, hasil berbeda per class.

Membangun di atas induk, mengubah rumus di tiap anak

Mari benar-benar bangun contoh gajinya. Employee adalah induk, dengan Manager (tambah berdasarkan ukuran tim) dan Engineer (tambah berdasarkan tingkat skill) sebagai anak. Kuncinya adalah ketiganya mendefinisikan method dengan nama sama calculate_salary.

class Employee:
    def __init__(self, name, base_salary):
        self.name        = name
        self.base_salary = base_salary

    def calculate_salary(self):                  # default = hanya gaji pokok
        return self.base_salary


class Manager(Employee):
    def __init__(self, name, base_salary, team_size):
        super().__init__(name, base_salary)
        self.team_size = team_size

    def calculate_salary(self):                  # bonus berdasarkan ukuran tim
        return self.base_salary + self.team_size * 50000


class Engineer(Employee):
    def __init__(self, name, base_salary, skill_level):
        super().__init__(name, base_salary)
        self.skill_level = skill_level

    def calculate_salary(self):                  # bonus berdasarkan tingkat skill
        return self.base_salary + self.skill_level * 20000

Definisikan Employee / Manager / Engineer dan konfirmasi bahwa setiap calculate_salary mengembalikan hasil berbeda.

① Bangun ketiga class mengikuti contoh di atas (Manager dan Engineer harus memanggil super().__init__(name, base_salary) untuk mendelegasikan init induk).

② Bangun Employee("Budi", 300000), Manager("Siti", 800000, 8), dan Engineer("Andi", 300000, 4), lalu print setiap hasil calculate_salary().

(Begitu berjalan benar, penjelasan akan muncul.)

Python Editor

Jalankan kode untuk melihat output
Bagaimana emp.calculate_salary() berperilaku di dalam loop
emp.calculate_salary()(baris sama)emp = budi(Employee)-> 300kemp = siti(Manager)-> 1,2Memp = andi(Engineer)-> 380kEmployeeManagerEngineer
Body loop hanya emp.calculate_salary() dalam satu baris, tetapi method berbeda dipilih otomatis sesuai class emp — itulah kekuatan polimorfisme.

Membungkus dalam list — Loop yang tidak peduli tipe

Polimorfisme benar-benar menunjukkan kekuatannya saat kamu menumpuk object dari tipe berbeda dalam satu list dan memprosesnya bersama. Bungkus perhitungan gaji di class PayrollSystem dan body loop menyusut jadi satu baris.

class PayrollSystem:
    def __init__(self):
        self.employees = []

    def add(self, employee):
        self.employees.append(employee)

    def total(self):
        result = 0
        for emp in self.employees:                       # tipe boleh berbeda-beda
            result += emp.calculate_salary()              # nama sama tetap jalan
        return result


payroll = PayrollSystem()
payroll.add(Employee("Budi", 300000))
payroll.add(Manager("Siti",  800000, 8))
payroll.add(Engineer("Andi", 300000, 4))

print(payroll.total())   # 1880000
PayrollSystem tidak peduli isinya apa
PayrollSystem
  • employees = [...] (tipe campur)
  • for emp in self.employees: menelusuri
  • Cukup panggil emp.calculate_salary()
Employee (default)
  • mengembalikan base_salary
Manager
  • base + team * 50k
Engineer
  • base + skill * 20k
Meski ada tiga class berbeda tercampur dalam list, PayrollSystem cukup memanggil nama method yang sama dan perhitungan yang tepat berjalan per tipe.

Pakai ulang tiga class dari Latihan 1 untuk membangun PayrollSystem yang menghitung total gaji (console menjaga state, jadi definisi class sebelumnya masih ada).

① Definisikan class PayrollSystem: dengan __init__ yang menginisialisasi self.employees = []. Implementasikan add(self, employee) dan total(self) (total menelusuri loop for sambil menjumlahkan setiap emp.calculate_salary()).

② Bangun payroll = PayrollSystem(), lalu add tiga karyawan: Employee("Budi", 300000) / Manager("Siti", 800000, 8) / Engineer("Andi", 300000, 4).

③ Telusuri payroll.employees mencetak emp.name dan emp.calculate_salary() baris demi baris, lalu cetak payroll.total() dengan prefix "total:".

Python Editor

Jalankan kode untuk melihat output

Bagaimana tampilannya tanpa polimorfisme

Coba lakukan hal yang sama tanpa polimorfisme dan kamu berakhir menulis cabang if type(emp) == ...:. Itu jalan, tetapi setiap peran baru berarti satu cabang if lagi — dan begitu kamu lupa satu, bug muncul.

# (BURUK) tanpa polimorfisme (cabang berdasarkan tipe)
def calc(emp):
    if type(emp) is Manager:
        return emp.base_salary + emp.team_size * 50000
    elif type(emp) is Engineer:
        return emp.base_salary + emp.skill_level * 20000
    else:
        return emp.base_salary


# (BAIK) versi polimorfisme (logika didorong ke class)
def calc(emp):
    return emp.calculate_salary()         # satu baris
Cabang berdasarkan tipe vs polimorfisme
BURUK: cabang tipeif type ==x N kalitipe baru= cabang baruBAIK: polimorfikemp.calculate()tipe baru =cukup classmenjalartertutup
Kode berdasarkan tipe menumpuk if type di pemanggil dan tumbuh dengan tiap tipe baru. Polimorfisme menjaga pemanggil di satu baris — menambah tipe baru berarti cukup menambah class.

«Pemanggil tidak peduli tipe» — itulah mantranya

Tes andal apakah desainmu polimorfik: apakah kode pemanggil punya tumpukan if type(...) atau if isinstance(...)? Jika ya, refactor standarnya adalah memindahkan cabang itu ke override method di class. «Menambah class» dan «menambah if» cenderung trade-off.

Duck typing — Cukup nama method yang sama

Polimorfisme Python juga punya rasa yang lebih longgar: duck typing. Pepatah «kalau jalan seperti bebek dan bersuara seperti bebek, itu bebek» berubah jadi «kalau sebuah class punya method yang tepat, tidak penting class apa».

Di contoh bawah, Cat dan Dog tidak berbagi induk Animal sama sekali — tetapi selama keduanya punya method speak(), fungsi yang sama menangani keduanya. Python memprioritaskan «apakah method-nya ada?» ketimbang grafik pewarisan.

class Cat:
    def speak(self):
        return "Meong"

class Dog:
    def speak(self):
        return "Guk"

def shout(animal):                # tipe tidak dipaksa
    print(animal.speak())

shout(Cat())   # Meong
shout(Dog())   # Guk

Bahasa seperti Java atau C# membutuhkan class induk bersama agar polimorfisme bekerja. Python senang asalkan method-nya ada saat dipanggil. Itu memberi kamu fleksibilitas, tetapi «memastikan nama method punya makna sama antar class» jadi tanggung jawab pemanggil — ingat itu.

Dua desain dalam satu slide

Desain polimorfik vs desain dengan cabang tipe
Desain polimorfik
  • Kode pemanggil — satu baris emp.calculate_salary()
  • Tambah tipe baru — cukup tulis class baru dengan calculate_salary
  • Radius dampak — tetap di dalam class
  • Keterbacaan — cukup baca «nama sama, beda menurut tipe»
Desain dengan cabang tipe
  • Kode pemanggil — tumpukan if type(emp) is ...
  • Tambah tipe baru — semua cabang perlu ditinjau
  • Radius dampak — bocor ke pemanggil
  • Keterbacaan — baca ulang logika cabang setiap kali
Kebutuhan sama, dua desain — tetapi berapa kode yang ditulis pemanggil dan seberapa jauh perubahan menyebar bisa sangat berbeda.
QUIZ

Cek Pemahaman

Jawab setiap pertanyaan satu per satu.

Soal 1Mana deskripsi paling tepat tentang polimorfisme?

Soal 2Polimorfisme cenderung menghapus struktur apa dari kode pemanggil?

Soal 3Mana deskripsi terbaik tentang duck typing Python?