Hola.. hola.. hola..!! Selamat datang kembali di Blog Ruang Developer. Gimana kabarnya hari ini? Tentu masih semangat dong 😎

Pada kesempatan ini kita akan berkenalan dengan salah satu prinsip yang sangat terkenal di dunia perkodingan yaitu SOLID Principle. Apa itu SOLID Principle? Apa gunanya? Mmm, daripada menerka-nerka, yuk kita mulai aja.

SOLID Principle

SOLID Principle adalah sebuah akronim dari 5 prinsip Object Oriented Design (OOD). Prinsip ini diperkenalkan oleh Robert C. Martin (juga dikenal sebagai Uncle Bob).

Prinsip ini memberikan praktik yang cocok untuk mengembangkan software dengan pertimbangan agar mudah untuk dipertahankan (maintain) dan dikembangkan seiring dengan pertumbuhan proyek aplikasi.

SOLID mewakili 5 buah prinsip sebagai berikut:

  • S - Single-responsibility Principle
  • O - Open-closed Principle
  • L - Liskov Subtitution Principle
  • I - Interface Segregation Principle
  • D - Dependency Inversion Principle

Pada tulisan ini kita akan belajar memahami bersama-sama bagaimana SOLID Principle dapat membantu kita untuk mengembangkan software yang baik.

Single-Responsibility Principle (SRP)

There should never be more than one reason for a class to change.

Single-responsibility Principle (SRP) merupakan salah prinsip yang terbilang cukup mudah untuk kita terapkan dalam mengembangkan software. Sederhananya, prinsip ini mengatur tanggung jawab (responsibility) sebuah entitas/class. Dalam prinsip ini, sebuah entitas/class seharusnya hanya memiliki satu buah tanggung jawab. Apabila ada sebuah entitas/class yang memiliki dua tanggung jawab yang sama sekali tidak memiliki keterkaitan, maka kita harus memisahkannya menjadi dua buah entitas/class yang berbeda.

Untuk lebih memahami prinsip ini, coba kamu perhatikan contoh kode berikut:

Note: contoh menggunakan bahasa Python

class Item:
  ...

class Order:
  def add_item(Item):
    ...

  def delete_item(Item):
    ...

  def get_daily_order_history():
    ...

  def get_monthly_order_history():
    ...

Dari contoh kode tersebut, class Order memiliki 2 tanggung jawab yaitu mengelola item dalam order dan order history. Tentu hal ini tidak sesuai dengan prinsip SRP. Dengan menerapkan prinsip SRP, kita bisa memisahkan tanggung jawab yang ada pada class Order. Perhatikan contoh berikut.

Note: contoh menggunakan bahasa Python

class Item:
  ...

class Order:
  def add_item(Item):
    ...

  def delete_item(Item):
    ...

class OrderHistory:
  def get_daily_history():
    ...

  def get_monthly_history():
    ...

Open-Closed Principle (OCP)

Software entities … should be open for extension, but closed for modification.

Open-Closed Principle (OCP) menjelaskan bahwa sebuah entitas/class dapat terbuka untuk ditambahkan tetapi tertutup untuk dimodifikasi. Secara sederhana, sebuah entitas/class seharusnya dapat dengan mudah untuk dikembangkan sesuai dengan kebutuhan namun tanpa memodifikasi entitas/class tersebut.

OCP dapat diterapkan dengan memanfaatkan interface dan kelas abstrak. Interface dan abstraksi kelas ini bertujuan untuk mempermudah perbaikan atau penambahan fitur setelah pengembangan selesai tanpa harus memodifikasi entitas/class yang sudah ada. Jadi, dengan menggunakan interface atau abstrak, kita cukup membuat sebuah kelas baru yang mewarisi interface atau abstrak kelasnya ketika ingin menambahkan fungsionalitas baru.

Note: contoh menggunakan bahasa PHP

Interface Cache

<?php

interface Cache
{
  public function set($key, $value);

  public function get($key);

  public function delete($key);
}

Cache menggunkan file

<?php

class FileCache implements Cache {
  public function set($key, $value)
  {
    ...
  }

  public function get($key)
  {
    ...
  }

  public function delete($key)
  {
    ...
  }
}

Cache menggunakan redis

<?php

class RedisCache implements Cache {
  public function set($key, $value)
  {
    ...
  }

  public function get($key)
  {
    ...
  }

  public function delete($key)
  {
    ...
  }
}

Dari contoh kode tersebut, kita memiliki sebuah interface Cache yang didalamnya terdapat method-method untuk mengelola cache. Interface tersebut kemudian diimplementasikan pada kelas FileCache. Berikutnya, anggap saja suatu saat kita ingin menggunakan driver cache lainnya seperti redis. Kita cukup membuat kelas baru bernama RedisCache yang tetap mengimplementasikan interface Cache. Dengan cara seperti ini, kita tidak perlu merubah kode pada kelas FileCache yang sebelumnya sudah kita buat.

Liskov Substitution Principle (LSP)

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

Secara sederhana, prinsip ini berlaku pada hirarki pewarisan sifat yang ada pada OOP. Pada prinsip ini, seluruh subclass harus dapat berjalan dengan cara yang sama seperti superclass-nya.

Ketika kita membuat sebuah subclass, selain harus menerapkan fungsi dan properti dari superclas, subclass juga harus memiliki perilaku yang sama seperti superclass-nya. Ada beberpa aturan yang harus dipatuhi untuk mencapai hal ini, diantaranya adalah sebagai berikut.

Contravariant dan Covariant

Contravariant adalah konversi kelas dari sub kelasnya ke kelas yang lebih tinggi hierarkinya. Sedangkan covariant adalah kebalikan dari konversi. Itu mengkonversi dari kelas tinggi dalam hierarki ke yang lebih spesifik.

Preconditions dan Postconditions

Preconditions adalah aturan yang harus ada sebelum tindakan dilakukan. Misalnya, sebelum memanggil method yang membaca data dari database, sebuah kelas perlu memenuhi prasyarat bahwa koneksi database telah terbuka. Sedangkan postconditions menggambarkan keadaan kelas setelah proses selesai.

Invariant

Invariant menggambarkan kondisi proses yang benar sebelum proses dimulai dan tetap benar setelahnya. Misalnya, kelas mungkin menyertakan method yang membaca teks dari file. Jika method menangani pembukaan dan penutupan file, invarian adalah kemungkinan bahwa file tidak dibuka sebelum pemanggilan atau sesudahnya. Untuk mematuhi LSP, invarian dari kelas dasar tidak boleh diubah oleh subkelas.

Constraint

Berdasarkan sifatnya, subclass mencakup semua metode dan properti dari superclass-nya. Mereka juga dapat menambahkan properti lainnya. Constraint mengatur agar bahwa properti baru atau yang dimodifikasi tidak boleh mengubah status objek dengan cara yang tidak diizinkan oleh superclass.

Untuk lebih memahami prinsip LSP, perhatikan contoh kode berikut.

Note: contoh menggunakan bahasa PHP

<?php

abstract Product
{
  public function getName()
  {
    throw new Error("Method not implemented");
  }
}

class Electronic extends Product
{
  public function getName()
  {
    return "Electronic Product";
  }
}

class SmartPhone extends Electronic
{
  public function getName()
  {
    return "SmartPhone Product";
  }

  public function getConnectifity()
  {
    return "5G";
  }
}

class Food extends Product
{
  public function getName()
  {
    return "Food Product";
  }

  public function getExpiryDate()
  {
    return date('Y-m-d');
  }
}

Dari contoh kode di atas, kita memiliki sebuah abstract Product yang memiliki satu method yaitu getName. Kemudian terdapat kelas Electronic yang meng-extends abstract Product. Berikutnya, kita juga memiliki kelas SmartPhone yang meng-extends kelas Electronic, di dalam kelas ini terdapat method tambahan yaitu getConnectivity. Kenapa kita menamhahkan method ini di kelas SmartPhone? Karena agar kelas Electronic tetap dapat diturunkan ke kelas Electronic lainnya yang tidak memiliki konektivitas, misalnya Kipas angin.

Begitu juga dengan kelas Food yang meng-extends abstract Product, disitu kita menambahkan method getExpiryDate karena produk lainnya tidak memiliki tanggal kadaluarsa.

Interface Segregation Principle (ISP)

Clients should not be forced to depend upon interfaces that they do not use.

Prinsip ini bertujuan untuk mengurangi dan membatasi jumlah ketergantungan sebuah kelas terhadap interface yang tidak diperlukan. Saat membuat sebuah interface, sebaiknya kita membuatnya sebagai bagian yang lebih kecil dan lebih spesifik fungsinya. Sehingga, ketika kita membutuhkannya pada kelas lain, kita tidak perlu mengimplementasikan semua method yang sebenarnya tidak kita gunakan pada kelas yang kita buat.

Untuk lebih memahaminya, perhatikan contoh kode berikut:

Note: contoh menggunakan bahasa PHP

<?php

interface Authenticatable
{
  public function login();
}

interface Notifiable
{
  public function notify();
}

class Admin implements Authenticatable, Notifiable
{
  public function login()
  {
    ...
  }

  public function notify()
  {
    ...
  }
}

class Subscriber implements Notifiable
{
  public function notify()
  {
    ...
  }
}

Berdasarkan contoh tersebut, kita memiliki dua buah interface yaitu Authenticatable dan Notifiable. Pada kelas Admin, kita mengimplementasikan kedua interface. Sedangkan pada kelas Subscriber, kita hanya mengimplementasikan interface Notifiable agar dapat mengirimkan notifikasi, misalnya menggunakan email subscription. Dengan cara ini, kelas Subscriber tidak dipaksa untuk mengimplementasikan interface Authenticatable yang sebenarnya tidak diperlukan.

Dependency Inversion Principle (DIP)

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Prinsip ini hampir sama dengan konsep layering dalam sebuah aplikasi. Low-level modules bertanggung jawab dengan fungsi yang sangat detail dan high-level modules menggunakan low-level classes untuk mencapai tugas yang lebih besar. Hal ini bisa dicapai dengan bergantung pada sebuah abstraksi, ketika ada ketergantungan antar kelas seperti interface, daripada referensi langsung ke kelas lainnya.

Perhatikan contoh berikut ini:

Note: contoh menggunakan bahasa Kotlin

Membuat interface

interface EngineInterface {
   fun start()
}

Membuat kelas Boat

class Boat(private val engine: EngineInterface) {
   fun start() {
       engine.start()
   }
}

Membuat kelas jenis-jenis Engine

class PetrolEngine : EngineInterface {
   override fun start() {}
}

class DieselEngine : EngineInterface {
   override fun start() {}
}

Membuat beberapa Boat dengan Engine yang berbeda hanya dengan menggunakan base class Boat yang sama.

fun main() {
   val petrolEngine = PetrolEngine()
   val petrolBoat = Boat(petrolEngine)
   val dieselEngine = DieselEngine()
   val dieselBoat = Boat(dieselEngine)

   petrolBoat.start()
   dieselBoat.start()
}

Menerapkan prinsip Dependency Inversion membuat kita lebih fleksibel dalam membangun sistem.

Sumber:

Bagikan:

Ingin Berdiskusi?

Yuk bergabung di Grup Telegram Ruang Developer atau mulai diskusi melalui GitHub. See You!

comments powered by Disqus

Berlangganan Gratis

Kamu akan menerima email update dari Ruang Developer

Beri Dukungan

Beri dukungan, dapatkan full source code project web untuk bahan referensi, tiru, dan modifikasi.

Lightbox