CMPE 135: Object-Oriented Analysis and Design August 23 Class Meeting

Slides:



Advertisements
Similar presentations
HFOOAD Chapter 1 A Simple Application. We build software to solve problems. People have problems. Therefore, we build software for people. Good software.
Advertisements

CS 151: Object-Oriented Design August 22 Class Meeting Department of Computer Science San Jose State University Fall 2013 Instructor: Ron Mak
Object-Oriented Analysis & Design Chapter 1 NTPCUG Study Series.
HFOOAD Chapter 1 A Simple Application. We build software to solve problems. People have problems. Therefore, we build software for people. Phần mềm tốt.
Iterators T.J. Niglio Computer & Systems Engineering Fall 2003 Software Design & Documentation Object Behavioral.
CSE 332: C++ Algorithms II From Last Time: Search with Generic Iterators Third generalization: separate iterator type parameter We arrive at the find algorithm.
REFACTORING Lecture 4. Definition Refactoring is a process of changing the internal structure of the program, not affecting its external behavior and.
C++ for Everyone by Cay Horstmann Copyright © 2012 by John Wiley & Sons. All rights reserved For Loops October 16, 2013 Slides by Evan Gallagher.
C ++ Programming Languages Omid Jafarinezhad Lecturer: Omid Jafarinezhad Fall 2013 Lecture 2 Department of Computer Engineering 1.
Refactoring Deciding what to make a superclass or interface is difficult. Some of these refactorings are helpful. Some research items include Inheritance.
CS 151: Object-Oriented Design August 27 Class Meeting Department of Computer Science San Jose State University Fall 2013 Instructor: Ron Mak
Nothing Stays the Same Chapter 5 HeadFirst OOAD Tom Perkins - NTPCUG.
CS 46B: Introduction to Data Structures July 23 Class Meeting Department of Computer Science San Jose State University Summer 2015 Instructor: Ron Mak.
Unified Modeling Language (UML)
Searching Arrays Linear search Binary search small arrays
C++ Lesson 1.
Chapter 13 Recursion Copyright © 2016 Pearson, Inc. All rights reserved.
Chapter 1.2 Introduction to C++ Programming
Chapter 1.2 Introduction to C++ Programming
Chapter 1.2 Introduction to C++ Programming
Chapter 1.2 Introduction to C++ Programming
Structures Revisited what is an aggregate construct? What aggregate constructs have we studied? what is a structure? what is the keyword to define a structure?
CMPE 135: Object-Oriented Analysis and Design September 26 Class Meeting Department of Computer Engineering San Jose State University Fall 2017 Instructor:
CMPE 135: Object-Oriented Analysis and Design October 3 Class Meeting
CS 153: Concepts of Compiler Design August 29 Class Meeting
CMPE Data Structures and Algorithms in C++ September 28 Class Meeting
CMPE 135: Object-Oriented Analysis and Design October 17 Class Meeting
CMPE 135: Object-Oriented Analysis and Design October 17 Class Meeting
Mr. Shaikh Amjad R. Asst. Prof in Dept. of Computer Sci. Mrs. K. S
CMPE Data Structures and Algorithms in C++ February 22 Class Meeting
CMPE 152: Compiler Design February 6 Class Meeting
Reserved Words.
CMPE 180A Data Structures and Algorithms in C++ March 8 Class Meeting
Namespaces CSCE 121 J. Michael Moore
Introduction to Classes
CMPE 152: Compiler Design September 18 Class Meeting
CMPE 152: Compiler Design September 13 Class Meeting
Function Overloading C++ allows us to define functions that have the same name but different sets of parameters This capability can be used to define similar.
CMPE 135: Object-Oriented Analysis and Design August 30 Class Meeting
CMPE 152: Compiler Design August 23 Class Meeting
CMPE 135: Object-Oriented Analysis and Design November 27 Class Meeting Department of Computer Engineering San Jose State University Fall 2018 Instructor:
CMPE 152: Compiler Design September 27 Class Meeting
CMPE 135 Object-Oriented Analysis and Design March 21 Class Meeting
Programming Introduction to C++.
CMPE 152: Compiler Design January 29 Class Meeting
CMPE 152: Compiler Design February 12 Class Meeting
CMPE 135: Object-Oriented Analysis and Design February 5 Class Meeting
CS 144 Advanced C++ Programming January 31 Class Meeting
CMSC 202 Lesson 6 Functions II.
CS 144 Advanced C++ Programming February 12 Class Meeting
CMPE 152: Compiler Design February 21 Class Meeting
CS 144 Advanced C++ Programming February 21 Class Meeting
CMPE 135 Object-Oriented Analysis and Design March 7 Class Meeting
CS 144 Advanced C++ Programming March 28 Class Meeting
CS31 Discussion 1D Fall18: week 2
Class rational part2.
CMPE 152: Compiler Design March 5 Class Meeting
CS 144 Advanced C++ Programming March 21 Class Meeting
CS 144 Advanced C++ Programming April 16 Class Meeting
Constructors and Deconstructor
CS 144 Advanced C++ Programming April 11 Class Meeting
CS 144 Advanced C++ Programming April 30 Class Meeting
CMPE 135 Object-Oriented Analysis and Design August 22 Class Meeting
CMPE 152: Compiler Design September 3 Class Meeting
CMPE 152: Compiler Design August 27 Class Meeting
CMPE 152: Compiler Design September 19 Class Meeting
Presentation transcript:

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

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

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.

Iteration #1: The Guitar Class #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; } }; Guitar.h

The Inventory Class #include <list> Inventory.h #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

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

The Inventory Class, cont’d 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 that's unique. Ignore price since that's unique. Inventory.cpp

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

The FindGuitarTester Class, cont’d void FindGuitarTester::initialize_inventory(Inventory *inventory) {     inventory->add_guitar("11277", 3999.95, "Collings", "CJ", "acoustic",                           "Indian Rosewood", "Sitka");     inventory->add_guitar("V95693", 1499.95, "Fender", "Stratocastor", "electric",                          "Alder", "Alder");     inventory->add_guitar("V9512", 1549.95, "Fender", "Stratocastor", "electric",                           "Alder", "Alder");     inventory->add_guitar("122784", 5495.95, "Martin", "D-18", "acoustic",                           "Mahogany", "Adirondack");     inventory->add_guitar("76531", 6295.95, "Martin", "OM-28", "acoustic",                           "Brazilian Rosewood", "Adriondack");     inventory->add_guitar("70108276", 2295.95, "Gibson", "Les Paul", "electric",                           "Mahogany", "Maple");     inventory->add_guitar("82765501", 1890.95, "Gibson", "SG '61 Reissue",                           "electric", "Mahogany", "Mahogany");     inventory->add_guitar("77023", 6275.95, "Martin", "D-28", "acoustic",                           "Brazilian Rosewood", "Adirondack");     inventory->add_guitar("1092", 12995.95, "Olson", "SJ", "acoustic",                           "Indian Rosewood", "Cedar");     inventory->add_guitar("566-62", 8999.95, "Ryan", "Cathedral", "acoustic",                           "Cocobolo", "Cedar");     inventory->add_guitar("6 29584", 2100.95, "PRS", "Dave Navarro Signature",                           "electric", "Mahogany", "Maple"); } FindGuitarTest.cpp

The FindGuitarTester Class, cont’d int main() {     // Set up Rick's guitar inventory.     Inventory *inventory = new Inventory();     FindGuitarTester::initialize_inventory(inventory);     Guitar *what_erin_likes = new Guitar("", 0, "fender", "Stratocastor",                                          "electric", "Alder", "Alder");     Guitar *guitar = inventory->search(what_erin_likes);     if (guitar != nullptr)     {         cout << "Erin, you might like this "              << guitar->get_builder() << " "              << guitar->get_model() << " "              << guitar->get_type() << " guitar:\n   "              << guitar->get_back_wood() << " back and sides,\n   "              << guitar->get_top_wood() << " top.\nYou can have it for only $"              << guitar->get_price() << "!";     }     else         cout << "Sorry, Erin, we have nothing for you."; } FindGuitarTest.cpp

Problems! Case-sensitive string comparisons. Badly used string fields. 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.

Take roll!

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.

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

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

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

Iteration #2: Remove String Fields, cont’d 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

Iteration #2: Remove String Fields, cont’d #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

Iteration #2: Remove String Fields, cont’d Inventory.h 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; } We add a small inline function that sets to lower-case all the letters of a string.

Iteration #2: Return Multiple Matches 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

Iteration #2: Return Multiple Matches, cont’d int main() {     // Set up Rick's guitar inventory.     Inventory *inventory = new Inventory();     FindGuitarTester::initialize_inventory(inventory);     Guitar *what_erin_likes = new Guitar("", 0, Builder::FENDER,                                          "stratocastor", Type::ELECTRIC,                                          Wood::ALDER, Wood::ALDER);     list<Guitar *> matching_guitars = inventory->search(what_erin_likes);     if (matching_guitars.size() > 0)     {         cout << "Erin, you might like these guitars:" << endl;         list<Guitar *>::iterator it;         for (it = matching_guitars.begin(); it != matching_guitars.end(); it++)         {             Guitar *guitar = *it;             cout << guitar->get_builder() << " "                  << guitar->get_model() << " "                  << guitar->get_type() << " guitar:\n   "                  << guitar->get_back_wood() << " back and sides,\n   "                  << guitar->get_top_wood() << " top.\nYou can have it for only $"                  << guitar->get_price() << "!" << endl;             cout << "  ----" << endl;         }     }     else         cout << "Sorry, Erin, we have nothing for you."; } FindGuitarTest.cpp Demo

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.

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.

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.

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.

Iteration #3: GuitarSpec Class GuitarSpec.h 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; } };

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.

Iteration #3: GuitarSpec Class, cont’d Guitar.h 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; } };

Iteration #3: GuitarSpec Class, cont’d Inventory.h 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); };

Iteration #3: GuitarSpec Class, cont’d vector<Guitar *> Inventory::search(GuitarSpec *search_spec) {     vector<Guitar *> matching_guitars;     for (Guitar *guitar : guitars)     {         GuitarSpec *guitar_spec = guitar->get_spec();         Builder builder = search_spec->get_builder();         if (builder != guitar_spec->get_builder()) continue;         string model = to_lower(search_spec->get_model());         if (   (model != "")             && (model != to_lower(guitar_spec->get_model()))) continue;         Type type = search_spec->get_type();         if (type != guitar_spec->get_type()) continue;         Wood back_wood = search_spec->get_back_wood();         if (back_wood!= guitar_spec->get_back_wood()) continue;         Wood top_wood = search_spec->get_top_wood();         if (top_wood != guitar_spec->get_top_wood()) continue;         matching_guitars.push_back(guitar);     }     return matching_guitars; } Inventory.cpp

Iteration #3: GuitarSpec Class, cont’d FindGuitarTest.cpp GuitarSpec *what_erin_likes =     new GuitarSpec(Builder::FENDER, "stratocastor",                    Type::ELECTRIC, Wood::ALDER, Wood::ALDER); vector<Guitar *> matching_guitars = inventory->search(what_erin_likes);

Encapsulation, Again By creating the GuitarSpec class, have we done a good enough job of isolating changes to the guitar attributes? What if Rick wants to add the number of strings to the guitar attributes? How will that change affect the code?

Iteration #4: New Attribute GuitarSpec.h class GuitarSpec { private:     string model;     Builder builder;     Type type;     int string_count;     Wood back_wood, top_wood; public:     GuitarSpec(Builder builder, string model, Type type,                int string_count, Wood back_wood, Wood top_wood)     : model(model), builder(builder), type(type), string_count(string_count),       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; }     int     get_string_count() const { return string_count; }     Wood    get_back_wood() const    { return back_wood; }     Wood    get_top_wood() const     { return top_wood; } };

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

Iteration #4: New Attribute, cont’d list<Guitar *> Inventory::search(GuitarSpec *search_spec) {     list<Guitar *> matching_guitars;     list<Guitar *>::iterator it;     for (it = guitars.begin(); it != guitars.end(); it++)     {         Guitar *guitar = *it;         GuitarSpec *guitar_spec = guitar->get_spec();         ...         int string_count = search_spec->get_string_count();         if (string_count != guitar_spec->get_string_count()) continue;         matching_guitars.push_back(guitar);     }     return matching_guitars; } Inventory.cpp

Time to Refactor Again! Refactor: To modify the structure of your code without modifying its behavior in order to improve it in some way. Why should the Inventory class also have to change? If the guitar attributes change, such as adding the number of guitar strings, then the search method needs to change. The customer may want to search for a guitar that matches a certain number of strings.

Time to Refactor Again! cont’d We need to move the guitar matching algorithm out of the Inventory class (the search() method) and into the new GuitarSpec class in order to completely encapsulate the changes to the search method. We delegate comparing two GuitarSpec objects to the GuitarSpec class itself. Delegate: When an object needs to perform a task, it asks another object to perform the task on its behalf.

Iteration #5: Delegate Matching GuitarSpec.h class GuitarSpec { public:     GuitarSpec(Builder builder, string model, Type type,                int string_count, Wood back_wood, Wood top_wood);     Builder get_builder() const;     string  get_model() const;     Type    get_type() const;     int     get_string_count() const;     Wood    get_back_wood() const;     Wood    get_top_wood() const;     bool matches(GuitarSpec *other_spec); private:     string model;     Builder builder;     Type type;     int string_count;     Wood back_wood, top_wood;     string to_lower(string str); }; inline string GuitarSpec::to_lower(string str) {     transform(str.begin(), str.end(), str.begin(), ::tolower);     return str; }

Iteration #5: Delegate Matching, cont’d GuitarSpec.cpp bool GuitarSpec::matches(GuitarSpec *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 (string_count != other_spec->string_count) return false;     if (back_wood != other_spec->back_wood) return false;     if (top_wood != other_spec->top_wood) return false;     return true; } This code was originally in the search() method of class Inventory.

Iteration #5: Delegate Matching, cont’d Inventory.cpp list<Guitar *> Inventory::search(GuitarSpec *search_spec) {     list<Guitar *> matching_guitars;     list<Guitar *>::iterator it;     for (it = guitars.begin(); it != guitars.end(); it++)     {         Guitar *guitar = *it;         GuitarSpec *guitar_spec = guitar->get_spec();         if (guitar_spec->matches(search_spec))         {             matching_guitars.push_back(guitar);         }     }     return matching_guitars; } Now Inventory::search() delegates the matching algorithm to the GuitarSpec object. This code is a lot easier to read!

Encapsulation Success! Now whenever the guitar attributes change, we only have to modify the GuitarSpec class. No other classes need to change. We don’t count the test class FindGuitarTester.