Kita mulai proyek pengelola inventaris dari fondasi paling dasar: bagaimana satu baris data produk direpresentasikan di dalam program. Tanpa model data yang jelas, operasi CRUD, pencarian, dan persistensi akan sulit ditulis dengan benar. Pada bagian ini, kita definisikan struct Product lengkap dengan field id, nama, harga, dan stok, lalu bungkus dengan fungsi product_create() yang memvalidasi field sebelum mengisi struct.
Konsep #
struct di C adalah kumpulan field yang ditempatkan berdampingan di memori. Kita pakai struct untuk merepresentasikan satu entitas domain (di sini: produk) sehingga data yang berhubungan tidak terpisah-pisah menjadi banyak variabel lepas.
Aturan yang kita pegang pada model Product:
- Field
idpositif dan unik; nilai 0 atau di atasPRODUCT_ID_MAXditolak. - Field
nameadalah string C klasik (null-terminated) dengan panjang maksimumPRODUCT_NAME_MAX - 1. - Field
priceharus non-negatif dan bukan NaN/Infinity. - Field
stocktidak boleh negatif. - Fungsi
product_create()adalah satu-satunya pintu masuk untuk membangunProductvalid. Kode lain tidak boleh mengisi field secara langsung tanpa validasi.
Mekanisme kerja di balik layar: saat product_create() dipanggil, fungsi memeriksa setiap argumen satu per satu. Bila ada yang gagal, fungsi mengembalikan kode error negatif tanpa mengubah struct (kontrak “all-or-nothing”). Bila semua validasi lulus, fungsi menyalin nama dengan strncpy dan selalu menulis terminator di akhir buffer untuk menjamin string tetap null-terminated walau input terpotong.
Implementasi #
Berikut definisi header lengkap untuk model Product:
/* product.h - Model data produk untuk sistem inventaris */
#ifndef PRODUCT_H
#define PRODUCT_H
#include <stdint.h>
#define PRODUCT_NAME_MAX 64
#define PRODUCT_ID_MAX 999999
typedef struct {
int id;
char name[PRODUCT_NAME_MAX];
double price;
int stock;
} Product;
int product_create(Product *p, int id, const char *name,
double price, int stock);
void product_print(const Product *p);
int product_is_valid(const Product *p);
#endif /* PRODUCT_H */Implementasi product_create() melakukan validasi lalu pengisian field:
/* product.c - Implementasi model Product */
#include "product.h"
#include <stdio.h>
#include <string.h>
#include <math.h>
int product_is_valid(const Product *p) {
if (p == NULL) {
return 0;
}
if (p->id <= 0 || p->id > PRODUCT_ID_MAX) {
return 0;
}
if (p->name[0] == '\0') {
return 0;
}
if (p->price < 0.0 || isnan(p->price) || isinf(p->price)) {
return 0;
}
if (p->stock < 0) {
return 0;
}
return 1;
}
int product_create(Product *p, int id, const char *name,
double price, int stock) {
if (p == NULL || name == NULL) {
return -1;
}
if (id <= 0 || id > PRODUCT_ID_MAX) {
return -4;
}
if (price < 0.0 || isnan(price) || isinf(price)) {
return -2;
}
if (stock < 0) {
return -3;
}
p->id = id;
strncpy(p->name, name, PRODUCT_NAME_MAX - 1);
p->name[PRODUCT_NAME_MAX - 1] = '\0';
p->price = price;
p->stock = stock;
if (!product_is_valid(p)) {
return -1;
}
return 0;
}
void product_print(const Product *p) {
if (p == NULL) {
printf("(null product)\n");
return;
}
printf("[%06d] %-30s Rp%10.2f stok=%4d\n",
p->id, p->name, p->price, p->stock);
}Mari kita uji model ini dengan program kecil:
/* demo_product.c - Uji coba model Product */
#include <stdio.h>
#include "product.h"
int main(void) {
Product p1;
int rc = product_create(&p1, 1, "Beras 5kg", 65000.0, 10);
printf("rc=%d, valid=%d\n", rc, product_is_valid(&p1));
product_print(&p1);
/* Contoh data invalid: id negatif */
Product p2;
rc = product_create(&p2, -5, "Gula", 15000.0, 5);
printf("rc=%d (id negatif ditolak)\n", rc);
/* Contoh data invalid: harga NaN */
Product p3;
rc = product_create(&p3, 2, "Teh", 0.0/0.0, 1);
printf("rc=%d (NaN ditolak)\n", rc);
return 0;
}Output yang Dihasilkan:
rc=0, valid=1
[000001] Beras 5kg Rp 65000.00 stok= 10
rc=-4 (id negatif ditolak)
rc=-2 (NaN ditolak)Mari kita telaah baris-baris kode di atas:
- Baris 13-17 (
PRODUCT_NAME_MAX/PRODUCT_ID_MAX): Konstanta batas didefinisikan sebagai macro. Angka 64 dipilih agar produk dengan nama panjang seperti “Beras Premium Organik 5kg Gold Label” tetap muat tanpa terpotong. - Baris 23-26 (
product_is_valid): Fungsi ini melakukan double-check setelah pengisian. Memvalidasiisnandanisinfpenting karenastrtoddapat menghasilkan NaN saat parsing input pengguna gagal. - Baris 33-46 (
product_create): Validasi dilakukan sebelum pengisian. Bila ada field invalid, fungsi langsungreturntanpa menyentuh struct, menjaga kontrak “all-or-nothing”. - Baris 47 (
strncpy): Salin nama dengan batasPRODUCT_NAME_MAX - 1. Selalu tulis terminator di baris berikutnya agar string tetap null-terminated walau sumber lebih panjang dari buffer. - Baris 65 (
printfdengan%06d): Format%06dmenghasilkan id dengan leading zero, misal000001, sehingga output mudah dibaca sebagai tabel.
Kode Lengkap #
Kode lengkap untuk bagian ini ada pada berkas product.h dan product.c di direktori proyek. Kode tersebut menjadi fondasi untuk semua bagian berikutnya. Setiap kali kita menambah operasi baru (CRUD, pencarian, persistensi), kita akan mengoper objek Product lewat pointer const Product * agar jelas mana fungsi yang hanya membaca dan mana yang memodifikasi.
Pengujian #
Pengujian model Product dilakukan dengan dua jalur. Pertama, program demo_product.c di atas yang memverifikasi kasus sukses dan dua kasus gagal. Kedua, unit test formal pada bagian 7 akan memeriksa lebih banyak edge case: nama kosong, harga negatif, stok negatif, id di luar rentang.
Compile dan jalankan demo:
gcc -std=c11 -Wall -Wextra -o /tmp/demo_product demo_product.c product.c -lm
/tmp/demo_product