Xem mẫu

Trash* clone(const Info& info) { return new Glass(info.data()); } }; #endif // GLASS_H ///:~ And here’s a new type of Trash: //: C09:Cardboard.h // The Cardboard class with prototyping #ifndef CARDBOARD_H #define CARDBOARD_H #include "Trash.h" class Cardboard : public Trash { static double val; protected: Cardboard() {} friend class TrashPrototypeInit; public: Cardboard(double wt) : Trash(wt) {} double value() const { return val; } static void value(double newVal) { val = newVal; } std::string id() { return "Cardboard"; } Trash* clone(const Info& info) { return new Cardboard(info.data()); } }; #endif // CARDBOARD_H ///:~ The static val data members must be defined and initialized in a separate code file: //: C09:TrashStatics.cpp {O} // Contains the static definitions for // the Trash type`s "val" data members #include "Trash.h" #include "Aluminum.h" #include "Paper.h" #include "Glass.h" #include "Cardboard.h" double Aluminum::val = 1.67; double Paper::val = 0.10; double Glass::val = 0.23; Chapter 16: Design Patterns 481 double Cardboard::val = 0.14; ///:~ There’s one other issue: initialization of the static data members. TrashPrototypeInit must create the prototype objects and add them to the static Trash::prototypes vector. So it’s very important that you control the order of initialization of the static objects, so the prototypes vector is created before any of the prototype objects, which depend on the prior existence of prototypes. The most straightforward way to do this is to put all the definitions in a single file, in the order in which you want them initialized. TrashPrototypeInit must be defined separately because it inserts the actual prototypes into the vector, and throughout the chapter we’ll be inheriting new types of Trash from the existing types. By making this one class in a separate file, a different version can be created and linked in for the new situations, leaving the rest of the code in the system alone. //: C09:TrashPrototypeInit.cpp {O} // Performs initialization of all the prototypes. // Create a different version of this file to // make different kinds of Trash. #include "Trash.h" #include "Aluminum.h" #include "Paper.h" #include "Glass.h" #include "Cardboard.h" // Allocate the static member object: std::vector Trash::prototypes; class TrashPrototypeInit { Aluminum a; Paper p; Glass g; Cardboard c; TrashPrototypeInit() { Trash::prototypes.push_back(&a); Trash::prototypes.push_back(&p); Trash::prototypes.push_back(&g); Trash::prototypes.push_back(&c); } static TrashPrototypeInit singleton; }; TrashPrototypeInit TrashPrototypeInit::singleton; ///:~ Chapter 16: Design Patterns 482 This is taken a step further by making TrashPrototypeInit a singleton (the constructor is private), even though the class definition is not available in a header file so it would seem safe enough to assume that no one could accidentally make a second instance. Unfortunately, this is one more separate piece of code you must maintain whenever you add a new type to the system. However, it’s not too bad since the linker should give you an error message if you forget (since prototypes is defined in this file as well). The really difficult problems come when you don’t get any warnings or errors if you do something wrong. Parsing Trash from an external file The information about Trash objects will be read from an outside file. The file has all of the necessary information about each piece of trash in a single entry in the form Trash:weight. There are multiple entries on a line, separated by commas: //:! C09:Trash.dat Glass:54, Paper:22, Paper:11, Glass:17, Aluminum:89, Paper:88, Aluminum:76, Cardboard:96, Aluminum:25, Aluminum:34, Glass:11, Glass:68, Glass:43, Aluminum:27, Cardboard:44, Aluminum:18, Paper:91, Glass:63, Glass:50, Glass:80, Aluminum:81, Cardboard:12, Glass:12, Glass:54, Aluminum:36, Aluminum:93, Glass:93, Paper:80, Glass:36, Glass:12, Glass:60, Paper:66, Aluminum:36, Cardboard:22, ///:~ To parse this, the line is read and the string member function find( ) produces the index of the ‘:’. This is first used with the string member function substr( ) to extract the name of the trash type, and next to get the weight that is turned into a double with the atof( ) function (from ). The Trash file parser is placed in a separate file since it will be reused throughout this chapter. To facilitate this reuse, the function fillBin( ) which does the work takes as its first argument the name of the file to open and read, and as its second argument a reference to an object of type Fillable. This uses what I’ve named the “interface” idiom at the beginning of the chapter, and the only attribute for this particular interface is that “it can be filled,” via a member function addTrash( ). Here’s the header file for Fillable: //: C09:Fillable.h // Any object that can be filled with Trash #ifndef FILLABLE_H #define FILLABLE_H class Fillable { public: virtual void addTrash(Trash* t) = 0; }; Chapter 16: Design Patterns 483 #endif // FILLABLE_H ///:~ Notice that it follows the interface idiom of having no non-static data members, and all pure virtual member functions. This way, any class which implements this interface (typically using multiple inheritance) can be filled using fillBin( ). Here’s the header file: //: C09:fillBin.h // Open a file and parse its contents into // Trash objects, placing each into a vector #ifndef FILLBIN_H #define FILLBIN_H #include "Fillablevector.h" #include #include void fillBin(std::string filename, Fillable& bin); // Special case to handle vector: inline void fillBin(std::string filename, std::vector& bin) { Fillablevector fv(bin); fillBin(filename, fv); } #endif // FILLBIN_H ///:~ The overloaded version will be discussed shortly. First, here is the implementation: //: C09:fillBin.cpp {O} // Implementation of fillBin() #include "fillBin.h" #include "Fillable.h" #include "../C01/trim.h" #include "../require.h" #include #include #include using namespace std; void fillBin(string filename, Fillable& bin) { ifstream in(filename.c_str()); assure(in, filename.c_str()); string s; while(getline(in, s)) { int comma = s.find(`,`); Chapter 16: Design Patterns 484 // Parse each line into entries: while(comma != string::npos) { string e = trim(s.substr(0,comma)); // Parse each entry: int colon = e.find(`:`); string type = e.substr(0, colon); double weight = atof(e.substr(colon + 1).c_str()); bin.addTrash( Trash::factory( Trash::Info(type, weight))); // Move to next part of line: s = s.substr(comma + 1); comma = s.find(`,`); } } } ///:~ After the file is opened, each line is read and parsed into entries by looking for the separating comma, then each entry is parsed into its type and weight by looking for the separating colon. Note the convenience of using the trim( ) function from chapter 17 to remove the white space from both ends of a string. Once the type and weight are discovered, an Info object is created from that data and passed to the factory( ). The result of this call is a Trash* which is passed to the addTrash( ) function of the bin (which is the only function, remember, that a Fillable guarantees). Anything that supports the Fillable interface can be used with fillBin( ). Of course, vector doesn’t implement Fillable, so it won’t work. Since vector is used in most of the examples, it makes sense to add the second overloaded fillBin( ) function that takes a vector, as seen previously in fillBin.h. But how to make a vector adapt to the Fillable interface, which says it must have an addTrash( ) member function? The key is in the word “adapt”; we use the adapter pattern to create a class that has a vector and is also Fillable. By saying “is also Fillable,” the hint is strong (is-a) to inherit from Fillable. But what about the vector? Should this new class inherit from that? We don’t actually want to be making a new kind of vector, which would force everyone to only use our vector in this situation. Instead, we want someone to be able to have their own vector and say “please fill this.” So the new class should just keep a reference to that vector: //: C09:Fillablevector.h // Adapter that makes a vector Fillable #ifndef FILLABLEVECTOR_H #define FILLABLEVECTOR_H #include "Trash.h" #include "Fillable.h" #include Chapter 16: Design Patterns 485 ... - tailieumienphi.vn
nguon tai.lieu . vn