Mungkin Anda telah sangat mengenal prinspi desain OOP yang begitu terkenal ini yaitu SOLID Principles. Saya ingin membahas ulang sebagai bahan pengingat buat saya dan juga buat Anda :)
Pernahkah Anda melihat kode yang begitu rumit hingga Anda takut menyentuhnya karena khawatir satu perubahan kecil akan merusak keseluruhan sistem?
Jika ya, kemungkinan besar kode tersebut melanggar SOLID Principles.
Dalam dunia software engineering, SOLID adalah lima prinsip desain Object-Oriented Programming (OOP) yang diperkenalkan oleh Robert C. Martin (Uncle Bob).
Menerapkan prinsip ini bukan hanya soal "terlihat keren", tapi soal investasi jangka panjang untuk aplikasi yang mudah dipelihara dan dikembangkan.
Artikel ini akan mengupas tuntas apa itu SOLID, kelebihan dan kelemahannya, serta contoh implementasi nyata menggunakan Python.
SOLID adalah akronim dari:
Single Responsibility Principle (SRP)
Open/Closed Principle (OCP)
Liskov Substitution Principle (LSP)
Interface Segregation Principle (ISP)
Dependency Inversion Principle (DIP)
Mari kita bedah satu per satu dengan contoh kasus.
"A class should have one, and only one, reason to change."
Konsep: Sebuah class hanya boleh melakukan satu tugas spesifik. Jangan mencampur logika bisnis, akses database, dan format laporan dalam satu class.
Contoh Use Case: Sistem User Management. Jangan gabungkan logika penyimpanan user ke database dengan logika pengiriman email notifikasi.
# ❌ BAD PRACTICE: Class melakukan terlalu banyak hal
class UserManager:
def register(self, username, password):
# 1. Simpan user (Logic DB)
print(f"User {username} saved to DB.")
# 2. Kirim email (Logic Notifikasi)
print(f"Email sent to {username}.")# ✅ GOOD PRACTICE: Pisahkan tanggung jawab
class EmailService:
def send_email(self, username):
print(f"Email sent to {username}.")class UserRepository:
def save(self, username):
print(f"User {username} saved to DB.")class UserManager:
def __init__(self, email_service, user_repo):
self.email_service = email_service
self.user_repo = user_repodef register(self, username):
self.user_repo.save(username)
self.email_service.send_email(username)
"Software entities should be open for extension, but closed for modification."
Konsep: Anda harus bisa menambahkan fitur baru tanpa mengubah kode yang sudah ada. Ini mencegah risiko bug baru pada fitur lama.
Contoh Use Case: Sistem Pembayaran (Payment Gateway). Anda ingin menambahkan metode pembayaran baru (misal: Crypto) tanpa mengedit class pembayaran utama.
from abc import ABC, abstractmethod# Abstract Base Class
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount):
passclass CreditCard(PaymentMethod):
def pay(self, amount):
print(f"Paid {amount} using Credit Card.")class PayPal(PaymentMethod):
def pay(self, amount):
print(f"Paid {amount} using PayPal.")# ✅ Kita bisa menambah class 'Crypto' tanpa mengubah PaymentProcessor
class PaymentProcessor:
def process_payment(self, payment_method: PaymentMethod, amount):
payment_method.pay(amount)# Usage
processor = PaymentProcessor()
processor.process_payment(CreditCard(), 100)
"Subtypes must be substitutable for their base types."
Konsep: Class anak (subclass) harus bisa menggantikan class induk (parent) tanpa merusak program. Jika class induk punya method fly(), jangan buat class anak yang melempar error saat fly() dipanggil.
Contoh Use Case: Klasifikasi Burung. Tidak semua burung bisa terbang (contoh: Penguin).
# ❌ BAD PRACTICE
class Bird:
def fly(self):
print("I can fly")class Penguin(Bird):
def fly(self):
raise Exception("I cannot fly!") # Melanggar LSP# ✅ GOOD PRACTICE: Ubah hirarki
class Bird(ABC):
passclass FlyingBird(Bird):
def fly(self):
print("I can fly")class NonFlyingBird(Bird):
def swim(self):
print("I can swim")class Eagle(FlyingBird):
passclass Penguin(NonFlyingBird):
pass
"Clients should not be forced to depend upon interfaces that they do not use."
Konsep: Lebih baik memiliki banyak interface yang spesifik daripada satu interface raksasa yang umum. Di Python, ini diimplementasikan menggunakan Abstract Base Classes (ABC).
Contoh Use Case: Mesin Kantor (Printer Multi-fungsi). Jangan paksa mesin fax lama mengimplementasikan fitur scan warna.
from abc import abstractmethod# ❌ BAD PRACTICE: Interface terlalu gemuk
class Machine(ABC):
@abstractmethod
def print(self, doc): pass
@abstractmethod
def scan(self, doc): pass
@abstractmethod
def fax(self, doc): pass# ✅ GOOD PRACTICE: Segregasi Interface
class Printer(ABC):
@abstractmethod
def print(self, doc): passclass Scanner(ABC):
@abstractmethod
def scan(self, doc): pass# Class hanya mengimplementasikan apa yang dia butuhkan
class SimplePrinter(Printer):
def print(self, doc):
print(f"Printing {doc}")
"Depend upon abstractions, not concretions."
Konsep: Modul tingkat tinggi (logika bisnis) tidak boleh bergantung langsung pada modul tingkat rendah (detail teknis seperti driver database). Keduanya harus bergantung pada abstraksi (interface).
Contoh Use Case: Mengganti Database dari MySQL ke PostgreSQL tanpa mengubah logika aplikasi.
# Abstraksi
class DatabaseInterface(ABC):
@abstractmethod
def connect(self): pass# Low-level modules
class MySQLDatabase(DatabaseInterface):
def connect(self):
print("Connected to MySQL")class MongoDatabase(DatabaseInterface):
def connect(self):
print("Connected to MongoDB")# High-level module
# ✅ App tidak peduli databasenya apa, asal sesuai kontrak interface
class App:
def __init__(self, db: DatabaseInterface):
self.db = dbdef start(self):
self.db.connect()# Usage
app = App(MongoDatabase()) # Mudah diganti ke MySQLDatabase()
app.start()
Tidak ada arsitektur yang sempurna. Berikut adalah pertimbangan sebelum Anda menerapkan SOLID secara penuh.
Maintainability (Kemudahan Pemeliharaan): Kode lebih terorganisir. Mencari bug atau mengubah fitur menjadi lebih cepat karena tanggung jawab setiap komponen jelas.
Scalability (Skalabilitas): Menambah fitur baru (seperti contoh OCP di atas) sangat mudah tanpa risiko merusak fitur lama.
Testability (Kemudahan Pengujian): Karena modul terpisah (Decoupled), kita bisa melakukan Unit Testing pada satu fungsi tanpa perlu memuat seluruh sistem.
Reusability: Komponen kecil yang spesifik lebih mudah digunakan kembali di proyek lain.
Kompleksitas Awal: Menerapkan SOLID seringkali berarti menulis lebih banyak kode (lebih banyak class dan interface). Untuk proyek kecil atau prototype (MVP), ini bisa terasa seperti over-engineering.
Kurva Belajar: Bagi pemula, konsep seperti Dependency Injection atau Interface Segregation bisa membingungkan dan butuh waktu untuk dipahami.
Waktu Pengembangan: Di awal, development time akan lebih lama karena harus merancang arsitektur dengan hati-hati. (Namun, ini akan terbayar di masa depan).
SOLID Principle adalah fondasi untuk membangun perangkat lunak level enterprise yang tangguh.
Meskipun terlihat rumit di awal, prinsip ini menyelamatkan Anda dari "Technical Debt" yang menumpuk di kemudian hari.
Tips Terakhir: Gunakan SOLID dengan bijak. Jika Anda membuat skrip sederhana yang hanya dipakai sekali, jangan paksakan menggunakan SOLID.
Namun, jika Anda membangun sistem yang akan hidup bertahun-tahun, SOLID adalah wajib hukumnya.