Menerapkan Best Practice dalam Programming

Adeline Sanusie
10 min readApr 27, 2023

--

Gambar 1. Best Practice Programming

Menulis kode yang baik tidak hanya tentang menyelesaikan tugas, tetapi juga mempersiapkan diri untuk kesuksesan jangka panjang dengan mengunakan best practice agar kode lebih mudah dipelihara, efisien, dan dapat dikembangkan. Object Oriented Programming Principles dan SOLID Principles merupakan dua konsep dasar yang harus dipahami dan diterapkan oleh programmer untuk menulis kode yang berkualitas dan dapat dimodifikasi dengan mudah.

Object Oriented Programming Principles

Object Oriented Programming (OOP) adalah sebuah konsep pemrograman yang berorientasi pada data atau objek untuk mengatur desain program. OOP sangat penting bagi pengembangan software karena menyediakan cara untuk mengorganisir kode dan data secara modular sehingga dapat meningkatkan maintainability dan scalability. Selain itu, OOP memungkinkan pengembangan software untuk memodelkan dunia nyata dengan lebih akurat dan intuitif. OOP memiliki prinsip utama yaitu:

1. Encapsulation

Gambar 2a. Contoh penerapan Encapsulation

Encapsulation merujuk pada konsep pengelompokan data dan fungsi menjadi satu unit, yang disebut sebagai class. Selain itu encapsulation juga menyembunyikan detail internal class dari dunia luar.

Kode di atas termasuk penerapan Encapsulation karena class RegisterService memiliki method _run yang berisi beberapa method dan algoritma untuk memvalidasi email dan username. Dengan menerapkan Encapsulation, algoritma validasi tersebut diisolasi di dalam class sehingga tidak dapat diakses secara langsung dari luar class. Dalam hal ini, hanya method _run yang dapat dipanggil dari luar class RegisterService, sementara method is_valid_email dan is_valid_username hanya dapat diakses di dalam class RegisterService. Hal tersebut membantu memperkuat keamanan kode, menghindari modifikasi atau pemanggilan dari luar class, dan membuat kode lebih terorganisir.

2. Inheritance

Gambar 2b. Contoh penerapan Inheritance

Inheritance merujuk pada kemampuan sebuah class untuk mewarisi properti dan method dari parent class, yang disebut sebagai superclass. Oleh karena hal tersebut, kode dapat digunakan kembali karena child class mewarisi sifat umum dari parent class tanpa harus menulis ulang.

Contoh kode di atas termasuk penerapan Inheritance karena class User menggunakan keyword extends untuk mewarisi atribut dari class Equatable. Dengan mewarisi class Equatable, class User dapat menggunakan fungsi yang telah diimplementasikan pada class Equatable tanpa perlu membuatnya dari awal.

3. Polymorphism

Gambar 2c. Contoh penerapan Polymorphism

Polymorphism merujuk pada kemampuan objek yang berbeda untuk merespon pemanggilan method yang sama dengan cara berbeda. Hal ini dapat dicapai melalui overriding dan overloading. Overriding memungkinkan child class untuk memberikan implementasi sendiri dari method yang diwarisi parent class, sedangkan overloading memungkinkan class memiliki beberapa method dengan nama yang sama namun parameter berbeda.

Kode di atas termasuk penerapan Polymorphism karena method str yang didefinisikan pada class Product meng-override method str yang didefinisikan pada class ParentModel. Hal ini membuat class Product dapat memodifikasi perilaku umum dari str pada class Parent .

4. Abstraction

Gambar 2d. Contoh penerapan Abstraction

Abstraction merupakan konsep pemrograman berorientasi objek yang menunjukkan hanya atribut penting dan menyembunyikan informasi yang tidak perlu. Tujuan utama dari abstraction adalah menyembunyikan detail yang tidak perlu dari user.
Contoh kode di atas merupakan penerapan abstraction pada penggunaan class CustomUser yang mewarisi class AbstractUser. AbstractUser sendiri merupakan sebuah abstract class yang menyediakan beberapa method dan atribut yang umum digunakan dalam model user. Dalam class CustomUser, hanya atribut-atribut yang dibutuhkan yang ditampilkan sehingga menghilangkan detail yang tidak perlu dan mengurangi kompleksitas.

SOLID Principles

SOLID Principles merupakan lima prinsip Object Oriented class design. SOLID Principles membantu programmer memahami kebutuhan pola desain dan arsitektur software secara umum. Kelima prinsip tersebut yaitu:

1. Single Responsibility Principle
Prinsip Single Responsibility Principle menyatakan bahwa setiap class atau modul bertanggung jawab untuk melakukan satu hal saja. Prinsip ini sangat penting dalam menghasilkan kode yang efisien dan maintainable.

Gambar 3a. Sebelum menerapkan Single Responsibility Principle

Pada gambar di atas, terdapat fungsi format_datetime. Jika ingin mengubah format waktu, class ProductSerializer harus dimodifikasi. Hal ini mengindikasikan adanya pelanggaran terhadap prinsip Single Responsibility Principle. Untuk mengatasi hal tersebut, perlu dilakukan perbaikan dengan cara berikut:

Contoh 3b. Contoh penerapan Single Responsibility Principle

Pada gambar diatas, kode tersebut diperbaiki dengan membuat class baru bernama DateSerializer. Dengan perubahan ini, setiap class bertanggung jawab atas satu aspek dari aplikasi dan mematuhi prinsip Single Responsibility Principle. Dengan demikian untuk mengubah format waktu, hanya perlu memodifikasi class DateSerializer. Class DateSerializerdapat dipanggil secara berulang jika ingin melakukan serialisasi pada waktu dengan format yang berbeda.

2. Open-Close Principle

Gambar. 3b Contoh penerapan Open Close Principle

Prinsip Open-Close menyatakan bahwa mengubah class yang telah ada dan diuji dapat menimbulkan masalah atau bug. Untuk memenuhi prinsip tersebut, sebuah class harus terbuka untuk extension (open for extension) dan tertutup untuk modifikasi (closed for modification). Open for extension berarti behavior dari sebuah class dapat diperluas, sedangkan close for modification berarti implementasi class tidak dapat diubah.

Gambar di atas merupakan contoh penerapan Open-Close Principle. Class UserSerializer dibuat dengan umum agar penggunaannya dapat diperluas. Seperti pada Class Autherializer dengan penambahan field token. Dengan itu, field user hanya perlu memanggil method UserSerializer().

3. Liskov Substitution Principle
Liskov Substitution Principle (LSP) adalah prinsip desain yang menyatakan bahwa objek dari child class harus dapat digunakan sebagai pengganti objek dari parent class tanpa menyebabkan kesalahan. Prinsip ini memastikan bahwa child class tidak mempengaruhi parent class dan setiap objek yang dikembalikan oleh child class memiliki sifat dan perilaku yang sesuai dengan parent class. LSP sangat penting dalam pemrograman berorientasi objek untuk memastikan kualitas dan keberlanjutan kode yang baik.

Salah satu contoh penerapan Liskov Substitution Principle adalah dengan tidak menerapkan return null atau pass pada sebuah function.

4. Interface Segregation Principle
Ide umum dari prinsip ini adalah lebih baik memiliki banyak interface yang lebih kecil daripada sedikit interface yang lebih besar. Bagi software engineers, hal ini berarti tidak hanya memulai dengan interface yang sudah ada dan menambahkan method baru. Sebaliknya, dimulai dengan interface baru dan biarkan class mengimplementasi beberapa interface sesuai kebutuhan. Interface yang lebih kecil berarti bahwa developers harus memiliki preferensi untuk composition daripada inheritence dan decoupling daripada coupling.

5. Dependency Inversion Principle

Gambar. 3b Contoh penerapan Dependency Inversion Principle

Prinsip ini menyarankan developers untuk bergantung pada abstraksi daripada implementasi konkret. Dalam Dependency Inversion Principle, high-level modules tidak boleh bergantung pada low-level modules, namun keduanya harus bergantung pada abstraksi. Tujuan dari prinsip ini adalah memudahkan pengembangan dan perubahan pada modul software sehingga kode lebih fleksibel, mudah diubah, dan dapat digunakan kembali.

Gambar di atas merupakan contoh penerapan Dependency Inversion Principle dengan menggunakan abstraksi melalui class abstrak Runnable dan method abstrak run dan tidak bergantung pada implementasi konkret, melainkan hanya pada abstraksi yang didefinisikan oleh class Runnable. Kemudian, setiap service yang ada akan mewarisi class Runnable dan melakukan override pada fungsi run sesuai kebutuhannya.

Best Practice in Programming

Programming dapat menjadi tugas yang rumit dan menantang, namun dengan mengikuti best practice, programmer dapat membuat kode yang mudah dipahami dan dapat berkembang seiring waktu. Berikut merupakan beberapa best practice yang dapat membantu seorang programmer untuk menjamin kualitas kode dan meningkatkan produktivitas secara keseluruhan.

1. Mengikuti Coding Standard

Gambar 4a. Implementasi mengikuti coding standard

Mengikuti coding standard yang diakui secara industri untuk bahasa pemrograman yang digunakan sangat penting. Hal ini akan membuat kode lebih mudah dibaca dan dipahami, terutama saat bekerja dalam sebuah tim. Kode di atas sudah mengikuti standar penulisan kode Dart yang umum digunakan seperti:
• Menggunakan upper camel case untuk penamaan class Auth
• Menggunakan lower camel case untuk variabel user dan token
• Menggunakan kurung kurawal untuk konstruktor Auth dan ditulis setelah nama class
• Menggunakan kata kunci final untuk properti yang immutable seperti user dan token
• Menggunakan anotasi @override pada metode props yang merupakan hasil override dari metode yang sama pada superclass

2. Menulis kode yang jelas (clear code)

Gambar 4b. Files dan folders pada branch Money Donation
Gambar 4c. Files dan folders pada folder lib

Menulis kode yang jelas dapat dicapai dengan menggunakan penamaan yang efektif, membuat struktur dan alignment yang jelas dan teratur, menambahkan komentar dan dokumentasi hanya ketika diperlukan, serta mengorganisir files dan folders.

Gambar di atas merupakan contoh mengorganisir files dan folders dengan menambahkan README file, memberi nama yang sesuai pada file, dan membuat main pada setiap project.

3. Memastikan kode mudah dipelihara (easy to maintain)

Gambar 4d. Contoh nilai numerik hard-coded

Gunakan kembali kode yang telah dibuat dan hindari duplikasi kode agar memudahkan pemeliharaan kode. Jika terdapat perubahan pada kode yang telah diduplikasi dan modifikasi tersebut tidak menyebar ke seluruh instace yang diduplikasi maka akan timbul error dan kerusakan pada kode.

Selain itu, untuk memastikan kode mudah dipelihara, sebaiknya hindari penggunaan nilai numerik langsung dalam kode (hard-coded). Agar kode lebih fleksibel dan mudah diubah di masa depan, sebaiknya nilai-nilai tersebut dipindahkan ke dalam variabel konstan yang didefinisikan secara terpisah, seperti berikut:

Gambar 4e. Perbaikan untuk nilai numerik hard-coded

4. Memeriksa kode sebelum code review

Gambar 4f. SonarQube
Gambar 4g. Code Smelss — Duplikasi Literal

Sangat penting untuk memeriksa dan menguji kode sebelum dilakukan code review. Hal ini dapat dilakukan dengan menambahkan analysis tool kode statik ke dalam proses pengembangan integrasi yang berkelanjutan. Hal ini memungkinkan programmer mendapatkan feedback pada setiap bagian kode yang dimodifikasi.

Gambar di atas menunjukkan SonarQube sebagai platform analisis kode yang menyediakan laporan tentang kualitas kode, keamanan, kinerja, dan lainnya. SonarQube mendeteksi terdapat 122 code smell dari repository Berdonasi Back End. Gambar 4a menunjukkan salah satu contoh dari code smell yang ada yaitu terdapat 4 literal yang sama. Untuk memperbaikinya, dapat didefinisikan variabel konstan dan mengisinya dengan nilai dari “REGISTER@gmail.com”

5. Menggunakan tools yang tepat

Gambar 4h. CI/CD Pipeline Engines
Gambar 4i. Test and Coverage Failed

Gunakan tools yang tepat untuk membantu menulis kode, memeriksa keamanan kode, dan menguji kode. Hal ini dapat meningkatkan produktivitas dan membantu mempercepat proses pengembangan software.

Gambar di atas merupakan salah satu contoh penggunaan tools CI/CD pipeline untuk membantu meningkatkan efisiensi dan kecepatan pengembangan software dengan mengurangi waktu yang dibutuhkan untuk memeriksa kode. Gambar 5 menunjukkan hasil test dan coverage setelah proses refaktor, terdapat beberapa test yang gagal. Penyebab kegagalan tersebut teridentifikasi pada Gambar 5a sebagai AssertionError.

6. Refactoring

Gambar 4j. Contoh Refactor

Refactor mengacu pada proses perubahan kode tanpa mengubah fungsionalitas agar kode lebih terstruktur dan mudah dipelihara. Hal ini dapat membantu meningkatkan kualitas kode dan mengurangi kerentanan terhadap bug.

Gambar di atas merupakan contoh dari refactor. Kode tersebut adalah sebuah unit test yang digunakan untuk menguji fitur payment admin. Tujuannya adalah untuk memastikan bahwa ketika method save_model() dari objek payment_admin dipanggil dengan method POST dan atribut update_from_admin dari objek yang disimpan menjadi True, maka objek donasi yang sesuai akan memiliki atribut donatur_total menjadi 0. Dengan melakukan perubahan, test seharusnya mencerminkan perilaku yang diharapkan dari kode dan membantu mendeteksi masalah yang mungkin timbul dari perubahan pada metode save_model() .

Masalah yang dialami ketika menerapkan Best Practice

Saya dan team mengalami beberapa masalah saat menerapkan best practice programming dalam mengembangkan software. Salah satu masalah utama adalah memerlukan waktu yang lebih lama untuk proses pengembangan karena kami harus memikirkan desain dan struktur program yang baik. Selain itu, untuk menerapkan best practice dengan benar, kami harus memahami konsep OOP dan SOLID principle dengan baik. Hal ini memerlukan keterampilan yang lebih tinggi dibandingkan dengan metode pengembangan software yang sederhana.

Gambar 5a. Pipeline passed with warnings

Masalah lain yang kami hadapi selama mengembangkan software adalah runner pada GitLab. Runner tersebut menyebabkan pipeline test kami menjadi failed karena script Flutter test team kami yang tidak kompatibel dengan runner tersebut. Selain itu, terkadang pipeline dapat lolos namun dengan warnings yang mengakibatkan nilai coverage tidak dapat dilihat. Ketika pipeline test failed, solusinya adalah menggunakan Docker.

Gambar 5b. Solusi ketika pipeline test passed with warnings

Kami mencoba mengatasi kegagalan pada pipeline test dengan menggunakan Docker. Namun, penggunaan Docker yang intensif menyebabkan team kami mengalami masalah kinerja (lag) pada device yang digunakan. Hal ini mungkin disebabkan oleh keterbatasan sumber daya device yang digunakan. Kemudian, untuk mengatasi pipeline yang lolos namun dengan warnings adalah dengan menggunakan extensions Flutter Coverage pada Visual Studio Code.

Demikian yang dapat saya sampaikan pada artikel kali ini. Semoga artikel ini dapat membantu Anda dalam menerapkan best practice dengan OOP principle dan SOLID principle dalam programming. Terima kasih telah membaca artikel ini, semoga bermanfaat!

Referensi
N, Afrisal. 2023. “Pengertian OOP (Object Oriented Programming) dan 4 Prinsipnya”. Diakses dari https://www.jagoanhosting.com/blog/oop-adalah/

Mulyawan, Rifqi. 2023. “Mengenal Pengertian OOP: Apa itu Object-Oriented Programming? Cara Kerja, Manfaat, Fitur, Prinsip Dasar, Contoh dan Perbedaannya dengan Procedural!”. Diakses dari https://rifqimulyawan.com/blog/pengertian-oop/

Watts, Stephen. 2020. “The Importance of SOLID Design Principles”. Diakses dari https://www.bmc.com/blogs/solid-design-principles/#:~:text=SOLID%20is%20an%20acronym%20that,principle%2C%20and%20dependency%20inversion%20principle

codesigningstore. 2023. “The Ultimate Programming Best Practices Guide”. Diakses dari https://codesigningstore.com/ultimate-programming-best-practices-guide

--

--

Adeline Sanusie
Adeline Sanusie

Written by Adeline Sanusie

Hi everyone! I'm a Computer Science student from the University of Indonesia. I want to share my experience and journey of learning programming.