Pemrograman · Pola desain
Pola Factory Method dijelaskan dengan contoh
Jawaban singkat
Factory Method adalah pola desain creational yang mendefinisikan sebuah method untuk membuat objek tetapi membiarkan subkelas menentukan kelas konkret mana yang diinstansiasi. Kelas dasar menulis logikanya terhadap interface produk dan memanggil factory method, seperti createTransport(), alih-alih new. Dengan begitu Anda dapat menambah tipe produk baru dengan menambah subkelas, tanpa mengedit kode yang memakai produk tersebut.
Tujuan
Mendefinisikan interface untuk membuat objek, tetapi membiarkan subkelas menentukan kelas mana yang diinstansiasi. Pola Factory Method memungkinkan sebuah kelas menunda instansiasi ke subkelas. Pola ini salah satu dari lima pola creational dalam buku Gang of Four (GoF), dan satu-satunya pola creational yang berlingkup kelas, karena bergantung pada inheritance.
Masalah yang diselesaikan
Bayangkan modul notifikasi. Modul itu memformat pesan, mengirimnya, dan mencatat hasilnya. Awalnya hanya mengirim email, jadi kode memanggil new EmailChannel() secara langsung. Lalu produk butuh SMS, kemudian notifikasi push. Setiap kanal baru berarti mengedit kelas yang sama dan menambah cabang if lagi, padahal langkah pemformatan dan pencatatan tidak pernah berubah.
Alur pengiriman bersifat stabil; hanya jenis kanalnya yang bervariasi. Pola Factory Method memindahkan satu keputusan yang bervariasi, yaitu kelas mana yang dibuat, ke dalam satu method yang dapat di-override.
Struktur dan peserta
| Peserta | Tanggung jawab | Dalam contoh |
|---|---|---|
| Product | Interface dari objek yang dibuat oleh factory method. | Channel |
| ConcreteProduct | Mengimplementasikan interface Product. | EmailChannel, SmsChannel |
| Creator | Mendeklarasikan factory method dan berisi logika yang memakai produk. | Notifier |
| ConcreteCreator | Meng-override factory method untuk mengembalikan ConcreteProduct tertentu. | EmailNotifier, SmsNotifier |
Implementasi TypeScript
interface Channel {
send(to: string, text: string): string;
}
class EmailChannel implements Channel {
send(to: string, text: string): string { return `email to ${to}: ${text}`; }
}
class SmsChannel implements Channel {
send(to: string, text: string): string { return `sms to ${to}: ${text.slice(0, 20)}`; }
}
abstract class Notifier {
// The factory method: subclasses choose the concrete product.
protected abstract createChannel(): Channel;
// Shared logic that works with any Channel.
notify(to: string, text: string): void {
const channel = this.createChannel();
const result = channel.send(to, text.trim());
console.log("[log]", result);
}
}
class EmailNotifier extends Notifier {
protected createChannel(): Channel { return new EmailChannel(); }
}
class SmsNotifier extends Notifier {
protected createChannel(): Channel { return new SmsChannel(); }
}
const notifiers: Notifier[] = [new EmailNotifier(), new SmsNotifier()];
for (const n of notifiers) n.notify("ada", " Your order has shipped today ");
// [log] email to ada: Your order has shipped today
// [log] sms to ada: Your order has shippMenambah notifikasi push sekarang berarti menulis PushChannel dan PushNotifier. Method notify, dan setiap pemanggilnya, tidak berubah.
Sebelum menerapkan pola ini, keputusan pembuatan terkubur dalam logika bersama:
class Notifier {
constructor(private kind: "email" | "sms") {}
notify(to: string, text: string): void {
// every new channel edits this method
const result = this.kind === "email"
? `email to ${to}: ${text}`
: `sms to ${to}: ${text.slice(0, 20)}`;
console.log("[log]", result);
}
}Alternatif yang lebih ringan: teruskan fungsi factory
Pola Factory Method memakai inheritance. Di TypeScript, Anda sering mendapatkan fleksibilitas yang sama dengan composition, yaitu meneruskan fungsi pembuat ke konstruktor. Cara ini menghindari satu subkelas per produk:
interface Channel { send(to: string, text: string): string; }
class Notifier {
constructor(private createChannel: () => Channel) {}
notify(to: string, text: string): void {
console.log("[log]", this.createChannel().send(to, text));
}
}
const push = new Notifier(() => ({ send: (to, text) => `push to ${to}: ${text}` }));
push.notify("ada", "Hello"); // [log] push to ada: HelloGunakan versi subkelas ketika setiap creator memang sudah punya kelas sendiri karena alasan lain, atau ketika framework mengharapkan Anda memperluas kelas dasar. Gunakan versi fungsi ketika satu-satunya perbedaan antarvarian adalah objek yang mereka buat.
Kapan memakainya, dan kapan tidak
Gunakan pola Factory Method ketika:
- Sebuah kelas berisi algoritma yang stabil tetapi tidak bisa tahu sebelumnya objek konkret mana yang harus dibuat.
- Anda menulis framework atau pustaka dan ingin pengguna memasang kelas produk mereka sendiri lewat subclassing.
- Anda ingin menyimpan logika pembuatan sebuah tipe di satu tempat alih-alih menyebar pemanggilan
new.
Hindari ketika:
- Hanya ada satu tipe produk dan tidak ada tanda nyata akan ada yang kedua.
newlangsung lebih jelas. - Satu-satunya alasan adalah agar pengujian memungkinkan. Meneruskan dependensi lewat konstruktor lebih sederhana.
- Anda akan membuat hierarki creator paralel yang mencerminkan produk satu lawan satu tanpa logika lain.
Pemakaian di dunia nyata
- Koleksi Java:
Iterable.iterator()dideklarasikan sekali, dan setiap kelas koleksi (ArrayList,HashSet, dan lainnya) meng-override-nya untuk mengembalikan kelas iterator miliknya sendiri. Buku GoF menjelaskan pemakaian pola Factory Method ini untuk iterasi polimorfik. - Iterable JavaScript: setiap iterable mengimplementasikan
[Symbol.iterator](), method yang mengembalikan iterator baru yang sesuai dengan koleksi tersebut. - Framework UI sering membiarkan subkelas meng-override method yang membuat komponen, sel, atau view, sementara kelas dasar mengendalikan tata letak dan siklus hidup.
Pola terkait
| Pola | Perbedaannya |
|---|---|
| Abstract Factory | Membuat satu keluarga produk terkait lewat objek dengan beberapa method pembuat. Method-nya sering diimplementasikan sebagai factory method. |
| Template Method | Factory Method sering dipanggil dari template method, seperti notify memanggil createChannel di atas. |
| Prototype | Membuat objek dengan menyalin instance alih-alih membuat subkelas creator, sehingga tidak memerlukan hierarki creator paralel. |
| Builder | Berfokus membangun satu objek kompleks langkah demi langkah, bukan memilih kelas mana yang dibuat. |
FAQ
Apa beda pola Factory Method dan pola Abstract Factory?
Factory Method adalah satu method yang di-override subkelas untuk membuat satu produk. Abstract Factory adalah objek dengan beberapa method pembuat yang menghasilkan keluarga produk terkait yang harus dipakai bersama.
Apakah fungsi factory sederhana sama dengan Factory Method?
Tidak persis. Fungsi seperti createShape(kind) dengan switch di dalamnya sering disebut simple factory. Factory Method ala GoF bergantung pada subkelas yang meng-override method pembuat, tetapi keduanya menyembunyikan new dari klien.
Apakah pola Factory Method mengikuti open/closed principle?
Ya. Anda menambah produk baru dengan menambah kelas produk baru dan subkelas creator baru, tanpa mengubah logika creator yang ada.
Haruskah factory method bersifat abstract?
Tidak. Kelas dasar dapat menyediakan implementasi default yang mengembalikan produk standar, dan subkelas meng-override-nya hanya ketika membutuhkan sesuatu yang berbeda.