SOLID Design Principle: Panduan Bangun Arsitektur Software
Halo DomaiNesians! Saat membangun aplikasi dengan pendekatan Object-Oriented Programming (OOP), kamu mungkin pernah menghadapi masalah seperti struktur kode yang kompleks, sulit di-maintenance, atau mudah rusak ketika ada perubahan kecil.Â
Masalah ini cukup umum terjadi, terutama pada proyek berskala menengah hingga besar yang terus berkembang seiring waktu. Untuk membantu mengatasi tantangan tersebut, Robert C. Martin atau yang lebih dikenal dengan sebutan Uncle Bob memperkenalkan lima prinsip desain OOP yang kemudian disebut dengan SOLID Principles. Prinsip-prinsip desain ini dirancang untuk membantu developer menulis kode yang bersih, modular, dan mudah dikembangkan tanpa mengorbankan stabilitas sistem.
Di artikel kali ini, kita akan membahas elemen dalam prinsip desain SOLID secara lengkap dengan contoh implementasi yang praktis dan relevan, agar kamu bisa mengembangkan kode yang lebih robust dan scalable.
Apa Itu SOLID Design Principles?
SOLID adalah akronim atau singkatan dari lima prinsip desain utama dalam OOP yang bertujuan untuk meningkatkan kualitas arsitektur perangkat lunak. Dengan menerapkan prinsip desain SOLID ini, kamu bisa membuat sistem yang lebih fleksibel, mudah diuji, serta lebih siap untuk menghadapi perubahan di masa depan.
Image Source: Medium – Solid Principle In C#
Berikut kelima prinsip yang dimaksud dalam SOLID Design Principle:
- S = Single Responsibility Principle (SRP)
- O = Open/Closed Principle (OCP)
- L = Liskov Substitution Principle (LSP)
- I = Interface Segregation Principle (ISP)
- D = Dependency Inversion Principle (DIP)
Kelima prinsip dalam SOLID Design Principle ini bukan sekadar teori, tapi bisa langsung diterapkan dalam pengembangan aplikasi sehari-hari, termasuk dengan PHP.Â
Single Responsibility Principle (SRP)
Prinsip Single Responsibility Principle (SRP) menyatakan bahwa sebuah kelas hanya boleh memiliki satu alasan untuk berubah. Artinya, setiap kelas harus memiliki satu fungsi atau peran yang spesifik.
Image Source: Medium – Single Responsibility Principle
Misalnya kamu punya aplikasi untuk menghitung luas beberapa bangun datar:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Square { Â Â public $length; Â Â public function __construct($length) { Â Â Â Â $this->length = $length; Â Â } } class Circle { Â Â public $radius; Â Â public function __construct($radius) { Â Â Â Â $this->radius = $radius; Â Â } } |
Kemudian ada kelas AreaCalculator untuk menjumlahkan luas semua bangun datar tersebut:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class AreaCalculator { Â Â protected $shapes; Â Â public function __construct($shapes = []) { Â Â Â Â $this->shapes = $shapes; Â Â } Â Â public function sum() { Â Â Â Â $area = []; Â Â Â Â foreach ($this->shapes as $shape) { Â Â Â Â Â Â if (is_a($shape, 'Square')) { Â Â Â Â Â Â Â Â $area[] = pow($shape->length, 2); Â Â Â Â Â Â } elseif (is_a($shape, 'Circle')) { Â Â Â Â Â Â Â Â $area[] = pi() * pow($shape->radius, 2); Â Â Â Â Â Â } Â Â Â Â } Â Â Â Â return array_sum($area); Â Â } Â Â public function output() { Â Â Â Â return 'Total luas: ' . $this->sum(); Â Â } } |
Kode di atas dianggap melanggar SRP karena AreaCalculator bertanggung jawab untuk menghitung dan juga mencetak hasil. Jika nanti format output berubah (misal ke JSON), kamu juga harus mengubah kelas ini.
- Solusi Tahap 1: Pisahkan logika perhitungan ke masing-masing bangun datar.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
interface ShapeInterface { Â Â public function area(); } class Square implements ShapeInterface { Â Â public $length; Â Â public function __construct($length) { Â Â Â Â $this->length = $length; Â Â } Â Â public function area() { Â Â Â Â return pow($this->length, 2); Â Â } } class Circle implements ShapeInterface { Â Â public $radius; Â Â public function __construct($radius) { Â Â Â Â $this->radius = $radius; Â Â } Â Â public function area() { Â Â Â Â return pi() * pow($this->radius, 2); Â Â } } class AreaCalculator { Â Â protected $shapes; Â Â public function __construct($shapes = []) { Â Â Â Â $this->shapes = $shapes; Â Â } Â Â public function sum() { Â Â Â Â $area = []; Â Â Â Â foreach ($this->shapes as $shape) { Â Â Â Â Â Â if (!($shape instanceof ShapeInterface)) { Â Â Â Â Â Â Â Â throw new Exception("Invalid shape."); Â Â Â Â Â Â } Â Â Â Â Â Â $area[] = $shape->area(); Â Â Â Â } Â Â Â Â return array_sum($area); Â Â } } |
- Solusi Tahap 2: Pisahkan logika output.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class SumCalculatorOutputter { Â Â protected $calculator; Â Â public function __construct(AreaCalculator $calculator) { Â Â Â Â $this->calculator = $calculator; Â Â } Â Â public function JSON() { Â Â Â Â return json_encode([ 'sum' => $this->calculator->sum() ]); Â Â } Â Â public function HTML() { Â Â Â Â return '<p>Total luas: ' . $this->calculator->sum() . '</p>'; Â Â } } |
Sekarang, AreaCalculator hanya akan berperan untuk menghitung dan tidak peduli bagaimana hasilnya ditampilkan.
Open-Closed Principle (OCP)
Prinsip ini menyatakan bahwa kelas harus terbuka atau fleksibel untuk ekstensi tetapi tertutup untuk modifikasi. Kamu harus bisa menambahkan fungsionalitas baru tanpa mengubah kode yang sudah ada.
Image Source: Medium – SOLID Principles Series — Part 2: Open/Closed Principle (OCP)
Dengan pendekatan sebelumnya, jika ingin menambahkan jenis bangun datar yang baru (misalnya segitiga), kamu cukup membuat kelas baru:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Triangle implements ShapeInterface { Â Â public $base; Â Â public $height; Â Â public function __construct($base, $height) { Â Â Â Â $this->base = $base; Â Â Â Â $this->height = $height; Â Â } Â Â public function area() { Â Â Â Â return 0.5 * $this->base * $this->height; Â Â } } |
Kamu tidak perlu memodifikasi AreaCalculator karena semua kelas bangun datar sudah memiliki kontrak area(). Inilah contoh penerapan OCP yang baik.
Liskov Substitution Principle (LSP)
LSP menyatakan bahwa kelas turunan harus dapat menggantikan kelas induknya tanpa harus merusak fungsionalitas program.
Image Source: Medium – The Liskov Substitution Principle in Python: A Guide to Cleaner Code
Misalnya kamu membuat kelas VolumeCalculator:
|
1 2 3 4 5 |
class VolumeCalculator extends AreaCalculator {   public function sum() {     return 100; // contoh saja   } } |
Jika kamu memakai SumCalculatorOutputter dengan VolumeCalculator, maka pastikan output-nya tetap sesuai:
|
1 2 3 |
$volume = new VolumeCalculator($shapes); $output = new SumCalculatorOutputter($volume); echo $output->HTML(); |
Jika fungsi sum() mengembalikan array, hal ini akan menimbulkan error pada HTML. Oleh karena itu, VolumeCalculator juga harus mengembalikan nilai numerik, bukan array. Ketentuan ini sejalan dengan prinsip LSP.
Interface Segregation Principle (ISP)
Prinsip ini menyatakan bahwa sebuah kelas tidak boleh dipaksa untuk mengimplementasikan interface yang tidak relevan atau tidak digunakannya.
Image Source: Medium – Interface Segregation Principle (ISP)
Contoh yang kurang tepat:
|
1 2 3 4 |
interface ShapeInterface { Â Â public function area(); Â Â public function volume(); } |
Permasalahannya, Square tidak memiliki volume(). Jadi, solusinya lebih baik pisahkan interface sesuai kebutuhan:
|
1 2 3 4 5 6 7 |
interface ShapeInterface { Â Â public function area(); } interface ThreeDimensionalShapeInterface { Â Â public function volume(); } |
Implementasi yang benar:
|
1 2 3 4 5 6 7 8 |
class Square implements ShapeInterface { Â Â // hanya perlu area() } class Cube implements ShapeInterface, ThreeDimensionalShapeInterface { Â Â public function area() {} Â Â public function volume() {} } |
Dengan ISP, setiap kelas hanya bergantung pada kontrak (interface) yang benar-benar relevan dengan fungsinya.
Dependency Inversion Principle (DIP)
Prinsip DIP menyatakan bahwa modul tingkat tinggi tidak boleh bergantung langsung pada modul tingkat rendah, tetapi harus bergantung pada abstraksi.
Image Source: YouTube – SOLID – DIP – Dependency Inversion Principle | Practical example
Contoh yang kurang tepat:
|
1 2 3 4 5 6 7 8 9 10 11 |
class MySQLConnection { Â Â public function connect() {} } class PasswordReminder { Â Â private $dbConnection; Â Â public function __construct(MySQLConnection $dbConnection) { Â Â Â Â $this->dbConnection = $dbConnection; Â Â } } |
Masalahnya, jika suatu hari kamu ingin mengganti database ke PostgreSQL, maka kamu juga harus mengubah kode di PasswordReminder. Solusinya, buat abstraksi menggunakan interface:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
interface DBConnectionInterface { Â Â public function connect(); } class MySQLConnection implements DBConnectionInterface { Â Â public function connect() { Â Â Â Â return 'Connected to MySQL'; Â Â } } class PasswordReminder { Â Â private $dbConnection; Â Â public function __construct(DBConnectionInterface $dbConnection) { Â Â Â Â $this->dbConnection = $dbConnection; Â Â } } |
Sekarang dengan cara ini, maka PasswordReminder tidak lagi bergantung pada implementasi spesifik (MySQLConnection), tetapi pada abstraksi (DBConnectionInterface). Artinya, kamu bisa dengan mudah mengganti database ke PostgreSQL atau database lain tanpa harus mengubah logika di PasswordReminder.
Implementasi Prinsip Desain SOLID dalam Proyek Web PHP di Cloud VPS DomaiNesia
Agar kamu lebih memahami bagaimana penerapan prinsip desain SOLID dapat membantu membangun arsitektur aplikasi yang lebih rapi dan fleksibel, kita akan melihat contoh studi kasus sederhana berikut.Â
Bayangkan kamu sedang membangun sistem manajemen artikel untuk blog berbasis PHP yang dihosting di Cloud VPS DomaiNesia.
1. Kebutuhan Proyek
- Menyimpan dan menampilkan artikel.
- Menambahkan fitur publikasi dan draf.
- Mengirim notifikasi saat artikel baru dipublikasikan.
- Menyediakan dua format output: HTML dan JSON (misal untuk API).
2. Sebelum Menggunakan Prinsip Desain SOLID
Berikut adalah contoh kode yang menunjukkan implementasi awal yang melanggar beberapa prinsip desain SOLID:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class ArticleManager {   public function save($article) {     // Simpan ke database   }   public function publish($article) {     // Simpan dan kirim notifikasi     mail($article->authorEmail, "Artikelmu telah dipublikasikan!", $article->title);   }   public function output($article, $format) {     if ($format === 'html') {       echo "<h1>{$article->title}</h1><p>{$article->content}</p>";     } else if ($format === 'json') {       echo json_encode($article);     }   } } |
Masalah pada kode di atas:
- Kelas ini menangani terlalu banyak peran atau tanggung jawab (Prinsip Single Responsibility Principle (SRP) dilanggar.).
- Kode sulit untuk diuji atau dikembangkan tanpa mengubah kelas inti (Prinsip Open-Closed Principle (OCP) & Dependency Inversion Principle (DIP) dilanggar.).
- Tidak ada pemisahan yang jelas antara antarmuka dan implementasi.
3. Setelah Menggunakan Prinsip Desain SOLID
a. Single Responsibility Principle (SRP)
Pisahkan peran atau tanggung jawab ke beberapa kelas kecil:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class ArticleStorage {   public function save($article) {     // Simpan ke database   } } class NotificationService {   public function notify($user, $message) {     // Kirim email atau notifikasi lainnya   } } class ArticlePublisher {   protected $storage;   protected $notifier;   public function __construct(ArticleStorage $storage, NotificationService $notifier) {     $this->storage = $storage;     $this->notifier = $notifier;   }   public function publish($article) {     $this->storage->save($article);     $this->notifier->notify($article->authorEmail, "Artikelmu telah dipublikasikan!");   } } |
b. Open-Closed Principle (OCP) & Interface Segregation Principle (ISP)
Pisahkan logika output ke dalam antarmuka agar lebih mudah diperluas atau dikembangkan:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
interface OutputFormatter { Â Â public function format($article); } class HtmlOutput implements OutputFormatter { Â Â public function format($article) { Â Â Â Â return "<h1>{$article->title}</h1><p>{$article->content}</p>"; Â Â } } class JsonOutput implements OutputFormatter { Â Â public function format($article) { Â Â Â Â return json_encode($article); Â Â } } |
c. Dependency Inversion Principle (DIP)
Gunakan abstraksi untuk menampilkan artikel:
|
1 2 3 4 5 6 7 8 9 10 11 |
class ArticlePresenter { Â Â protected $formatter; Â Â public function __construct(OutputFormatter $formatter) { Â Â Â Â $this->formatter = $formatter; Â Â } Â Â public function display($article) { Â Â Â Â echo $this->formatter->format($article); Â Â } } |
Hasilnya, dengan arsitektur software berbasis SOLID Design Principle, maka:
- Kelas lebih terfokus pada satu tanggung jawab.
- Lebih fleksibel, karena mudah menambahkan fitur baru (misalnya output ke XML) tanpa harus mengubah kelas utama.
- Lebih mudah diuji, karena setiap bagian terpisah dan dapat diganti dengan mock/stub.
Berikut diagram alurnya:
Kesimpulan
Dengan memahami dan menerapkan SOLID Design Principle, kita bisa membuat kode yang lebih bersih, fleksibel, dan mudah dikelola dalam jangka panjang. Prinsip desain SOLID bukan hanya teori akademis, tetapi pedoman praktis yang sangat berguna dalam membangun software modern.
Jika kamu sedang mengembangkan aplikasi berbasis PHP dan butuh server yang handal dan bisa dikonfigurasi sesuai kebutuhan, kamu bisa menggunakan layanan Cloud VPS DomaiNesia.Â
Dengan performa tinggi dan harga terjangkau, layanan Cloud VPS ini juga sangat cocok untuk hosting aplikasi yang menerapkan prinsip-prinsip SOLID seperti yang kita bahas di atas. Yuk, mulai praktikkan prinsip desain SOLID sekarang, dan deploy aplikasi kamu di Cloud VPS DomaiNesia!






