Ada berbagai jenis polimorfisme, yang menarik biasanya polimorfisme runtime / pengiriman dinamis.
Deskripsi polimorfisme runtime tingkat sangat tinggi adalah bahwa pemanggilan metode melakukan hal-hal yang berbeda tergantung pada tipe runtime argumennya: objek itu sendiri bertanggung jawab untuk menyelesaikan pemanggilan metode. Ini memungkinkan fleksibilitas yang sangat besar.
Salah satu cara paling umum untuk menggunakan fleksibilitas ini adalah untuk injeksi dependensi , misalnya agar saya dapat beralih di antara implementasi yang berbeda atau untuk menyuntikkan objek tiruan untuk pengujian. Jika saya tahu sebelumnya bahwa hanya akan ada sejumlah pilihan yang mungkin saya dapat mencoba untuk melakukan hardcode dengan kondisional, misalnya:
void foo() {
if (isTesting) {
... // do mock stuff
} else {
... // do normal stuff
}
}
Ini membuat kode sulit untuk diikuti. Alternatifnya adalah dengan memperkenalkan antarmuka untuk operasi tersebut dan menulis implementasi normal dan implementasi tiruan dari antarmuka itu, dan "menyuntikkan" ke implementasi yang diinginkan pada saat runtime. "Injeksi ketergantungan" adalah istilah yang rumit untuk "melewatkan objek yang benar sebagai argumen".
Sebagai contoh dunia nyata, saya saat ini sedang mengerjakan masalah pembelajaran mesin. Saya memiliki algoritma yang memerlukan model prediksi. Tapi saya ingin mencoba algoritma pembelajaran mesin yang berbeda. Jadi saya mendefinisikan sebuah antarmuka. Apa yang saya butuhkan dari model prediksi saya? Diberikan beberapa sampel input, prediksi dan kesalahannya:
interface Model {
def predict(sample) -> (prediction: float, std: float);
}
Algoritme saya mengambil fungsi pabrik yang melatih model:
def my_algorithm(..., train_model: (observations) -> Model, ...) {
...
Model model = train_model(observations);
...
y, std = model.predict(x)
...
}
Saya sekarang memiliki berbagai implementasi dari antarmuka model dan dapat membandingkannya satu sama lain. Salah satu implementasi ini sebenarnya mengambil dua model lain dan menggabungkannya menjadi model yang dikuatkan. Jadi berkat antarmuka ini:
- algoritma saya tidak perlu tahu tentang model spesifik sebelumnya,
- Saya dapat dengan mudah menukar model, dan
- Saya memiliki banyak fleksibilitas dalam mengimplementasikan model saya.
Kasus penggunaan klasik polimorfisme ada di GUI. Dalam kerangka kerja GUI seperti Java AWT / Swing / ... ada komponen yang berbeda . Antarmuka komponen / kelas dasar menjelaskan tindakan seperti mengecat dirinya sendiri ke layar, atau bereaksi terhadap klik mouse. Banyak komponen yang merupakan wadah yang mengelola sub-komponen. Bagaimana wadah seperti itu menarik dirinya sendiri?
void paint(Graphics g) {
super.paint(g);
for (Component child : this.subComponents)
child.paint(g);
}
Di sini, wadah tidak perlu tahu tentang jenis subkomponen yang tepat di muka - selama mereka sesuai dengan Component
antarmuka wadah hanya dapat memanggil paint()
metode polimorfik . Ini memberi saya kebebasan untuk memperpanjang hirarki kelas AWT dengan komponen baru yang sewenang-wenang.
Ada banyak masalah berulang sepanjang pengembangan perangkat lunak yang dapat diselesaikan dengan menerapkan polimorfisme sebagai teknik. Pasangan solusi-masalah yang berulang ini disebut pola desain , dan beberapa di antaranya dikumpulkan dalam buku dengan nama yang sama. Dalam istilah buku itu, model pembelajaran mesin injeksi saya akan menjadi strategi yang saya gunakan untuk "mendefinisikan keluarga algoritma, merangkum masing-masing, dan membuatnya saling dipertukarkan". Contoh Java-AWT di mana komponen dapat berisi sub-komponen adalah contoh komposit .
Tetapi tidak setiap desain perlu menggunakan polimorfisme (di luar memungkinkan injeksi ketergantungan untuk pengujian unit, yang merupakan kasus penggunaan yang sangat baik). Sebagian besar masalah dinyatakan sangat statis. Akibatnya, kelas dan metode sering tidak digunakan untuk polimorfisme, tetapi hanya sebagai ruang nama yang nyaman dan untuk metode panggilan sintaksis cantik. Misalnya banyak pengembang lebih suka pemanggilan metode seperti account.getBalance()
pada pemanggilan fungsi yang sebagian besar setara Account_getBalance(account)
. Itu pendekatan yang sangat bagus, hanya saja banyak "metode" panggilan tidak ada hubungannya dengan polimorfisme.