Nel suo libro Patterns in C , Adam Petersen descrive l'uso di un puntatore a una struttura, dichiarata in un file di intestazione, per creare un tipo di dati astratto di prima classe:
/* Customer.h */
/* A pointer to an incomplete type (hides the implementation details). */
typedef struct Customer* CustomerPtr;
/* Create a Customer and return a handle to it. */
CustomerPtr createCustomer(const char* name, const Address* address);
/* Destroy the given Customer. All handles to it will be invalidated. */
void destroyCustomer(CustomerPtr customer);
La struct e le due funzioni sono definite in Customer.c:
#include ”Customer.h”
#include <stdlib.h>
struct Customer
{
const char* name;
Address address;
size_t noOfOrders;
Order orders[42];
};
CustomerPtr createCustomer(const char* name, const Address* address)
{
CustomerPtr customer = malloc(sizeof * customer);
if(customer)
{
/* Initialize each field in the customer... */
}
return customer;
}
void destroyCustomer(CustomerPtr customer)
{
/* Perform clean-up of the customer internals, if necessary. */
free(customer);
}
Il modo in cui organizzerei il mio codice è un po 'diverso. Il file di intestazione dichiarerebbe la struct stessa, non un puntatore ad esso. Ecco come mi è stato insegnato a scrivere qualcosa di simile:
/* Customer.h */
struct Customer;
extern struct Customer* newCustomer(const char* name);
e dichiarazioni:
/* Customer.c */
struct Customer {
char name[CUSTNAMELENGTH +1];
};
struct Customer* newCustomer(const char* name) {
struct Customer* newCustomer;
newCustomer = malloc(sizeof(struct Customer));
/* initialize customer instance */
return newCustomer;
}
Qual è la differenza pratica tra i due stili? C'è un potenziale problema con il mio modo che non vedo? Sembra incapsulare l'oggetto con successo e fornire un'interfaccia simile al design di Petersen. Quando dovrei usare il suo design piuttosto che quello che ho fatto?