// Purpose. Strategy #include // #include // Discussion. The class Stat has a #include // Bubble sort algorithm hard-wired in // it. It would be nice if the choice class SortImp; // of algorithm were configurable. // The Strategy pattern suggests "de- class Stat { // fine a family of algo's, encapsu- public: // late each one, and make them inter- Stat(); // changeable" via an abstract base void upGrade(); // class. void downGrade(); void readVector( int[], int ); class Stat { /* Bubble sort */ int getMin() { return min_; } public: int getMax() { return max_; } void readVector( int v[], int n ) { int getMed() { return med_; } sort_( v, n ); private: min_ = v[0]; max_ = v[n-1]; int min_, max_, med_; med_ = v[n/2]; } SortImp* imp_; int getMin() { return min_; } }; int getMax() { return max_; } int getMed() { return med_; } class SortImp { public: private: virtual void sort( int[], int ) = 0; int min_, max_, med_; }; void sort_( int v[], int n ) { for (int i=n-1; i > 0; i--) class SortBubble : public SortImp { for (int j=0; j < i; j++) public: if (v[j] > v[j+1]) { void sort( int v[], int n ); int t = v[j]; }; v[j] = v[j+1]; v[j+1] = t; } class SortShell : public SortImp { cout << "Bubble: "; public: for (int k=0; k < n; k++) void sort( int v[], int n ); cout << v[k] << ' '; }; cout << endl; } #include "strategy2.inc" }; Stat::Stat() { imp_ = new SortBubble; } void main( void ) void Stat::upGrade() { delete imp_; { imp_ = new SortShell; } const int NUM = 9; void Stat::downGrade() { delete imp_; int array[NUM]; imp_ = new SortBubble; } time_t t; void Stat::readVector(int v[], int n) { srand((unsigned) time(&t)); imp_->sort( v, n ); cout << "Vector: "; min_ = v[0]; max_ = v[n-1]; for (int i=0; i < NUM; i++) { med_ = v[n/2]; } array[i] = rand() % 9 + 1; cout << array[i] << ' '; } void main( void ) cout << endl; { const int NUM = 9; Stat obj; int array[NUM]; obj.readVector( array, NUM ); time_t t; cout << "min is " << obj.getMin() srand((unsigned) time(&t)); << ", max is " << obj.getMax() cout << "Vector: "; << ", median is " << obj.getMed() for (int i=0; i < NUM; i++) { << endl; array[i] = rand() % 9 + 1; } cout << array[i] << ' '; } cout << endl; /***** current implementation *****/ // Vector: 6 9 9 8 6 5 7 9 2 Stat obj; // Bubble: 2 5 6 6 7 8 9 9 9 obj.upGrade(); // min is 2, max is 9, median is 7 obj.readVector( array, NUM ); /*** an upgraded implementation ***/ cout << "min is " << obj.getMin() // Vector: 4 8 6 4 6 7 4 7 2 << ", max is " << obj.getMax() // Shell: 2 4 4 4 6 6 7 7 8 << ", median is " << obj.getMed() // min is 2, max is 8, median is 6 << endl; } // Purpose. Strategy (template #include // approach) #include // A template can be used to configure #include // a client with a Strategy. This using namespace std; // technique is appropriate if: 1) the // Strategy can be selected at com- template // pile-time, and 2) it does not have class Stat { // to be changed at run-time. With a public: // template, there is no need to spe- void readVector( int v[], int n ) { // cify the interface in a SortImp imp_.sort( v, n ); // base class. The Stat class now has min_ = v[0]; max_ = v[n-1]; // an instance of the sort object, in- med_ = v[n/2]; } // stead of a ptr to the base class. int getMin() { return min_; } // The inheritance approach offers int getMax() { return max_; } // more options and expressiveness. int getMed() { return med_; } // The template approach offers mildly private: // better efficiency. int min_, max_, med_; STRATEGY imp_; class Stat { /* Bubble sort */ }; public: void readVector( int v[], int n ) { class SortBubble { sort_( v, n ); public: min_ = v[0]; max_ = v[n-1]; void sort( int v[], int n ); med_ = v[n/2]; } }; int getMin() { return min_; } int getMax() { return max_; } class SortShell { int getMed() { return med_; } public: private: void sort( int v[], int n ); int min_, max_, med_; }; void sort_( int v[], int n ) { for (int i=n-1; i > 0; i--) #include "strategy2.inc" for (int j=0; j < i; j++) if (v[j] > v[j+1]) { void main( void ) { int t = v[j]; const int NUM = 9; v[j] = v[j+1]; int array[NUM]; v[j+1] = t; } time_t t; cout << "Bubble: "; srand((unsigned) time(&t)); for (int k=0; k < n; k++) cout << "Vector: "; cout << v[k] << ' '; for (int i=0; i < NUM; i++) { cout << endl; array[i] = rand() % 9 + 1; } cout << array[i] << ' '; } }; cout << endl; void main( void ) { Stat obj; const int NUM = 9; obj.readVector( array, NUM ); int array[NUM]; cout << "min is " << obj.getMin() time_t t; << ", max is " << obj.getMax() srand((unsigned) time(&t)); << ", median is " << obj.getMed() cout << "Vector: "; << endl; for (int i=0; i < NUM; i++) { array[i] = rand() % 9 + 1; Stat two; cout << array[i] << ' '; } two.readVector( array, NUM ); cout << endl; cout << "min is " << two.getMin() << ", max is " << two.getMax() Stat obj; << ", median is " << two.getMed() obj.readVector( array, NUM ); << endl; cout << "min is " << obj.getMin() } << ", max is " << obj.getMax() << ", median is " << obj.getMed() // Vector: 3 5 4 9 7 1 4 9 2 << endl; // Bubble: 1 2 3 4 4 5 7 9 9 } // min is 1, max is 9, median is 4 // Shell: 1 2 3 4 4 5 7 9 9 // Vector: 6 9 9 8 6 5 7 9 2 // min is 1, max is 9, median is 4 // Bubble: 2 5 6 6 7 8 9 9 9 // min is 2, max is 9, median is 7 // Purpose. Strategy design pattern demo // // Discussion. The Strategy pattern suggests: encapsulating an algorithm // in a class hierarchy, having clients of that algorithm hold a pointer // to the base class of that hierarchy, and delegating all requests for // the algorithm to that "anonymous" contained object. In this example, // the Strategy base class knows how to collect a paragraph of input and // implement the skeleton of the "format" algorithm. It defers some // details of each individual algorithm to the "justify" member which is // supplied by each concrete derived class of Strategy. The TestBed class // models an application class that would like to leverage the services of // a run-time-specified derived "Strategy" object. #include #include #include class Strategy; class TestBed { public: enum StrategyType { Dummy, Left, Right, Center }; TestBed() { strategy_ = NULL; } void setStrategy( int type, int width ); void doIt(); private: Strategy* strategy_; }; class Strategy { public: Strategy( int width ) : width_( width ) { } void format() { char line[80], word[30]; ifstream inFile( "quote.txt", ios::in ); line[0] = '\0'; inFile >> word; strcat( line, word ); while (inFile >> word) { if (strlen(line) + strlen(word) + 1 > width_) justify( line ); else strcat( line, " " ); strcat( line, word ); } justify( line ); } protected: int width_; private: virtual void justify( char* line ) = 0; }; class LeftStrategy : public Strategy { public: LeftStrategy( int width ) : Strategy( width ) { } private: /* virtual */ void justify( char* line ) { cout << line << endl; line[0] = '\0'; } }; class RightStrategy : public Strategy { public: RightStrategy( int width ) : Strategy( width ) { } private: /* virtual */ void justify( char* line ) { char buf[80]; int offset = width_ - strlen( line ); memset( buf, ' ', 80 ); strcpy( &(buf[offset]), line ); cout << buf << endl; line[0] = '\0'; } }; class CenterStrategy : public Strategy { public: CenterStrategy( int width ) : Strategy( width ) { } private: /* virtual */ void justify( char* line ) { char buf[80]; int offset = (width_ - strlen( line )) / 2; memset( buf, ' ', 80 ); strcpy( &(buf[offset]), line ); cout << buf << endl; line[0] = '\0'; } }; void TestBed::setStrategy( int type, int width ) { delete strategy_; if (type == Left) strategy_ = new LeftStrategy( width ); else if (type == Right) strategy_ = new RightStrategy( width ); else if (type == Center) strategy_ = new CenterStrategy( width ); } void TestBed::doIt() { strategy_->format(); } void main() { TestBed test; int answer, width; cout << "Exit(0) Left(1) Right(2) Center(3): "; cin >> answer; while (answer) { cout << "Width: "; cin >> width; test.setStrategy( answer, width ); test.doIt(); cout << "Exit(0) Left(1) Right(2) Center(3): "; cin >> answer; } return 0; } // Exit(0) Left(1) Right(2) Center(3): 2 // Width: 75 // The important lesson we have learned is that development for reuse is // complex. If making a good design is difficult, then making a good reusable // design is even harder, and any amount of process description cannot // substitute for the skill, imagination, and experience of a good designer. A // process can only support the creative work, and ensure that things are done // and recorded properly. [Karlsson et al, REBOOT] // Exit(0) Left(1) Right(2) Center(3): 3 // Width: 75 // The important lesson we have learned is that development for reuse is // complex. If making a good design is difficult, then making a good reusable // design is even harder, and any amount of process description cannot // substitute for the skill, imagination, and experience of a good designer. A // process can only support the creative work, and ensure that things are done // and recorded properly. [Karlsson et al, REBOOT]