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

//Hummingbird B decides to sleep in: f.openNotifier.deleteObserver(hb.openObserver());

//Something changes that interests observers: f.open();

f.open(); // It's already open, no change.

//Bee A doesn't want to go to bed: f.closeNotifier.deleteObserver(

ba.closeObserver());

f.close();

f.close(); // It's already closed; no change f.openNotifier.deleteObservers();

f.open();

f.close();

}///:~

The events of interest are that a Flower can open or close. Because of the use of the inner class idiom, both these events can be separately-observable phenomena. OpenNotifier and CloseNotifier both inherit Observable, so they have access to setChanged( ) and can be handed to anything that needs an Observable. You’ll notice that, contrary to InnerClassIdiom.cpp, the Observable descendants are public. This is because some of their member functions must be available to the client programmer. There’s nothing that says that an inner class must be private; in InnerClassIdiom.cpp I was simply following the design guideline “make things as private as possible.” You could make the classes private and expose the appropriate methods by proxy in Flower, but it wouldn’t gain much.

The inner class idiom also comes in handy to define more than one kind of Observer, in Bee and Hummingbird, since both those classes may want to independently observe Flower openings and closings. Notice how the inner class idiom provides something that has most of the benefits of inheritance (the ability to access the private data in the outer class, for example) without the same restrictions.

In main( ), you can see one of the prime benefits of the observer pattern: the ability to change behavior at runtime by dynamically registering and un-registering Observers with

Observables.

If you study the code above you’ll see that OpenNotifier and CloseNotifier use the basic Observable interface. This means that you could inherit other completely different Observer classes; the only connection the Observers have with Flowers is the Observer interface.

Multiple dispatching

When dealing with multiple types which are interacting, a program can get particularly messy. For example, consider a system that parses and executes mathematical expressions. You want to be able to say Number + Number, Number * Number, etc., where Number is the base class for a family of numerical objects. But when you say a + b, and you don’t know the exact type of either a or b, so how can you get them to interact properly?

Chapter 16: Design Patterns

459

The answer starts with something you probably don’t think about: C++ performs only single dispatching. That is, if you are performing an operation on more than one object whose type is unknown, C++ can invoke the dynamic binding mechanism on only one of those types. This doesn’t solve the problem, so you end up detecting some types manually and effectively producing your own dynamic binding behavior.

The solution is called multiple dispatching. Remember that polymorphism can occur only via member function calls, so if you want double dispatching to occur, there must be two member function calls: the first to determine the first unknown type, and the second to determine the second unknown type. With multiple dispatching, you must have a virtual call to determine each of the types. Generally, you’ll set up a configuration such that a single member function call produces more than one dynamic member function call and thus determines more than one type in the process. To get this effect, you need to work with more than one virtual function: you’ll need a virtual function call for each dispatch. The virtual functions in the following example are called compete( ) and eval( ), and are both members of the same type. (In this case there will be only two dispatches, which is referred to as double dispatching). If you are working with two different type hierarchies that are interacting, then you’ll have to have a virtual call in each hierarchy.

Here’s an example of multiple dispatching:

//: C09:PaperScissorsRock.cpp

// Demonstration of multiple dispatching #include "../purge.h"

#include <iostream> #include <vector> #include <algorithm> #include <cstdlib> #include <ctime> using namespace std;

class Paper; class Scissors; class Rock;

enum Outcome { win, lose, draw };

ostream&

operator<<(ostream& os, const Outcome out) { switch(out) {

default:

case win: return os << "win"; case lose: return os << "lose"; case draw: return os << "draw";

}

}

Chapter 16: Design Patterns

460

class Item { public:

virtual Outcome compete(const Item*) = 0; virtual Outcome eval(const Paper*) const = 0; virtual Outcome eval(const Scissors*) const= 0; virtual Outcome eval(const Rock*) const = 0; virtual ostream& print(ostream& os) const = 0; virtual ~Item() {}

friend ostream&

operator<<(ostream& os, const Item* it) { return it->print(os);

}

};

class Paper : public Item { public:

Outcome compete(const Item* it) { return it->eval(this);

}

Outcome eval(const Paper*) const { return draw;

}

Outcome eval(const Scissors*) const { return win;

}

Outcome eval(const Rock*) const { return lose;

}

 

 

ostream&

print(ostream&

os) const {

return

os << "Paper

";

}

 

 

};

class Scissors : public Item { public:

Outcome compete(const Item* it) { return it->eval(this);

}

Outcome eval(const Paper*) const { return lose;

}

Outcome eval(const Scissors*) const { return draw;

}

Chapter 16: Design Patterns

461

Outcome eval(const Rock*) const { return win;

}

ostream& print(ostream& os) const { return os << "Scissors";

}

};

class Rock : public Item { public:

Outcome compete(const Item* it) { return it->eval(this);

}

Outcome eval(const Paper*) const { return win;

}

Outcome eval(const Scissors*) const { return lose;

}

Outcome eval(const Rock*) const { return draw;

}

 

 

ostream&

print(ostream&

os) const {

return

os << "Rock

";

}

 

 

};

 

 

struct ItemGen {

ItemGen() { srand(time(0)); } Item* operator()() {

switch(rand() % 3) { default:

case 0:

return new Scissors; case 1:

return new Paper; case 2:

return new Rock;

}

}

};

struct Compete {

Outcome operator()(Item* a, Item* b) {

Chapter 16: Design Patterns

462

Соседние файлы в предмете Численные методы
  • #
    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