1 1 Modularity!The material for this lecture is drawn, in part, from!The Practice of Programming (Kernighan & Pike) Chapter 4!2 Goals of this Lecture!• Help you learn:!• How to create high quality modules in C!• Why?!• Abstraction is a powerful (the only?) technique available for understanding large, complex systems!• A power programmer knows how to find the abstractions in a large program!• A power programmer knows how to convey a large programʼs abstractions via its modularity!2 3 Module Design Heuristics!• We propose 7 module design heuristics!• Letʼs consider one at a time…!4 Interfaces!(1) A well-designed module separates interface and implementation!• Why?!• Hides implementation details from clients!• Thus facilitating abstraction!• Allows separate compilation of each implementation!• Thus allowing partial builds!3 5 Interface Example 1!• Stack: A stack whose items are strings!• Data structure!• Linked list!• Algorithms!• new: Create a new Stack object and return it (or NULL if not enough memory)!• free: Free the given Stack object!• push: Push the given string onto the given Stack object and return 1 (or 0 if not enough memory)!• top: Return the top item of the given Stack object!• pop: Pop a string from the given Stack object and discard it!• isEmpty: Return 1 the given Stack object is empty, 0 otherwise!6 Interfaces Example 1!• Stack (version 1)!• Stack module consists of one file (stack.c); no interface!• Problem: Change stack.c => must rebuild stack.c and client!• Problem: Client “sees” Stack function definitions; poor abstraction!/* stack.c */ struct Node { const char *item; struct Node *next; }; struct Stack { struct Node *first; }; struct Stack *Stack_new(void) { … } void Stack_free(struct Stack *s) {…} int Stack_push(struct Stack *s, const char *item) {…} char *Stack_top(struct Stack *s) {…} void Stack_pop(struct Stack *s) {…} int Stack_isEmpty(struct Stack *s) {…} /* client.c */ #include "stack.c" /* Use the functions defined in stack.c. */4 7 Interfaces Example 1!• Stack (version 2)!• Stack module consists of two files:!!(1) stack.h (the interface) declares functions and defines data structures!/* stack.h */ struct Node { const char *item; struct Node *next; }; struct Stack { struct Node *first; }; struct Stack *Stack_new(void); void Stack_free(struct Stack *s); int Stack_push(struct Stack *s, const char *item); char *Stack_top(struct Stack *s); void Stack_pop(struct Stack *s); int Stack_isEmpty(struct Stack *s); 8 Interfaces Example 1!• Stack (version 2)!!(2) stack.c (the implementation) defines functions!• #includes stack.h so!• Compiler can check consistency of function declarations and definitions!• Functions have access to data structures!/* stack.c */ #include "stack.h" struct Stack *Stack_new(void) { … } void Stack_free(struct Stack *s) {…} int Stack_push(struct Stack *s, const char *item) {…} char *Stack_top(struct Stack *s) {…} void Stack_pop(struct Stack *s) {…} int Stack_isEmpty(struct Stack *s) {…}5 9 Interfaces Example 1!• Stack (version 2)!• Client #includes only the interface!• Change stack.c => must rebuild stack.c, but not the client!• Client does not “see” Stack function definitions; better abstraction!/* client.c */ #include "stack.h" /* Use the functions declared in stack.h. */ 10 Interface Example 2!• string (also recall Str from Assignment 2)!/* string.h */ size_t strlen(const char *s); char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, size_t n); char *strcat(char *dest, const char *src); char *strncat(char *dest, const char *src, size_t n); char *strcmp(const char *s, const char *t); char *strncmp(const char *s, const char *t, size_t n); char *strstr(const char *haystack, const char *needle); …6 11 Interface Example 3!• stdio (from C90, vastly simplified)!/* stdio.h */ struct FILE { int cnt; /* characters left */ char *ptr; /* next character position */ char *base; /* location of buffer */ int flag; /* mode of file access */ int fd; /* file descriptor */ }; #define OPEN_MAX 20 FILE _iob[OPEN_MAX]; #define stdin (&_iob[0]); #define stdout (&_iob[1]); #define stderr (&_iob[2]); … Donʼt be concerned!with details!12 Interface Example 3!• stdio (cont.)!… FILE *fopen(const char *filename, const char *mode); int fclose(FILE *f); int fflush(FILE *f); int fgetc(FILE *f); int getc(FILE *f); int getchar(void); int putc(int c, FILE *f); int putchar(int c); int fscanf(FILE *f, const char *format, …); int scanf(const char *format, …); int fprintf(FILE *f, const char *format, …); int printf(const char *format, …); …7 13 Encapsulation!(2) A well-designed module encapsulates data!• An interface should hide implementation details!• A module should use its functions to encapsulate its data!• A module should not allow clients to manipulate the data directly!• Why?!• Clarity: Encourages abstraction!• Security: Clients cannot corrupt object by changing its data in unintended ways!• Flexibility: Allows implementation to change – even the data structure – without affecting clients!14 • Stack (version 1)!• Thatʼs bad!• Interface reveals how Stack object is implemented (e.g., as a linked list)!• Client can access/change data directly; could corrupt object!Encapsulation Example 1!/* stack.h */ struct Node { const char *item; struct Node *next; }; struct Stack { struct Node *first; }; struct Stack *Stack_new(void); void Stack_free(struct Stack *s); void Stack_push(struct Stack *s, const char *item); char *Stack_top(struct Stack *s); void Stack_pop(struct Stack *s); int Stack_isEmpty(struct Stack *s); Structure type definitions!in .h file!8 15 Encapsulation Example 1!• Stack (version 2)!• Thatʼs better!• Interface does not reveal how Stack object is implemented!• Client cannot access data directly!/* stack.h */ struct Stack; struct Stack *Stack_new(void); void Stack_free(struct Stack *s); void Stack_push(struct Stack *s, const char *item); char *Stack_top(struct Stack *s); void Stack_pop(struct Stack *s); int Stack_isEmpty(struct Stack *s); Place declaration of !struct Stack in interface;!move
View Full Document