💻 Prep UAS DDP2 (Notes)

📃 Table of Contents

Semangat UAS nya guys :D

Current progress:

Additional resources:

Credits:

Buat lompat ke bagian tertentu, pake outline yang ada di bagian kanan page ini.

🌟 Soal Fast Track

📥 Download semua file

2️⃣ Soal II: Romawi Matrix

Main.java

public class Main {
    public static void main(String[] args) {
        System.out.println("=== DEMO OPERASI MATRIKS ANGKA ROMAWI ===\n");

        RomawiMatriks romawiMatriks = new RomawiMatriks();

        Romawi[][] matriks1 = {
            { new Romawi("IX"), new Romawi("IV") },
            { new Romawi("V"),  new Romawi("II") }
        };

        Romawi[][] matriks2 = {
            { new Romawi("III"), new Romawi("VI") },
            { new Romawi("I"),   new Romawi("X") }
        };

        System.out.println("=== Perkalian Matriks ===");
        Number[][] hasilKali = romawiMatriks.multiplyMatrix(matriks1, matriks2);
        
        GenericMatrix.printResult(matriks1, matriks2, hasilKali, '*');
        System.out.println();


        System.out.println("=== Penambahan Matriks ===");
        Number[][] hasilTambah = romawiMatriks.addMatrix(matriks1, matriks2);
        GenericMatrix.printResult(matriks1, matriks2, hasilTambah, '+');
        System.out.println();


        System.out.println("=== Informasi Matriks 1 ===");
        int totalElemen = romawiMatriks.hitungElemen(matriks1);
        Romawi jumlahElemen = romawiMatriks.jumlahElemen(matriks1);
        
        System.out.println("Banyaknya elemen dalam Matriks 1 : " + totalElemen);
        System.out.println("Jumlah total nilai di Matriks 1   : " + jumlahElemen + " (" + jumlahElemen.intValue() + ")");
        System.out.println();


        System.out.println("=== Simulasi Validasi Simbol Salah ===");
        try {
            System.out.println("Mencoba membuat angka Romawi 'LC'");
            new Romawi("LC");
        } catch (ExceptionInvalidRomeNumber e) {
            System.out.println("Terjadi Exception: " + e.getMessage());
        }

        try {
            System.out.println("\nMencoba membuat angka Romawi 'Z'");
            new Romawi("Z");
        } catch (ExceptionInvalidRomeNumber e) {
            System.out.println("Terjadi Exception: " + e.getMessage());
        }

        try {
            System.out.println("\nMencoba membuat angka Romawi dari bilangan negatif (-15)");
            new Romawi(-15);
        } catch (ExceptionInvalidRomeNumber e) {
            System.out.println("Terjadi Exception: " + e.getMessage());
        }
    }
}

Romawi.java

import java.util.regex.Pattern;

public class Romawi extends Number implements Comparable<Romawi> {
    private int value;
    private String roman_text;

    private static final String roman_regex = "^(C|XC|L?X{0,3})(IX|IV|V?I{0,3})$";
    private static final Pattern PATTERN = Pattern.compile(roman_regex);

    public Romawi(int value) {
        if (value < 0) {
            throw new ExceptionInvalidRomeNumber("Symbol -" + value + " bukan bilangan romawi!");
        }
        this.value = value;
        this.roman_text = intToRoman(value);
    }

    public Romawi(String roman_string) {
        if (roman_string == null || roman_string.trim().isEmpty()) throw new ExceptionInvalidRomeNumber("Symbol " + roman_string + " bukan bilangan romawi!");

        String sanitized = roman_string.trim().toUpperCase();

        // validasi regexxxxx
        if (!PATTERN.matcher(sanitized).matches()) throw new ExceptionInvalidRomeNumber("Symbol " + roman_string + " bukan bilangan romawi!");

        this.value = romanToInt(sanitized);
        this.roman_text = sanitized;
    }

    private int romanToInt(String roman) {
        int total = 0;
        int previousVal = 0;

        for (int i = roman.length()-1; i >= 0; i--) {
            int currentVal = charToValue(roman.charAt(i));
            if (currentVal < previousVal) {
                total -= currentVal;
            } else {
                total += currentVal;
            }
            previousVal = currentVal;
        }
        return total;
    }

    private int charToValue(char c) {
        switch(c) {
            case 'I': return 1;
            case 'V': return 5;
            case 'X': return 10;
            case 'L': return 50;
            case 'C': return 100;
            default: throw new ExceptionInvalidRomeNumber("Symbol " + c + " bukan bilangan romawi!");
        }
    }

    private String intToRoman(int num) {
        if (num == 0) return "0";

        int[] values = {100,90,50,40,10,9,5,4,1};
        String[] symbols = {"C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < values.length && num > 0; i++) {
            while (num >= values[i]) {
                num -= values[i];
                sb.append(symbols[i]);
            }
        }
        return sb.toString();
    }

    @Override
    public String toString() {
        return roman_text;
    }

    @Override public int intValue() { return value; }
    @Override public long longValue() { return value; }
    @Override public float floatValue() { return (float) value; }
    @Override public double doubleValue() { return (double) value; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Romawi)) return false;
        return this.value == ((Romawi) o).value;
    }

    @Override
    public int compareTo(Romawi o) {
        return Integer.compare(this.value, o.value);
    }
}

RomawiMatriks.java

public class RomawiMatriks extends GenericMatrix<Romawi> {
    @Override
    protected Romawi add(Romawi o1, Romawi o2) {
        return new Romawi(o1.intValue() + o2.intValue());
    }

    @Override
    protected Romawi multiply(Romawi o1, Romawi o2) {
        return new Romawi(o1.intValue() * o2.intValue());
    }

    @Override
    protected Romawi zero() {
        return new Romawi(0);
    }
}

Exception

public class ExceptionInvalidRomeNumber extends RuntimeException {
    public ExceptionInvalidRomeNumber(String s) {
        super(s);
    }

    public ExceptionInvalidRomeNumber(String s, Throwable t) {
        super(s, t);
    }
}

GenericMatrix.java (Template)

  public abstract class GenericMatrix<E extends Number> {
  /** Abstract method for adding two elements of the matrices */
  protected abstract E add(E o1, E o2);

  /** Abstract method for multiplying two elements of the matrices */
  protected abstract E multiply(E o1, E o2);

  /** Abstract method for defining zero for the matrix element */
  protected abstract E zero();

  /** Add two matrices */
  public E[][] addMatrix(E[][] matrix1, E[][] matrix2) {
    // Check bounds of the two matrices
    if ((matrix1.length != matrix2.length) ||  // banyaknya baris
        (matrix1[0].length != matrix2[0].length)) {  // banyaknya kolom
      throw new RuntimeException(
        "The matrices do not have the same size");
    }

    //E[][] result = new E[][] ; // tidak bisa, karena type erasure java.

    E[][] result =
      (E[][])new Number[matrix1.length][matrix1[0].length];

    // Perform addition
    for (int i = 0; i < result.length; i++)
      for (int j = 0; j < result[i].length; j++) {
        result[i][j] = add(matrix1[i][j],matrix2[i][j]) ;
      }

    return result;
  }

  public int hitungElemen(E[][] matriks){
    int count = 0;
    for(int i=0 ; i<matriks.length; i++)
        for(int j=0; j<matriks[0].length; j++)
          count++;
    return count;
  } 

  public E jumlahElemen(E[][] matriks){
    E jumlah = zero();
    for(int i=0 ; i<matriks.length; i++)
        for(int j=0; j<matriks[0].length; j++)
          jumlah = add(jumlah,matriks[i][j]);

    return jumlah;
  }

  /** Multiply two matrices */
  public E[][] multiplyMatrix(E[][] matrix1, E[][] matrix2) {
    // Check bounds
    if (matrix1[0].length != matrix2.length) {
      throw new RuntimeException(
        "The matrices do not have compatible size");
    }

    // Create result matrix
    E[][] result =
      (E[][])new Number[matrix1.length][matrix2[0].length];

    // Perform multiplication of two matrices
    for (int i = 0; i < result.length; i++) {
      for (int j = 0; j < result[0].length; j++) {
        result[i][j] = zero();

        for (int k = 0; k < matrix1[0].length; k++) {
          result[i][j] = add(result[i][j],
            multiply(matrix1[i][k], matrix2[k][j]));
        }
      }
    }

    return result;
  }

  /** Print matrices, the operator, and their operation result */
  public static void printResult(
      Number[][] m1, Number[][] m2, Number[][] m3, char op) {
    for (int i = 0; i < m1.length; i++) {
      for (int j = 0; j < m1[0].length; j++)
        System.out.print(" " + m1[i][j]);

      if (i == m1.length / 2)
        System.out.print("  " + op + "  ");
      else
        System.out.print("     ");

      for (int j = 0; j < m2.length; j++)
        System.out.print(" " + m2[i][j]);

      if (i == m1.length / 2)
        System.out.print("  =  ");
      else
        System.out.print("     ");

      for (int j = 0; j < m3.length; j++)
        System.out.print(m3[i][j] + " ");

      System.out.println();
    }
  }
}

4️⃣ Soal IV: Crazy Eight

SOLID Principles Analysis

Singkatan dari SOLID:

  1. Single Responsibility Principles (SRP)
    Intinya satu class/method cuman boleh punya satu tugas atau tujuan yang spesifik -> pecahin beberapa hal besar, menjadi lebih spesifik lagi. Tujuannya biar kode lebih kecil, gampang buat di test dan debug, dan menghindari coupling.
  2. Open / Close Principles
    Intinya kode harus terbuka dalam artian saat diperluas, tapi tertutup dalam modifikasi (sebisa mungkin jangan dimodifikasi kedepannya kecuali class/method paling bawah). Tujuannya pas ada fitur baru, cukup tambahin / turunin class baru tanpa ubah class utama, untuk ngejamin legacy code tetep didukung.
  3. Liskov Substitution Principles
    Intinya kelas anak harus bisa menggantikan induknya tanpa ketergantungan, ini buat ngejamin inheritance yang dilakuin udah benar dan kelas anak tetep patuh ke spesifikasi dan "kontrak" yang dimiliki superclass -> makanya kenapa kita pakai interface, biar nggak ketergantungan seandainya class parent rusak/hilang.
  4. Interface Segregation Principles
    Intinya lebih baik bikin interface yang kecil, spesifik, daripada bikin satau interface gede. Tujuannya? biar lebih modular, jadi gak semua method di enforce ke kelas kelas lainnya, misalnya kasus Burung, ada burung yang terbang sama burung yang gak terbang, disinilah kita harus pecah jadi dua interface -> burungTerbang dan burungGakTerbang (misal burung onta).
  5. Dependency Inversion Principles
    Intinya program sebisa mungkin bergantung ke abstraksi kyk interface, jangan ke hal yang konkret, kelas induk (super) gak boleh bergantung ke kelas anak (sub) buat berfungsi dengan baik, jadi usahain class parent gak pakai metode atau bergantung ke class childrennya.
CrazyEight.java

Apakah CrazyEight sudah mengikuti SOLID principles dengan baik?

1. Single Responsibility Principle (SRP)

Status: Cukup Baik, namun ada kekurangan.

2. Open/Closed Principle (OCP)

Status: Kurang Baik.

3. Liskov Substitution Principle (LSP)

Status: Baik.

4. Interface Segregation Principle (ISP)

Status: Tidak Relevan (Belum Ada Interface).

5. Dependency Inversion Principle (DIP)

Status: Buruk (Pelanggaran Signifikan).

Kesimpulan dan Rekomendasi

Proyek CrazyEight belum sepenuhnya menerapkan prinsip SOLID dengan baik.

Rekomendasi Perbaikan:

  1. Gunakan Interface: Buat interface untuk Player dan logika pencocokan kartu agar aturan permainan bisa diganti tanpa mengubah kelas yang ada.
  2. Pisahkan UI dan Logika: Pindahkan penggunaan Scanner dan println ke kelas khusus UI/View.
  3. Hapus Circular Dependency: Gunakan pola desain seperti Strategy atau Observer agar Player tidak perlu mengenal kelas Eights secara langsung.
  4. Ekstrak Logika Skor: Pindahkan perhitungan skor ke kelas khusus (misalnya ScoreCalculator).

Penambahan Game Cangkul

File yang diubah/ditambah:

CrazyEight
  Cangkulan.java       (+)
  CangkulanPlayer.java (+)
  IPlayer.java         (+)
Cangkulan.java
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Cangkulan {

    private List<IPlayer> players; // List ini boleh berisi objek dari class apa pun, asalkan class tersebut mengimplementasikan interface IPlayer
    private Deck drawPile;
    private Hand discardPile;
    private Scanner in;

    public Cangkulan(int numPlayers) {
        if (numPlayers < 2 || numPlayers > 5) {
            throw new IllegalArgumentException("Jumlah pemain harus antara 2 hingga 5.");
        }

        drawPile = new Deck("Draw Pile");
        drawPile.shuffle();

        players = new ArrayList<>();
        for (int i = 1; i <= numPlayers; i++) {
            CangkulanPlayer player = new CangkulanPlayer("Pemain " + i);
            drawPile.deal(player.getHand(), 7); // Bagikan 7 kartu per pemain
            players.add(player);
        }

        discardPile = new Hand("Discards");
        in = new Scanner(System.in);
    }

    public boolean isDone() {
        for (IPlayer p : players) {
            if (p.getHand().isEmpty()) {
                return true;
            }
        }
        return false;
    }

    private int getCangkulanValue(Card card) {
        if (card.getRank() == 1) { // Ace lebih tinggi dari King
            return 14;
        }
        return card.getRank();
    }

    public void playGame() {
        int leadingPlayerIndex = 0; // Pemain 1 memulai ronde pertama

        while (!isDone()) {
            System.out.println("\n--- RONDE BARU ---");
            List<Card> trickCards = new ArrayList<>();
            List<IPlayer> trickPlayers = new ArrayList<>();

            Card leadingCard = null;
            int highestValue = -1;
            int winnerIndex = leadingPlayerIndex;

            // Setiap pemain bergiliran dalam satu ronde (trick)
            for (int i = 0; i < players.size(); i++) {
                int currentPlayerIndex = (leadingPlayerIndex + i) % players.size();
                IPlayer currentPlayer = players.get(currentPlayerIndex);

                System.out.println("Giliran: " + currentPlayer.getName());
                // Tampilkan tangan pemain (Optional)
                currentPlayer.display();

                Card playedCard = currentPlayer.play(leadingCard, drawPile);
                
                if (playedCard != null) {
                    System.out.println(currentPlayer.getName() + " mengeluarkan kartu " + playedCard);
                    trickCards.add(playedCard);
                    trickPlayers.add(currentPlayer);
                    discardPile.addCard(playedCard);

                    if (leadingCard == null) {
                        leadingCard = playedCard;
                        highestValue = getCangkulanValue(playedCard);
                        winnerIndex = currentPlayerIndex;
                    } else {
                        // Jika jenis sama dan nilainya lebih tinggi
                        if (playedCard.getSuit() == leadingCard.getSuit()) {
                            int val = getCangkulanValue(playedCard);
                            if (val > highestValue) {
                                highestValue = val;
                                winnerIndex = currentPlayerIndex;
                            }
                        }
                    }
                }

                // Cek jika pemain ini kehabisan kartu dan menang, hentikan trick
                if (currentPlayer.getHand().isEmpty()) {
                    System.out.println(">>> " + currentPlayer.getName() + " KEHABISAN KARTU DAN MENANG! <<<");
                    return;
                }

                System.out.println("-----------------");
            }

            System.out.println("Pemenang ronde ini adalah: " + players.get(winnerIndex).getName());
            leadingPlayerIndex = winnerIndex; // Pemenang jalan pertama di ronde berikutnya
            
            System.out.println("Ketik ENTER untuk lanjut...");
            in.nextLine();
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Masukkan jumlah pemain Cangkulan (2-5): ");
        int numPlayers = 2;
        if (scanner.hasNextInt()) {
            numPlayers = scanner.nextInt();
        }
        
        Cangkulan game = new Cangkulan(Math.max(2, Math.min(5, numPlayers)));
        game.playGame();
        scanner.close();
    }
}
CangkulanPlayer.java
public class CangkulanPlayer implements IPlayer {
    private String name;
    private Hand hand;

    // DIP: UI diisolasi atau disederhanakan, tidak hardcoded ke game logic yang kaku.
    public CangkulanPlayer(String name) {
        this.name = name;
        this.hand = new Hand(name);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Hand getHand() {
        return hand;
    }

    @Override
    public boolean hasSuit(int suit) {
        for (int i = 0; i < hand.size(); i++) {
            if (hand.getCard(i).getSuit() == suit) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Card play(Card leadingCard, CardCollection drawPile) {
        if (leadingCard == null) {
            // First to play, play the first card in hand (or random/strategy)
            return hand.popCard(0);
        }

        int requiredSuit = leadingCard.getSuit();
        
        // Cangkul (Draw) if no matching suit
        while (!hasSuit(requiredSuit) && !drawPile.isEmpty()) {
            Card drawn = drawPile.popCard();
            System.out.println(name + " mengcangkul (draw) " + drawn);
            hand.addCard(drawn);
        }

        // Play the matching suit card
        for (int i = 0; i < hand.size(); i++) {
            if (hand.getCard(i).getSuit() == requiredSuit) {
                return hand.popCard(i);
            }
        }

        // If draw pile is empty and still no suit, just discard any card
        if (!hand.isEmpty()) {
            return hand.popCard(0);
        }

        return null;
    }

    @Override
    public void display() {
        hand.display();
    }
}
IPlayer.java (Interface)
public interface IPlayer {
    String getName();
    Hand getHand();
    Card play(Card leadingCard, CardCollection drawPile);
    boolean hasSuit(int suit);
    void display();
}

🌟 Soal Latihan (Pak Abie)

1️⃣ Soal I: Inheritance & Abstract Class

Anda diminta untuk membuat aplikasi pemesanan makanan MakaraCafe. Anda harus mengimplementasikan sistem penentu promo harian berdasarkan kategori keanggotaan pelanggan dan kategori menu favorit mereka.

Telah disediakan sebuah abstract class sebagai kerangka dasar sistem:

abstract class PromoSystem {
    String username;
    String membershipCategory;
    String favoriteCategory;

    PromoSystem(String username, String membershipCategory, String favoriteCategory) {
        this.username = username;
        this.membershipCategory = membershipCategory;
        this.favoriteCategory = favoriteCategory;
    }

    abstract String getPromo();
}

Tugas Anda adalah membuat sebuah inherited class bernama MakaraPromo yang meng-extends class PromoSystemClass ini bertugas untuk memberikan string promo yang sesuai. Method getPromo() harus mengembalikan string promo atau pesan penolakan berdasarkan aturan berikut:

Aturan Kategori Menu & Pilihan Promo:
Menu favorit dibagi menjadi empat kategori. Setiap kategori memiliki tiga pilihan promo yang harus dikembalikan secara acak (random) jika pelanggan berhak mendapatkan promo tersebut.

Batasan:

  1. Jika membershipCategory pelanggan adalah “Newbie” dan menu favoritnya adalah “Makanan” atau “Dessert”, maka method getPromo() harus mengembalikan string"Promo Makanan dan Dessert belum tersedia untuk tingkat Newbie."
  2. Jika membershipCategory pelanggan adalah “Member” dan menu favoritnya adalah “Dessert”, maka method getPromo() harus mengembalikan string"Promo Dessert khusus untuk pelanggan VIP."

Contoh Kasus Eksekusi Soal 1:

Unit Test

import static org.junit.Assert.*;

import org.junit.Test;

public class MakaraPromoTest {

    @Test
    public void testNewbieMakananRestriction() {
        MakaraPromo advisor = new MakaraPromo("Joko", "Newbie", "Makanan");
        assertEquals(
            "Promo Makanan dan Dessert belum tersedia untuk tingkat Newbie.",
            advisor.getPromo()
        );
    }

    @Test
    public void testNewbieDessertRestriction() {
        MakaraPromo advisor = new MakaraPromo("Ayu", "Newbie", "Dessert");
        assertEquals(
            "Promo Makanan dan Dessert belum tersedia untuk tingkat Newbie.",
            advisor.getPromo()
        );
    }

    @Test
    public void testMemberDessertRestriction() {
        MakaraPromo advisor = new MakaraPromo("Bima", "Member", "Dessert");
        assertEquals(
            "Promo Dessert khusus untuk pelanggan VIP.",
            advisor.getPromo()
        );
    }

    @Test
    public void testKopiPromo() {
        MakaraPromo advisor = new MakaraPromo("Siti", "VIP", "Kopi");
        String result = advisor.getPromo();
        assertTrue(
            result.equals("Today's Promo: Diskon 10% Kopi Susu") ||
            result.equals("Today's Promo: Gratis Espresso Shot") ||
            result.equals("Today's Promo: Kopi Beli 1 Gratis 1")
        );
    }

    @Test
    public void testDessertPromoVIP() {
        MakaraPromo advisor = new MakaraPromo("Reza", "VIP", "Dessert");
        String result = advisor.getPromo();
        assertTrue(
            result.equals("Today's Promo: Diskon Brownies") ||
            result.equals("Today's Promo: Gratis Waffle") ||
            result.equals("Today's Promo: Ekstra Scoop Es Krim")
        );
    }

    @Test
    public void testInvalidCategory() {
        MakaraPromo advisor = new MakaraPromo("Tari", "Member", "Seafood");
        assertEquals(
            "Kategori menu tidak dikenali.",
            advisor.getPromo()
        );
    }

    @Test
    public void testBoundarySubscribed4() {
        MakaraPromo advisor = new MakaraPromo("Eko", "VIP", "Makanan");
        String result = advisor.getPromo();
        assertTrue(result.startsWith("Today's Promo:"));
    }
}

Solusi (To Be Added)

Empty

2️⃣ Soal II: Exception Handling

Untuk meningkatkan keandalan aplikasi, anda memutuskan untuk mengubah cara penanganan kesalahan pada kategori menu. Sistem tidak boleh lagi sekadar mengembalikan string biasa ketika menerima kategori yang tidak valid, melainkan harus melempar sebuah Exception.

Tugas Anda:

  1. Buat Custom Exception InvalidMenuCategoryException:
  1. Modifikasi Perilaku Evaluasi Promo:

Contoh Kasus Eksekusi Soal 2:

Unit Test

import static org.junit.Assert.*;

import org.junit.Test;

public class MakaraPromoExceptionTest {

    @Test
    public void testValidCategoryDoesNotThrow() {
        MakaraPromo advisor = new MakaraPromo("Andi", "Member", "Kopi");
        
        // Memastikan tidak ada exception yang terlempar untuk kategori yang valid
            String result = "";
            try {
                result = advisor.getPromoStrict();
            } catch (InvalidMenuCategoryException e) {
                fail("Exception should not have been thrown");
            }
            assertTrue(result.startsWith("Today's Promo:"));
    }

    @Test
    public void testInvalidCategoryThrowsException() {
        MakaraPromo advisor = new MakaraPromo("Budi", "Newbie", "Seafood");
        
        // Memastikan exception terlempar
        InvalidMenuCategoryException exception = assertThrows(
            InvalidMenuCategoryException.class, 
            () -> {
                advisor.getPromoStrict();
            }
        );
        
        // Memastikan pesan error sesuai dengan format yang diminta
        assertEquals("Kategori menu Seafood tidak valid!", exception.getMessage());
    }

    @Test
    public void testAnotherInvalidCategoryThrowsException() {
        MakaraPromo advisor = new MakaraPromo("Siti", "VIP", "Minuman Dingin");
        
        InvalidMenuCategoryException exception = assertThrows(
            InvalidMenuCategoryException.class, 
            () -> {
                advisor.getPromoStrict();
            }
        );
        
        assertEquals("Kategori menu Minuman Dingin tidak valid!", exception.getMessage());
    }
}

Solusi (To Be Added)

Empty

3️⃣ Soal III: Generics & Wildcard

Seiring berkembangnya aplikasi MakaraCafe, sistem membutuhkan struktur data untuk mencatat riwayat promo. Karena di masa depan mungkin akan ada tipe sistem promo lain, Anda diminta merancang penyimpanan yang fleksibel menggunakan Generics dan fungsi utilmenggunakan Wildcard.

Tugas Anda:

  1. Buat Generic Class PromoHistory<T>:
  1. Buat Class PromoUtil:

Contoh Kasus Eksekusi Soal 3:

Unit Test

import static org.junit.Assert.*;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

public class PromoGenericsTest {

    @Test
    public void testPromoHistoryGenerics() {
        PromoHistory<MakaraPromo> history = new PromoHistory<>();
        MakaraPromo promo1 = new MakaraPromo("Budi", "VIP", "Kopi");
        MakaraPromo promo2 = new MakaraPromo("Siti", "Newbie", "Camilan");
        
        // Memastikan history diinisialisasi kosong (null saat diminta getLastPromo)
        assertNull(history.getLastPromo());

        history.addPromo(promo1);
        history.addPromo(promo2);

        // Memastikan tipe T terjaga dan method bekerja
        assertEquals("Siti", history.getLastPromo().username);
        assertEquals("Newbie", history.getLastPromo().membershipCategory);
    }

    @Test
    public void testWildcardUtilCount() {
        List<MakaraPromo> listPromo = new ArrayList<>();
        listPromo.add(new MakaraPromo("Budi", "VIP", "Kopi"));
        listPromo.add(new MakaraPromo("Budi", "Member", "Dessert"));
        listPromo.add(new MakaraPromo("Ayu", "VIP", "Makanan"));

        // Wildcard method harus bisa menerima List<MakaraPromo>
        int countBudi = PromoUtil.countPromosByUsername(listPromo, "Budi");
        assertEquals(2, countBudi);

        int countAyu = PromoUtil.countPromosByUsername(listPromo, "Ayu");
        assertEquals(1, countAyu);
        
        int countJoko = PromoUtil.countPromosByUsername(listPromo, "Joko");
        assertEquals(0, countJoko);
    }
}

Solusi (To Be Added)

Empty

🌟 Soal Latihan (Pak Apri)

1️⃣ Soal I: Kerucut

📜 Lihat soal disini
📩 Download semua file

Main.java

package KerucutSort;

import java.util.Collections;
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Kerucut> kerucuts = new ArrayList<>();
        kerucuts.add(new Kerucut(4, 6, 1));
        kerucuts.add(new Kerucut(6, 7, 2));
        kerucuts.add(new Kerucut(10, 20, 3));

        Collections.sort(kerucuts, new SortKerucut());

        for (Kerucut kerucut : kerucuts) {
            System.out.println(kerucut.toString());
        }
    }
}

Kerucut.java

package KerucutSort;

public class Kerucut {
    private int id;
    private double alas;
    private double tinggi;

    public Kerucut(double alas, double tinggi, int id) {
        this.alas = alas;
        this.tinggi = tinggi;
        this.id = id;
    }

    public double getTinggi() {
        return tinggi;
    }

    public double getVolume() {
        return Math.round((1.0/3.0)*Math.PI*(Math.pow(this.alas,2))*this.tinggi);
    }

    public String toString() {
        return "Kerucut ke-" + id + "\nAlas: " + alas + "\nTinggi: " + tinggi + "\nVolume: " + this.getVolume() + "\n";
    }
}

SortKerucut.java

package KerucutSort;

import java.util.Comparator;

public class SortKerucut implements Comparator<Kerucut> {
    @Override
    public int compare(Kerucut a, Kerucut b) {
        return Double.compare(a.getVolume(), b.getVolume());
    }
}