Ketika pipeline hanya punya dua langkah, fungsi lepas masih mudah diikuti. Namun pipeline produksi punya belasan langkah dengan aturan yang berbeda-beda, dan tanpa wadah yang rapi, kode berubah menjadi tumpukan fungsi yang saling memanggil tanpa bentuk yang jelas. Pemrograman berorientasi objek (OOP) menggabungkan data dan perilaku di dalam satu class: dataclass menampung record, class menampung langkah, dan komposisi menyusun pipeline dari daftar langkah. Chapter ini membangun ketiganya di atas contoh penjualan yang sama, dari dataclass Transaksi hingga class Pipeline yang menggabungkan banyak step.
Record Data dengan dataclass #
Dict cocok untuk record mentah, tetapi dict tidak menjaga bentuk: kolom bisa hilang, bertambah, atau salah nama tanpa ada yang protes. Dataclass memperbaiki hal tersebut dengan mendeklarasikan field secara eksplisit:
from dataclasses import dataclass
@dataclass
class Transaksi:
"""Record satu baris transaksi penjualan."""
id: str
kasir: str
produk: str
qty: int
harga: int
@property
def total(self):
return self.qty * self.harga
t1 = Transaksi("TRX-001", "Budi", "kopi lampung", 2, 24000)
print(t1)
print(t1.produk.title())
print("total Rp" + str(t1.total))Output yang Dihasilkan:
Transaksi(id='TRX-001', kasir='Budi', produk='kopi lampung', qty=2, harga=24000)
Kopi Lampung
total Rp48000Penulisan id: str adalah anotasi tipe — bagian dari sintaks deklarasi field dataclass; topik anotasi dibahas tuntas di chapter Type Hints, dan untuk sekarang cukup dibaca sebagai “field id bertipe teks”. Decorator @dataclass otomatis menghasilkan __init__ dari daftar field, __repr__ yang tampak pada baris pertama output, dan __eq__ yang membandingkan dua record berdasarkan nilai. Property total menghitung atribut turunan tanpa disimpan — t1.total terbaca seperti field biasa. Dokumentasi lengkapnya ada di https://docs.python.org/3/library/dataclasses.html
.
Class PipelineStep dan Komposisi Pipeline #
Satu langkah pipeline punya nama dan satu tugas. Class dasar PipelineStep mendefinisikan kontrak tersebut; langkah konkrit tinggal mengisi method proses(). Class Pipeline lalu mengomposisikan daftar langkah — inilah komposisi: pipeline memiliki sekumpulan step, bukan mewarisi dari step. Dokumentasi class dan inheritance ada di https://docs.python.org/3/tutorial/classes.html
.
from dataclasses import dataclass
@dataclass
class Transaksi:
"""Record satu baris transaksi penjualan."""
id: str
kasir: str
produk: str
qty: int
harga: int
@property
def total(self):
return self.qty * self.harga
class PipelineStep:
"""Kelas dasar untuk satu langkah pipeline."""
def __init__(self, nama):
self.nama = nama
def proses(self, data):
raise NotImplementedError("setiap langkah harus mendefinisikan proses()")
def __repr__(self):
return f"<{self.__class__.__name__} nama={self.nama!r}>"
class ValidasiHarga(PipelineStep):
"""Langkah konkrit: buang record dengan harga tidak positif."""
def proses(self, data):
hasil = []
for t in data:
if t.harga <= 0:
print(f" {t.id} ditolak: harga {t.harga}")
else:
hasil.append(t)
return hasil
class NormalisasiProduk(PipelineStep):
"""Langkah konkrit: rapikan nama produk di tempat."""
def proses(self, data):
for t in data:
t.produk = t.produk.title()
return data
class Pipeline:
"""Komposisi: menampung daftar langkah lalu menjalankannya berurutan."""
def __init__(self, nama, langkah):
self.nama = nama
self.langkah = langkah
def jalankan(self, data):
print(f"Menjalankan pipeline {self.nama!r}")
for langkah in self.langkah:
data = langkah.proses(data)
print(f" selesai: {langkah!r}")
return data
mentah = [
Transaksi("TRX-001", "Budi", "kopi lampung", 2, 24000),
Transaksi("TRX-002", "Siti", "teh melati", 3, 15000),
Transaksi("TRX-003", "Dewi", "gula aren", 1, -5000),
]
pipeline = Pipeline(
"penjualan-harian",
[ValidasiHarga("validasi"), NormalisasiProduk("normalisasi")],
)
bersih = pipeline.jalankan(mentah)
print("Hasil:")
for t in bersih:
print(" ", t)
print("Total pendapatan:", sum(t.total for t in bersih))python3 pipeline_oop.pyOutput yang Dihasilkan:
Menjalankan pipeline 'penjualan-harian'
TRX-003 ditolak: harga -5000
selesai: <ValidasiHarga nama='validasi'>
selesai: <NormalisasiProduk nama='normalisasi'>
Hasil:
Transaksi(id='TRX-001', kasir='Budi', produk='Kopi Lampung', qty=2, harga=24000)
Transaksi(id='TRX-002', kasir='Siti', produk='Teh Melati', qty=3, harga=15000)
Total pendapatan: 93000Mari kita telaah baris-per-baris kode di atas:
- Baris 22-23 (
__init__padaPipelineStep): constructor menyimpan nama langkah keself.nama; setiap instance membawa konfigurasinya sendiri, jadi dua instance bisa bernama berbeda untuk tugas yang sama. - Baris 25-26 (
prosesraiseNotImplementedError): class dasar menyatakan kontrak — anak yang lupa mendefinisikanproses()langsung terdengar sebagai galat saat dipanggil, bukan senyap mengembalikanNone. - Baris 28-29 (
__repr__): representasi ringkas muncul pada barisselesai: <ValidasiHarga nama='validasi'>— log yang enak dibaca tanpa kerja tambahan. - Baris 32-42 (
ValidasiHarga): class tersebut mewarisiPipelineSteplalu mengisiproses(); record dengan harga negatif ditolak lebih dulu, terlihat pada baris kedua output. - Baris 54-66 (
Pipeline.jalankan): komposisi bekerja di sini —self.langkahadalah list berisi instancePipelineStep; setiap langkah menerima data, mengembalikan data, lalu hasilnya menjadi masukan langkah berikutnya. Menambah langkah baru cukup menambah satu elemen list, tanpa menyentuh classPipeline.
Kapan Memakai Class, Kapan Cukup Fungsi #
OOP bukan tujuan, melainkan alat. Panduan berikut menjaga pilihan tetap sederhana:
| Situasi | Bentuk yang cocok |
|---|---|
| Record dengan beberapa kolom dan sedikit perilaku | dataclass |
| Langkah dengan konfigurasi atau keadaan sendiri | class turunan PipelineStep |
| Transformasi satu masukan menjadi satu keluaran | fungsi murni |
| Skrip sekali jalan di bawah 50 baris | fungsi + modul, class justru berlebihan |
Class Pipeline menampung langkah; setiap langkah menerima dan mengembalikan data. Kontrak seperti itu paling enak dibaca bila jenis data dituliskan secara eksplisit pada fungsi — dan penulisan eksplisit tersebut adalah topik chapter berikutnya, Type Hints.