Polymorphism
Memahami polymorphism, method overriding, dan duck typing
Apa itu Polymorphism?
Polymorphism (dari bahasa Yunani: "poly" = banyak, "morph" = bentuk) adalah kemampuan objek dari class yang berbeda untuk merespons method dengan nama yang sama dengan cara yang berbeda.
Analogi
Bayangkan tombol "Play" pada remote control. Untuk TV, tombol play menyalakan acara. Untuk AC, play menyalakan pendingin. Untuk stereo, play memutar musik. Tombol yang sama, tapi perilaku berbeda tergantung perangkat.
Jenis-jenis Polymorphism
1. Method Overriding
Method overriding terjadi ketika subclass memberikan implementasi spesifik untuk method yang sudah didefinisikan di superclass:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass # Method yang akan di-override
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
class Cow(Animal):
def speak(self):
return f"{self.name} says Moo!"
# Polymorphism dalam aksi
animals = [
Dog("Buddy"),
Cat("Whiskers"),
Cow("Milly")
]
for animal in animals:
print(animal.speak())
# Output:
# Buddy says Woof!
# Whiskers says Meow!
# Milly says Moo!2. Duck Typing
Python menggunakan "duck typing": "If it walks like a duck and quacks like a duck, it must be a duck."
Python tidak peduli tipe object apa yang diberikan, selama object tersebut memiliki method yang dipanggil:
class Dog:
def speak(self):
return "Woof!"
class Cat:
def speak(self):
return "Meow!"
class Robot:
def speak(self):
return "Beep boop!"
# Fungsi yang menerima object apa pun dengan method speak()
def make_sound(entity):
return entity.speak()
# Semua bisa dipanggil, tidak peduli class-nya
print(make_sound(Dog())) # Woof!
print(make_sound(Cat())) # Meow!
print(make_sound(Robot())) # Beep boop!Duck Typing vs Static Typing
Dalam bahasa dengan static typing (Java, C++), Kalian harus mendefinisikan interface atau parent class yang sama. Python lebih fleksibel dengan duck typing - yang penting memiliki method yang dibutuhkan.
Polymorphism dengan Function
Function yang Polymorphic
Buat function yang dapat bekerja dengan berbagai tipe object:
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
import math
return math.pi * self.radius ** 2
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Triangle:
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return 0.5 * self.base * self.height
# Function polymorphic
def print_area(shape):
"""Function ini bekerja dengan object apa pun yang memiliki method area()"""
print(f"Area: {shape.area():.2f}")
# Penggunaan
shapes = [
Circle(5),
Rectangle(4, 6),
Triangle(3, 8)
]
for shape in shapes:
print_area(shape)Method Chaining dengan Polymorphism
class Shape:
def __init__(self):
self.color = "black"
def set_color(self, color):
self.color = color
return self # Return self untuk chaining
def draw(self):
pass # Override di subclass
class Circle(Shape):
def __init__(self, radius):
super().__init__()
self.radius = radius
def draw(self):
return f"Drawing {self.color} circle with radius {self.radius}"
class Square(Shape):
def __init__(self, side):
super().__init__()
self.side = side
def draw(self):
return f"Drawing {self.color} square with side {self.side}"
# Method chaining
circle = Circle(5).set_color("red")
print(circle.draw()) # Drawing red circle with radius 5
square = Square(10).set_color("blue")
print(square.draw()) # Drawing blue square with side 10Operator Overloading
Python memungkinkan kita untuk mendefinisikan perilaku operator (+, -, *, ==, dll) untuk class kita sendiri:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __repr__(self):
return f"Vector({self.x}, {self.y})"
# Operator + (addition)
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
# Operator - (subtraction)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
# Operator * (scalar multiplication)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
# Operator == (equality)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
# Operator < (less than)
def __lt__(self, other):
# Compare by magnitude
return (self.x**2 + self.y**2) < (other.x**2 + other.y**2)
# len() function
def __len__(self):
import math
return int(math.sqrt(self.x**2 + self.y**2))
# Penggunaan
v1 = Vector(2, 3)
v2 = Vector(4, 5)
print(v1 + v2) # Vector(6, 8)
print(v1 - v2) # Vector(-2, -2)
print(v1 * 3) # Vector(6, 9)
print(v1 == v2) # False
print(v1 < v2) # True
print(len(v1)) # 3Common Operator Overloading Methods
| Operator | Method | Deskripsi |
|---|---|---|
+ | __add__ | Addition |
- | __sub__ | Subtraction |
* | __mul__ | Multiplication |
/ | __truediv__ | Division |
// | __floordiv__ | Floor division |
% | __mod__ | Modulus |
** | __pow__ | Power |
== | __eq__ | Equality |
!= | __ne__ | Not equal |
< | __lt__ | Less than |
> | __gt__ | Greater than |
<= | __le__ | Less than or equal |
>= | __ge__ | Greater than or equal |
len() | __len__ | Length |
str() | __str__ | String representation |
Contoh Lengkap: Payment System
Mari lihat contoh lengkap polymorphism pada sistem pembayaran:
from abc import ABC, abstractmethod
from datetime import datetime
# Base class
class Payment(ABC):
"""Abstract base class untuk semua jenis payment"""
def __init__(self, amount):
self.amount = amount
self.timestamp = datetime.now()
self.status = "pending"
@abstractmethod
def process_payment(self):
"""Method yang harus diimplementasikan oleh subclass"""
pass
@abstractmethod
def get_payment_info(self):
"""Method yang harus diimplementasikan oleh subclass"""
pass
def __str__(self):
return f"{self.__class__.__name__}: Rp{self.amount:,.0f}"
# Concrete implementations
class CreditCardPayment(Payment):
def __init__(self, amount, card_number, cvv):
super().__init__(amount)
self.card_number = self._mask_card_number(card_number)
self._cvv = cvv # Private
def _mask_card_number(self, card_number):
"""Private method untuk mask card number"""
return f"****-****-****-{card_number[-4:]}"
def process_payment(self):
# Simulasi proses pembayaran
print(f"Processing credit card payment...")
print(f"Card: {self.card_number}")
self.status = "completed"
return True
def get_payment_info(self):
return {
'type': 'Credit Card',
'card': self.card_number,
'amount': self.amount,
'status': self.status
}
class BankTransferPayment(Payment):
def __init__(self, amount, bank_name, account_number):
super().__init__(amount)
self.bank_name = bank_name
self.account_number = account_number
def process_payment(self):
print(f"Processing bank transfer...")
print(f"Bank: {self.bank_name}")
print(f"Account: {self.account_number}")
self.status = "completed"
return True
def get_payment_info(self):
return {
'type': 'Bank Transfer',
'bank': self.bank_name,
'account': self.account_number,
'amount': self.amount,
'status': self.status
}
class EWalletPayment(Payment):
def __init__(self, amount, wallet_type, phone_number):
super().__init__(amount)
self.wallet_type = wallet_type
self.phone_number = phone_number
def process_payment(self):
print(f"Processing e-wallet payment...")
print(f"Wallet: {self.wallet_type}")
print(f"Phone: {self.phone_number}")
self.status = "completed"
return True
def get_payment_info(self):
return {
'type': 'E-Wallet',
'wallet': self.wallet_type,
'phone': self.phone_number,
'amount': self.amount,
'status': self.status
}
# Payment Processor - menggunakan polymorphism
class PaymentProcessor:
"""Class yang memproses berbagai jenis payment"""
def __init__(self):
self.payments = []
def process(self, payment):
"""
Method polymorphic - bekerja dengan semua jenis Payment
tanpa perlu tahu tipe spesifiknya
"""
print(f"\n{'='*50}")
print(f"Processing payment: {payment}")
print(f"{'='*50}")
if payment.process_payment():
self.payments.append(payment)
print(f"Payment successful!")
return True
else:
print(f"Payment failed!")
return False
def get_total_payments(self):
return sum(p.amount for p in self.payments)
def print_summary(self):
print(f"\n{'='*50}")
print(f"Payment Summary")
print(f"{'='*50}")
for i, payment in enumerate(self.payments, 1):
info = payment.get_payment_info()
print(f"\n{i}. {info['type']}")
for key, value in info.items():
if key != 'type':
print(f" {key.capitalize()}: {value}")
print(f"\nTotal: Rp{self.get_total_payments():,.0f}")
print(f"{'='*50}")
# Penggunaan
processor = PaymentProcessor()
# Berbagai jenis payment
payments = [
CreditCardPayment(500000, "1234567890123456", "123"),
BankTransferPayment(750000, "BCA", "1234567890"),
EWalletPayment(250000, "GoPay", "081234567890"),
CreditCardPayment(1000000, "9876543210987654", "456")
]
# Process semua payment dengan method yang sama
for payment in payments:
processor.process(payment)
# Print summary
processor.print_summary()Polymorphism dengan Built-in Functions
Python built-in functions menggunakan polymorphism:
# len() works with different types
print(len("Hello")) # 5 (string)
print(len([1, 2, 3])) # 3 (list)
print(len({'a': 1, 'b': 2})) # 2 (dict)
# Custom class dengan __len__
class Playlist:
def __init__(self, name):
self.name = name
self.songs = []
def add_song(self, song):
self.songs.append(song)
def __len__(self):
return len(self.songs)
playlist = Playlist("My Favorites")
playlist.add_song("Song 1")
playlist.add_song("Song 2")
print(len(playlist)) # 2
# sum() works with different iterables
print(sum([1, 2, 3])) # 6
print(sum((1, 2, 3))) # 6
print(sum({1, 2, 3})) # 6Praktik Terbaik
1. Design for Polymorphism
# Good - design dengan interface yang konsisten
class Shape:
def area(self):
pass
def perimeter(self):
pass
class Circle(Shape):
def area(self):
# Implementation
pass
def perimeter(self):
# Implementation
pass
# Avoid - interface yang tidak konsisten
class Circle:
def calculate_area(self):
pass
class Square:
def get_area(self): # Different method name
pass2. Use Abstract Base Classes
from abc import ABC, abstractmethod
class Vehicle(ABC):
@abstractmethod
def start(self):
"""All vehicles must implement start"""
pass
@abstractmethod
def stop(self):
"""All vehicles must implement stop"""
pass3. Follow Liskov Substitution Principle
# Good - subclass can replace parent without breaking
class Bird:
def move(self):
return "Flying"
class Sparrow(Bird):
def move(self):
return "Flying fast"
# Avoid - subclass breaks parent's contract
class Bird:
def fly(self):
return "Flying"
class Penguin(Bird):
def fly(self):
raise Exception("Penguins can't fly!") # Breaks contractLatihan
-
Buat hierarchy untuk sistem notifikasi:
- Base class
Notificationdengan methodsend() - Subclass:
EmailNotification,SMSNotification,PushNotification - Implementasikan polymorphism untuk mengirim berbagai jenis notifikasi
- Base class
-
Buat class
Moneydengan operator overloading untuk:- Addition (
+) - Subtraction (
-) - Comparison (
==,<,>) - String representation
- Addition (
-
Implementasikan payment gateway dengan:
- Multiple payment methods
- Unified processing interface
- Transaction logging
Langkah Selanjutnya
Setelah memahami polymorphism, kita akan mempelajari Abstract Classes untuk membuat blueprint yang lebih formal untuk inheritance.
