Выбрать главу

  class CloseObserver;

  friend class Hummingbird::CloseObserver;

  class CloseObserver : public Observer {

    Hummingbird* parent;

  public:

    CloseObserver(Hummingbird* h) : parent(h) {}

    void update(Observable*, Argument *) {

      cout << "Hummingbird " << parent->name

        << "'s bed time!\n";

    }

  } closeObsrv;

public:

  Hummingbird(string nm) : name(nm),

    openObsrv(this), closeObsrv(this) {}

  Observer& openObserver() { return openObsrv; }

  Observer& closeObserver() { return closeObsrv;}

};

int main() {

  Flower f;

  Bee ba("A"), bb("B");

  Hummingbird ha("A"), hb("B");

  f.openNotifier.addObserver(ha.openObserver());

  f.openNotifier.addObserver(hb.openObserver());

  f.openNotifier.addObserver(ba.openObserver());

  f.openNotifier.addObserver(bb.openObserver());

  f.closeNotifier.addObserver(ha.closeObserver());

  f.closeNotifier.addObserver(hb.closeObserver());

  f.closeNotifier.addObserver(ba.closeObserver());

  f.closeNotifier.addObserver(bb.closeObserver());

  // 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. The OpenNotifier and CloseNotifier classes both derive from 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 we were simply following the design guideline "make things as private as possible." You could make the classes private and expose the appropriate member functions 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).

In main( ), you can see one of the primary benefits of the Observer pattern: the ability to change behavior at runtime by dynamically registering and unregistering Observers with Observables.

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

Multiple dispatching

When dealing with multiple types that 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, and so on, 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, how can you get them to interact properly?

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 for 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 services 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, you’ll need a virtual call in each hierarchy.

Here’s an example of multiple dispatching:

//: C10:PaperScissorsRock.cpp

// Demonstration of multiple dispatching

#include <algorithm>

#include <cstdlib>

#include <ctime>

#include <iostream>

#include <iterator>

#include <vector>

#include "../purge.h"

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";

  }

}

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) {