Setelah model Product siap, kita lanjut ke lapisan berikutnya: koleksi produk yang menyimpan banyak Product dan menyediakan operasi CRUD (Create, Read, Update, Delete). Pada bagian ini, kita bangun struct Inventory yang membungkus array statis Product items[INVENTORY_CAPACITY] dengan field count untuk melacak jumlah produk aktif dan next_id untuk menjamin id unik berurutan.
Konsep #
CRUD adalah empat operasi dasar pada koleksi data persisten. Untuk Inventory kita tetapkan kontrak berikut:
- Create —
inventory_add()menambah produk baru dan mengembalikan id yang baru dialokasikan. Id otomatis diincrement darinext_id. - Read —
inventory_find_by_id()daninventory_find_by_name()mencari produk;inventory_print_all()mencetak seluruh daftar. - Update —
inventory_update()memperbarui harga dan stok produk berdasarkan id. Field lain (id, nama) tidak boleh diubah agar integritas referensi tetap terjaga. - Delete —
inventory_delete()menghapus produk berdasarkan id denganmemmoveuntuk menggeser elemen setelahnya.
Mekanisme kerja di balik layar: array items dialokasikan di stack dengan ukuran tetap INVENTORY_CAPACITY = 256. Penambahan baru menulis pada indeks count lalu menambah count. Pencarian linear O(n) cukup untuk kapasitas kecil; bila perlu lebih cepat, kita bisa beralih ke hash table atau binary search (proyek 6 membahas struktur data tersebut).
Implementasi #
Header inventory.h:
/* inventory.h - Koleksi produk dengan operasi CRUD */
#ifndef INVENTORY_H
#define INVENTORY_H
#include "product.h"
#define INVENTORY_CAPACITY 256
typedef struct {
Product items[INVENTORY_CAPACITY];
int count;
int next_id;
} Inventory;
void inventory_init(Inventory *inv);
int inventory_add(Inventory *inv, const char *name,
double price, int stock);
Product *inventory_find_by_id(Inventory *inv, int id);
int inventory_find_by_name(Inventory *inv, const char *keyword,
Product *out, int out_cap);
int inventory_update(Inventory *inv, int id,
double new_price, int new_stock);
int inventory_delete(Inventory *inv, int id);
void inventory_print_all(const Inventory *inv);
#endif /* INVENTORY_H */Implementasi inti CRUD:
/* inventory.c - Implementasi operasi CRUD Inventory */
#include "inventory.h"
#include <stdio.h>
#include <string.h>
void inventory_init(Inventory *inv) {
if (inv == NULL) {
return;
}
inv->count = 0;
inv->next_id = 1;
}
int inventory_add(Inventory *inv, const char *name,
double price, int stock) {
if (inv == NULL || name == NULL) {
return -1;
}
if (inv->count >= INVENTORY_CAPACITY) {
return -1;
}
Product *slot = &inv->items[inv->count];
int id = inv->next_id;
int rc = product_create(slot, id, name, price, stock);
if (rc != 0) {
return -1;
}
inv->next_id++;
inv->count++;
return id;
}
Product *inventory_find_by_id(Inventory *inv, int id) {
if (inv == NULL) {
return NULL;
}
for (int i = 0; i < inv->count; i++) {
if (inv->items[i].id == id) {
return &inv->items[i];
}
}
return NULL;
}
int inventory_update(Inventory *inv, int id,
double new_price, int new_stock) {
Product *p = inventory_find_by_id(inv, id);
if (p == NULL) {
return -1;
}
if (new_price < 0.0 || new_stock < 0) {
return -1;
}
p->price = new_price;
p->stock = new_stock;
return 0;
}
int inventory_delete(Inventory *inv, int id) {
if (inv == NULL) {
return -1;
}
for (int i = 0; i < inv->count; i++) {
if (inv->items[i].id == id) {
memmove(&inv->items[i], &inv->items[i + 1],
(size_t)(inv->count - i - 1) * sizeof(Product));
inv->count--;
return 0;
}
}
return -1;
}Mari kita uji operasi CRUD:
/* demo_crud.c - Uji coba CRUD Inventory */
#include <stdio.h>
#include "inventory.h"
int main(void) {
Inventory inv;
inventory_init(&inv);
int id1 = inventory_add(&inv, "Beras 5kg", 65000.0, 10);
int id2 = inventory_add(&inv, "Gula Pasir", 15000.0, 5);
printf("Tambah: id1=%d id2=%d count=%d\n",
id1, id2, inv.count);
Product *p = inventory_find_by_id(&inv, 2);
printf("Cari id=2: ");
product_print(p);
inventory_update(&inv, 1, 70000.0, 8);
printf("Setelah update id=1: ");
product_print(&inv.items[0]);
inventory_delete(&inv, 2);
printf("Setelah delete id=2, count=%d\n", inv.count);
inventory_print_all(&inv);
return 0;
}Output yang Dihasilkan:
Tambah: id1=1 id2=2 count=2
Cari id=2: [000002] Gula Pasir Rp 15000.00 stok= 5
Setelah update id=1: [000001] Beras 5kg Rp 70000.00 stok= 8
Setelah delete id=2, count=1
Total produk: 1/256
[000001] Beras 5kg Rp 70000.00 stok= 8Mari kita telaah baris-baris kode di atas:
- Baris 9-22 (
inventory_add): Fungsi menerima data produk (bukanProduct*jadi) lalu memanggilproduct_create()pada slot kosong di akhir array. Id baru didapat darinext_idsebelum diincrement. Pola ini menjamin id selalu unik dan naik monoton. - Baris 24-33 (
inventory_find_by_id): Linear searchO(n). Mengembalikan pointer ke elemen array (bukan salinan), sehingga pemanggil bisa memodifikasi produk langsung lewat pointer. - Baris 35-44 (
inventory_update): Hanya mengizinkan perubahan harga dan stok. Fieldiddannamesengaja tidak boleh diubah karena bisa memutus referensi dari history transaksi atau laporan. - Baris 46-57 (
inventory_delete): Setelah menemukan indeks yang akan dihapus,memmovemenggeser seluruh elemen setelahnya satu posisi ke kiri. Berbeda darimemcpy,memmoveaman untuk region yang overlap. Setelah itucountdikurangi satu. - Baris 16 (
memmovesize): Ukuran yang dipindahkan adalah(inv->count - i - 1) * sizeof(Product). Bilaiadalah indeks terakhir, ukuran menjadi 0 danmemmovemenjadi no-op.
Kode Lengkap #
Kode lengkap untuk bagian ini ada pada berkas inventory.h dan inventory.c. Implementasi inventory_find_by_name() dan inventory_print_all() juga tersedia di berkas yang sama.
Pengujian #
Verifikasi CRUD dengan demo di atas. Untuk test formal, kita akan menambahkan assertion pada bagian 7. Beberapa kasus yang harus dipastikan:
inventory_addmengembalikan -1 bila kapasitas penuh.inventory_find_by_idmengembalikan NULL bila id tidak ada.inventory_updatemenolak harga negatif.inventory_deletepada id yang sudah dihapus mengembalikan -1.inventory_deletemenggeser elemen dengan benar (cek denganinventory_find_by_id).
gcc -std=c11 -Wall -Wextra -o /tmp/demo_crud demo_crud.c \
inventory.c product.c -lm
/tmp/demo_crud