本文继续介绍了迭代,包括了移除字符串字段、封装更改的内容、迭代的新特性以及重构代码等。本文先复习了上一章学习的内容,然后通过举例说明学习移除字符串字段、封装更改的内容、迭代的新特性以及重构代码。

注脚

展开查看详情

1.CMPE 135: Object-Oriented Analysis and Design August 23 Class Meeting Department of Computer Engineering San Jose State University Fall 2018 Instructor: Ron Mak www.cs.sjsu.edu/~mak 1

2.Reminder: By Sunday, August 26 Form teams. Email me your team information. team name team members and email addresses 2

3.3 Example: Rick’s Guitars Inventory Management Application for Rick’s Guitars Maintain a guitar inventory. Locate guitars for customers. UML class diagrams Head First Object-Oriented Analysis & Design by Brett D. McLaughlin, et al. O’Reilly, 2006.

4.4 #include <string> using namespace std ; class Guitar { private:     string serial_number , builder, model, type, back_wood , top_wood ;     double price; public:     Guitar(string serial_number , double price,            string builder, string model, string type,            string back_wood , string top_wood )     : serial_number ( serial_number ), builder(builder), model(model),       type(type), back_wood ( back_wood ), top_wood ( top_wood ), price(price)     {}     string get_serial_number () const { return serial_number ; }     double get_price () const { return price; }     void   set_price (float new_price ) { price = new_price ; }     string get_builder () const { return builder; }     string get_model () const { return model; }     string get_type () const { return type; }     string get_back_wood () const { return back_wood ; }     string get_top_wood () const { return top_wood ; } }; Iteration #1: The Guitar Class Guitar.h

5.5 The Inventory Class #include <list> #include " Guitar.h " using namespace std ; class Inventory { private:     list<Guitar *> guitars; public:     Inventory();     void add_guitar (string serial_number , double price,                     string builder, string model, string type,                     string back_wood , string top_wood );     Guitar * get_guitar (string serial_number );     Guitar * search (Guitar * search_guitar ); }; Inventory.h

6.6 The Inventory Class , cont’d void Inventory:: add_guitar (string serial_number , double price,                            string builder, string model, string type,                            string back_wood , string top_wood ) {     Guitar *guitar = new Guitar( serial_number , price, builder,                                model, type, back_wood , top_wood );     guitars.push_back (guitar); } Guitar *Inventory:: get_guitar (string serial_number ) {     list<Guitar *>::iterator it;     for (it = guitars.begin (); it != guitars.end (); it++)     {         Guitar *guitar = *it;         if (guitar-> get_serial_number () == serial_number ) return *it;     }     return nullptr ; } Inventory.cpp

7.The Inventory Class , cont’d 7 Guitar *Inventory:: search (Guitar * search_guitar ) {     list<Guitar *>::iterator it;     for (it = guitars.begin (); it != guitars.end (); it++)     {         Guitar *guitar = *it;         string builder = search_guitar -> get_builder ();         if (builder != guitar-> get_builder ())  continue;         string model = search_guitar -> get_model ();         if (model != guitar-> get_model ()) continue;         string type = search_guitar -> get_type ();         if (type != guitar-> get_type ()) continue;         string back_wood = search_guitar -> get_back_wood ();         if ( back_wood != guitar-> get_back_wood ()) continue;         string top_wood = search_guitar -> get_top_wood ();         if ( top_wood != guitar-> get_top_wood ()) continue;         return *it; // found a match     }     return nullptr ; // no match } Ignore serial number since thats unique. Ignore price since thats unique. Inventory.cpp

8.The FindGuitarTester Class 8 #include " Inventory.h " using namespace std ; class FindGuitarTester { public:     static void initialize_inventory (Inventory *inventory); }; FindGuitarTest.h

9.The FindGuitarTester Class 8 #include " Inventory.h " using namespace std ; class FindGuitarTester { public:     static void initialize_inventory (Inventory *inventory); }; FindGuitarTest.h

10.The FindGuitarTester Class 8 #include " Inventory.h " using namespace std ; class FindGuitarTester { public:     static void initialize_inventory (Inventory *inventory); }; FindGuitarTest.h

11.11 Problems! Case-sensitive string comparisons. Make them case insensitive. Badly used string fields. Replace them with enumerated types. Assumes at most only one guitar match. Return a list of matching guitars.

12.12 Take roll!

13.Iteration #2: Remove String Fields There are only a limited number each of guitar builders, types, and woods. Therefore, using the string type for those attributes is too general and prone to errors. Use enumerated types instead! We’ll assume that model names keep changing, so we’ll keep that attribute as a string. For each enumerated type, overload operator << to print an enumerated value as a string. 13

14.14 Iteration #2: Remove String Fields , cont’d #include <iostream> using namespace std ; enum class Builder {     FENDER, MARTIN, GIBSON, COLLINGS, OLSON, RYAN, PRS, ANY, }; inline ostream & operator <<( ostream & ostr , const Builder builder) {     switch (builder)     {         case Builder::FENDER:   ostr << " Fender ";   break;         case Builder::MARTIN:   ostr << " Martin ";   break;         case Builder::GIBSON:   ostr << "Gibson";   break;         case Builder::COLLINGS: ostr << " Collings "; break;         case Builder::OLSON:    ostr << " Olson ";    break;         case Builder::RYAN:     ostr << " Ryan ";     break;         case Builder::PRS :     ostr << "PRS";      break;         default:                ostr << "Unspecified";     }     return ostr ; } Builder.h

15.15 Iteration #2: Remove String Fields , cont’d #include <iostream> using namespace std ; enum class Type {     ACOUSTIC, ELECTRIC }; inline ostream & operator <<( ostream & ostr , const Type type) {     switch (type)     {         case Type::ACOUSTIC: ostr << "acoustic"; break;         case Type::ELECTRIC: ostr << "electric"; break;         default:             ostr << "unspecified";     }     return ostr ; } Type.h

16.16 Iteration #2: Remove String Fields , cont’d #include <iostream> using namespace std ; enum class Wood {     INDIAN_ROSEWOOD, BRAZILIAN_ROSEWOOD, MAHOGANY, MAPLE,     COCOBOLO, CEDAR, ADIRONDACK, ALDER, SITKA, }; inline ostream & operator <<( ostream & ostr , const Wood wood) {     switch (wood)     {         case Wood::INDIAN_ROSEWOOD:    ostr << "Indian Rosewood ";     break;         case Wood::BRAZILIAN_ROSEWOOD: ostr << "Brazilian Rosewood ";  break;         case Wood::MAHOGANY:           ostr << "Mahogany";            break;         case Wood::MAPLE:              ostr << "Maple";               break;         case Wood::COCOBOLO:           ostr << " Cocobolo ";            break;         case Wood::CEDAR:              ostr << "Cedar";               break;         case Wood::ADIRONDACK:         ostr << " Adirondack ";          break;         case Wood::ALDER:              ostr << "Alder";               break;         case Wood::SITKA:              ostr << " Sitka ";               break;         default:                       ostr << "unspecified";     }     return ostr ; } Wood.h

17.Iteration #2: Remove String Fields , cont’ d 17 class Guitar { private:     string serial_number , model;     double price;     Builder builder;     Type type;     Wood back_wood , top_wood ; public:     Guitar(string serial_number , double price,            Builder builder, string model, Type type,            Wood back_wood , Wood top_wood )     : serial_number ( serial_number ), model(model), price(price),       builder(builder), type(type), back_wood ( back_wood ), top_wood ( top_wood )     {}     string  get_serial_number () const   { return serial_number ; }     double  get_price () const           { return price; }     void    set_price (float new_price ) { price = new_price ; }     Builder get_builder () const         { return builder; }     string  get_model () const           { return model; }     Type    get_type () const           { return type; }     Wood    get_back_wood () const       { return back_wood ; }     Wood    get_top_wood () const       { return top_wood ; } }; Guitar.h

18.Iteration #2: Remove String Fields , cont’ d 18 #include <vector> #include " Guitar.h " #include " Builder.h " #include " Type.h " #include " Wood.h " using namespace std ; class Inventory { public:     Inventory() {}     void add_guitar (string serial_number , double price,                     Builder builder, string model, Type type,                     Wood back_wood , Wood top_wood );     Guitar * get_guitar (string serial_number );     vector<Guitar *> search(Guitar * search_guitar ); Inventory.h

19.Iteration #2: Remove String Fields , cont’ d 19 private:     vector<Guitar *> guitars;     string to_lower (string str ); }; inline string Inventory:: to_lower (string str ) {     transform( str.begin (), str.end (), str.begin (), :: tolower );     return str ; } Inventory.h We add a small inline function that sets to lower-case all the letters of a string.

20.Iteration #2: Return Multiple Matches 20 list<Guitar *> Inventory::search(Guitar * search_guitar ) {     list<Guitar *> matching_guitars ;     list<Guitar *>::iterator it;     for (it = guitars.begin (); it != guitars.end (); it++)     {         Guitar *guitar = *it;         Builder builder = search_guitar -> get_builder ();         if (builder != guitar-> get_builder ()) continue;         if (   to_lower ( search_guitar -> get_model ())             != to_lower (guitar-> get_model ())) continue;         Type type = search_guitar -> get_type ();         if (type != guitar-> get_type ()) continue;         Wood back_wood = search_guitar -> get_back_wood ();         if ( back_wood != guitar-> get_back_wood ()) continue;         Wood top_wood = search_guitar -> get_top_wood ();         if ( top_wood != guitar-> get_top_wood ()) continue;         matching_guitars.push_back (guitar);     }     return matching_guitars ; } Inventory.cpp

21.Iteration #2: Return Multiple Matches 20 list<Guitar *> Inventory::search(Guitar * search_guitar ) {     list<Guitar *> matching_guitars ;     list<Guitar *>::iterator it;     for (it = guitars.begin (); it != guitars.end (); it++)     {         Guitar *guitar = *it;         Builder builder = search_guitar -> get_builder ();         if (builder != guitar-> get_builder ()) continue;         if (   to_lower ( search_guitar -> get_model ())             != to_lower (guitar-> get_model ())) continue;         Type type = search_guitar -> get_type ();         if (type != guitar-> get_type ()) continue;         Wood back_wood = search_guitar -> get_back_wood ();         if ( back_wood != guitar-> get_back_wood ()) continue;         Wood top_wood = search_guitar -> get_top_wood ();         if ( top_wood != guitar-> get_top_wood ()) continue;         matching_guitars.push_back (guitar);     }     return matching_guitars ; } Inventory.cpp

22.22 Still More Problems! Customers don’t always know all the attributes of the guitar they want. Do we need wildcard search fields? Rick may decide to add more guitar attributes to his inventory. Example: Number of guitar strings The Inventory::search() method is going to get complicated really fast. It will be difficult to maintain if we have to add more guitar attributes.

23.23 What’s Changing? The attributes of a guitar can change. Rick can decide to add, remove, or modify them. The inventory keeps track of guitars, not guitar attributes. Therefore, the inventory code should not change when the guitar attributes change.

24.What’s Changing? cont’d If we encapsulate what changes, we can isolate the changes from the rest of the code. What changes? The guitar attributes. Goal: When the guitar attributes change, the rest of the code does not need to change. 24

25.25 The Solution: Encapsulation Create a new GuitarSpec class that represents the attributes of a guitar. Only the GuitarSpec class needs to change if the attributes change. Therefore, the GuitarSpec class encapsulates the changes and isolates them from the rest of the code.

26.26 Iteration #3: GuitarSpec Class class GuitarSpec { private:     string model;     Builder builder;     Type type;     Wood back_wood , top_wood ; public:     GuitarSpec (Builder builder, string model, Type type,                Wood back_wood , Wood top_wood )     : model(model), builder(builder), type(type),       back_wood ( back_wood ), top_wood ( top_wood )     {}     Builder get_builder () const   { return builder; }     string  get_model () const     { return model; }     Type    get_type () const       { return type; }     Wood    get_back_wood () const { return back_wood ; }     Wood    get_top_wood () const   { return top_wood ; } }; GuitarSpec.h

27.27 Iteration #3: GuitarSpec Class , cont’d This UML class diagram shows that: A Guitar aggregates a GuitarSpec . A GuitarSpec is part of a Guitar . The relationship is one-to-one. From: Head First Object-Oriented Analysis & Design , O’Reilly, 2006.

28.28 Iteration #3: GuitarSpec Class , cont’d class Guitar { private:     string serial_number , model;     double price;     GuitarSpec *spec; public:     Guitar(string serial_number , double price,            Builder builder, string model, Type type,            Wood back_wood , Wood top_wood )     : serial_number ( serial_number ), price(price),       spec(new GuitarSpec (builder, model, type, back_wood , top_wood ))     {}     string      get_serial_number () const   { return serial_number ; }     double      get_price () const           { return price; }     void        set_price (float new_price ) { price = new_price ; }     GuitarSpec * get_spec () const           { return spec; } }; Guitar.h

29.Iteration #3: GuitarSpec Class , cont’d 29 class Inventory { public:     Inventory() {}     void add_guitar (string serial_number , double price,                     Builder builder, string model, Type type,                     Wood back_wood , Wood top_wood );     Guitar * get_guitar (string serial_number );     vector<Guitar *> search( GuitarSpec * search_spec ); private:     vector<Guitar *> guitars;     string to_lower (string str ); }; Inventory.h