object and class creational design patterns
DESCRIPTION
Object and Class Creational Design Patterns. The practicality of modules. CS 236700: Software Design. Usage (1/2). Let’s consider the following directory…. …Then, we can use imgdir.exe as follows:. C:>imgdir.exe a.gif b.jpg c.bmp a.gif,GIF,273,310 b.jpg,JPG,128,82 c.bmp,BMP,128,82. - PowerPoint PPT PresentationTRANSCRIPT
1
Object and ClassObject and ClassCreationalCreational
Design PatternsDesign Patterns
The practicality of modulesThe practicality of modules
CS 236700: Software Design
2
Usage (1/2)Usage (1/2)Let’s consider the following directory…
…Then, we can use imgdir.exe as follows:
C:>imgdir.exe a.gif b.jpg c.bmpa.gif,GIF,273,310b.jpg,JPG,128,82c.bmp,BMP,128,82
C:>imgdir.exe a.gif b.jpg c.bmpa.gif,GIF,273,310b.jpg,JPG,128,82c.bmp,BMP,128,82
3
Usage (2/2)Usage (2/2) Command line options:
C:\temp>imgdir.exeimgdir [-S <sep>] [-f <fields>] [-V] file1 file2 ... -s field separator enclosed in quotes
-f fields to display: N-name T-type, W-width, H-height example "NTWH“
-v verbose mode: non image files are listed as well
C:\temp>imgdir.exeimgdir [-S <sep>] [-f <fields>] [-V] file1 file2 ... -s field separator enclosed in quotes
-f fields to display: N-name T-type, W-width, H-height example "NTWH“
-v verbose mode: non image files are listed as well
4
Current state: Imgdir is written in C.
Original source code - 465 lines. Some bugs were fixed so we can have a clean start Code quality is poor Code style is poor
Our mission:
The missionThe mission
Rewrite imgdir.c in a modular wayRewrite imgdir.c in a modular way
We CANNOT use classes We CAN use other C++ features: namespaces, overloading,… No textbook solution
5
Source code overview (1/3)Source code overview (1/3)
/* Command line codes: */#define FIELDOPTION 1 /* for –f <fields> */#define SEPOPTION 2 /* for –s <sep> */#define VERBOSEOPTION 3 /* for –v */#define NOTOPTION 0 /* indicates a file name */
typedef struct { char *sep; /* seperator string for fields */ char *fields; /* fields to display: nthw */ int fieldlen; /* == strlen(fields) short verbose; /* 1 – Verbose mode is ON */} DISPLAYOPTIONS;
/* Command line codes: */#define FIELDOPTION 1 /* for –f <fields> */#define SEPOPTION 2 /* for –s <sep> */#define VERBOSEOPTION 3 /* for –v */#define NOTOPTION 0 /* indicates a file name */
typedef struct { char *sep; /* seperator string for fields */ char *fields; /* fields to display: nthw */ int fieldlen; /* == strlen(fields) short verbose; /* 1 – Verbose mode is ON */} DISPLAYOPTIONS;
6
Source code overview (2/3)Source code overview (2/3)
#define NOTYPE 0 #define JPEGTYPE 1#define GIFTYPE 2#define BMPTYPE 3
typedef struct { int imgtype; /* NOTYPE, JPEGTYPE, etc..*/ char *imgtypename; /* “BMP”, “GIF”, or “JPG” */ char *filename; long width; /* width of image in pixels */ long height; /* height of image in pixels */} IMAGEINFO;
#define NOTYPE 0 #define JPEGTYPE 1#define GIFTYPE 2#define BMPTYPE 3
typedef struct { int imgtype; /* NOTYPE, JPEGTYPE, etc..*/ char *imgtypename; /* “BMP”, “GIF”, or “JPG” */ char *filename; long width; /* width of image in pixels */ long height; /* height of image in pixels */} IMAGEINFO;
7
Source code overview (3/3)Source code overview (3/3)
void swapShort(short, short *); void swapLong(long, long *);int ffindMarker(FILE *, int, int,long);int setOption(int,char **, int, DISPLAYOPTIONS *); int getOptionCode(char *); void imageType(char *, IMAGEINFO *);void imageSize(IMAGEINFO *);
int isjpg(char *); /* 1 – true, 0 - false */int isgif(char *);int isbmp(char *);void jpegSize(char *, long *,long *); void gifSize(char *, long *,long *);void bmpSize(char *, long *,long *);
void dspEntry(DISPLAYOPTIONS*, IMAGEINFO*);void dspUsage();
void main(int,char **);
void swapShort(short, short *); void swapLong(long, long *);int ffindMarker(FILE *, int, int,long);int setOption(int,char **, int, DISPLAYOPTIONS *); int getOptionCode(char *); void imageType(char *, IMAGEINFO *);void imageSize(IMAGEINFO *);
int isjpg(char *); /* 1 – true, 0 - false */int isgif(char *);int isbmp(char *);void jpegSize(char *, long *,long *); void gifSize(char *, long *,long *);void bmpSize(char *, long *,long *);
void dspEntry(DISPLAYOPTIONS*, IMAGEINFO*);void dspUsage();
void main(int,char **);
8
Why do we want to rewrite the source code ? Suppose, we want to change the value of NOTOPTION to -1:
#define NOTOPTION -1 //previously 0The problem:
The mission (continued)The mission (continued)
void main(int argc, char *argv[]){ // .. for(i = 1; i < argc; ++i) { int t; if(t = getOptionCode(argv[i]) { // Do something } else break; } // .. Rest of main()
void main(int argc, char *argv[]){ // .. for(i = 1; i < argc; ++i) { int t; if(t = getOptionCode(argv[i]) { // Do something } else break; } // .. Rest of main()
NOTOPTION is now -1 => t is never 0
9
In imgdir.c a small change can create program-wide bugs Poor continuity
The program has a flat structure => we must check all 15 functions No separation => every function can be effected
Relevant terms: dependency, coupling, cohesion, continuity
So, let’s make this thing modular…
AnalysisAnalysis
10
We will use namespaces as modules Each function will “move” into an appropriate module The initial list of modules:
<main>:
main() <isimage>:
imageType(), isjpg(), isbmp(), isgif() <sizeofimage>:
imageSize(), jpegSize(), bmpSize(), gifSize() <utils>:
ffindMarker(), swapShort(), swapLong() <commandline>:
getOptionCode(), setOption() <display>:
dspEntry(), dspUsage()
Module breakdown – stage 1Module breakdown – stage 1
Imgdir.c-stage1
11
Shows the “usage” relation between modules Built by checking function calls between modules
Dependency graphDependency graph
<Main>
<Commandline> <sizeofimage>
<utils>
<isimage><display>
Activities of main(): scans the command line builds display the option For each image:
Check image type Find size Display the data
Number of edges
= 6
A little problem:How does <display> know the details of the current image (width, height, type…) ??
Imgdir.c-stage1
12
The image details are passed via an IMAGEINFO struct. A function call is not the only source of dependency
Other C elements can also yield a dependency: typedef, struct, enum
=> Let’s place more elements in our modules IMAGEINFO is placed inside <isimage> DISPLAYOPTIONS is placed inside <display>
Dependency graphDependency graph
<main>
<Commandline> <sizeofimage>
<utils>
<isimage><display>
Number of edges
= 9
Imgdir.c-stage2
13
After adding IMAGEINFO and DISPLAYOPTIONS <main>:
main() <isimage>:
imageType(), isjpg(), isbmp(), isgif(), IMAGEINFO <sizeofimage>:
imageSize(), jpegSize(), bmpSize(), gifSize() <utils>:
ffindMarker(), swapShort(), swapLong() <commandline>:
getOptionCode(), setOption() <display>:
dspEntry(), dspUsage(), DISPLAYOPTIONS
Module breakdown – stage 2Module breakdown – stage 2
Imgdir.c-stage2
14
Considering <isimage>, <sizeofimage>, <utils>: <utils> is a shared module <utils> is an implementation detail There are only three elements used by <main>, <display>:
IMAGEINFO, imageType(), imageSize() => Define a new module: <image>
A module which contains sub-modules:• <utils>, <sizeofimage>, <isimage>
We can simulate public/private access level
Coupling reduction (1/4)Coupling reduction (1/4)
<main>
<commandline> <sizeofimage>
<utils>
<isimage><display>
Number of edges
= 5
<main>
<commandline>
<image>(<isimage>,<sizeofimage>, <utils>)
<display>
15
A new module was created: <image> <main>:
main() <image>
imageType(), imageSize(), IMAGEINFO <isimage>:
isjpg(), isbmp(), isgif(), <sizeofimage>:
jpegSize(), bmpSize(), gifSize() <utils>:
ffindMarker(), swapShort(), swapLong() <commandline>:
getOptionCode(), setOption() <display>:
dspEntry(), dspUsage(), DISPLAYOPTIONS
Coupling reduction (2/4)Coupling reduction (2/4)
16
Considering <main>,<display>,<commandline>: <main> is coupled with 3 other modules – Too much The reason for <main>’s coupling with <commandline>:
for(i = 1; i < argc; ++i) {
if(t = getOption(..)) setOption(..)
else break; }
Our next step: Move the loop into <display>
• A new function in <display>: buildOptions() Place <commandline> inside <display>
Coupling reduction (3/4)Coupling reduction (3/4)
Imgdir.c-stage3
Number of edges
= 3
<display>(<commandline>)
<main>
<image>(<isimage>,<sizeofimage>, <utils>)
<display>
<main>
<image>(<isimage>,<sizeofimage>, <utils>)
<commandline>
17
<commandline> moved into <display>: <main>:
main() <image>
imageType(), imageSize(), IMAGEINFO <isimage>:
isjpg(), isbmp(), isgif(), <sizeofimage>:
jpegSize(), bmpSize(), gifSize() <utils>:
ffindMarker(), swapShort(), swapLong() <display>:
dspEntry(), dspUsage(), DISPLAYOPTIONS, buildOptions() <commandline>:
getOptionCode(), setOption()
Coupling reduction (4/4)Coupling reduction (4/4)
Imgdir.c-stage3
18
What else? use enum instead of #define
Obeys scope rules => Can be hidden inside a module
Rewrite using classes ?!
Additional stepsAdditional steps
19
Friendly suggestion: coupling reduction at the system’s upper-level is usually the best place to start.
We used number of edges as a “modularity meter” There are other important factors, such as:
circles number of modules number of functions per module
The “algorithm” Work on the “Most-coupled” modules
In our case: <main> Shared modules => make them private sub-modules
In our case: <utils> was moved into the new module <image> Creating new modules can decrease coupling
<image> Don’t forget the non-function elements: typedefs, structs, enums,…
Summary: Coupling reductionSummary: Coupling reduction
20
Imgdir.c - Source code Imgdir.c - Source code
21
ffindMarker()ffindMarker()
int ffindMarker(FILE *infile, int c1, int c2,long mxbytes){ int c,done,rval,state; long bytecount;
rval = 0; state = 0; done = 0; bytecount = 0; while (!done) { if ((bytecount == mxbytes) && (mxbytes != 0)) { done = 1; } else { c = fgetc(infile); if (mxbytes != 0) bytecount++; }
int ffindMarker(FILE *infile, int c1, int c2,long mxbytes){ int c,done,rval,state; long bytecount;
rval = 0; state = 0; done = 0; bytecount = 0; while (!done) { if ((bytecount == mxbytes) && (mxbytes != 0)) { done = 1; } else { c = fgetc(infile); if (mxbytes != 0) bytecount++; }
switch (state) { case 0: if ( c == c1) state = 1; else if ( c == EOF) done = 1; break; case 1: if ( c == c2) { done = 1; rval = 1; } else if (c == EOF) done = 1; else { fseek(infile, -1, SEEK_CUR); state = 0; } break; } } return rval;}
switch (state) { case 0: if ( c == c1) state = 1; else if ( c == EOF) done = 1; break; case 1: if ( c == c2) { done = 1; rval = 1; } else if (c == EOF) done = 1; else { fseek(infile, -1, SEEK_CUR); state = 0; } break; } } return rval;}
22
setOption()setOption()
int setOption(int opt, char **argv, int index, DISPLAYOPTIONS *dsp){ switch (opt) { case FIELDOPTION : dsp->fields = argv[++index]; dsp->fieldlen = dsp->fields; break; case SEPOPTION : dsp->sep = argv[++index]; break; case VERBOSEOPTION : dsp->verbose = 1; break; } return index; /* index of last argv entry that was read */}
int setOption(int opt, char **argv, int index, DISPLAYOPTIONS *dsp){ switch (opt) { case FIELDOPTION : dsp->fields = argv[++index]; dsp->fieldlen = dsp->fields; break; case SEPOPTION : dsp->sep = argv[++index]; break; case VERBOSEOPTION : dsp->verbose = 1; break; } return index; /* index of last argv entry that was read */}
23
swapShort()swapShort()
void swapShort(short i, short *t){ char *pi; char *pt; pi = (char *)&i; pt = (char *)t; pt[0] = pi[1]; pt[1] = pi[0];}
void swapShort(short i, short *t){ char *pi; char *pt; pi = (char *)&i; pt = (char *)t; pt[0] = pi[1]; pt[1] = pi[0];}
24
getOptionCode()getOptionCode()
int getOptionCode(char *opt){ int rval;
if(strcmp(opt,"-f")== 0) rval = FIELDOPTION; else if(strcmp(opt,"-s")== 0) rval = SEPOPTION; else if(strcmp(opt,"-v") == 0) rval = VERBOSEOPTION; else rval = NOTOPTION;
return rval;}
int getOptionCode(char *opt){ int rval;
if(strcmp(opt,"-f")== 0) rval = FIELDOPTION; else if(strcmp(opt,"-s")== 0) rval = SEPOPTION; else if(strcmp(opt,"-v") == 0) rval = VERBOSEOPTION; else rval = NOTOPTION;
return rval;}
25
isJpg()isJpg()
int isjpg(char *fname){ int rval; FILE *infile; rval = 0; infile = fopen(fname,"rb");
/* look for jpeg markers in first two bytes */ if(ffindMarker(infile,0xff,0xd8,2L)) rval = 1;
/* check last two bytes for end marker */ fseek(infile, -2, SEEK_END); rval = rval && ffindMarker(infile,0xff,0xd9,2L); fclose(infile); return rval;}
int isjpg(char *fname){ int rval; FILE *infile; rval = 0; infile = fopen(fname,"rb");
/* look for jpeg markers in first two bytes */ if(ffindMarker(infile,0xff,0xd8,2L)) rval = 1;
/* check last two bytes for end marker */ fseek(infile, -2, SEEK_END); rval = rval && ffindMarker(infile,0xff,0xd9,2L); fclose(infile); return rval;}
26
bmpSize()bmpSize()
void bmpSize(char *fname, long *width, long *height){ FILE *infile; long w,h; short bigindian; bigindian = ((char *)(&INDIANFLAG))[0]; /* determine endianess */
infile = fopen(fname,"rb"); fseek(infile,18,SEEK_SET); /* skip header header string */ fread(&w,sizeof(long),1,infile); fread(&h,sizeof(long),1,infile); if (bigindian) /* bmp are little indian format */ { swapLong(w,width); swapLong(h,height); } else { *width = w; *height = h; } fclose(infile);}
void bmpSize(char *fname, long *width, long *height){ FILE *infile; long w,h; short bigindian; bigindian = ((char *)(&INDIANFLAG))[0]; /* determine endianess */
infile = fopen(fname,"rb"); fseek(infile,18,SEEK_SET); /* skip header header string */ fread(&w,sizeof(long),1,infile); fread(&h,sizeof(long),1,infile); if (bigindian) /* bmp are little indian format */ { swapLong(w,width); swapLong(h,height); } else { *width = w; *height = h; } fclose(infile);}
27
dspEntry()dspEntry()
void dspEntry(DISPLAYOPTIONS* dsp, IMAGEINFO img){ int i; if(dsp.fieldlen > 0) { for( i = 0; i < dsp.fieldlen-1; i++) { switch (dsp.fields[i]) { case 'N' : case 'n' : printf("%s%s",img.filename, dsp.sep); break; case 'T' : case 't': printf("%s%s",img.imgtypename, dsp.sep); break; case 'W' : case 'w' : printf("%ld%s",img.width, dsp.sep); break; case 'H' : case 'h' : printf("%ld%s", img.height, dsp.sep); } }
void dspEntry(DISPLAYOPTIONS* dsp, IMAGEINFO img){ int i; if(dsp.fieldlen > 0) { for( i = 0; i < dsp.fieldlen-1; i++) { switch (dsp.fields[i]) { case 'N' : case 'n' : printf("%s%s",img.filename, dsp.sep); break; case 'T' : case 't': printf("%s%s",img.imgtypename, dsp.sep); break; case 'W' : case 'w' : printf("%ld%s",img.width, dsp.sep); break; case 'H' : case 'h' : printf("%ld%s", img.height, dsp.sep); } }
switch(dsp.fields[dsp.fieldlen-1]) { case 'N' : case 'n': printf("%s\n",img.filename); break; case 'T' : case 't' : printf("%s\n",img.imgtypename); break; case 'W' : case 'w' : printf("%ld\n",img.width); break; case 'H' : case 'h' : printf("%ld\n", img.height); } } }
switch(dsp.fields[dsp.fieldlen-1]) { case 'N' : case 'n': printf("%s\n",img.filename); break; case 'T' : case 't' : printf("%s\n",img.imgtypename); break; case 'W' : case 'w' : printf("%ld\n",img.width); break; case 'H' : case 'h' : printf("%ld\n", img.height); } } }
28
main()main()
void main(int argc, char *argv[]){ int i; if(argc == 1) dspUsage(); for(i = 1; i < argc; ++i) { int t; if(t = getOptionCode(argv[i]) { i = setOption(t, argv, i, &displayOptions); } else break; }
void main(int argc, char *argv[]){ int i; if(argc == 1) dspUsage(); for(i = 1; i < argc; ++i) { int t; if(t = getOptionCode(argv[i]) { i = setOption(t, argv, i, &displayOptions); } else break; }
for( ; i < argc; ++i) { imageType(argv[i],¤tImage); if(displayOptions.verbose || currentImage.imgtype != NOTYPE) { imageSize(¤tImage); dspEntry(displayOptions, currentImage); } } // for
} // main()
for( ; i < argc; ++i) { imageType(argv[i],¤tImage); if(displayOptions.verbose || currentImage.imgtype != NOTYPE) { imageSize(¤tImage); dspEntry(displayOptions, currentImage); } } // for
} // main()
29
imageType()imageType()
void imageType(char *img, IMAGEINFO *info){ int rval; info->filename = img; if(isjpg(img)) info->imgtype = JPEGTYPE; else if(isgif(img)) info->imgtype = GIFTYPE; else if(isbmp(img)) info->imgtype = BMPTYPE; else info->imgtype = NOTYPE;}
void imageType(char *img, IMAGEINFO *info){ int rval; info->filename = img; if(isjpg(img)) info->imgtype = JPEGTYPE; else if(isgif(img)) info->imgtype = GIFTYPE; else if(isbmp(img)) info->imgtype = BMPTYPE; else info->imgtype = NOTYPE;}
30
imageSize()imageSize()
void imageSize(IMAGEINFO *info){ switch (info->imgtype) { case JPEGTYPE : jpegSize(info->filename,&(info->width),&(info->height)); info->imgtypename = "JPG"; break; case BMPTYPE : bmpSize(info->filename,&(info->width),&(info->height)); info->imgtypename = "BMP"; break;
case GIFTYPE : gifSize(info->filename,&(info->width),&(info->height)); info->imgtypename = "GIF"; break;
case NOTYPE : info->width = info->height = 0; info->imgtypename = "XXX"; }}
void imageSize(IMAGEINFO *info){ switch (info->imgtype) { case JPEGTYPE : jpegSize(info->filename,&(info->width),&(info->height)); info->imgtypename = "JPG"; break; case BMPTYPE : bmpSize(info->filename,&(info->width),&(info->height)); info->imgtypename = "BMP"; break;
case GIFTYPE : gifSize(info->filename,&(info->width),&(info->height)); info->imgtypename = "GIF"; break;
case NOTYPE : info->width = info->height = 0; info->imgtypename = "XXX"; }}
31
buildOptions()buildOptions()
int buildOptions(int argc, char** argv, DISPLAYOPTIONS* result) { /* this function replaces the first loop in main() */ int i; result->sep =","; result->fields = "ntwh"; result->fieldlen = 4; result->verbose = 0; for(i = 1; i < argc; ++i) { int t; if(t = getOptionCode(argv[i]); i = setOption(t, argv, i, &displayOptions); else break; } return i; }
int buildOptions(int argc, char** argv, DISPLAYOPTIONS* result) { /* this function replaces the first loop in main() */ int i; result->sep =","; result->fields = "ntwh"; result->fieldlen = 4; result->verbose = 0; for(i = 1; i < argc; ++i) { int t; if(t = getOptionCode(argv[i]); i = setOption(t, argv, i, &displayOptions); else break; } return i; }