This "post" really just lists a bunch of morals we should learn from the UNIX philosophy.
- You cannot tell where a program is going to spend its time. Bottlenecks occur in surprising places, so do not try to second guess and put in a speed hack until you've proven that's where the bottleneck is.
- Measure. Do not tune for speed until your performance analysis tool tells you which part of the code overwhelms the rest.
- Fancy algorithms tend to run more slowly on small data sets than simple algorithms. They tend to have a large constant factor in O(n) analysis, and n is usually small. So don't get fancy unless Rule 2 indicates that n is big enough.
- Simplify your algorithms and data structures wherever it makes sense because fancy algorithms are more difficult to implement without defects. The data structures in most programs can be built from array lists, linked lists, hash tables, and binary trees.
- Data dominates. If you have chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
Additionally, Mike Gancarz mentions some that are overlooked:
- Small is beautiful.
- Make each program do one thing well.
- Build a prototype as soon as possible.
- Choose portability over efficiency.
- Store data in flat text files.
Eric S. Raymond also gives a few worth noting:
- Rule of Modularity:
- Write simple parts connected by clean interfaces.
- Rule of Clarity:
- Clarity is better than cleverness.
- Rule of Composition:
- Design programs to be connected to other programs.
- Rule of Separation:
- Separate policy from mechanism; separate interfaces from engines.
- Rule of Simplicity:
- Design for simplicity; add complexity only where you must.
- Rule of Parsimony:
- Write a big program only when it is clear by demonstration that nothing else will do.
- Rule of Transparency:
- Design for visibility to make inspection and debugging easier.
- Rule of Robustness:
- Robustness is the child of transparency and simplicity.
- Rule of Representation:
- Fold knowledge into data so program logic can be stupid and robust.
- Rule of Least Surprise:
- In interface design, always do the least surprising thing.
- Rule of Silence:
- When a program has nothing surprising to say, it should say nothing.
- Rule of Repair:
- When you must fail, fail noisily and as soon as possible.
- Rule of Optimization:
- Prototype before polishing. Get it working before you optimize it.
So What Have We Learned?
Well, each data structure should do one thing and only one thing. We should also start out by writing the data structures in header files, and the various "accessor/mutator" methods for the data structures should be prototyped in the corresponding header files.
I think that I will try to use a more "data-driven" approach than a "purist object oriented" approach, composing data structures instead of using kind of bloated inheritance.
In fact, I may use regular, old fashioned C and use Phil's Object Oriented ANSI C approach. To reproduce his example (with some modifications), we have
/* FooObj.h */
#ifndef FOO_OBJ_H_
#define FOO_OBJ_H_
struct fooobj {
int privateint;
char *privateString;
/* Depending on your preferences, you
* may prefer privateString to be a char buffer[],
* OR malloc it and free on delete.
*/
};
typedef struct fooobj * FooOBJ;
FooOBJ newFooOBJ();
void setFooNumber(FooOBJ,int);
void setFooString(FooOBJ,char *); /* make comments about copy or not here */
void dumpFooState(FooOBJ); /* dumps debug contents of FooOBJ to stdout */
void deleteFooOBJ(FooOBJ);
#endif /* def FOO_OBJ_H_ */
See the beauty here? And if we have, say, Bar
extend fooobj
, we can do the following:
/* bar.h */
#include "FooObj.h"
#ifndef BAR_H_
#define BAR_H_
struct bar{
fooobj *super;
/* rest not shown */
};
typedef struct bar* Bar;
Bar newBar();
void deleteBar(Bar);
FooOBJ toFooOBJ(Bar); /* simply "return Bar->super;" */
#if __STDC_VERSION__ >= 199901L /* if C99 compliant */
inline void setBarNumber(Bar B, int i) { setFooNumber(B->super,i); }
inline void setBarString(Bar B, char *s) { setFooString(B->super, s); }
#else /* not C99 compliant, use macros instead of inline functions */
#define setBarNumber(B,i) setFooNumber(B->super,i)
#define setBarString(B,s) setFooString(B->super,s)
#endif /* if C99 compliant*/
/* rest not shown */
#endif /* def BAR_H_ */
This is precisely what happens with C++ when it does its inheritance voodoo. At least, in memory this is how it looks like.
If one is using a C compiler that does not allow explicit declaration of inline functions, one could easily use macros instead for setBarNumber()
and setBarString
. Or one could use a full blown function.
No comments:
Post a Comment