Pengantar Menyelami Dunia OOP

 

Pengantar Menyelami Dunia OOP

“All Roads Lead to Rome” - Pepatah Romawi.

Banyak jalan menuju Roma adalah pepatah populer di Indonesia yang mengajarkan bahwa ada banyak rute menuju kota tersebut. Begitu pula dalam menyelesaikan masalah, terdapat berbagai cara yang bisa ditempuh. Di dunia pemrograman, terdapat berbagai teknik dan metode. 

Meskipun berbeda dalam pendekatan dan langkah-langkahnya, metode-metode ini menghasilkan hasil akhir yang sama. Misalnya, paradigma OOP dan functional, meskipun memiliki langkah-langkah berbeda, hasilnya tetap sama. Dengan begitu, kita tidak terpaku pada satu metode saja sehingga solusi yang dihasilkan bisa lebih beragam.

Pada modul ini, kita akan fokus membahas paradigma OOP. Berikut adalah objektif pembelajaran yang akan dicapai pada modul ini.

  • Menerangkan alasan belajar paradigma pemrograman. 
  • Mendefinisikan pengertian Object-Oriented Programming dan konsep inti di dalamnya. 
  • Mendefinisikan properti dan method di dalam object dan class. 
  • Membuat object dan class menggunakan Constructor Function dan ES6 Class. 
  • Menerapkan enkapsulasi properti dan method melalui access modifier. 
  • Mendemonstrasikan inheritance atau prototype-chain. 
  • Menerapkan constructor dan method overriding. 
  • Menggunakan object composition untuk memecahkan masalah yang kompleks.

Siap untuk menghadapi tantangan selanjutnya? Yuk, kita mulai!

Paradigma Pemrograman

Paradigma pemrograman adalah gaya atau pendekatan yang dilakukan oleh programmer dalam menulis program. Paradigma dapat menjadi pedoman dalam menulis program. Selain itu, paradigma juga memberikan pandangan yang unik dalam menyelesaikan masalah. Misalkan, paradigma object-oriented programming (OOP) memberikan pandangan bahwa menyelesaikan masalah dapat dilakukan dengan pendekatan berbasis objek.

Untuk menyelesaikan masalah dengan solusi yang optimal, kita harus memiliki berbagai macam pendekatan atau pandangan. Oleh karena itu, memahami paradigma pemrograman sangat penting karena setiap paradigma menawarkan pendekatan unik yang dapat menghasilkan solusi yang optimal. Dengan menulis kode sesuai paradigma yang ada, kode akan lebih bersih, dapat digunakan kembali (reuse), dan memudahkan berkolaborasi dengan orang lain. 

Kolaborasi akan lebih mudah dan minim masalah karena kita memiliki pemahaman dan pedoman yang sama. Misalnya, ketika menggunakan paradigma OOP, setiap orang yang terlibat di projek tersebut akan menggunakan pendekatan OOP dalam menyelesaikan masalah sehingga miskomunikasi dapat dihindari. Perlu diingat bahwa paradigma bukanlah rule yang wajib diikuti tetapi pedoman yang dapat memudahkan kita dalam menulis program.

Ada berbagai jenis paradigma pemrograman seperti imperativeobject-orientedprocedural, dan functional. Namun, dalam modul ini, kita hanya akan berfokus ke paradigma object-oriented. Bagaimana, sudah penasaran dengan paradigma tersebut?

Paradigma Berbasis Objek

Sesuai dengan namanya, di paradigma ini kita akan banyak berhubungan dengan objek. Object-oriented programming (OOP) adalah paradigma pemrograman yang memiliki pendekatan berbasis object. Object akan berinteraksi satu sama lain untuk menyelesaikan tugas sehingga membentuk keseluruhan program. Selain itu, object merupakan representasi dari entitas.

Object terdiri dari atribut informasi (property) dan perilaku (method). Property adalah informasi tentang objek tersebut seperti nama, warna, dan jenis, sedangkan method adalah aksi atau perilaku yang dapat dilakukan oleh objek seperti berjalan, berlari, dan terbang. Kita ambil contoh pada kehidupan sehari-hari, misalnya entitas kucing direpresentasikan menjadi object kucing dengan memiliki properti dan atribut. Property pada kucing adalah warna, jenis ras, nama, dan umur. Method pada kucing adalah berlari, tidur, makan, dan mencakar. 

Misalkan, Anda memiliki dua ekor kucing dengan nama Ziggy dan Garfield. Untuk setiap kucing, kita perlu untuk menentukan nama, warna, berat badan, dan perilakunya. 

Kucing pertama, kita mendefinisikan namanya adalah Ziggy, warnanya hitam putih, berat badannya 3 kg, dan perilakunya adalah suka tidur. Kemudian, kita melakukan hal yang sama untuk kucing kedua, Garfield. Kita mendefinisikan namanya Garfield, warnanya oranye, berat badannya 2 kg, dan perilakunya adalah suka bermain. Setiap memiliki kucing baru, kita akan melakukan hal ini secara berulang.

Jika memiliki lima kucing, bayangkan betapa repotnya kita untuk mendefinisikan setiap ciri-cirinya. Begitu pula dengan pemrograman, bila kita memiliki 5 objek yang berbeda, tentunya akan memakan waktu yang lama untuk mendefinisikan properti dan methodnya satu per satu. Lalu, apa solusinya? Solusinya adalah menggunakan object dan class. Selama menggunakan paradigma OOP, kita akan sering berhubungan dengan object dan class.

Object adalah bentuk nyata dari suatu entitas, sedangkan class adalah cetak biru (blueprint), cetakan atau template yang dapat kita gunakan berulang kali untuk membuat object. Object dan class mempermudah ketika ingin membuat entitas yang kompleks dengan cepat dan efektif. Anda perlu untuk mencermati kedua hal ini agar bisa menguasai paradigma OOP.

dos-6c41a555cb4fdf81dd1b83047001c8f620240730151902.jpeg

Karena object dan class memiliki peran sangat penting di paradigma OOP, kita akan membahas cara membuatnya dengan dua cara di bawah ini.

Constructor function

JavaScript bukanlah bahasa pemrograman berbasis class sehingga JavaScript tidak mengenal class. Meskipun tidak mengenal class, prinsip OOP tetap dapat diterapkan. Constructor function adalah cara yang digunakan untuk membuat object dan class sebelum adanya ES6. Cara membuat object dan class menggunakan constructor function dapat dilihat pada contoh berikut.

  1. function Person(name, age) {
  2.   this.name = name;
  3.   this.age = age;
  4. }
  5. Person.prototype.eat = function() {
  6.   console.log(`${this.name} is eating`);
  7. }

Kode di atas merupakan contoh membuat blueprint dari entitas person. JavaScript bukan bahasa pemrograman berbasis class, melainkan bahasa pemrograman berbasis prototype (prototype-based language). Oleh karena itu, Anda melihat penggunaan prototype pada contoh kode di atas. 

Prototype adalah salah satu konsep fundamental dalam JavaScript yang memungkinkan pewarisan sifat dan method antar object (akan dibahas nanti). Semua object di JavaScript memiliki properti tersembunyi bernama [[Prototype]] yang mengarah ke object prototype lain atau null.

Catatan
Properti dari sebuah object yang merujuk ke prototype-nya tidak disebut prototype. Namanya tidak standar antar JavaScript runtime, tetapi dalam praktiknya semua browser menggunakan nama __proto__.
Cara standar untuk mengakses prototype sebuah object adalah dengan metode Object.getPrototypeOf().

Kemudian, untuk membuat object person dengan constructor function dapat dilakukan seperti berikut.

  1. const person1 = new Person('Alice', 30);
  2. const person2 = new Person('Bob', 25);
  3. console.log(person1.name); // Output: Alice
  4. console.log(person2.name); // Output: Bob
  5. person1.eat();
  6. person2.eat();

Jangan heran, kalau kita dapat membuat sebuah object dari sebuah function di JavaScript, itu merupakan kemampuan dari function JavaScript. Perlu diingat bahwa function tersebut berbeda dengan function biasa karena ia adalah constructor function. Selain itu, kita tidak dapat membuat object dari arrow function karena ia tidak dapat dipanggil dengan keyword new.

Catatan
Biasanya penamaan constructor function ditulis dengan awalan huruf besar untuk membedakan dengan penamaan fungsi biasa.


ES6 Class

Cara yang lebih modern untuk membuat object dan class adalah menggunakan ES6. ES6 sudah mendukung class sehingga membuat JavaScript mirip dengan bahasa lain yang berbasis class seperti Java, C++, dan C#. Berikut cara untuk membuat class.

Dengan adanya ES6, penerapan salah satu pilar OOP akan lebih mudah terutama bagi Anda yang merupakan programmer Java, C++, dan C#. Selain itu, ES6 Class juga memungkinkan kita untuk menggunakan method super untuk memanggil constructor SuperClass (akan kita bahas di materi berikutnya).

Walaupun di JavaScript sudah mendukung class, hal itu tidak mengubah JavaScript menjadi bahasa pemrograman berbasis class. Faktanya, sintaks class di JavaScript hanyalah syntactic sugar atau cara alternatif dalam mendefinisikan constructor function. Untuk membuktikan hal tersebut, kita bisa mengecek tipe class melalui operator typeof.

Dapat terlihat bahwa outputnya adalah function. Sejauh ini, Anda sudah mengetahui cara membuat object dan class. Pemahaman ini akan sangat membantu Anda untuk memahami OOP lebih lanjut. Ke depannya, kami hanya fokus menggunakan sintaks class daripada menggunakan constructor function. Materi selanjutnya akan lebih menarik karena membahas konsep OOP. Yuk, lanjut ke materi selanjutnya!

Tonggak Utama dari OOP

OOP sangat cocok digunakan pada program yang kompleks karena dapat mengelompokkan kode menjadi object dan class. Selain itu, kode yang kompleks akan menjadi lebih bersih dan ringkas karena bisa digunakan kembali melalui konsep inheritance (class dan inheritance akan dibahas lebih detail di materi berikutnya). OOP memiliki empat pilar yang membentuknya yaitu encapsulationinheritancepolymorphism, dan abstraction.

dos-43fd9ea1788824b5d3fc5971e118136920240730152530.jpeg

Empat prinsip ini menjadi panduan dalam menulis program untuk memastikan bahwa kompleksitas selama pengembangan perangkat lunak berkurang. Di bahasa pemrograman yang kental dengan praktik OOP seperti Java, keempat konsep didukung dan dapat diterapkan dengan baik. Namun, beberapa praktik yang ada di JavaScript, seperti abstraction, tidak dapat diterapkan secara maksimal karena keterbatasan fitur yang tersedia. JavaScript sendiri sampai saat ini belum memiliki cara untuk membuat abstract class secara standar yang dibutuhkan dalam menerapkan konsep abstraction.

Untuk itu, di modul ini, kita akan membahas konsep inheritance, encapsulation, dan polymorphism di JavaScript sebagai pilar utama di dalam paradigma OOP.

Yuk, lanjut ke materi selanjutnya untuk mengupasnya lebih dalam!

Inheritance

Pilar yang akan kita bahas pertama adalah inheritance. Inheritance jika diterjemahkan ke dalam bahasa Indonesia artinya adalah pewarisan. Sesuai dengan namanya, kita bisa mewariskan harta property dan method dari sebuah class ke class lain. Umumnya, properti dan method yang diwariskan berasal dari class (induk) dan digunakan oleh class baru (anak). Sama halnya di kehidupan sehari-hari, sedikit banyaknya sebagai anak, kita memperoleh sifat dan perilaku dari orang tua.

Di OOP, inheritance memungkinkan class untuk mewarisi property dan method yang dimilikinya sehingga membantu mengurangi penulisan kode secara berulang (mengurangi redundancy kode). Misalnya, ketika kita membuat sebuah class dengan property dan method, keduanya dapat digunakan kembali oleh class lainnya melalui inheritance. Berikut adalah contohnya.

  1. kelas SuperClass {}
  2. kelas SubKelas memperluas SuperKelas { }

Istilah SuperClass dan SubClass akan sering kita dengar ketika bahas inheritance di OOP. Class yang mewariskan property dan method-nya disebut dengan SuperClass, Induk, Base, atau Parent Class. Class yang mewarisi property dan method dari class lain disebut dengan SubClass dan Children Class (Anak).

Misalnya, Anda memiliki smartphones dengan jenis Android dan iOS, setiap smartphones tersebut pasti memiliki property color, brand, model, dan method-nya adalah charging. Dengan paradigma OOP, property dan method yang memiliki kesamaan bisa kita abstraksikan menjadi sebuah class baru bernama Smartphones. Kemudian kita bisa membuat dua class baru, yaitu Android dan iOS.

dos-13f5027c4feb3f946ca8283971ec7c3a20240730152812.jpeg

Android dan iOS akan mewariskan property dan method dari class Smartphones seperti yang ada pada gambar di atas. Dengan begitu, class Android dan iOS akan memiliki property color, brand, model dan method charging. Selain itu, di masing-masing class kita dapat menambahkan property yang hanya ada pada dirinya. Misalkan, class Android memungkinkan untuk memiliki method split screen, sedangkan class iOS memungkinkan untuk memiliki method AirDrop.

dos-5d39c33682733986f428004d4328796120240730152842.jpeg

Jika contoh di atas kita terapkan pada kode JavaScript, kodenya akan menjadi seperti berikut ini.

Di implementasi kode, SubClass (AndroidiOS) hanya mendefinisikan method yang hanya ada pada dirinya. Selain itu, kita tetap dapat mengakses property dan method yang ada pada SuperClass (colorbrandmodelcharging) sehingga mengurangi penulisan kode yang berulang.

Ternyata, mengimplementasikan pewarisan di JavaScript dengan ES6 Class sangat mudah, ya. Masih ingatkah Anda bahwa sebelum adanya ES6 Class, untuk membuat class di JavaScript menggunakan constructor function. Sekarang, kita akan kilas balik ke masa sebelum adanya ES6. Kita akan mencoba merasakan penderitaan orang terdahulu dalam mengimplementasikan pewarisan sebelum adanya ES6 Class.

Tanpa menggunakan ES6 Class, pewarisan akan menggunakan teknik prototype inheritance seperti berikut ini.

Bagaimana menurut Anda? Apakah penggunaan constructor function dalam pewarisan lebih sulit untuk dibaca atau lebih mudah? Asumsi kami, Anda menjawab lebih sulit untuk dibaca. Yap, memang lebih sulit untuk dibaca. Inilah alasan mengapa ES6 hadir untuk membuat kode lebih mudah dibaca dan dipahami. Namun, perlu dicatat bahwa kedua cara tersebut sama-sama menggunakan prototype inheritance.

Untuk mengetahui asal muasal dari sebuah class, Anda dapat menggunakan instanceof. Instanceof dapat digunakan untuk menguji apakah suatu object merupakan instance dari sebuah class atau constructor function tertentu. Nilai keluaran dari instanceof adalah boolean. Jika object tersebut merupakan instance dari kelas yang diuji, nilainya akan true. Jika tidak, nilainya akan false. Perhatikan contoh berikut ini.

Berdasarkan contoh di atas, terbukti bahwa iOS dan Android adalah class yang terbuat dari constructor SmartPhones. Secara sederhana, iOS dan Android memiliki rantai prototype dengan SmartPhones.

Encapsulation

Setelah kelar membahas pilar OOP pewarisan, berikutnya kita akan membahas pilar utama berikutnya dari OOP yaitu encapsulation. Encapsulation adalah proses untuk membungkus data di suatu wadah yang disebut dengan class. Menyembunyikan data adalah bagian kunci dari encapsulation.

Desain OOP yang baik adalah object hanya akan menampilkan data yang dibutuhkan oleh object lain. Data akan diisolasi dan tidak dapat diakses langsung dari luar. Secara sederhana, encapsulation adalah membuat data yang ada di class sebagai private.

Di dunia nyata, kita bisa lihat contohnya pada mesin kopi. Mesin kopi memiliki data dan method yang bersifat private seperti pengatur suhu, pemanas, dan method memanaskan air. Data dan method tersebut tidak bisa diakses oleh pihak luar (kita sebagai pengguna).

Properti dan Method

Seperti yang Anda ketahui, di dalam sebuah class kita dapat mendefinisikan dua hal, yaitu property dan method. Karena kita menerapkan encapsulation, kita harus mengatur akses dari keduanya. Secara umum, property yang ada di dalam instance class bersifat mutable (dapat diubah). Perhatikan contoh berikut ini.

Pada contoh di atas, kita menetapkan temperature mesin kopi 90 derajat Celcius, tetapi ada pengguna yang iseng mengubahnya menjadi 60. Mengubah nilai tersebut bisa saja mengakibatkan mesin kopi rusak. Meskipun kita sudah menetapkan nilai temperature, nilainya tetap dapat diubah. Hal ini tidaklah baik. Untuk mencegah hal itu terjadi lagi, kita dapat menerapkan getter dan setter.

Getter terdiri dari method get. get adalah cara untuk mendapatkan nilai dari property, sedangkan setter terdiri dari method set. set adalah method untuk menetapkan nilai property. Dengan begitu, kita dapat mengatur akses ke property yang dimiliki oleh object. Perhatikan contoh berikut ini.

Penambahan underscore (_) di variable temperature untuk menandakan bahwa nilai temperature tidak dapat diubah. Namun, sebenarnya penggunaan tanda underscore tidak benar-benar membuat property temperature tidak dapat diubah, ia masih dapat diubah. Penggunaan underscore hanyalah code convention yang disepakati oleh komunitas JavaScript.

Untuk membuat nilainya benar-benar tidak dapat diubah, Anda dapat melakukannya dengan cara berikut.

Sejak JavaScript versi ES2022, kita dapat menggunakan tanda hashtag (#) untuk membuat hak akses private pada property dan method. Pada contoh di atas, kita menambahkan tanda hashtag di variable dan method yang bersifat private. Selain itu, kita mendeklarasikan property yang bersifat private di enclosing class seperti berikut.

  1. class CoffeeMachine {
  2.   #temperature = 90; // enclosing class
  3.  
  4.   constructor(waterAmount) {
  5.     this.waterAmount = waterAmount;
  6.     this.#temperature = this.#defaultTemperature();
  7.   }
  8.  
  9.   // Kode lainnya disembunyikan
  10. }

Jika mencoba mengakses property yang bersifat private, Anda akan mendapatkan pesan error seperti berikut ini.

dos-6bfc0de8b3a881caeaf6347cc2f8aac220240730153534.jpeg

Seperti itulah konsep encapsulation, membatasi bagian kode yang dapat diakses. Secara default buatlah bagian kode menjadi tidak dapat diakses, jika tidak diperlukan.

Polymorphism

Seperti yang Anda ketahui sebelumnya bahwa kita dapat mewariskan property dan method ke class lainnya. Namun, apa yang terjadi jika SubClass ingin mengubah implementasi dari method yang diwariskan dari SuperClass? Layaknya kita sebagai anak, ingin mengubah suatu sifat atau perilaku dari orang tua yang kita mungkin tidak setuju atau butuhkan. Jangan khawatir, di OOP kita dapat mengubah implementasi method yang diturunkan dari SuperClass.

Bagaimana cara untuk mengubah implementasi yang diturunkan dari SuperClass? Caranya adalah menggunakan pilar utama lainnya dari OOP yaitu Polymorphism. Polymorphism berasal dari bahasa Yunani yang memiliki arti secara harfiah yaitu memiliki banyak bentuk. Polymorphism merupakan konsep di mana suatu entitas menjadi SuperClass untuk mewariskan property atau method ke SubClass. 

Polymorphism berhubungan erat dengan pewarisan. Sebelumnya kita memiliki SuperClass Smartphones yang memiliki property color, brand, model dan method charging. Kemudian kita memiliki SubClass yang implementasinya berbeda tergantung dengan jenisnya seperti Android dan iOS.

dos-d5d391ddead8f52be1a927af603f72bb20240730153718.jpeg

Kini, bentuk implementasi dari Smartphones berbeda untuk setiap jenis. Inilah yang disebut dengan polymorphism. Lalu, bedanya apa dong dengan pewarisan? Bedanya terdapat pada implementasi method yang diubah. Untuk mengubah implementasi method tersebut, terdapat konsep yang disebut dengan overriding. 


Overriding

OOP memiliki konsep overriding yang sangat erat kaitannya dengan pewarisan. Overriding adalah cara kita untuk membuat implementasi yang berbeda di SubClass untuk method yang diturunkan dari SuperClass. Overriding dapat diterapkan untuk membuat method yang lebih spesifik di SubClass. Selain itu, overriding juga dapat diterapkan untuk menambah properti baru di SubClass. Overriding dapat diterapkan pada constructor maupun pada method.


Overriding Constructor

Constructor adalah method khusus yang dipanggil ketika instance class dibuat. Misalnya, ketika membuat instance class dengan keyword new, constructor akan terpanggil.

  1. const android = new Android(); // constructor di class Android akan dipanggil.

Jika kita telusuri kembali pada SuperClass Smartphones, terdapat constructor yang berisikan property color, brand, dan model.

  1. class SmartPhones {
  2.   constructor(color, brand, model) {
  3.     this.color = color;
  4.     this.brand = brand;
  5.     this.model = model;
  6.   }
  7.  
  8.   charging() {
  9.     console.log(`Charging ${this.model}`);
  10.   }
  11. }

Bagaimana ketika kita butuh untuk menambahkan property baru pada SubClass Android atau iOS? Nah, kita dapat melakukan overriding constructor, caranya sesederhana mendefinisikan ulang constructor-nya seperti contoh berikut.

  1. class SmartPhones {
  2.   constructor(color, brand, model) {
  3.     this.color = color;
  4.     this.brand = brand;
  5.     this.model = model;
  6.   }
  7.  
  8.   charging() {
  9.     console.log(`Charging ${this.model}`);
  10.   }
  11. }
  12.  
  13.  
  14. class Android extends SmartPhones {
  15.   // overriding constructor
  16.   constructor(color, brand, model, device) {
  17.     super(color, brand, model);
  18.     this.device = device;
  19.   }
  20.  
  21.   splitScreen() {
  22.     console.log('Android have a Split Screen');
  23.   }
  24. }

Karena Android tidak hanya bisa dijalankan di smartphone, tetapi juga di perangkat lain seperti tablet atau smart TV, kita menambahkan properti baru yaitu device di dalam constructor SubClass Android. Properti device ini ditambahkan untuk memenuhi kebutuhan penamaan perangkat yang menjalankan sistem operasi Android.

Ketika melakukan overriding constructor, kita wajib memanggil method super() di dalam constructor. Hal ini digunakan untuk menandakan apa saja property yang diturunkan dari SuperClass. Di contoh, property yang diturunkan adalah colorbrand, dan model. Jika tidak memanggil method super(), akan terjadi error Referrence Error.

Berikut adalah kode lengkap untuk overriding constructor.

  1. class SmartPhones {
  2.   constructor(color, brand, model) {
  3.     this.color = color;
  4.     this.brand = brand;
  5.     this.model = model;
  6.   }
  7.  
  8.   charging() {
  9.     console.log(`Charging ${this.model}`);
  10.   }
  11. }
  12.  
  13.  
  14. class Android extends SmartPhones {
  15.   constructor(color, brand, model, device) {
  16.     super(color, brand, model);
  17.     this.device = device;
  18.   }
  19.  
  20.   splitScreen() {
  21.     console.log('Android have a Split Screen');
  22.   }
  23. }
  24.  
  25.  
  26. const android = new Android('white', 'B', 'Galaxy S21', 'smart TV');


Overriding Method

Selain kita bisa mengubah dan menambahkan property di constructor, kita juga dapat mengubah implementasi pada method yang diturunkan dari SuperClass. Konsep ini disebut dengan overriding method. Overriding method memungkinkan SubClass untuk membuat implementasi spesifik dari metode yang sudah ada di SuperClass.

Misalkan, kita ingin mengubah method charging() yang diturunkan dari SuperClass di SubClass Android karena Android sudah mendukung untuk fast charging. Caranya adalah dengan menulis ulang method yang ingin kita override. Perhatikan contoh berikut ini.

  1. class Android extends SmartPhones {
  2.   constructor(color, brand, model, device) {
  3.     super(color, brand, model);
  4.     this.device = device;
  5.   }
  6.  
  7.   charging() {
  8.     console.log(`Charging ${this.model} with fast charger`);
  9.   }
  10.  
  11.   splitScreen() {
  12.     console.log('Android have a Split Screen');
  13.   }
  14. }

Kita menulis ulang method charging() dengan implementasi spesifik di SubClass Android. Berbeda dengan overriding constructor, overriding method tidak wajib untuk menulis method super(). Namun, jika Anda butuh untuk memanggil method charging() dari SuperClass bersamaan dengan method charging yang sudah di-override, hal itu dapat dilakukan dengan memanggil method super() seperti contoh berikut.

  1. class Android extends SmartPhones {
  2.   constructor(color, brand, model, device) {
  3.     super(color, brand, model);
  4.     this.device = device;
  5.   }
  6.  
  7.   charging() {
  8.     // memanggil method charging dari SuperClass (SmartPhones)
  9.     super.charging();
  10.     console.log(`Charging ${this.model} with fast charger`);
  11.   }
  12.  
  13.   splitScreen() {
  14.     console.log('Android have a Split Screen');
  15.   }
  16. }

Untuk mengujinya, Anda dapat menjalankan kode berikut ini.

Object Composition

Sebelumnya, Anda sudah menguasai pilar pewarisan di OOP, bahkan Anda sudah menguasai konsep overriding. Pewarisan memungkinkan kita untuk mengurangi menulis kode secara berulang (tidak efektif). Namun, apakah pewarisan mampu untuk memecahkan masalah kode yang kompleks? Apakah pewarisan hanya mampu untuk kasus sesederhana sebelumnya? Yuk, kita eksplorasi bersama.

dos-0da5f713ad4884ccf33e564c78195dd220240805163124.png

Misalnya, Anda sedang mengembangkan sebuah video game. Video game tersebut memiliki banyak karakter seperti monster, wizard dan guardian. Setiap karakter memiliki kemampuan yang sama yaitu bergerak. Selain itu, setiap karakter memiliki kemampuan yang unik pada dirinya seperti menyerang, bertahan, dan mengeluarkan sihir. Jika skenario video game ini kita gambarkan dengan konsep OOP, karakter akan menjadi SuperClass, sedangkan monster, wizard, dan guardian akan menjadi SubClass seperti contoh berikut ini.

  1. class Character {
  2.   constructor(name, health, position) {
  3.     this.name = name;
  4.     this.health = health;
  5.     this.position = position;
  6.   }
  7.  
  8.   canMove() {
  9.     console.log(`${this.name} moves to ${this.position}!`);
  10.   }
  11. }
  12.  
  13.  
  14. class Monster extends Character {
  15.   canAttack() {
  16.     console.log(`${this.name} attacks with a weapon!`);
  17.   }
  18. }
  19.  
  20. class Guardian extends Character {
  21.   canDefend() {
  22.     console.log(`${this.name} defends with a shield!`);
  23.   }
  24. }
  25.  
  26. class Wizard extends Character {
  27.   canCastSpell() {
  28.     console.log(`${this.name} casts a magic spell!`);
  29.   }
  30. }

Oke, tidak ada yang salah dengan implementasi kode tersebut kan? Nah, timbul masalah ketika kita ingin menambahkan satu karakter lagi, misalnya karakter warrior. Warrior adalah karakter yang memiliki kekuatan super, ia bisa menyerang, bertahan, dan bergerak.

dos-bfeaf96481c8f301e1caa3ace92db43920240805142450.png

Bagaimana cara kita untuk membuat class Warrior? Anda mungkin menjawab dengan melakukan pewarisan dari SuperClass Character. Yup, hal itu benar karena memang itulah satu-satunya cara. 

  1. class Warrior extends Character {
  2.   canAttack() {
  3.     console.log(`${this.name} attacks with a weapon!`);
  4.   }
  5.  
  6.   canDefend() {
  7.     console.log(`${this.name} defends with a shield}!`);
  8.   }
  9. }

Namun, cara tersebut tidak efektif karena ketika kita mengubah implementasi salah satu method, kita perlu untuk mengubahnya di dua tempat. Katakanlah, kita mengubah method canAttack(), kita perlu untuk mengubahnya di SubClass Monster dan Warrior. Lantas, apa solusinya? Solusinya adalah mengubah pewarisan menjadi object composition.

dos-22957c1b57229211c998a57acef2eb9820240805142534.png

Kenali, Object Composition.

Object composition dapat menjadi solusi untuk masalah pewarisan yang kompleks seperti di kasus polymorphism. Jika sebelumnya, pewarisan menggunakan pendekatan peran atau identitas dalam menstrukturkan kode, yakni Monster, Wizard, Warrior, dan Guardian. Ketika menggunakan object composition, pendekatan yang digunakan adalah berbasis kemampuan, bukanlah peran atau identitas. 

Kode distrukturkan berdasarkan kemampuan, apakah ia bisa menyerang, bertahan atau mengeluarkan sihir seperti berikut ini.

  1. function canAttack(character) {
  2.   return {
  3.     attack: () => {
  4.       console.log(`${character} attacks with a weapon!`);
  5.     }
  6.   }
  7. }
  8.  
  9. function canDefend(character) {
  10.   return {
  11.     defend: () => {
  12.       console.log(`${character} defends with a shield!`);
  13.     }
  14.   }
  15. }
  16.  
  17. function canCastSpell(character) {
  18.   return {
  19.     castSpell: () => {
  20.       console.log(`${character} casts a spell!`);
  21.     }
  22.   }
  23. }

Karena struktur kode sudah dipecah berdasarkan kemampuan, bukan peran atau identitas, ke depannya ketika ada karakter baru yang memiliki kombinasi kemampuan, akan lebih mudah untuk membuatnya. Untuk membuat object, kita dapat membuat function sebagai object creator dan mengomposisikan kemampuan-kemampuan tersebut.

Di JavaScript, kita dapat mengomposisikan objek secara mudah dengan menggunakan method Object.assign()Object.assign() adalah method statis untuk menyalin semua properti dari satu atau lebih object ke objek target. Object.assign() akan mengembalikan objek target yang dimodifikasi.

  1. function createMonster(name) {
  2.   const character = new Character(name, 100, 0);
  3.   return Object.assign(character, canAttack(name));
  4. }
  5.  
  6. function createGuardian(name) {
  7.   const character = new Character(name, 100, 0);
  8.   return Object.assign(character, canDefend(name));
  9. }
  10.  
  11. function createWizard(name) {
  12.   const character = new Character(name, 100, 0);
  13.   return Object.assign(character, canCastSpell(name));
  14. }
  15.  
  16. function createWarrior(name) {
  17.   const character = new Character(name, 100, 0);
  18.   return Object.assign(character, canAttack(character), canDefend(character));
  19. }

Setelah membuat object creator, Buatlah object MonsterWarriorWizard, dan Guardian. Anda dapat melihat dan menjalankan kode lengkapnya di bawah ini.

  1. class Character {
  2.   constructor(name, health, position) {
  3.     this.name = name;
  4.     this.health = health;
  5.     this.position = position;
  6.   }
  7.  
  8.   canMove() {
  9.     console.log(`${this.name} moves to another position!`);
  10.   }
  11. }
  12.  
  13. function canAttack(character) {
  14.   return {
  15.     attack: () => {
  16.       console.log(`${character.name} attacks with a weapon!`);
  17.     }
  18.   };
  19. }
  20.  
  21. function canDefend(character) {
  22.   return {
  23.     defend: () => {
  24.       console.log(`${character.name} defends with a shield!`);
  25.     }
  26.   };
  27. }
  28.  
  29. function canCastSpell(character) {
  30.   return {
  31.     castSpell: () => {
  32.       console.log(`${character.name} casts a spell!`);
  33.     }
  34.   };
  35. }
  36.  
  37. function createMonster(name) {
  38.   const character = new Character(name, 100, 0);
  39.   return Object.assign(character, canAttack(character));
  40. }
  41.  
  42. function createGuardian(name) {
  43.   const character = new Character(name, 100, 0);
  44.   return Object.assign(character, canDefend(character));
  45. }
  46.  
  47. function createWizard(name) {
  48.   const character = new Character(name, 100, 0);
  49.   return Object.assign(character, canCastSpell(character));
  50. }
  51.  
  52. function createWarrior(name) {
  53.   const character = new Character(name, 100, 0);
  54.   return Object.assign(character, canAttack(character), canDefend(character));
  55. }
  56.  
  57. const monster = createMonster('Monster');
  58. monster.canMove();
  59. monster.attack();
  60.  
  61. const guardian = createGuardian('Guardian');
  62. guardian.canMove();
  63. guardian.defend();
  64.  
  65. const wizard = createWizard('Wizard');
  66. wizard.canMove();
  67. wizard.castSpell();
  68.  
  69. const warrior = createWarrior('Warrior');
  70. warrior.canMove();
  71. warrior.attack();
  72. warrior.defend();

Huft, tak terasa perjalanan kita sudah sejauh ini untuk menyelami dunia OOP. Anda sudah memahami cara memecahkan pewarisan yang rumit dengan object composition. Object composition dapat menjadi solusi untuk masalah pewarisan yang kompleks. Selain itu, dengan menggunakan object composition, maka pendekatan yang digunakan adalah berbasis kemampuan, bukanlah peran atau identitas.

Rangkuman Materi


Paradigma Pemrograman

Paradigma pemrograman adalah gaya atau pendekatan yang dilakukan oleh programmer dalam menulis program. Paradigma dapat menjadi pedoman dalam menulis program. Selain itu, paradigma juga memberikan pandangan yang unik dalam menyelesaikan masalah. Misalkan, paradigma object-oriented programming (OOP) memberikan pandangan bahwa menyelesaikan masalah dapat dilakukan dengan pendekatan berbasis objek.

Untuk menyelesaikan masalah dengan solusi yang optimal, kita harus memiliki berbagai macam pendekatan atau pandangan. Oleh karena itu, memahami paradigma pemrograman sangat penting karena setiap paradigma menawarkan pendekatan unik yang dapat menghasilkan solusi yang optimal. Dengan menulis kode sesuai paradigma yang ada, kode akan lebih bersih, dapat digunakan kembali (reuse), dan memudahkan berkolaborasi dengan orang lain. 

Kolaborasi akan lebih mudah dan minim masalah karena kita memiliki pemahaman dan pedoman yang sama. Misalnya, ketika menggunakan paradigma OOP, setiap orang yang terlibat di projek tersebut akan menggunakan pendekatan OOP dalam menyelesaikan masalah sehingga miskomunikasi dapat dihindari. Perlu diingat bahwa paradigma bukanlah rule yang wajib diikuti tetapi pedoman yang dapat memudahkan kita dalam menulis program.


Paradigma Berbasis Objek

Sesuai dengan namanya, di paradigma ini kita akan banyak berhubungan dengan objek. Object-oriented programming (OOP) adalah paradigma pemrograman yang memiliki pendekatan berbasis object. Object akan berinteraksi satu sama lain untuk menyelesaikan tugas sehingga membentuk keseluruhan program. Selain itu, object merupakan representasi dari entitas.

Object terdiri dari atribut informasi (property) dan perilaku (method). Property adalah informasi tentang objek tersebut seperti nama, warna, dan jenis, sedangkan method adalah aksi atau perilaku yang dapat dilakukan oleh objek seperti berjalan, berlari, dan terbang.

Object adalah bentuk nyata dari suatu entitas, sedangkan class adalah cetak biru (blueprint), cetakan atau template yang dapat kita gunakan berulang kali untuk membuat object. Object dan class mempermudah ketika ingin membuat entitas yang kompleks dengan cepat dan efektif. Anda perlu untuk mencermati kedua hal ini agar bisa menguasai paradigma OOP.

dos-a1c8f7282ec319aa60040b76e053eb9420240801154454.jpeg

Tonggak Utama dari OOP

OOP sangat cocok digunakan pada program yang kompleks karena dapat mengelompokkan kode menjadi object dan class. Selain itu, kode yang kompleks akan menjadi lebih bersih dan ringkas karena bisa digunakan kembali melalui konsep inheritance. OOP memiliki empat pilar yang membentuknya yaitu encapsulation, inheritance, polymorphism, dan abstraction.

dos-34f5f762445b523bf6f42099fecbdc4220240801154517.jpeg

Empat prinsip ini menjadi panduan dalam menulis program untuk memastikan bahwa kompleksitas selama pengembangan perangkat lunak berkurang. Di bahasa pemrograman yang kental dengan praktik OOP seperti Java, keempat konsep didukung dan dapat diterapkan dengan baik. Namun, beberapa praktik yang ada di JavaScript, seperti abstraction, tidak dapat diterapkan secara maksimal karena keterbatasan fitur yang tersedia. JavaScript sendiri sampai saat ini belum memiliki cara untuk membuat abstract class secara standar yang dibutuhkan dalam menerapkan konsep abstraction.


Inheritance

Pilar yang akan kita bahas pertama adalah inheritance. Inheritance jika diterjemahkan ke dalam bahasa Indonesia artinya adalah pewarisan. Sesuai dengan namanya, kita bisa mewariskan harta property dan method dari sebuah class ke class lain. Umumnya, properti dan method yang diwariskan berasal dari class (induk) dan digunakan oleh class baru (anak). Sama halnya di kehidupan sehari-hari, sedikit banyaknya sebagai anak, kita memperoleh sifat dan perilaku dari orang tua.

Di OOP, inheritance memungkinkan class untuk mewarisi property dan method yang dimilikinya sehingga membantu mengurangi penulisan kode secara berulang (mengurangi redundancy kode). Misalnya, ketika kita membuat sebuah class dengan property dan method, keduanya dapat digunakan kembali oleh class lainnya melalui inheritance. Berikut adalah contohnya.

  1. class SuperClass { }
  2.  
  3. class SubClass extends SuperClass { }

Istilah SuperClass dan SubClass akan sering kita dengar ketika bahas inheritance di OOP. Class yang mewariskan property dan method-nya disebut dengan SuperClass, Induk, Base, atau Parent Class. Class yang mewarisi property dan method dari class lain disebut dengan SubClass dan Children Class (Anak).

Encapsulation

Encapsulation adalah proses untuk membungkus data di suatu wadah yang disebut dengan class. Menyembunyikan data adalah bagian kunci dari encapsulation.


Desain OOP yang baik adalah object hanya akan menampilkan data yang dibutuhkan oleh object lain. Data akan diisolasi dan tidak dapat diakses langsung dari luar. Secara sederhana, encapsulation adalah membuat data yang ada di class sebagai private.


Di dunia nyata, kita bisa lihat contohnya pada mesin kopi. Mesin kopi memiliki data dan method yang bersifat private seperti pengatur suhu, pemanas, dan method memanaskan air. Data dan method tersebut tidak bisa diakses oleh pihak luar (kita sebagai pengguna).


Polymorphism

Seperti yang Anda ketahui sebelumnya bahwa kita dapat mewariskan property dan method ke class lainnya. Namun, apa yang terjadi jika SubClass ingin mengubah implementasi dari method yang diwariskan dari SuperClass? Layaknya kita sebagai anak, ingin mengubah suatu sifat atau perilaku dari orang tua yang kita mungkin tidak setuju atau butuhkan. Jangan khawatir, di OOP kita dapat mengubah implementasi method yang diturunkan dari SuperClass.

Bagaimana cara untuk mengubah implementasi yang diturunkan dari SuperClass? Caranya adalah menggunakan pilar utama lainnya dari OOP yaitu Polymorphism. Polymorphism berasal dari bahasa Yunani yang memiliki arti secara harfiah yaitu memiliki banyak bentuk. Polymorphism merupakan konsep di mana suatu entitas menjadi SuperClass untuk mewariskan property atau method ke SubClass.


Video Object Oriented Programming

Untuk memperdalam dan mempermudah pemahaman pada materi ini, Anda dapat menyimak video pembahasan berikut.


Materi Pendukung

Berikut ini beberapa materi tambahan yang bisa Anda pelajari untuk mendalami seputar Object-Oriented Programming (OOP):


Bersambung ke: 

KUIS Paradigma Pemrograman


 

Comments