Paradigma Object Oriented Programming
Paradigma Object Oriented Programming
Pada modul awal kita sudah mengetahui bahwa Dart adalah bahasa yang
mendukung pemrograman berorientasi objek. OOP adalah paradigma
pemrograman yang banyak digunakan saat ini untuk mengembangkan
aplikasi.
Paradigma OOP berdasarkan pada konsep objek yang memiliki atribut serta
dapat melakukan operasi atau prosedur tertentu. Dengan OOP, kita bisa
dengan mudah memvisualisasikan objek yang ada di dunia nyata ke dalam
program komputer. Sebagai contoh, bayangkan kucing sebagai sebuah
objek. Objek kucing ini memiliki karakteristik seperti warna bulu, usia kucing,
dan berat badan. Ciri-ciri ini disebut dengan attributes atau properties. Selain
itu kucing juga bisa melakukan beberapa hal seperti makan, tidur, dan
bermain. Perilaku pada objek kucing ini adalah sebuah method.
Encapsulation
Abstraction
Inheritance
Polymorphism
Class
Salah satu fitur utama dari OOP adalah fitur seperti class. Class merupakan
sebuah blueprint untuk membuat objek. Di dalam kelas ini kita mendefinisikan
sifat (attribute) dan perilaku (behaviour) dari objek yang akan dibuat. Sebagai
contoh kelas Animal memiliki atribut berupa nama, berat, dan umur, dll.
Kemudian perilakunya adalah makan, tidur, dsb.
Animal
+ String name
+ int age
+ double weight
- eat()
- sleep()
- poop()
2. }
Kemudian kita bisa menambahkan variabel dan fungsi pada kelas tersebut.
1. class Animal {
2. String name;
3. int age;
4. double weight;
5.
7.
8. void eat() {
9. print('$name is eating.');
11. }
12.
15. }
16.
17.
21. }
22. }
Kita menggunakan nama kelas serta diikuti dengan tanda kurung. Di dalam
tanda kurung ini kita bisa memasukkan parameter untuk menginisialisasi nilai
di dalam objek. Tanda kurung ini merupakan sebuah constructor yang akan
kita pelajari pada materi selanjutnya.
Setelah objek terbuat kita bisa menjalankan fungsi atau menampilkan nilai
dari property yang ada di dalamnya.
1. void main() {
3. dicodingCat.eat();
4. dicodingCat.poop();
5. print(dicodingCat.weight);
6. }
2. Gray is pooping.
3. 4.300000000000001
3. int _age = 0;
4. double _weight = 0;
5. }
main.dart
Animal.dart
1. import 'Animal.dart';
2.
3. void main() {
5.
6. dicodingCat.eat();
7. dicodingCat.poop();
8. print(dicodingCat.weight);
9. }
2. int _age = 0;
3. double _weight = 0;
3. _name = value;
4. }
5.
6. // Getter
2. print('$_name is eating.');
4. }
5.
6. void poop() {
7. print('$_name is pooping.');
9. }
3. int _age = 0;
4. double _weight = 0;
5.
7.
8. // Setter
11. }
12.
13. // Getter
Constructor
Ketika suatu objek dibuat, semua properti pada kelas tersebut harus memiliki
nilai. Kita dapat langsung menginisialisasi pada properti tertentu atau
menginisialisasinya melalui constructor. Constructor adalah fungsi spesial
dari sebuah kelas yang digunakan untuk membuat objek.
3. int age = 0;
4. double weight = 0;
5. }
Untuk membuat objek baru dari kelas tersebut, Anda tidak perlu memberikan
argumen apapun.
1. var dicodingCat = Animal();
Karena kita tidak memasukkan nilai ketika membuat objek, maka
nilai default dari properti atau variabel akan digunakan. Anda perlu berhati-hati
jika tidak memberikan nilai pada properti, karena akan membuat properti
bernilai null sehingga bisa menyebabkan eror.
Untuk memberikan nilai pada properti, silakan akses properti yang ada di
dalam sebuah kelas.
1. var dicodingCat = Animal();
2. dicodingCat.name = 'Gray';
3. dicodingCat.age = 2;
4. dicodingCat.weight = 4.2;
2. this.name = name;
3. this.age = age;
4. this.weight = weight;
5. // other instructions
6. }
Named Constructor
Pada beberapa kasus kita mungkin akan membutuhkan
beberapa constructor untuk skenario yang berbeda-beda. Pada situasi ini kita
bisa memanfaatkan named constructor.
2. // Statements
3. }
2. ...
3. Animal.Name(this._name);
4. Animal.Age(this._age);
5. Animal.Weight(this._weight);
6. ...
7. }
Cascade Notation
Dart juga dilengkapi dengan cascade notation atau cascade operator.
Operator ini memungkinkan kita untuk melakukan beberapa urutan operasi
pada objek yang sama. Kita bisa mengakses property dari object dan
menjalankan method di dalamnya bersamaan ketika kita menginstansiasi
object. Cascade operator dituliskan dengan dua tanda titik (.. atau ?..).
4. ..eat();
5. }
2. dicodingCat.name = 'Gray';
3. dicodingCat.eat();
2. ..name = 'Gray'
3. ..eat();
4.
5.
6. /* output
7. Gray is eating.
8. */
Cascade notation juga akan sering kita temui pada builder pattern seperti ini:
1. final addressBook = (AddressBookBuilder()
2. ..name = 'jenny'
3. ..email = '[email protected]'
4. ..phone = '415-555-0100')
5. .build();
Inheritance
Beberapa objek bisa memiliki beberapa karakteristik atau perilaku yang
sama, namun sebenarnya mereka bukanlah objek yang sama. Di sini
hadirlah peran inheritance atau pewarisan. Apa definisi
keduanya? Inheritance adalah kemampuan suatu program untuk membuat
kelas baru dari kelas yang ada. Konsep inheritance ini bisa dibayangkan
layaknya seorang anak mewarisi sifat dari orang tuanya. Di dalam OOP kelas
yang menurunkan sifat disebut sebagai kelas induk (parent class/superclass)
sementara kelas yang mewarisi kelas induknya disebut sebagai kelas anak
(child class/subclass).
Yuk kembali lagi pada contoh objek kucing. Selain kucing ada jenis hewan
lain yang bersifat sama. Misalnya ikan dan burung juga memiliki nama, berat,
dan umur. Selain itu mereka juga melakukan aktivitas seperti makan dan
tidur. Yang membedakan objek tersebut adalah cara mereka bernafas dan
bergerak. Untuk lebih memahami, perhatikanlah tabel kelas berikut:
Animal
+ name
+ weight
+age
- eat()
- sleep()
- poop()
2.
3. }
Cat.dart
Animal.dart
1. import 'Animal.dart';
2.
5.
7. this.furColor = furColor;
8. }
9.
12. }
13.
14. }
2.
3. void main() {
5. dicodingCat.walk();
6. dicodingCat.eat();
7. print(dicodingCat.weight);
8. }
9.
10. /*
11. Output :
14. 2.4000000000000004
15. */
Inheritance constructor
2. this.furColor = furColor;
3. }
Atau, kita bisa meringkasnya seperti yang telah kita pelajari pada
materi constructor.
1. Cat(String name, int age, double weight, this.furColor) : super(name,
age, weight);
Abstract Class
Sesuai namanya, abstract merupakan gambaran umum dari sebuah kelas. Ia
tidak dapat direalisasikan dalam sebuah objek. Pada modul sebelumnya kita
sudah mempunyai kelas Animal. Secara harfiah hewan merupakan sebuah
sifat. Kita tidak tahu bagaimana objek hewan tersebut. Kita bisa melihat
bentuk kucing, ikan, dan burung namun tidak untuk hewan. Maka dari itu
konsep abstract class perlu diterapkan agar kelas Animal tidak dapat
direalisasikan dalam bentuk objek namun tetap dapat menurunkan sifatnya
kepada kelas turunannya.
Untuk menjadikan sebuah kelas menjadi abstract, kita hanya perlu
menambah keyword abstract sebelum penulisan kelas:
1. abstract class Animal {
2. String name;
3. int age;
4. double weight;
5.
6. // ...
7. }
Implicit Interface
Selain abstract class, cara lain yang bisa kita gunakan untuk menerapkan
abstraksi dalam OOP adalah dengan interface. Interface atau antarmuka
merupakan set instruksi yang bisa diimplementasi oleh objek. Secara
umum, interface berfungsi sebagai penghubung antara sesuatu yang abstrak
dengan sesuatu yang nyata.
2. void fly() { }
3. }
2. String featherColor;
3.
5.
6. }
2. String featherColor;
3.
5.
6. @override
7. void fly() {
8. print('$name is flying');
9. }
10.
11. }
Enumerated Types
Bagaimana kita bisa menyimpan banyak nilai konstan di satu tempat dan
menanganinya secara bersamaan? Solusinya, Dart menyediakan
Enumerated Type, sering disebut Enumerations atau Enums. Enums mewakili
kumpulan konstan yang membuat kode kita lebih jelas dan mudah dibaca.
1. enum Rainbow {
3. }
4.
5. enum Weather {
7. }
Enums pada Dart memiliki beberapa properti bawaan yang dapat kita
gunakan untuk menampilkan seluruh nilai dalam bentuk list serta
menampilkan item dan indeks dari item tersebut.
1. print(Rainbow.values);
2. print(Rainbow.blue);
3. print(Rainbow.orange.index);
Ketika kode di atas dijalankan, maka konsol akan tampil seperti berikut:
1. [Rainbow.red, Rainbow.orange, Rainbow.yellow, Rainbow.green,
2. Rainbow.blue
3. 1
Semenjak Dart versi 2.15, Enums memiliki properti baru untuk menampilkan
nilai String setiap itemnya [3]. Anda dapat gunakan properti .name.
1. print(Rainbow.blue.name); // blue
2.
3. switch(weatherForecast) {
4. case Weather.sunny:
6. break;
7. case Weather.cloudy:
9. break;
12. break;
16. }
Dengan kecanggihan fitur yang disediakan oleh Enums, Dart versi 2.17 rilis
dengan fitur baru [4]. Enums mempunyai fitur layaknya kelas yang
memiliki attribute dan behaviour. Dengan begitu, kita dapat memberikan
variabel ataupun constructor di dalam Enums.
1. enum Weather {
2. sunny(15),
3. cloudy(34),
4. rain(69),
5. storm(83);
6.
8.
9. const Weather(this.rainAmount);
10. }
2. …
3.
4. @override
5. String toString() => "Today's weather forecast is $name with a
6. }
2. // atau
3. print(Weather.cloudy);
Mixins
Mixin adalah cara menggunakan kembali kode kelas dalam banyak hirarki
kelas. Konsep mixin mungkin adalah konsep yang baru bagi Anda karena
konsep ini tidak ada pada bahasa C# atau Java. Jadi kenapa dan kapan kita
perlu menggunakan mixin?
1. mixin Flyable {
2. void fly() {
3. print("I'm flying");
4. }
5. }
6.
7. mixin Walkable {
8. void walk() {
9. print("I'm walking");
10. }
11. }
12.
16. }
17. }
2.
4.
5. garfield.walk();
6.
7. donald.walk();
8. donald.swim();
9. }
2. void perform();
3. }
2. @override
3. void perform() {
4. print('Dancing');
5. }
6. }
7.
11. print('Singing');
12. }
13. }
2. void showTime() {
3. perform();
4. }
5. }
2. @override
3. void perform() {
4. print('Dancing');
5. }
6. }
7.
8. mixin Singer implements Performer {
9. @override
11. print('Singing');
12. }
13. }
14.
17. perform();
18. }
19. }
3. arielNoah.perform();
4. }
Coba jalankan fungsi main di atas, apakah yang akan tampil pada konsol?
Mengapa demikian? Seperti yang telah dijelaskan,
kelas mixin bersifat stack atau bertumpuk. Kelas-kelas ini berurutan dari yang
paling umum hingga paling spesifik. Sehingga sesuai urutan mixin di atas
kelas Musician akan menampilkan method dari Singer karena berada di
urutan terakhir atau paling spesifik.
Extension Methods
Pada versi 2.7 Dart mengenalkan fitur baru yaitu extension methods.
Tujuan dari fitur ini adalah supaya kita bisa membuat fungsionalitas
tambahan dari library yang sudah ada.
2. (<member definition>)*
3. } */
4.
6. List<int> sortAsc() {
9.
10.
15. min = j;
16. }
17. }
18.
19.
23. }
24.
25.
27. }
28. }
3. print(unsortedNumbers);
5. print(sortedNumbers);
6.
7.
8. /*
9. Output: [2, 5, 3, 1, 4]
10. [1, 2, 3, 4, 5]
11. */
12. }
main.dart
extensions.dart
1. import 'extension.dart';
2.
3. void main() {
5. print(unsortedNumbers);
7. print(sortedNumbers);
8. }
Rangkuman Materi
Kita telah berada di akhir dari modul Object Oriented Programming. Mari kita
uraikan materi yang sudah Anda pelajari.
Pure functions
3. }
Recursion
2. if (n <= 0) {
3. return 0;
4. } else if(n == 1) {
5. return 1;
6. } else {
8. }
9. }
Immutable variables
Anonymous Functions
Masih ingatkah Anda dengan materi function dan cara membuatnya? Seperti
yang kita tahu, untuk mendeklarasikan sebuah fungsi kita perlu
mendefinisikan nilai kembalian dan juga nama fungsinya.
1. int sum(int num1, int num2) {
3. }
Kebanyakan fungsi pada Dart memiliki nama seperti sum(), main(),
atau print(). Pada Dart kita bisa membuat fungsi yang tidak bernama
alias nameless atau anonymous. Anonymous function ini juga dikenal dengan
nama lambda.
4. };
5. }
Lalu bagaimana kita bisa menggunakan fungsi tersebut? Seperti yang telah
dijelaskan sebelumnya bahwa function adalah first-class citizen, maka fungsi
juga merupakan sebuah objek yang bisa disimpan ke dalam variabel. Kita
bisa menggunakan keyword var atau secara eksplisit menggunakan tipe
data Function.
1. void main() {
4. };
5.
6. Function printLambda = () {
8. };
9. }
2. print(sum(3, 4));
Higher-Order Functions
Setelah mempelajari modul sebelumnya, Anda mungkin bertanya apa yang
bisa dilakukan dengan lambda atau anonymous function?
2. print(message);
3. print(myFunction(3, 4));
4. }
3. myHigherOrderFunction('Hello', sum);
4.
5.
6. // Opsi 2
2. print(message);
4. }
2. print(message);
3. print(myFunction(4));
4. }
Karena kita tidak menentukan spesifikasi dari fungsi seperti jumlah parameter
atau nilai kembaliannya, maka semua jenis fungsi akan bisa dijalankan
termasuk pemanggilan myFunction seperti di atas. Untuk mengatasinya kita
bisa lebih spesifik menentukan seperti apa fungsi yang valid untuk menjadi
parameter.
1. void myHigherOrderFunction(String message, int Function(int num1, int
num2) myFunction) { }
Pada fungsi di atas kita perlu memasukkan fungsi dengan dua parameter dan
nilai kembali berupa int sebagai parameter.
Pada materi collection sebenarnya kita telah menggunakan satu fungsi yang
merupakan higher order function yaitu fungsi forEach(). Sebagai contoh kita
punya daftar bilangan fibonacci yang disimpan ke sebuah variabel.
1. var fibonacci = [0, 1, 1, 2, 3, 5, 8, 13];
Sehingga ketika memanggil fungsi ini kita bisa melakukan operasi pada
masing-masing item misalnya mencetak ke konsol.
1. fibonacci.forEach((item) {
2. print(item);
3. });
Closures
Suatu fungsi dapat dibuat dalam lingkup global atau di dalam fungsi lain.
Suatu fungsi yang dapat mengakses variabel di dalam lexical scope-nya
disebut dengan closure. Lexical scope berarti bahwa pada sebuah fungsi
bersarang (nested functions), fungsi yang berada di dalam memiliki akses ke
variabel di lingkup induknya.
3. closureExample();
4. closureExample();
5. }
6.
7. Function calculate(base) {
8. var count = 1;
9.
11. }
2. Value is 4
Rangkuman Materi
Anda berada di akhir dari modul Functional Programming. Mari kita uraikan
materi yang sudah Anda pelajari untuk mempertajam pemahaman.
Sound type system pada Dart ini sama dengan type system pada Java atau C#.
Di mana kondisi soundness ini dicapai dengan menggunakan kombinasi
pemeriksaan statis (compile-time error) dan pemeriksaan saat runtime. Sebagai
contoh, menetapkan String ke variabel int adalah kesalahan compile-time.
Casting Object ke String dengan as String akan gagal ketika runtime jika objek
tersebut bukan String.
Generic
Jika Anda perhatikan pada dokumentasi collection seperti List, sebenarnya
tipe dari List tersebut adalah List<E>. Tanda <...> ini menunjukkan
bahwa List adalah tipe generic, tipe yang memiliki tipe parameter.
Menurut coding convention dari Dart, tipe parameter dilambangkan dengan
satu huruf kapital seperti E, T, K, atau V.
Tipe parameter yang digunakan pada variabel list di atas adalah int, maka
nilai yang bisa kita masukkan adalah nilai dengan tipe int. Begitu juga jika kita
menentukan tipe parameter String, maka tipe yang bisa kita masukkan ke
dalam list hanya berupa String.
1. List<int> numberList = [1, 2, 3, 4, 5];
Dari kasus di atas kita bisa simpulkan bahwa Dart membantu kita
menghasilkan kode yang type safe dengan membatasi tipe yang bisa
digunakan ke dalam suatu objek dan menghindari bug. Selain itu generic juga
bermanfaat mengurangi duplikasi kode. Misalnya ketika Anda perlu untuk
menyimpan objek cache bertipe String dan int. Alih-alih membuat dua
objek StringCache dan IntCache, Anda bisa membuat satu objek saja
dengan memanfaatkan tipe parameter dari generic.
1. abstract class Cache<T> {
2. T getByKey(String key);
4. }
Kompiler tidak akan menunjukkan eror namun ketika kode dijalankan akan
terjadi runtime error karena List<Animal> bukanlah subtype dari List<BIrd>.
1. Unhandled exception:
Type Inference
Seperti yang kita tahu Dart mendukung type inference. Dart
memiliki analyzer yang dapat menentukan menyimpulkan tipe
untuk field, method, variabel lokal, dan beberapa tipe argumen generic.
Ketika analyzer tidak memiliki informasi yang cukup untuk menyimpulkan tipe
tertentu, maka tipe dynamic akan digunakan.
2015};
Map<String, Object>
Type inference menentukan tipe dari entri kemudian menentukan tipe dari
variabelnya. Pada contoh di atas, kedua key dari map adalah String, namun
nilainya memiliki tipe yang berbeda, yaitu String dan int, di mana keduanya
merupakan turunan dari Object. Sehingga variabel company akan memiliki
tipe Map<String, Object>.
Saat menetapkan nilai objek ke dalam objek lain, kita bisa mengganti tipenya
dengan tipe yang berbeda tergantung pada apakah objek tersebut
adalah consumer atau producer. Perhatikan assignment berikut:
1. Fish fish = Fish();
Rangkuman Materi
Anda berada di akhir dari modul Dart Type System. Mari kita uraikan materi
yang sudah Anda pelajari untuk mempertajam pemahaman.