patterncMinor
Fluent interface for manipulating employee records
Viewed 0 times
manipulatingemployeerecordsinterfaceforfluent
Problem
I wanted to try experimenting with fluent interface design in the C programming language. That's why I wanted to ask you, Dear Code Review users, comments on the way I have implement this simple employee record interface.
Please note that for maximum simplicity I am resorting to as simple data structures as possible.
```
#include
#include
/ if this weren't just an example I'd make a linked list /
#define MAX_EMPLOYEE_COUNT 10
/ global this pointer, there is no way around this /
void* this;
typedef struct Employee {
struct Employee (set_salary)(int);
struct Employee (set_employee_id)(int);
char* name;
int salary;
int employee_id;
} Employee;
Employee* do_set_salary(int s)
{
((Employee*)this)->salary = s;
return this;
}
Employee* do_set_employee_id(int id)
{
((Employee*)this)->employee_id = id;
return this;
}
typedef struct Record {
Employee (add_employee)(char*);
Employee employees[MAX_EMPLOYEE_COUNT];
int cnt;
} Record;
Employee do_add_employee(char n)
{
Record this_record = (Record)this;
assert(this_record->cnt employees[this_record->cnt].set_salary = do_set_salary;
this_record->employees[this_record->cnt].set_employee_id =
do_set_employee_id;
this_record->employees[this_record->cnt].name = n;
this = &this_record->employees[this_record->cnt];
this_record->cnt++;
return this;
}
void init_record(Record* r)
{
r->cnt = 0;
r->add_employee = do_add_employee;
}
Record edit(Record r)
{
this = r;
return r;
}
void print_record(Record* r)
{
int i;
for (i = 0; i cnt; i++) {
printf("%s, salary %d, id %d\n",
r->employees[i].name,
r->employees[i].salary,
r->employees[i].employee_id);
}
}
int main(void)
{
Record record;
init_record(&record);
/ behold, fluent interface design! /
edit(&record)->
add_employee("Alice")->
set_salary(1500)->
set_employ
Please note that for maximum simplicity I am resorting to as simple data structures as possible.
```
#include
#include
/ if this weren't just an example I'd make a linked list /
#define MAX_EMPLOYEE_COUNT 10
/ global this pointer, there is no way around this /
void* this;
typedef struct Employee {
struct Employee (set_salary)(int);
struct Employee (set_employee_id)(int);
char* name;
int salary;
int employee_id;
} Employee;
Employee* do_set_salary(int s)
{
((Employee*)this)->salary = s;
return this;
}
Employee* do_set_employee_id(int id)
{
((Employee*)this)->employee_id = id;
return this;
}
typedef struct Record {
Employee (add_employee)(char*);
Employee employees[MAX_EMPLOYEE_COUNT];
int cnt;
} Record;
Employee do_add_employee(char n)
{
Record this_record = (Record)this;
assert(this_record->cnt employees[this_record->cnt].set_salary = do_set_salary;
this_record->employees[this_record->cnt].set_employee_id =
do_set_employee_id;
this_record->employees[this_record->cnt].name = n;
this = &this_record->employees[this_record->cnt];
this_record->cnt++;
return this;
}
void init_record(Record* r)
{
r->cnt = 0;
r->add_employee = do_add_employee;
}
Record edit(Record r)
{
this = r;
return r;
}
void print_record(Record* r)
{
int i;
for (i = 0; i cnt; i++) {
printf("%s, salary %d, id %d\n",
r->employees[i].name,
r->employees[i].salary,
r->employees[i].employee_id);
}
}
int main(void)
{
Record record;
init_record(&record);
/ behold, fluent interface design! /
edit(&record)->
add_employee("Alice")->
set_salary(1500)->
set_employ
Solution
I would drop the
The goal of the API is to create a new employee and append them, so make that the entry point. If you can't have a valid employee record with a name, then this becomes a first-class parameter of the entry point. Not sure about the requirements, so it may make sense for ID to be a required parameter as well. I also changed the names to make them More Fluent™.
edit function and make add_employee the entry point to the API. I've written a number of fluent APIs in C++ and found that placing the required arguments (e.g., employee name) in the API entry point invaluable. From the usage point of view, something like:int main(void) {
Record db;
init_record(&db);
add_employee_to(&db, "Bob")
->having_salary(2000)
->with_id(10202);
return 0;
}The goal of the API is to create a new employee and append them, so make that the entry point. If you can't have a valid employee record with a name, then this becomes a first-class parameter of the entry point. Not sure about the requirements, so it may make sense for ID to be a required parameter as well. I also changed the names to make them More Fluent™.
Code Snippets
int main(void) {
Record db;
init_record(&db);
add_employee_to(&db, "Bob")
->having_salary(2000)
->with_id(10202);
return 0;
}Context
StackExchange Code Review Q#6254, answer score: 3
Revisions (0)
No revisions yet.