Tutorial Pemrograman Lanjut (Advanced Programming) 2023/2024 Genap
- Nama : Akmal Ramadhan
- NPM : 2206081534
- Kelas : Pemrograman Lanjut - A
Aplikasi dapat diakses melalui link diatas.
Diterapkan pada HomePageController
, ProductController
, dan CarController
.
HomePageController
memiliki tanggung jawab untuk melakukan mapping dengan endpoint/
.ProductController
memiliki tanggung jawab untuk melakukan mapping dengan endpoint/product
.CarController
memiliki tanggung jawab untuk melakukan mapping dengan endpoint/car
. Oleh karena itu, saya membuat tiga class yang berbeda.
Pada branch before-solid
, ProductController.java
memiliki subclass yaitu CarController
. Padahal, CarController
memiliki perilaku yang berbeda dengan superclass-nya. Contohnya editProductPost
pada ProductController
menggunakan method
PUT
dan editCarPost
pada CarController
menggunakan method POST
. Oleh karena itu, objek dari superclass
tidak dapat digantikan oleh objek dari subclass-nya.
Solusi saya menghapus extends dari CarController
dan membuat CarController
menjadi class yang berdiri sendiri di
file yang berbeda.
Sudah diterapkan pada CarService
. Menurut saya, tidak perlu dipisah lagi karena interface ini fokus pada satu hal yaitu
CRUD (Create, Read, Update, Delete) untuk Car
.
Pada branch before-solid
, CarController
bergantung langsung dengan CarServiceImpl
. Hal ini tidak baik karena
CarController
seharusnya bergantung dengan interface CarService
. Oleh karena itu, saya mengganti tipe data dari variabel
carService
pada CarController
menjadi CarService
.
Menurut saya, dengan menerapkan prinsip S.O.L.I.D, kode yang saya buat menjadi lebih modular, rapi, dan mudah untuk di-maintain secara berkelanjutan. Ketika ingin melakukan modifikasi kode jadi lebih low effort karena tidak perlu mengubah banyak bagian kode. Terakhir, apabila bekerja dalam tim, menurut saya S.O.L.I.D dapat mempermudah code review dan menghindari kode yang sulit dipahami. Contoh penerapannya sudah saya jelaskan di atas.
Bagi saya, ini hanya keterbalikan dari apa yang sudah sebutkan diatas. Kode yang saya buat sulit untuk di-maintain dan membutuhkan effort yang lebih besar untuk melakukan modifikasi kode. Selain itu, apabila bekerja dalam tim, akan sulit untuk melakukan code review dan kode yang sulit dipahami.
Contoh:
Misalkan saya tidak menerapkan SRP CarController
, bagi saya akan mudah saja mencari kode bagian mana yang mengatur
mapping dengan endpoint /car
karena saya sendiri yang membuatnya. Namun, jika orang lain membaca repositori kita,
akan sulit mencari kode tersebut karena disatukan dengan file ProductController
. Selain itu, jika tidak diterapkan LSP,
subclass CarController
tidak dapat menggantikan superclass-nya.
Module 2: CI/CD Dev Ops
Berikut adalah isu-isu code quality yang dideteksi oleh PMD dan SonarCloud yang saya perbaiki:
Permasalahan: Perlu memposisikan literals terlebih dahulu dalam perbandingan String. Jika tidak, apabila argumen
.equals()
bernilai null
, maka akan terjadi NullPointerException.
Contoh Isu pada Proyek:
if (product.getProductName().equals("")) product.setProductName("Produk Tidak Diketahui");
Solusi: Menukar kedua argumen pada perbandingan String.
if ("".equals(product.getProductName())) product.setProductName("Produk Tidak Diketahui");
Permasalahan: Pernyataan percabangan atau perulangan yang tidak memiliki braces {}
.
Contoh Isu pada Proyek:
if (product.getProductQuantity() < 0) product.setProductQuantity(0);
Solusi: Menambahkan braces {}
pada pernyataan tersebut.
if (product.getProductQuantity() < 0) {
product.setProductQuantity(0);
}
Permasalahan: Modifier public
tidak diperlukan karena dalam interface sudah secara default
bersifat public
.
Contoh Isu pada Proyek:
public interface ProductService {
public Product create (Product product);
}
Solusi: Menghapus modifier public
pada method tersebut.
public interface ProductService {
Product create (Product product);
}
Permasalahan tidak relevan, karena import tersebut memang tidak digunakan.
Permasalahan: Komentar yang tidak jelas atau tidak ada.
Contoh Isu pada Proyek:
<table border="1" class="table table-striped table-responsive-md">
<thead>
<tr>
<th scope="col">Product Name</th>
Solusi: Menambahkan komentar yang jelas pada tag <table>
.
<table border="1" class="table table-striped table-responsive-md">
<!-- Tabel untuk menampilkan produk dengan detail nama, kuantitas, dan tombol edit serta delete -->
<thead>
<tr>
<th scope="col">Product Name</th>
Contoh Isu pada Proyek:
FROM docker.io/library/eclipse-temurin:21-jdk-alpine as builder
Solusi: Mengubah as
menjadi AS
pada Dockerfile.
FROM docker.io/library/eclipse-temurin:21-jdk-alpine AS builder
Permasalahan: Terdapat modifier private yang tidak berguna.
Contoh Isu pada Proyek:
public class Product {
private String productId;
private String productName;
private int productQuantity;
...
}
Solusi: Menghapus modifier private yang tidak berguna.
public class Product {
String productId;
String productName;
int productQuantity;
...
}
Permalsahan: Tag <img>
yang tidak memiliki attribute alt
.
Contoh Isu pada Proyek:
<img src="https://cdn.vcgamers.com/news/wp-content/uploads/2023/02/PODUSZKA-ROBLOX-MAN-FACE-PREZENT.jpg">
Solusi: Menambahkan attribute alt
pada tag <img>
.
<img src="https://cdn.vcgamers.com/news/wp-content/uploads/2023/02/PODUSZKA-ROBLOX-MAN-FACE-PREZENT.jpg" alt="Roblox Man Face">
Permasalahan: Nama method menggunakan snake case.
Contoh Isu pada Proyek:
void createProductPage_isCorrect(ChromeDriver driver) throws Exception {
...
}
Solusi: Mengubah nama method menggunakan camel case.
void createProductPageIsCorrect(ChromeDriver driver) throws Exception {
...
}
Permasalahan: Terdapat duplikasi kode yang tidak perlu seperti pada unit test untuk ProductRepositoryTest.java
.
Solusi: Menggabungkan kode yang duplikat ke dalam satu method yang sama.
Product initiateProduct() {
Product product = new Product();
product.setProductId("eb558e9f-1c39-460e-8860-71af6af63bd6");
product.setProductName("Sampo Cap Bambang");
product.setProductQuantity(100);
productRepository.create(product);
return product;
}
Saya pikir, saya sudah mengimplementasikan CI/CD pada proyek saya. Saya menggunakan GitHub Actions untuk menjalankan
workflow yang saya buat seperti ci.yml
, scorecard
, sonarcloud.yml
, dan pmd.yml
. Workflow-workflow ini
secara otomatis akan dijalankan ketika ada push atau pull request ke suatu branch. Pada titik ini, saya sudah
menerapkan Continuous Integration (CI). Untuk Continuous Deployment (CD), saya menggunakan Koyeb sebagai platform
yang akan secara otomatis deploy aplikasi saya ketika ada push atau pull request ke suatu branch.
Module 1: Coding Standard
1. Meaningful Names
Saya menggunakan nama yang jelas untuk penamaan variabel, fungsi, kelas, dan argumen dalam tutorial kali ini. Dengan nama yang jelas tersebut, saya tidak perlu lagi memberikan komentar untuk menjelaskan apa maksud dari keempat hal tersebut. Contoh:
@Test
void testEditQuantityToNegative() {
Product product = new Product();
...
2. Functions
Saya membuat fungsi yang menggunakan nama yang deskriptif, pendek, dan hanya melakukan satu hal. Saya juga berusaha untuk membuat fungsi yang dapat tampil pada layar saya tanpa harus melakukan scroll.
3. Comments
Saya berusaha untuk membuat komentar yang jelas dan tidak terlalu panjang. Saya juga menghindari penggunaan komentar bagi kode yang sudah terlihat jelas maksudnya. Selama pengerjaan, saya juga menerapkan "TO-DO" untuk menandai kode yang belum selesai.
4. Objects and Data Structures
Salah satu contoh yang saya terapkan yaitu ketika generate string UUID untuk tiap Product
yang dibuat.
Kode tersebut saya letakkan pada constructor dari Product
itu sendiri. Hal ini sesuai dengan prinsip OOP
daripada saya meletakkan kode tersebut pada ProductService.java
atau ProductRepository.java
.
Praktis yang saya terapkan yaitu input validation ketika membuat sebuah Product
tanpa sebuah nama atau
ketika jumlahnya bernilai negatif.
Hal pertama yang saya lakukan ketika mendapatkan sebuah kesalahan kode yaitu cek forum Discord Advanced Programming. Jika tidak ada, saya akan mencari di Stack Overflow atau Google. Apabila masih belum terpecahkan, saya mencoba untuk menghubungi asisten dosen, terkadang juga bertanya kepada teman. Selama menunggu jawaban, saya juga tidak jarang untuk mencoba bantuan AI seperti ChatGPT.
- Perasaan dalam Membuat Unit Test
Setelah menulis kode unit test, saya merasa mudah untuk mencari bug dalam kode saya. Misal, saya
ingin tau apakah kode ketika membuat Product
saya benar atau tidak. Alih-alih dengan membuka
localhost:8080/product/create
dan manual memasukkan data, dengan unit test, saya cukup menjalankan
test-nya saja. Selain itu, unit test membuat saya lebih yakin dengan kode yang saya buat.
- Banyaknya Test yang Diperlukan dalam Sebuah
Class
dan Cara untuk Yakin Bahwa Test Sudah Cukup
Menurut saya tidak ada batasan seberapa banyak test yang perlu kita buat dalam sebuah class
. Semakin
banyak test yang kita buat, semakin baik. Namun, kita juga harus memperhatikan code coverage yang
dibutuhkan. Code coverage adalah alat ukur untuk mengukur test yang sudah dibuat oleh developer.
Yang pernah saya baca, rule of thumb untuk code coverage yaitu >= 80%.
- Arti Code Coverage 100%
Dengan code coverage 100%, belum tentu menjamin bahwa kode yang kita buat terhindar dari bugs dan errors. Misalkan kita punya kode seperti berikut ini.
public int pangkat (int a, int b) {
return a * b;
}
Dan kita memiliki test seperti dibawah ini.
@Test
void testPangkat() {
assertEquals(4, pangkat(2, 2));
}
Walaupun test tersebut sudah benar dan code coverage terpenuhi, namun ada kasus dimana fungsi
pangkat
tersebut salah karena tidak sesuai dengan tujuannya.
Menurut saya, hal tersebut bukan cara yang baik dalam menerapkan prinsip clean code. Tentunya mengurangi kualitas dari clean code-nya. Isu yang muncul ada karena kita menggunakan suatu prosedur dan variabel yang sama. Saran saya, hal tersebut bisa kita buatkan ke dalam satu 'Class'. Lalu kita dapat memisahkan ke dalam fungsi yang berbeda untuk setiap test yang kita buat seperti prinsip Do One Thing.