public class NotifikasiBerlapis {
    public static void main(String[] args) {
        // Komponen dasar: hanya email
        Notifikasi dasar = new NotifikasiEmail();
        System.out.println("== email saja ==");
        dasar.kirim("Pesanan sudah dikirim");
        System.out.println("biaya per kirim: Rp" + dasar.biaya());

        // Bungkus dengan sms: perilaku bertambah tanpa mengubah class dasar
        Notifikasi denganSms = new DenganSms(dasar);
        System.out.println("== email + sms ==");
        denganSms.kirim("Pesanan sudah dikirim");
        System.out.println("biaya per kirim: Rp" + denganSms.biaya());

        // Bungkus lagi dengan log: dekorator boleh bertumpuk
        Notifikasi lengkap = new DenganLog(new DenganSms(dasar));
        System.out.println("== email + sms + log ==");
        lengkap.kirim("Pesanan sudah dikirim");
        System.out.println("biaya per kirim: Rp" + lengkap.biaya());
    }
}

// Komponen: antarmuka yang sama untuk objek dasar maupun pembungkusnya
interface Notifikasi {
    void kirim(String pesan);

    long biaya(); // biaya per kiriman dalam rupiah
}

class NotifikasiEmail implements Notifikasi {
    @Override
    public void kirim(String pesan) {
        System.out.println("email : mengirim \"" + pesan + "\"");
    }

    @Override
    public long biaya() {
        return 100;
    }
}

// Dekorator dasar: memegang komponen yang dibungkus dan meneruskan seluruh method
abstract class DekoratorNotifikasi implements Notifikasi {
    protected final Notifikasi dibungkus;

    DekoratorNotifikasi(Notifikasi dibungkus) {
        this.dibungkus = dibungkus;
    }
}

class DenganSms extends DekoratorNotifikasi {
    DenganSms(Notifikasi dibungkus) {
        super(dibungkus);
    }

    @Override
    public void kirim(String pesan) {
        dibungkus.kirim(pesan);
        System.out.println("sms   : mengirim \"" + pesan + "\"");
    }

    @Override
    public long biaya() {
        return dibungkus.biaya() + 500; // biaya berjenjang: sms menambah 500
    }
}

class DenganLog extends DekoratorNotifikasi {
    DenganLog(Notifikasi dibungkus) {
        super(dibungkus);
    }

    @Override
    public void kirim(String pesan) {
        System.out.println("log   : mulai pengiriman \"" + pesan + "\"");
        dibungkus.kirim(pesan);
        System.out.println("log   : pengiriman selesai \"" + pesan + "\"");
    }

    @Override
    public long biaya() {
        return dibungkus.biaya() + 10; // pencatatan murah: hanya 10
    }
}
