Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
B.Eckel - Thinking in C++, Vol.2, 2nd edition.pdf
Скачиваний:
50
Добавлен:
08.05.2013
Размер:
2.09 Mб
Скачать

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 <cstdlib>).

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

<vector>

 

 

#include

<string>

 

 

void

 

 

 

fillBin(std::string filename, Fillable& bin);

 

 

// Special case to handle vector:

 

 

inline void fillBin(std::string filename,

 

 

std::vector<Trash*>& 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

<fstream>

 

 

#include

<string>

 

 

#include

<cstdlib>

 

 

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<Trash*> 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<Trash*>? 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<Trash*> Fillable #ifndef FILLABLEVECTOR_H

#define FILLABLEVECTOR_H #include "Trash.h" #include "Fillable.h" #include <vector>

Chapter 16: Design Patterns

485

class Fillablevector : public Fillable { std::vector<Trash*>& v;

public: Fillablevector(std::vector<Trash*>& vv)

: v(vv) {}

void addTrash(Trash* t) { v.push_back(t); }

};

#endif // FILLABLEVECTOR_H ///:~

You can see that the only job of this class is to connect Fillable’s addTrash( ) member function to vector’s push_back( ) (that’s the “adapter” motivation). With this class in hand, the overloaded fillBin( ) member function can be used with a vector in fillBin.h:

inline void fillBin(std::string filename, std::vector<Trash*>& bin) { Fillablevector fv(bin); fillBin(filename, fv);

}

Notice that the adapter object fv only exists for the duration of the function call, and it wraps bin in an interface that works with the other fillBin( ) function.

This approach works for any container class that’s used frequently. Alternatively, the container can multiply inherit from Fillable. (You’ll see this later, in DynaTrash.cpp.)

Recycling with prototyping

Now you can see the new version of the recycling solution using the prototyping technique:

//: C09:Recycle3.cpp //{L} TrashPrototypeInit

//{L} fillBin Trash TrashStatics

// Recycling with RTTI and Prototypes #include "Trash.h"

#include "Aluminum.h" #include "Paper.h" #include "Glass.h" #include "fillBin.h" #include "sumValue.h" #include "../purge.h" #include <fstream> #include <vector> using namespace std;

ofstream out("Recycle3.out");

int main() { vector<Trash*> bin;

// Fill up the Trash bin:

Chapter 16: Design Patterns

486

fillBin("Trash.dat", bin); vector<Aluminum*> alBin; vector<Paper*> paperBin; vector<Glass*> glassBin;

vector<Trash*>::iterator it = bin.begin(); while(it != bin.end()) {

// Sort the Trash: Aluminum* ap =

dynamic_cast<Aluminum*>(*it);

Paper* pp = dynamic_cast<Paper*>(*it); Glass* gp = dynamic_cast<Glass*>(*it); if(ap) alBin.push_back(ap);

if(pp) paperBin.push_back(pp); if(gp) glassBin.push_back(gp); it++;

}

sumValue(alBin);

sumValue(paperBin);

sumValue(glassBin);

sumValue(bin);

purge(bin); } ///:~

The process of opening the data file containing Trash descriptions and the parsing of that file have been wrapped into fillBin( ), so now it’s no longer a part of our design focus. You will see that throughout the rest of the chapter, no matter what new classes are added, fillBin( ) will continue to work without change, which indicates a good design.

In terms of object creation, this design does indeed severely localize the changes you need to make to add a new type to the system. However, there’s a significant problem in the use of RTTI that shows up clearly here. The program seems to run fine, and yet it never detects any cardboard, even though there is cardboard in the list of trash data! This happens because of the use of RTTI, which looks for only the types that you tell it to look for. The clue that RTTI is being misused is that every type in the system is being tested, rather than a single type or subset of types. But if you forget to test for your new type, the compiler has nothing to say about it.

As you will see later, there are ways to use polymorphism instead when you’re testing for every type. But if you use RTTI a lot in this fashion, and you add a new type to your system, you can easily forget to make the necessary changes in your program and produce a difficult- to-find bug. So it’s worth trying to eliminate RTTI in this case, not just for aesthetic reasons – it produces more maintainable code.

Chapter 16: Design Patterns

487

Соседние файлы в предмете Численные методы
  • #
    08.05.20133.99 Mб22A.Menezes, P.van Oorschot,S.Vanstone - HANDBOOK OF APPLIED CRYPTOGRAPHY.djvu
  • #
  • #
    08.05.20135.91 Mб24B.Eckel - Thinking in Java, 3rd edition (beta).pdf
  • #
  • #
    08.05.20136.09 Mб17D.MacKay - Information Theory, Inference, and Learning Algorithms.djvu
  • #
    08.05.20133.85 Mб15DIGITAL Visual Fortran ver.5.0 - Programmers Guide to Fortran.djvu
  • #
    08.05.20131.84 Mб12E.A.Lee, P.Varaiya - Structure and Interpretation of Signals and Systems.djvu