Pola desain OOP adalah solusi teruji untuk masalah desain berulang. Pelajari creational, structural, dan behavioral patterns dengan contoh TypeScript praktis agar kode Anda lebih bersih, mudah diuji, dan mudah dikembangkan.
December 20, 2025 (4mo ago) — last updated February 18, 2026 (2mo ago)
Pola Desain OOP Praktis (Contoh TypeScript)
Pelajari pola creational, structural, dan behavioral dengan contoh TypeScript praktis untuk menulis kode OOP yang lebih bersih dan mudah dipelihara.
← Back to blog
Panduan Pola Desain OOP Praktis (Contoh TypeScript)
Master design pattern OOP dengan panduan ini. Pelajari pola creational, structural, dan behavioral dengan contoh TypeScript praktis untuk menulis kode yang lebih bersih dan mudah dipelihara.
Pendahuluan
Pola desain dalam pemrograman berorientasi objek adalah cetak biru yang teruji untuk menyelesaikan masalah desain berulang. Mereka bukan potongan kode siap-pakai; melainkan template yang membantu Anda menyusun kelas dan objek agar kode lebih mudah dipelihara, diperluas, dan diuji. Memahami pola umum—creational, structural, dan behavioral—mempercepat komunikasi desain di tim dan mempermudah refaktorisasi kode warisan. Karya akademis yang mendasari manfaat pola desain menunjukkan nilai penggunaan ulang dan produktivitas dalam proyek besar1, sementara kumpulan klasik 23 pola tetap menjadi acuan praktis bagi pengembang2.
Apa itu Pola Desain dalam OOP
Tanpa pola desain, basis kode berisiko menjadi kusut dan sulit diskalakan. Pola desain berfungsi layaknya resep: solusi yang sudah diuji yang dapat Anda sesuaikan untuk kebutuhan spesifik. Mereka juga menyediakan kosakata bersama—sebut “Factory” atau “Singleton” dan rekan tim Anda langsung memahami niat arsitektural.
Sebuah pola desain bukanlah desain jadi yang dapat langsung diubah menjadi kode. Ia adalah deskripsi atau template tentang cara memecahkan masalah yang dapat digunakan dalam banyak situasi berbeda.
Membiasakan diri dengan prinsip inti OOP seperti polimorfisme dan pewarisan membantu penerapan pola. Untuk bahan bacaan tambahan, lihat panduan kami tentang polimorfisme vs pewarisan di Clean Code Guy.
Tiga Kategori Inti Pola Desain
Gang of Four mengelompokkan pola ke tiga kategori: creational, structural, dan behavioral. Mengetahui kategori ini mempercepat pemilihan pola yang tepat untuk masalah Anda.
Ikhtisar Kategori Pola Desain OOP
| Kategori | Tujuan Inti | Contoh |
|---|---|---|
| Creational | Abstraksi pembuatan objek | Factory, Builder, Singleton, Prototype |
| Structural | Menyusun kelas dan objek menjadi struktur yang lebih besar | Adapter, Decorator, Facade, Composite |
| Behavioral | Mengatur interaksi dan komunikasi antar objek | Observer, Strategy, Command, Iterator |
Creational Patterns: Mengontrol Konstruksi Objekt
Pola creational memperkenalkan lapisan abstraksi untuk pembuatan objek sehingga kode klien tak bergantung pada kelas konkret. Mereka meningkatkan fleksibilitas dan memudahkan penggantian implementasi.
Singleton: Menjamin Satu Instansi
Singleton memastikan sebuah kelas hanya memiliki satu instansi dan menyediakan titik akses global. Berguna untuk sumber daya bersama seperti koneksi basis data atau logger, tetapi dapat memperkenalkan state global yang menyulitkan pengujian—pertimbangkan dependency injection sebagai alternatif saat perluabilitas pengujian penting3.
Contoh Singleton TypeScript untuk koneksi basis data:
class DatabaseConnection {
private static instance: DatabaseConnection;
private constructor() {
// A private constructor prevents external 'new' calls
console.log("Connecting to the database...");
}
public static getInstance(): DatabaseConnection {
if (!DatabaseConnection.instance) {
DatabaseConnection.instance = new DatabaseConnection();
}
return DatabaseConnection.instance;
}
public query(sql: string): void {
console.log(`Executing query: ${sql}`);
}
}
// Usage
const db1 = DatabaseConnection.getInstance();
const db2 = DatabaseConnection.getInstance();
db1.query("SELECT * FROM users");
console.log(db1 === db2); // true
Trade-off Singleton: gunakan Singleton untuk sumber daya yang benar-benar global, dan utamakan dependency injection untuk kemampuan pengujian dan modularitas yang lebih baik3.
Factory Method: Subkelas Memilih Produk
Factory Method menyediakan antarmuka untuk membuat objek, sementara subkelas menentukan produk konkret. Ini memisahkan kode klien dari kelas konkret dan mempermudah ekstensi.
Contoh: merender tombol spesifik OS dalam TypeScript.
interface Button {
render(): void;
onClick(f: () => void): void;
}
class WindowsButton implements Button {
render() { console.log("Rendering a button in Windows style."); }
onClick(f: () => void) { console.log("Windows button click event."); f(); }
}
class MacButton implements Button {
render() { console.log("Rendering a button in macOS style."); }
onClick(f: () => void) { console.log("Mac button click event."); f(); }
}
abstract class Dialog {
abstract createButton(): Button;
render() {
const okButton = this.createButton();
okButton.render();
}
}
class WindowsDialog extends Dialog {
createButton(): Button { return new WindowsButton(); }
}
class MacDialog extends Dialog {
createButton(): Button { return new MacButton(); }
}
// Client
const os: string = "windows";
let dialog: Dialog;
if (os === "windows") dialog = new WindowsDialog(); else dialog = new MacDialog();
dialog.render();
Factory Method menjaga agar pembuat tidak mengetahui produk konkret, sehingga penambahan seperti LinuxDialog menjadi mudah.
Structural Patterns: Menyusun Sistem yang Fleksibel
Pola struktural membantu merangkai objek menjadi sistem yang tangguh dan adaptif. Mereka menyederhanakan hubungan antar komponen sehingga bagian dapat berubah tanpa merusak keseluruhan.
Adapter: Menjembatani Antarmuka yang Tidak Kompatibel
Adapter membungkus antarmuka yang tidak kompatibel sehingga sesuai dengan yang diharapkan sistem. Ini berguna saat mengintegrasikan pustaka pihak ketiga atau kode warisan.
Contoh: mengadaptasi ModernLogger ke antarmuka ILogger.
class ModernLogger {
public logInfo(message: string): void {
console.log(`[INFO]: ${message}`);
}
}
interface ILogger { log(message: string): void; }
class LoggerAdapter implements ILogger {
private modernLogger: ModernLogger;
constructor() { this.modernLogger = new ModernLogger(); }
public log(message: string): void { this.modernLogger.logInfo(message); }
}
const logger: ILogger = new LoggerAdapter();
logger.log("User logged in successfully.");
Decorator: Menambahkan Fungsionalitas Secara Dinamis
Decorator menambahkan tanggung jawab ke objek pada runtime dengan membungkusnya. Ini lebih fleksibel daripada pewarisan dan mendukung prinsip tanggung jawab tunggal.
Contoh: menyusun langganan dengan tambahan opsional.
interface Subscription { getDescription(): string; getCost(): number; }
class BasicSubscription implements Subscription {
getDescription(): string { return "Basic Plan"; }
getCost(): number { return 10; }
}
abstract class SubscriptionDecorator implements Subscription {
protected subscription: Subscription;
constructor(subscription: Subscription) { this.subscription = subscription; }
abstract getDescription(): string;
abstract getCost(): number;
}
class PremiumSupportDecorator extends SubscriptionDecorator {
getDescription(): string { return `${this.subscription.getDescription()}, Premium Support`; }
getCost(): number { return this.subscription.getCost() + 5; }
}
class CloudStorageDecorator extends SubscriptionDecorator {
getDescription(): string { return `${this.subscription.getDescription()}, 1TB Cloud Storage`; }
getCost(): number { return this.subscription.getCost() + 7; }
}
let mySubscription: Subscription = new BasicSubscription();
mySubscription = new PremiumSupportDecorator(mySubscription);
mySubscription = new CloudStorageDecorator(mySubscription);
console.log(mySubscription.getDescription());
console.log(mySubscription.getCost());
Pendekatan komposisional ini membuat fitur modular dan mudah diuji.
Behavioral Patterns: Mengatur Interaksi Objek
Pola perilaku mengelola komunikasi antar objek sehingga sistem tetap fleksibel dan dapat dipelihara.
Observer: Memberitahu Pihak yang Berkepentingan
Observer mengatur hubungan satu-ke-banyak sehingga ketika Subject berubah, Observer diberi tahu otomatis. Ini cocok untuk sistem berbasis peristiwa.
Contoh: layanan notifikasi sederhana.
interface Subject { attach(observer: Observer): void; detach(observer: Observer): void; notify(): void; }
interface Observer { update(subject: Subject): void; }
class NotificationService implements Subject {
public state: string = '';
private observers: Observer[] = [];
attach(observer: Observer): void { this.observers.push(observer); }
detach(observer: Observer): void { const i = this.observers.indexOf(observer); if (i !== -1) this.observers.splice(i, 1); }
notify(): void { for (const o of this.observers) o.update(this); }
public createNewPost(title: string): void { this.state = `New Post: ${title}`; console.log(`\nNotificationService: A new post was created.`); this.notify(); }
}
class EmailNotifier implements Observer { public update(subject: Subject): void { if (subject instanceof NotificationService) console.log(`EmailNotifier: Sending email about “${subject.state}”`); } }
class PushNotifier implements Observer { public update(subject: Subject): void { if (subject instanceof NotificationService) console.log(`PushNotifier: Sending push notification for “${subject.state}”`); } }
const notificationService = new NotificationService();
const emailer = new EmailNotifier();
const pusher = new PushNotifier();
notificationService.attach(emailer);
notificationService.attach(pusher);
notificationService.createNewPost("Understanding Observer Pattern");
notificationService.detach(pusher);
notificationService.createNewPost("Why Strategy is Awesome");
Observer menghasilkan desain longgar terikat karena Subject tidak perlu mengetahui Observer konkret.
Strategy: Mengenkapsulasi Algoritma
Strategy memungkinkan pertukaran algoritma pada runtime, mengurangi blok kondisional besar dan selaras dengan prinsip Open/Closed4.
Contoh: strategi pembayaran untuk keranjang belanja.
interface PaymentStrategy { pay(amount: number): void; }
class CreditCardStrategy implements PaymentStrategy { pay(amount: number): void { console.log(`Paying $${amount} with Credit Card.`); } }
class PayPalStrategy implements PaymentStrategy { pay(amount: number): void { console.log(`Paying $${amount} via PayPal.`); } }
class ShoppingCart {
private paymentStrategy: PaymentStrategy;
constructor(strategy: PaymentStrategy) { this.paymentStrategy = strategy; }
public setPaymentStrategy(strategy: PaymentStrategy) { this.paymentStrategy = strategy; }
public checkout(amount: number): void { this.paymentStrategy.pay(amount); }
}
const cart = new ShoppingCart(new CreditCardStrategy());
cart.checkout(150);
cart.setPaymentStrategy(new PayPalStrategy());
cart.checkout(150);
Strategy menghilangkan kompleksitas kondisional dan memudahkan penambahan strategi baru.
Perangkap Desain Umum dan Refaktorisasi
Memilih pola hanyalah setengah perjuangan. Hindari anti-pola seperti God Object yang menumpuk tanggung jawab. Waspadai code smells: metode panjang, dependensi berlebihan, atau kelas raksasa—atasinya dengan refaktorisasi disiplin.
Refaktorisasi memperbaiki struktur internal tanpa mengubah perilaku eksternal. Misalnya, refaktorkan rantai if/else menjadi Strategy untuk meningkatkan ekstendabilitas dan kemampuan pengujian4.
Kesimpulan dan Praktik Terbaik
Fokuskan pada 5–7 pola inti terlebih dahulu: Singleton, Factory, Adapter, Decorator, Observer, dan Strategy. Pelajari masalah yang diselesaikan tiap pola sehingga Anda bisa mengenali kapan menerapkannya tanpa menambah kompleksitas yang tidak perlu. TypeScript merupakan bahasa populer untuk menerapkan pola ini dalam pengembangan frontend dan full‑stack modern5.
Tanya Jawab Singkat
Apa pola desain OOP paling penting untuk dipelajari dulu?
Fokus pada pola yang sering muncul di proyek nyata: Singleton, Factory, Adapter, Decorator, Observer, dan Strategy. Kuasai masalah yang mereka selesaikan, bukan hanya struktur kelasnya.
Kapan sebaiknya saya tidak menggunakan pola desain?
Jangan menambahkan pola hanya karena “bagus untuk dimiliki.” Hindari menambahkan kompleksitas tanpa kebutuhan nyata—ikuti prinsip YAGNI (You Aren’t Gonna Need It).
Bagaimana pola desain bekerja dengan pemrograman fungsional?
Banyak pola mudah diadaptasi: Strategy bisa berupa fungsi yang dipass-in, dan Decorator bisa berupa fungsi orde‑tinggi. Intinya adalah menerapkan prinsip desain, bukan memaksakan bentuk OOP.
Di Clean Code Guy, kami membantu tim membangun perangkat lunak yang bertahan dengan memasukkan prinsip dasar ke alur kerja mereka. Temukan layanan audit kode dan refaktorisasi kami di https://cleancodeguy.com.
AI menulis kode.Anda membuatnya bertahan.
Di era akselerasi AI, kode bersih bukan hanya praktik yang baik — ini adalah perbedaan antara sistem yang berkembang dan codebase yang runtuh di bawah beratnya sendiri.