简体   繁体   中英

Observer pattern and virtual templated functions

I am into a scenario that I have to (best thing I thought of) use virtual templated functions (for polymorphism), however, as far as I know, it is not possible.

I chose to 'use' virtual templated functions so I wouldnt need to use pointers and inheritance (for the observing part). My observable (subject) class is a Parser: it parses a XML file and is supposed to notify each observer about each node found. Every XML node is mapped into a class, for example:

// Maps the '?xml' tag
struct XML { int version; // other stuff... };

Instead, if I opted to use inheritance, I'd also have this class:

struct Node { // Probably blank? }

And then, the XML class would be as follows:

// Maps the '?xml' tag
struct XML : public Node { int version; // other stuff... };

I believe this would solve my problem, however I dont want to deal with pointers, since there would be a class (which I'm parsing the XML into) that has non-pointer attributes (nor I wish to make them pointers).

Here's how I implemented the observer pattern, with non-virtual templated functions. It does not work. Its just an example of how Id like it to be:

#include <iostream>
#include <vector>

enum Type { XML, // other xml tag types };

struct SomeXMLNode { int data; };

struct Observer {
    // This is what Id like to make virtual
    template<typename T>
    void onObserved(T t, Type type)
    {
        std::cout << "[base] observing " << t.data << std::endl;

        // 'Safely' cast t, according to the specified type
    }
};

class Observable
{
public:
    void attach(Observer* o) { observers.push_back(o); }

protected:
    template<class Node>
    void notify(Node node, Type type)
    {
        for (std::vector<Observer*>::iterator it = observers.begin(); it != observers.end(); it++) {
            (*it)->onObserved(node, type);
        }
    }

    Observable() {}

protected:
    std::vector<Observer*> observers;
};

class Parser : public Observable
{
public:
    void parse()
    {
        SomeXMLNode s;
        s.data = 1234;
        notify(s, Types::XML); // Notify all observers about 'some xml node' during the parsing
    }
};

struct SomeLoadableClass : public Observer
{
    void load()
    {
        Parser p;
        p.attach(this);
        p.parse();
    }
    template<typename T>
    void onObserved(T t, Type type)
    {
        std::cout << "[derived] observing " << t.data << std::endl; // Will never(?) get called! :(
    }
};

int main()
{
    SomeLoadableClass m;
    m.load();
    return 0;
}

If inheritance were used, onObserved and notify functions could be changed to:

void onObserved(Node* node, Types type) { }
void notify(Node* node, Types type) { }

The 'Node' above is a struct, not the template I used in the other example.

Is the observer pattern even the best approach for this kind of task? Are there other choices without using inheritance? How could I get my derived class to dispatch the onObserved instead of the base class without declaring it virtual?

Since Parser derieves from Observable, the protected members should be accessible within Parser.

class Parser : public Observable
{
public:
    void parse()
    {
        SomeXMLNode s;
        s.data = 1234;
        std::vector<Observer*>::iterator it = observers.begin();
        for (;it != observers.end(); it++)
            it->onObserved(s, Types::XML);
    }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM