Home > Software engineering >  Is it bad to leave events unprotected?
Is it bad to leave events unprotected?

Time:08-24

I'm writing some code that includes events which are custom-implemented and class that uses these events and invokes them when something happens(ex. window closes). It looks something like this:

// phony.h
class Phony
{
    public:
        Phony();
        ~Phony();
        // some stuff
        void i_invoke_click();
        void i_invoke_resize();
    private:
        Event<Arg1> click;
        Event<> resize;
        Event<Arg2> event3;
        Event<Arg3> event4;
};

// event.h
template <typename... Args>
class Event
{
    public:
        void invoke(Args... params) const
        void add(std::function<void(Args...)> func)
        void remove(std::function<void(Args...)> func)

        void operator =(std::function<void(Args...)> func) // same as add
        void operator-=(std::function<void(Args...)> func) // same as remove
        void operator()(Args... params) // invoke

    private:
        std::list<std::function<void(Args...)>> invoke_list;
};

Now I need to implement some interface so other classes could subscribe/unsubscribe to events like click. I have considered two ways to do this:

  1. Make access methods like subscribe_to_click(func)(which will make many methods and not convenient)
  2. Or make events public so you could directly access them via =(which is convenient)

But I think that second way of putting events violates encapsulation but I'm not sure in it. So the question is: Is it okay to leave events public or I should make it the ugly(but safe) way?

CodePudding user response:

What I could suggest coming from a C# background is to use a base class like IObservable for the Events which implements some subscribe method or your approach with operator overloading. Then make the IObservables public by returning a reference that refers to the events themselves:

#include <functional>
#include <list>

template <typename... Args>
class IObservable
{
public:
    void operator =(std::function<void(Args...)> func); // same as add
    void operator-=(std::function<void(Args...)> func); // same as remove
};

template <typename... Args>
class Event : public IObservable<Args...>
{
public:
    void operator()(Args... params); // invoke

private:
    std::list<std::function<void(Args...)>> invoke_list;
};

class Phony
{
public:
    Phony();
    ~Phony();

    void i_invoke_click();
    void i_invoke_resize();

    IObservable<int>& Click() { return click; }
    IObservable<>& Resize() { return resize; }
    IObservable<int>& Event3() { return event3; }
    IObservable<int>& Event4() { return event4; }

private:
    Event<int> click;
    Event<> resize;
    Event<int> event3;
    Event<int> event4;
};

void foo(int x);

int main()
{
    Phony p;
    p.Click()  = &foo;
    // p.Click()(); no invoke possible
}
  • Related