saindras

pbo-pertemuan-9-1

Oct 28th, 2025 (edited)
209
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.61 KB | None | 0 0
  1. from abc import ABC, abstractmethod
  2. from datetime import datetime, timedelta
  3. from enum import Enum
  4. import uuid
  5.  
  6. # ==============================================================================
  7. # Definisi Enum untuk Status
  8. # ==============================================================================
  9.  
  10. class StatusItem(Enum):
  11.     """Enum untuk status item di perpustakaan."""
  12.     TERSEDIA = "Tersedia"
  13.     DIPINJAM = "Dipinjam"
  14.     HILANG = "Hilang"
  15.  
  16. class StatusTransaksi(Enum):
  17.     """Enum untuk status transaksi peminjaman."""
  18.     AKTIF = "Aktif"
  19.     SELESAI = "Selesai"
  20.     TERLAMBAT = "Terlambat"
  21.  
  22. # ==============================================================================
  23. # Hierarki Kelas Item Pustaka (LoanableItem, Buku, Majalah)
  24. # ==============================================================================
  25.  
  26. class LoanableItem(ABC):
  27.     """
  28.    Kelas abstrak untuk semua item yang dapat dipinjam di perpustakaan.
  29.    Ini adalah superclass (induk) untuk Buku, Majalah, dll.
  30.    """
  31.     def __init__(self, item_id: str, judul: str):
  32.         self._item_id = item_id
  33.         self._judul = judul
  34.         self._status = StatusItem.TERSEDIA
  35.  
  36.     @abstractmethod
  37.     def get_info(self) -> dict:
  38.         """Metode abstrak untuk mendapatkan informasi detail dari item."""
  39.         pass
  40.  
  41.     def ubah_status(self, status_baru: StatusItem):
  42.         """Mengubah status ketersediaan item."""
  43.         self._status = status_baru
  44.         print(f"INFO: Status '{self._judul}' telah diubah menjadi {status_baru.value}.")
  45.  
  46.     @property
  47.     def item_id(self):
  48.         return self._item_id
  49.  
  50.     @property
  51.     def judul(self):
  52.         return self._judul
  53.  
  54.     @property
  55.     def status(self):
  56.         return self._status
  57.  
  58. class Buku(LoanableItem):
  59.     """Kelas turunan untuk merepresentasikan sebuah buku."""
  60.     def __init__(self, item_id: str, judul: str, pengarang: str):
  61.         super().__init__(item_id, judul)
  62.         self._pengarang = pengarang
  63.  
  64.     def get_info(self) -> dict:
  65.         """Implementasi spesifik untuk menampilkan info buku (method overriding)."""
  66.         return {
  67.             "ID": self._item_id,
  68.             "Judul": self._judul,
  69.             "Tipe": "Buku",
  70.             "Pengarang": self._pengarang,
  71.             "Status": self._status.value
  72.         }
  73.  
  74. class Majalah(LoanableItem):
  75.     """Kelas turunan untuk merepresentasikan sebuah majalah/jurnal."""
  76.     def __init__(self, item_id: str, judul: str, edisi: str):
  77.         super().__init__(item_id, judul)
  78.         self._edisi = edisi
  79.  
  80.     def get_info(self) -> dict:
  81.         """Implementasi spesifik untuk menampilkan info majalah (method overriding)."""
  82.         return {
  83.             "ID": self._item_id,
  84.             "Judul": self._judul,
  85.             "Tipe": "Majalah",
  86.             "Edisi": self._edisi,
  87.             "Status": self._status.value
  88.         }
  89.  
  90. # ==============================================================================
  91. # Kelas-kelas Utama Sistem
  92. # ==============================================================================
  93.  
  94. class Member:
  95.     """Merepresentasikan anggota perpustakaan."""
  96.     def __init__(self, nama: str, alamat: str):
  97.         self._member_id = "MEM-" + str(uuid.uuid4())[:8].upper()
  98.         self._nama = nama
  99.         self._alamat = alamat
  100.  
  101.     @property
  102.     def member_id(self):
  103.         return self._member_id
  104.    
  105.     @property
  106.     def nama(self):
  107.         return self._nama
  108.  
  109.     def get_riwayat_peminjaman(self, perpus: 'Perpustakaan') -> list['Transaksi']:
  110.         """Mendapatkan daftar transaksi yang pernah dilakukan oleh member."""
  111.         riwayat = []
  112.         for transaksi in perpus.daftar_transaksi:
  113.             if transaksi.member.member_id == self._member_id:
  114.                 riwayat.append(transaksi)
  115.         return riwayat
  116.  
  117. class Transaksi:
  118.     """Merepresentasikan sebuah transaksi peminjaman."""
  119.     def __init__(self, member: Member, item: LoanableItem):
  120.         self._transaksi_id = "TRX-" + str(uuid.uuid4())[:8].upper()
  121.         self._member = member
  122.         self._item = item
  123.         self._tgl_pinjam = datetime.now()
  124.         self._tgl_kembali = self._tgl_pinjam + timedelta(days=14) # Batas peminjaman 14 hari
  125.         self._status = StatusTransaksi.AKTIF
  126.         self._denda = 0.0
  127.  
  128.     @property
  129.     def transaksi_id(self):
  130.         return self._transaksi_id
  131.  
  132.     @property
  133.     def member(self):
  134.         return self._member
  135.        
  136.     @property
  137.     def item(self):
  138.         return self._item
  139.  
  140.     @property
  141.     def status(self):
  142.         return self._status
  143.  
  144.     def hitung_denda(self) -> float:
  145.         """Menghitung denda jika pengembalian terlambat."""
  146.         if datetime.now() > self._tgl_kembali:
  147.             terlambat = (datetime.now() - self._tgl_kembali).days
  148.             self._denda = float(terlambat * 1000) # Denda 1000 per hari
  149.             self._status = StatusTransaksi.TERLAMBAT
  150.         return self._denda
  151.        
  152.     def selesaikan_transaksi(self):
  153.         self.hitung_denda()
  154.         if self._status != StatusTransaksi.TERLAMBAT:
  155.             self._status = StatusTransaksi.SELESAI
  156.  
  157. class Perpustakaan:
  158.     """
  159.    Kelas utama yang mengelola seluruh sistem perpustakaan.
  160.    Menerapkan pola Singleton.
  161.    """
  162.  
  163.     _instance = None # Variabel kelas untuk menyimpan instance tunggal
  164.  
  165.     def __init__(self):
  166.         """
  167.        Konstruktor dibuat 'semi-privat'.
  168.        Harusnya tidak dipanggil langsung dari luar.
  169.        Panggil melalui get_instance().
  170.        """
  171.         if Perpustakaan._instance is not None:
  172.              raise Exception("Kelas ini adalah Singleton! Gunakan get_instance().")
  173.         else:
  174.             self._daftar_item = []
  175.             self._daftar_member = []
  176.             self._daftar_transaksi = []
  177.             print("LOG: Instance Sistem Perpustakaan berhasil dibuat.")
  178.             Perpustakaan._instance = self # Simpan instance yang baru dibuat
  179.  
  180.     @property
  181.     def daftar_item(self):
  182.         return self._daftar_item
  183.        
  184.     @property
  185.     def daftar_member(self):
  186.         return self._daftar_member
  187.        
  188.     @property
  189.     def daftar_transaksi(self):
  190.         return self._daftar_transaksi
  191.    
  192.     @staticmethod
  193.     def get_instance() -> 'Perpustakaan':
  194.         """Metode statis untuk mendapatkan instance tunggal."""
  195.         if Perpustakaan._instance is None:
  196.             Perpustakaan() # Panggil __init__ secara internal jika belum ada instance
  197.         return Perpustakaan._instance
  198.  
  199.     def tambah_item(self, item: LoanableItem):
  200.         """Menambahkan item baru (buku/majalah) ke perpustakaan."""
  201.         self._daftar_item.append(item)
  202.         print(f"ITEM DITAMBAHKAN: '{item.judul}' ({item.__class__.__name__}) telah ditambahkan ke koleksi.")
  203.  
  204.     def tambah_member(self, nama: str, alamat: str) -> Member:
  205.         """Mendaftarkan anggota baru."""
  206.         member_baru = Member(nama, alamat)
  207.         self._daftar_member.append(member_baru)
  208.         print(f"MEMBER BARU: {nama} dengan ID {member_baru.member_id} berhasil terdaftar.")
  209.         return member_baru
  210.  
  211.     def cari_item(self, item_id: str) -> LoanableItem | None:
  212.         """Mencari item berdasarkan ID."""
  213.         for item in self._daftar_item:
  214.             if item.item_id == item_id:
  215.                 return item
  216.         return None
  217.  
  218.     def cari_member(self, member_id: str) -> Member | None:
  219.         """Mencari member berdasarkan ID."""
  220.         for member in self._daftar_member:
  221.             if member.member_id == member_id:
  222.                 return member
  223.         return None
  224.        
  225.     def cari_transaksi(self, transaksi_id: str) -> Transaksi | None:
  226.         """Mencari transaksi berdasarkan ID."""
  227.         for trx in self._daftar_transaksi:
  228.             if trx.transaksi_id == transaksi_id:
  229.                 return trx
  230.         return None
  231.  
  232.     def pinjamkan_item(self, member_id: str, item_id: str) -> Transaksi:
  233.         """Memproses peminjaman item oleh member."""
  234.         member = self.cari_member(member_id)
  235.         item = self.cari_item(item_id)
  236.  
  237.         if not member:
  238.             raise Exception("ERROR: Member tidak ditemukan.")
  239.         if not item:
  240.             raise Exception("ERROR: Item tidak ditemukan.")
  241.         if item.status != StatusItem.TERSEDIA:
  242.             raise Exception(f"ERROR: Item '{item.judul}' sedang tidak tersedia.")
  243.  
  244.         transaksi_baru = Transaksi(member, item)
  245.         self._daftar_transaksi.append(transaksi_baru)
  246.         item.ubah_status(StatusItem.DIPINJAM)
  247.         print(f"TRANSAKSI PEMINJAMAN: '{item.judul}' dipinjam oleh {member.nama}. ID Transaksi: {transaksi_baru.transaksi_id}")
  248.         return transaksi_baru
  249.  
  250.     def terima_pengembalian(self, transaksi_id: str):
  251.         """Memproses pengembalian item."""
  252.         transaksi = self.cari_transaksi(transaksi_id)
  253.         if not transaksi:
  254.             raise Exception("ERROR: Transaksi tidak ditemukan.")
  255.         if transaksi.status != StatusTransaksi.AKTIF:
  256.             raise Exception("ERROR: Transaksi ini sudah tidak aktif.")
  257.        
  258.         transaksi.selesaikan_transaksi()
  259.         item = transaksi.item
  260.         item.ubah_status(StatusItem.TERSEDIA)
  261.        
  262.         denda = transaksi.hitung_denda()
  263.         print(f"TRANSAKSI PENGEMBALIAN: '{item.judul}' telah dikembalikan oleh {transaksi.member.nama}.")
  264.         if denda > 0:
  265.             print(f"   -> Dikenakan denda sebesar: Rp{denda:,.2f}")
  266.         else:
  267.             print("   -> Pengembalian tepat waktu.")
  268.  
  269. class Pustakawan:
  270.     """Merepresentasikan pustakawan yang mengelola perpustakaan."""
  271.     def __init__(self, pustakawan_id: str, nama: str):
  272.         self._pustakawan_id = pustakawan_id
  273.         self._nama = nama
  274.  
  275.     def lakukan_pendaftaran(self, perpus: Perpustakaan, nama: str, alamat: str) -> Member:
  276.         """Wrapper method untuk mendaftarkan member."""
  277.         print(f"\n[Aksi Pustakawan: {self._nama}] Mendaftarkan anggota baru...")
  278.         return perpus.tambah_member(nama, alamat)
  279.  
  280.     def tambah_item_ke_koleksi(self, perpus: Perpustakaan, item: LoanableItem):
  281.         """Wrapper method untuk menambah item baru."""
  282.         print(f"\n[Aksi Pustakawan: {self._nama}] Menambahkan item ke koleksi...")
  283.         perpus.tambah_item(item)
  284.  
  285.     def lakukan_peminjaman(self, perpus: Perpustakaan, member_id: str, item_id: str) -> Transaksi:
  286.         """Wrapper method untuk melakukan peminjaman."""
  287.         print(f"\n[Aksi Pustakawan: {self._nama}] Memproses peminjaman...")
  288.         return perpus.pinjamkan_item(member_id, item_id)
  289.        
  290.     def lakukan_pengembalian(self, perpus: Perpustakaan, transaksi_id: str):
  291.         """Wrapper method untuk melakukan pengembalian."""
  292.         print(f"\n[Aksi Pustakawan: {self._nama}] Memproses pengembalian...")
  293.         perpus.terima_pengembalian(transaksi_id)
  294.  
  295. # ==============================================================================
  296. # CONTOH PENGGUNAAN PROGRAM (MAIN)
  297. # ==============================================================================
  298. if __name__ == "__main__":
  299.     print("===== MEMULAI SIMULASI SISTEM PERPUSTAKAAN =====")
  300.    
  301.     # 1. Inisialisasi sistem
  302.     # perpustakaan_utama = Perpustakaan() <- INI TIDAK BOLEH LAGI
  303.     perpustakaan_utama = Perpustakaan.get_instance()
  304.     perpustakaan_kedua = Perpustakaan.get_instance() # Mencoba mendapatkan instance lagi
  305.  
  306.     # Membuktikan keduanya adalah objek yang sama
  307.     print(f"\nApakah perpustakaan_utama dan perpustakaan_kedua objek yang sama? {perpustakaan_utama is perpustakaan_kedua}")
  308.  
  309.     # 2. Menambahkan pustakawan
  310.     pustakawan_andi = Pustakawan("P-001", "Andi")
  311.    
  312.     # 3. Pustakawan mendaftarkan member baru
  313.     member_budi = pustakawan_andi.lakukan_pendaftaran(perpustakaan_utama, "Budi Santoso", "Jl. Merdeka No. 10")
  314.     member_citra = pustakawan_andi.lakukan_pendaftaran(perpustakaan_utama, "Citra Lestari", "Jl. Pahlawan No. 5")
  315.  
  316.     # 4. Pustakawan menambahkan item baru (Buku dan Majalah)
  317.     buku_oop = Buku("BK-001", "Pemrograman Berorientasi Objek dengan Python", "Dr. Inovatif")
  318.     majalah_sains = Majalah("MGZ-001", "Quanta Magazine", "Edisi Oktober 2025")
  319.     buku_data = Buku("BK-002", "Sains Data untuk Pemula", "Prof. Analitika")
  320.    
  321.     pustakawan_andi.tambah_item_ke_koleksi(perpustakaan_utama, buku_oop)
  322.     pustakawan_andi.tambah_item_ke_koleksi(perpustakaan_utama, majalah_sains)
  323.     pustakawan_andi.tambah_item_ke_koleksi(perpustakaan_utama, buku_data)
  324.  
  325.     print("\n===== STATUS AWAL KOLEKSI DAN MEMBER =====")
  326.     print("--- Daftar Item ---")
  327.     # Contoh polimorfisme saat menampilkan info
  328.     for item in perpustakaan_utama.daftar_item:
  329.         print(item.get_info())
  330.  
  331.     print("\n--- Daftar Item di perpustakaan_kedua (seharusnya sama) ---")
  332.     for item in perpustakaan_kedua.daftar_item: # Mengakses dari variabel berbeda
  333.        print(item.get_info())
  334.    
  335.     print("\n--- Daftar Member ---")
  336.     for member in perpustakaan_utama.daftar_member:
  337.         print(f"ID: {member.member_id}, Nama: {member.nama}")
  338.    
  339.     # 5. Simulasi Transaksi Peminjaman
  340.     print("\n===== SIMULASI TRANSAKSI =====")
  341.     try:
  342.         # Budi meminjam buku OOP
  343.         transaksi1 = pustakawan_andi.lakukan_peminjaman(perpustakaan_utama, member_budi.member_id, "BK-001")
  344.        
  345.         # Citra meminjam majalah sains
  346.         transaksi2 = pustakawan_andi.lakukan_peminjaman(perpustakaan_utama, member_citra.member_id, "MGZ-001")
  347.        
  348.         # Budi mencoba meminjam buku yang sama lagi (akan gagal)
  349.         pustakawan_andi.lakukan_peminjaman(perpustakaan_utama, member_budi.member_id, "BK-001")
  350.  
  351.     except Exception as e:
  352.         print(e)
  353.        
  354.     print("\n===== STATUS SETELAH PEMINJAMAN =====")
  355.     for item in perpustakaan_utama.daftar_item:
  356.         print(f"Judul: {item.judul}, Status: {item.status.value}")
  357.  
  358.     # 6. Simulasi Transaksi Pengembalian
  359.     try:
  360.         # Budi mengembalikan buku OOP
  361.         pustakawan_andi.lakukan_pengembalian(perpustakaan_utama, transaksi1.transaksi_id)
  362.        
  363.         # Citra mencoba mengembalikan majalah (simulasi terlambat dengan "memajukan" waktu)
  364.         transaksi2._tgl_kembali = datetime.now() - timedelta(days=3) # Anggap terlambat 3 hari
  365.         pustakawan_andi.lakukan_pengembalian(perpustakaan_utama, transaksi2.transaksi_id)
  366.  
  367.     except Exception as e:
  368.         print(e)
  369.        
  370.     print("\n===== STATUS AKHIR SETELAH PENGEMBALIAN =====")
  371.     for item in perpustakaan_utama.daftar_item:
  372.         print(f"Judul: {item.judul}, Status: {item.status.value}")
  373.        
  374.     print("\n===== RIWAYAT PEMINJAMAN BUDI =====")
  375.     riwayat_budi = member_budi.get_riwayat_peminjaman(perpustakaan_utama)
  376.     for trx in riwayat_budi:
  377.         print(f"ID: {trx.transaksi_id}, Item: '{trx.item.judul}', Status: {trx.status.value}")
  378.        
  379.     print("\n===== SIMULASI SELESAI =====")
Advertisement
Add Comment
Please, Sign In to add comment