Design for Rick’s Music

本文通过设计 Rick’s Music来学习计算机语言的应用。利用一些实际例子来学习,首先设计我们需要的程序,然后重新设计我们的时间,再使用哈希映射封装来完善我们的封装,利用Boost建一个测试套件,来检测我们的设计,最后改进我们的设计。
展开查看详情

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

2.2 Design for Rick’s Music Head First Object-Oriented Analysis & Design by Brett D. McLaughlin, et al. O’Reilly, 2006.

3.3 New InstrumentSpec Class class InstrumentSpec { public:     InstrumentSpec (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 )     {}     virtual ~ InstrumentSpec () = 0;     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 ; }     bool matches( InstrumentSpec * other_spec ); private:     string model;     Builder builder;     Type type;     Wood back_wood , top_wood ;     string to_lower (string str ); }; inline InstrumentSpec ::~ InstrumentSpec () {} Common base class attributes. InstrumentSpec.h

4.4 New InstrumentSpec Class , cont’d bool InstrumentSpec :: matches ( InstrumentSpec * other_spec ) {     if ( builder != other_spec ->builder) return false;     if (   ( model != "")         && ( to_lower (model) != to_lower ( other_spec ->model))) return false;     if ( type != other_spec ->type) return false;     if ( back_wood != other_spec -> back_wood ) return false;     if ( top_wood != other_spec -> top_wood ) return false;     return true; } Match base class attributes only. InstrumentSpec.cpp

5.class GuitarSpec : public InstrumentSpec { public:     GuitarSpec (Builder builder, string model, Type type,                int string_count , Wood back_wood , Wood top_wood )         : InstrumentSpec (builder, model, type, back_wood , top_wood ),           string_count ( string_count )     {}     int get_string_count () const { return string_count ; }     bool matches( InstrumentSpec * other_spec ); private:     int string_count ; }; class MandolinSpec : public InstrumentSpec { public:     MandolinSpec (Builder builder, string model, Type type,                  Style style, Wood back_wood , Wood top_wood )         : InstrumentSpec (builder, model, type, back_wood , top_wood ),           style(style)     {}     Style get_style () const { return style; }     bool matches( InstrumentSpec * other_spec ); private:     Style style; }; GuitarSpec and MandolinSpec Classes 5 enum class Style {     A, F , }; GuitarSpec.h MandolinSpec.h Style.h

6.6 GuitarSpec and MandolinSpec Classes , cont’d bool GuitarSpec ::matches ( InstrumentSpec * other_spec ) {     if ( typeid ( other_spec ) != typeid ( GuitarSpec )) return false;     if (! InstrumentSpec ::matches( other_spec ) ) return false;     GuitarSpec * guitar_spec = static_cast < GuitarSpec *>( other_spec );     if ( string_count != guitar_spec -> string_count ) return false;     return true; } Match base class attributes. bool MandolinSpec ::matches ( InstrumentSpec * other_spec ) {     if ( typeid ( other_spec ) != typeid ( MandolinSpec )) return false;     if (! InstrumentSpec ::matches( other_spec ) ) return false;     MandolinSpec * mandolin_spec = static_cast < MandolinSpec *>( other_spec );     if (style != mandolin_spec ->style) return false;     return true; } Match base class attributes. Type-specific match. Type-specific match. GuitarSpec.cpp MandolinSpec.cpp Invoke the base class’s member function. Invoke the base class’s member function.

7.7 Rick Wants More Instruments! YIKES! Our design doesn’t scale very well ... Head First Object-Oriented Analysis & Design by Brett D. McLaughlin, et al. O’Reilly, 2006.

8.8 Our Design Doesn’t Scale Very Well For each new instrument: We need to add a subclass of Instrument . We need to add a subclass of InstrumentSpec . We need to add a new overloaded function to Inventory::search() for the new instrument. We need to add a new type test in the Inventory:: add_instrument () function list< Guitar *> Inventory:: search( GuitarSpec * search_spec ) list< Mandolin *> Inventory:: search( MandolinSpec * search_spec )

9.9 Time to Revisit Our Design! Having all those Instrument and InstrumentSpec subtypes may not be such a good idea after all.

10.10 Time to Revisit Our Design! cont’d What varies among different kinds of instruments? attribute sets matching algorithm What doesn’t vary ? kind of instrument serial number price Different kinds of instruments have different sets of attributes. Guitars have the number of strings attribute, and mandolins have the style attribute. Every instrument has these.

11.11 Encapsulation to the Rescue? If we can encapsulate the varying sets of instrument attributes in one place, we can get rid of all the Instrument and InstrumentSpec subclasses. Then both the Instrument and InstrumentSpec classes can become concrete classes (instead of being abstract).

12.12 Encapsulation Using a Hash Map Idea: Encapsulate the varying attributes in a hash map . Pairs of attribute name and attribute value Each kind of instrument can have a different set of name:value pairs in the hash map. Examples: Guitar Builder: Gibson Model: “Les Paul” Type: electric Strings: 6 Top wood: maple Back wood: maple Mandolin Builder: Gibson Model: “F-5G” Type: acoustic Style: A Top wood: maple Back wood: maple Banjo Builder: Gibson Model: “SRB-3 Wreath” Type: acoustic Strings: 5 Back wood: maple

13.Encapsulation Using a Hash Map , cont’d The hash map can use the following keys : The enum class contains keys for all the attributes across all the kinds of instruments. We may need to add new enum values when we add more kinds of instruments. 13 enum class Key {     BUILDER, MODEL, STRINGS, STYLE, TYPE, TOP_WOOD, BACK_WOOD };

14.Encapsulation Using a Hash Map , cont’d What about the values in the hash map? When the key is Key::BUILDER , the value is one of When the key is Key::STYLE , the value is one of When the key is Key::TYPE , the value is one of 14 enum class Builder {     FENDER, MARTIN, GIBSON, COLLINGS, OLSON, RYAN, PRS, ANY, }; enum class Style {     A, F, }; enum class Type {     ACOUSTIC, ELECTRIC };

15.Encapsulation Using a Hash Map , cont’d When the key is Key::TOP_WOOD or Key::BACK_WOOD , the value is one of When the key is Key::STRINGS , the value is an integer (the number of strings). When the key is Key::MODEL , the value is a string. 15 enum class Wood {     INDIAN_ROSEWOOD, BRAZILIAN_ROSEWOOD, MAHOGANY, MAPLE,     COCOBOLO, CEDAR, ADIRONDACK, ALDER, SITKA };

16.Encapsulation Using a Hash Map , cont’d How can we store values of different datatypes into the hash map? One possible solution: 16 union Value {     Builder builder;     Kind kind;     Style style;     Type type;     Wood wood;     int strings;     string model; }

17.Using Boost A better solution is to use the Boost C++ library . Download from https://www.boost.org/ For our program, we only need the Boost header files. You don’t need to build the library. 17

18.Using Boost Tell Eclipse where you put the header files. 18 On the Mac and Linux, put them in the standard header files location / usr /local/include/boost and then you won’t need to tell Eclipse.

19.The boost::any Class A variable of type boost::any can hold a value of any datatype. To assign a value: To retrieve a value, you must do a type cast with boost:: any_cast < Type > 19 #include <boost/ any.hpp > ... boost::any value ; value = Builder::FENDER;  Builder b; b = boost:: any_cast <Builder>(value);

20.The Builder Attribute Class 20 #include <iostream> #include <boost/ any.hpp > using namespace std ; enum class Builder {     FENDER, MARTIN, GIBSON, COLLINGS, OLSON, RYAN, PRS, ANY, }; inline Builder to_builder (boost::any value) {     return boost:: any_cast <Builder>(value); } inline bool equal_builders (boost::any value1, boost::any value2) {     return to_builder (value1) == to_builder (value2); } Builder.h Return a Builder value. Compare two Builder values.

21.The Builder Attribute Class , cont’d Similarly define enum classes Kind , Model , Strings , Type , and Wood and their helper functions. 21 inline ostream & operator << ( ostream & ostr , const Builder builder) {     switch (builder)     {         case Builder::FENDER:   cout << "Fender";   break;         case Builder::MARTIN:   cout << "Martin";   break;         case Builder::GIBSON:   cout << "Gibson";   break;         case Builder::COLLINGS: cout << "Collings"; break;         case Builder::OLSON:    cout << "Olson";    break;         case Builder::RYAN:     cout << "Ryan";     break;         case Builder::PRS :     cout << "PRS";      break;         default:                cout << "Unspecified";     }     return ostr ; } Builder.h

22.The Attributes Map The attribute keys: Add more keys as needed for new kinds of instruments. The attributes map: The boost::any value can be any value of type Builder , Style , Wood , Model , Strings , etc. 22 enum class Key {     BUILDER, MODEL, STRINGS, STYLE, TYPE, TOP_WOOD, BACK_WOOD }; typedef map<Key, boost::any > AttributesMap ; Attributes.h Attributes.h

23.Class Attributes 23 class Attributes { public:     AttributesMap get_attributes () { return attributes; }     boost::any get_value (Key key) { return attributes[key]; }     void set(Key key, boost::any value) { attributes[key] = value; }     bool matches(Attributes *other);     void print(); private:     AttributesMap attributes ;  // the attributes map     bool has_key (Key key);     bool equals(Attributes *other, const Key key); }; ostream & operator <<( ostream & ostr , const Key attr ); Attributes.h

24.Class Attributes , cont’d 24 bool Attributes:: matches (Attributes *other) {     AttributesMap ::iterator it;     for (it = attributes.begin (); it != attributes.end (); it++)     {         Key key = it->first;         // Are the corresponding attribute values equal?         if (! (other-> has_key (key) && equals (other, key)) ) return false;     }     return true;  // the values all match } bool Attributes:: has_key (Key key) {     return attributes.find (key) != attributes.end (); } Attributes.cpp

25.Class Attributes , cont’d 25 bool Attributes:: equals (Attributes *other, const Key key) {     boost::any  this_value = get_value (key);     boost::any other_value = other-> get_value (key);     switch (key)     {         case Key::BUILDER:   return equal_builders ( this_value , other_value );         case Key::MODEL:     return equal_models ( this_value , other_value );         case Key::STRINGS:   return equal_strings ( this_value , other_value );         case Key::STYLE:     return equal_styles ( this_value , other_value );         case Key::TYPE:      return equal_types ( this_value , other_value );         case Key::TOP_WOOD:  return equal_woods ( this_value , other_value );         case Key::BACK_WOOD: return equal_woods ( this_value , other_value );         default:                   return false;     } } Attributes.cpp inline bool equal_builders (boost::any value1, boost::any value2) {     return to_builder (value1) == to_builder (value2); } Builder.h

26.Class InstrumentSpec 26 class InstrumentSpec { public:     InstrumentSpec (Attributes *attributes) : attributes(attributes) {}     Attributes * get_attributes () const { return attributes; }     bool matches ( InstrumentSpec * other_spec ); private:     Attributes *attributes; }; inline bool InstrumentSpec :: matches ( InstrumentSpec * other_spec ) {     return attributes->matches ( other_spec -> get_attributes ()); } InstrumentSpec.h Delegate to class Attributes

27.class Instrument { public:     Instrument(Kind kind, string serial_number , double price,                InstrumentSpec *spec)         : kind(kind), serial_number ( serial_number ), price(price), spec(spec)     {}     string get_serial_number () const   { return serial_number ; }     double get_price () const { return price; }     void set_price (float new_price ) { price = new_price ; }     InstrumentSpec * get_spec () const { return spec; }     void print_match (); private:     Kind kind;     string serial_number ;     double price;     InstrumentSpec *spec; }; Class Instrument 27 Every instrument has this set of attributes, so they don’t vary . Encapsulate the varying set of attributes Instrument.h

28.Class Inventory 28 class Inventory { public:     void add_instrument (Kind kind, string serial_number , double price,                         InstrumentSpec *spec);     Instrument *get(string serial_number );     vector<Instrument *> search ( InstrumentSpec * search_spec ); private:     vector<Instrument *> inventory; }; Inventory.h

29.Class Inventory 28 class Inventory { public:     void add_instrument (Kind kind, string serial_number , double price,                         InstrumentSpec *spec);     Instrument *get(string serial_number );     vector<Instrument *> search ( InstrumentSpec * search_spec ); private:     vector<Instrument *> inventory; }; Inventory.h