开发者

Function returning reference, what to return on failure?

As a consequence of the design of a framework I'm targeting with a plugin, I've implemented a part of my code as a singleton. This class is responsible for handling connections to an external program with which I'm communicating from within the framework.

Enabling the external communication is a runtime setting, however, and if it is disabled, I don't want to allow access to it from models within the framework. I've implemented it using the version which is frequently recommended here:

class Communicator {
public: 
    static Communicator& getInstance() {
        static Communicator instance;
        return instance;
    }
    // ...
private: 
    static bool ServiceEnabled;
    // Constructors, operator=, etc ...
}

Now, given that ServiceEnab开发者_如何学Pythonled is false, I don't want to allow getInstance to return a valid Communicator. But since I return a reference, I can't simply return 0 or some such... What would proper behaviour be? Note that it is perfectly valid to continue execution even if ServiceEnabled is false, so I can't just abort if it is.


Add a public function

static bool IsServiceEnabled();

and throw an exception in getInstance, when it's called while ServiceEnabled == false;


There are, actually, many possibilities... Here is the beginning of a list, in no particular order.

Pointer

class Communicator {
public:
  static Communicator const* Instance(); // returns 0 if not Enabled
};

This can actually be replaced by a "safer" pointer type (that assert/throw if the pointer is null and someone tries to use it).

Query + Throw

class Communicator {
public:
  static bool IsEnabled();
  static Communicator const& Instance(); // throw if not Enabled
};

Null Object

class Communicator {
public:
  static Communicator const& Instance(); //returns a null instance if not Enabled

  void doit() { if (!enabled) { return; } }
};

I personally do not like the last one much, because by hiding away the fact that it was not enabled you may prevent users from noticing the problem early on. Think of a transactional system convinced of having registered its transactions when it sent everything to /dev/null...


Proper behaviour is to throw an exception when you encounter a failure:

#include <stdexcept>

class Communicator {
public: 
    static Communicator& getInstance() {
        static Communicator instance;
        if (ServiceEnabled)
          return instance;
        else
          throw std::exception("Get communicator while service is not enabled");
    }
    // ...
private: 
    static bool ServiceEnabled;
    // Constructors, operator=, etc ...
}


I would consider the design decision again, and then probably make an exception class and throw that. This would of course require handling the possible exception in the other end.


Perhaps you should consider a communicator with a false ServiceEnabled as 'valid'

To implement, you need a method bool IsEnabled() and your other methods need to check if the servoce is enabled and, most often, return immediately if it is not.


Why not make the class just ignore all calls with side effects if it is not enabled? This way you can call all functions you want and don't have to worry about whether it is on or off. Provide a "IsServiceEnabled" (as Henrik answer) to allow the user to know whether it should be communicating or not.


If you really want to be able to switch the communication on and off at runtime, you may need to worry about the fact that the user could save a Communicator reference while it's enabled, and try to use it later when it's been disabled. This problem isn't unique to singletons, of course.
You could introduce another layer of indirection to handle it:

class CommunicatorImpl
{
public:
    virtual bool isEnabled() const = 0;
    virtual void doSomething() = 0;
};

class CommunicatorImpl_Enabled : public CommunicatorImpl
{
    public:
        virtual bool isEnabled() const { return true; }
        virtual void doSomething()  { /* Do something... */}
};


class CommunicatorImpl_Disabled : public CommunicatorImpl
{
    public:
        virtual bool isEnabled() const { return false; }
        virtual void doSomething()  { throw CommunicationIsDisabled("SRY"); }
};



class Communicator {
public: 
    static Communicator& getInstance() {
        static Communicator instance;
        return instance;
    }

    void enable () { m_impl = &m_enabled; }
    void disable () { m_impl = &m_disabled; }
    bool isEnabled() const { return m_impl->isEnabled(); }
    void doSomething() { m_impl->doSomething(); }

private: 
    CommunicatorImpl* m_impl;
    CommunicatorImpl_Enabled m_enabled;
    CommunicatorImpl_Disabled m_disabled;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜