OOP dan SOLID

Erick Ezrandy
10 min readApr 18, 2023

--

Sumber: https://www.boxpiper.com/static/433c2c22a5faafa867405b40da29b2ab/be464/solid-design-principle-1.jpg

Artikel ini dibuat sebagai bagian dari tugas individual review mata kuliah Proyek Perangkat Lunak Genap 2022/2023, Fasilkom UI

Saat mengembangkan suatu perangkat lunak, kita harus mengetahui terlebih dahulu paradigma dalam dunia pemrograman. Beberapa contoh paradigma yang mungkin pernah kita dengar, seperti imperative, object-oriented, procedural, dan functional. Namun, apa itu paradigma pemrograman? Paradigma pemrograman adalah pendekatan dalam memecahkan masalah dengan menggunakan bahasa pemrograman tertentu. Di artikel kali ini, saya akan membahas mengenai paradigma OOP dan prinsip SOLID.

OOP (Object Oriented Programming)

Hal-hal dasar yang perlu kita ketahui jika menggunakan paradigma ini ialah:

1. Class
Class merupakan sebuah blueprint dimana kita mendefinisikan sesuatu yang merupakan attribute ataupun behaviour/method. Misal kita buat class Mahasiswa, seperti di bawah ini:

Line 1 sampai 6 merupakan blueprint dari Mahasiswa, sedangkan line 8 merupakan instance dari blueprint tersebut. Jika line 9 dieksekusi, maka hasilnya seperti berikut:

2. Attribute/Property
Attribute/Property adalah data yang terdapat dalam sebuah class. Sebuah class tentu memiliki attribute dan mempresentasikan karakteristik dari class tersebut. Setiap class tentunya memiliki attribute yang berbeda-beda. Pada contoh sebelumnya, yaitu class Mahasiswa memiliki attribute berupa name, npm, prodi, dan tanggal lahir.

3. Method
Method menggambarkan bagaimana perilaku yang dimiliki oleh suatu class atau dengan bahasa yang mudah dipahami adalah “class tersebut bisa melakukan apa saja”.

Berdasarkan kode di atas, class Mahasiswa memiliki kemampuan untuk memperkenalkan diri (baris kode 8 hingga 9) dan kita bisa menggunakan kemampuan tersebut seperti pada baris 12.

Setelah mengetahui hal-hal dasar OOP, mari kita lanjutkan pada pilar-pilar penting dalam OOP.

1. Inheritance
Inheritance adalah sebuah kemampuan dimana class dapat menurunkan attribute dan method yang dimilikinya kepada class lain. Konsep inheritance digunakan untuk menghindari terjadinya duplikasi kode program.

Konsep inheritance membuat sebuah struktur atau hierarchy class dalam kode program. Class yang akan diturunkan disebut sebagai class induk (parent class) atau superclass, sedangkan class yang menerima penurunan disebut sebagai class anak (child class), subclass, atau derived class.

Tidak semua attribute dan method class induk akan diturunkan. Attribute dan method dengan modifier private tidak akan diturunkan kepada class anak, tetapi hanya attribute dan method dengan modifier protected dan public saja yang bisa diakses dari class anak. Selain itu, constructor parent class juga tidak diwariskan. Oleh karena itu, ketika kita ingin mengkases attribute atau method yang berada di parent class, kita bisa menggunakan keyword super(). Berikut contohnya:

Terlihat class Mahasiswa merupakan subclass dari Person dan kedua class tersebut sama-sama memiliki attribute nama dan tanggal lahir. Kita bisa menggunakan keyword super() untuk memanggil constructor parent class seperti pada line code 11. Selain itu, karena class Mahasiswa merupakan subclass dari class Person, instance class Mahasiswa (line 19) juga dapat memanggil method yang dimiliki oleh parent classnya seperti pada line code 21.

Selain itu, terdapat 3 jenis inheritance, yaitu:

a. Single Inheritance
Contoh single Inheritance adalah seperti code sebelumnya dimana class Mahasiswa merupakan subclass dari class Person.

b. Multilevel Inheritance

Inheritance ini mengacu di mana subclass dapat mewarisi superclass yang mana merupakan sebuah subclass dari superclass lain. Berikut contohnya:

c. Multiple Inheritance

Inheritance ini mengacu pada sebuah class yang dapat mewarisi lebih dari satu SuperClass. Namun, tidak semua bahasa pemrograman mendukungnya secara penuh. Berikut contoh codenya:

2. Encapsulation
Encapsulation adalah metode untuk mengatur struktur pada class dengan cara menentukan modifier pada attribute atau methodnya. Hal ini bertujuan agar informasi yang dimiliki oleh class tersebut tidak langsung dapat diubah dari luar class. Dalam OOP, terdapat 3 modifier, yaitu:

a. Public
Jika sebuah attribute dan method menggunakan modifier public, maka attribute dan method tersebut dapat diakses dari luar class tersebut. By default, modifer untuk attribute dan method adalah public. Contoh pada kode-kode sebelumnya memiliki modifier public.

b. Private
Jika sebuah attribute dan method menggunakan modifier private, attribute dan method tersebut HANYA dapat diakses dari dalam class tersebut saja. Berikut contohnya:

Ketika line 16 dan 17 dieksekusi, hasilnya seperti berikut:

Terlihat bahwa attribute dengan modifier tidak dapat diakses secara langsung dari luar class. Oleh karena itu, ketika kita ingin mendapatkan value dari attribute yang memiliki modifier private, kita harus membuat fungsi getter seperti pada baris 12–13.

c. Protected
Jika attribute dan method menggunakan modifier protected, maka attribute dan method tersebut tidak bisa diakses dari luar class, tetapi bisa diakses dari dalam class itu sendiri dan subclassnya. Berikut contohnya:

Ketika baris 25–27 dieksekusi hasilnya adalah sebagai berikut:

Terlihat bahwa dalam bahasa Python, protected attribute masih bisa diakses secara langsung dari luar, namun dalam bahasa C++ dan Java, protected attribute hanya bisa diakses dari dalam class dan subclassnya. Jika ingin membuat modifier private pada atrribute, cukup tambahkan double underscode “__” dan jika ingin membuat modifier protected pada atrribute, cukup tambahkan single underscode “_”.

3. Abstraction
Abstraction merupakan proses penanganan kompleksitas dengan menyembunyikan informasi yang tidak perlu dari pengguna. Berikut contoh implementasi pada Java:

Ketika kita membuat abstract class dan mendefinisikan suatu method dengan tipe abstract seperti kode di atas, maka ketika kita membuat subclass dari class tersebut, kita harus mengimplementasikan abstrak method tersebut. Perlu diperhatikan bahwa kita tidak dapat membuat instance dari abstract class!

4. Polymorphism
Polymorphism merupakan konsep yang mengklasifikasikan objek persis seperti induk classnya. Objek-objek yang berbeda bisa diakses melalui antarmuka atau interface yang sama. Hal tersebut berguna untuk mencegah terjadinya kebingungan data campuran. Berikut contohnya:

Kita tahu bahwa dalam OOP, subclass mewarisi attribute dan method yang dimiliki oleh superclassnya. Method tersebut dapat kita definisikan ulang di subclassnya dan hal tersebut dikenal dengan Method Overriding. Polymorphism memungkinkan kita untuk mengakses method dan attribute yang diganti ini yang memiliki nama dan parameter yang sama dengan superclassnya. Jika line 28–29 dan 32–33 dieksekusi, berikut hasilnya:

Karena polimorfisme, interpreter Python secara otomatis mengenali bahwa metode deviceType() untuk objek hp (class HandPhone) diganti. Oleh karena itu, method deviceType yang digunakan ialah method yang didefinisikan di subclassnya.

SOLID

SOLID merupakan prinsip-prinsip yang dibuat untuk membantu para programmer yang menggunakan bahasa pemograman berbasis OOP dalam membuat kodingan yang bersih, kokoh, dan mudah maintain. Prinsip ini dicetuskan oleh Robert C. Martin. Berikut prinsip-prinsipnya:

S : Single Responsibility Principle

Setiap class HANYA memiliki 1 tanggung jawab. Perhatikan kode berikut:

Terlihat bahwa class tersebut terlalu banyak tanggung jawabnya, bukan? Selain itu, bagaimana jika suatu saat nanti kamu membuat suatu class yang memiliki method lebih dari 30, tentu akan sulit memaintain kode tersebut, bukan? Solusi dari contoh kode di atas adalah sebagai berikut:

Dengan membuat class sesuai dengan tanggung jawabnya seperti pada gambar di atas, tentu akan sangat mudah memantain kode jika ada perubahan attribute atau method pada salah satu profesi tersebut.

O : Open For extension, Closed For Modification Principle

Class yang sudah dibuat dan disepakati, behaviornya harus mudah diwariskan dan tidak boleh diubah lagi. Cara mengubahnya adalah dengan mewariskan ke subclass yang punya behavior baru. Hal tersebut bisa dicapai dengan menggunakan konsep method overriding seperti yang telah dibahas sebelumnya. Misal kita ingin menambah class baru, yaitu class StarChef dan class PremiumChef, maka kode di atas akan dimodifikasi menjadi berikut:

Dengan menerapkan prinsip ini, kita dapat dengan mudah memaintain class StarChef dan class PremiumChef jika terdapat perubahan attribute atau methodnya.

L : Liskov Substitute Principle

Subclass harus dapat berjalan dengan cara yang sama seperti superclassnya atau dengan bahasa yang lebih mudah dipahami adalah “apa yang bisa dilakukan oleh parent class, maka child classnya juga dapat melakukan hal tersebut”. Misalnya, class A memiliki kemampuan untuk membuat kopi dan ada class B yang merupakan turunan dari class A. Dengan prinsip ini, seharusnya class B juga dapat membuat kopi. Berikut contohnya:

Pada contoh kode di atas, kita memiliki sebuah abstract class bernama Person yang di dalamnya terdapat sebuah method abstract, yaitu prepareExam(). Class tersebut diwariskan oleh class lain yaitu class Murid dan Mahasiswa. Untuk saat ini, class tersebut dapat berjalan dengan baik sesuai dengan fungsinya.

Selanjutnya, kita membutuhkan sebuah class person baru, misalnya class Karyawan. Untuk itu, kita tinggal membuat class baru yang mewarisi class Person. Kurang lebih kodenya akan seperti berikut:

Jika kita perhatikan kode di atas, class Karyawan memiliki behavior untuk mempersiapkan ujian. Namun seperti yang kita ketahui, seorang karyawan jarang atau bisa dibilang hanpir tidak pernah mempersiapkan ujian. Dalam kasus ini, class Person menjadi tidak relevan untuk diwariskan ke class Karyawan dan ini tentunya melanggar aturan subclass yang sudah kita bahas sebelumnya.

Oleh karena itu, kita perlu melakukan substitusi fungsi yang tidak relevan tersebut ke dalam abstract class sendiri dan diwariskan pada class yang relevan. Namun, perubahan ini tetap menjadikan class Person sebagai superclass dari hierarki yang ada saat ini. Berikut hasil modifikasinya:

I : Interface Segregation Principle

Prinsip ini bertujuan untuk mengurangi jumlah ketergantungan sebuah class terhadap interface class yang tidak dibutuhkan atau dengan kata lain satu interface mempunyai tanggung jawab yang spesifik untuk suatu class. Berikut contohnya:

Terlihat bahwa class Tiger meng-implements AnimalInterface sehingga semua behavior interface tersebut harus diimplementasikan. Namun, ada hal yang kurang tepat, yaitu terdapat function fly() dimana kenyataannya tiger tidak memiliki kemampuan untuk terbang. Bagaimana mengatasi hal tersebut?

Kita dapat menggunakan prinsip Interface Segregation Principle dimana kita perlu memisahkan behavior tersebut ke dalam interface lain. Berikut hasil modifikasinya:

Terlihat bahwa jika kita membuat class Bird, maka class tersebut akan meng-implements AnimalInterface dan FlyInterface karena class tersebut memang membutuhkan semua behavior yang ada di interface tersebut. Namun, untuk class Tiger hanya akan meng-implements AnimalInterface.

D : Dependency inversion Principle

Prinsip ini memiliki konsep dimana high-level modules tidak boleh bergantung pada low-level modules dan keduanya harus bergantung pada abstraction. Selain itu, abstraction seharusnya tidak bergantung pada detail, namun detail seharusnya bergantung pada abstraction. Berikut contohnya:

Berdasarkan kode di atas, terdapat class Athlete dimana constructornya dapat menambahkan raket dalam hal ini dimodelkan sebagai class YonexRacket. Namun, bagaimana jika kasusnya pada model Athlete yang sama, ingin mengubah raketnya? Dalam hal ini ingin menggantinya dengan raket lining, seperti ini:

Kita tidak dapat menerapkannya karena parameter dari constructor class Athlete adalah class YonexRacket. Jika kita memasukkan class LiningRacket, maka hasilnya akan seperti ini:

Terlihat bahwa kita tidak bisa langsung membuat instance dari class LiningRacket karena type yang dibutuhkan oleh constructor class Athlete adalah YonexRacket. Lantas bagaimana menyelesaikan masalah tersebut? Kita bisa menerapkan Dependency Inversion Principle dengan membuat Racket interface seperti berikut ini:

Oleh karena itu kita perlu memodifikasi kode sebelumnya dan hasilnya sebagai berikut:

Setelah memodifikasi kode seperti di atas, kita dapat dengan mudah membuat jenis Racket yang berbeda-beda, cukup dari satu model class Athlete saja. Berikut penerapan untuk pemanggilan kodenya:

Dengan menerapkan prinsip ini, dependency antar class hanya mengacu pada abstractions bukan pada concrete class. Pada contoh di atas, digambarkan dalam bentuk interface RacketInterface.

Sekian dari artikel saya terkait prinsip-prinsip OOP dan SOLID. Semoga bermanfaat 😁.

Referensi

--

--