I've looked at some other threads but they don't quite answer my question, I think...

I have a CircularBuffer object which I have separated from a ThreadSafeCircularBuffer object so that the CB logic is separated from the concurrency logic, mostly for a clear separation of logic/function and partly to make testing easier.

I would have thought that a ThreadSafeCircularBuffer is a CircularBuffer. But this leads me to an implementation problem which is represented by a small artificial example below:

#include <iostream>

class A
{
public:
    virtual void GetBytesAvailable()
    {
        std::cout << "GetBytesAvailable() from A\n";
    }

    virtual void doit()
    {
        std::cout << "doit() from A\n";
        GetBytesAvailable();
    }
};    

class B: public A
{
public:
    virtual void GetBytesAvailable() override
    {
        std::cout << "GetBytesAvailable() from B\n";
        std::cout << "LOCKED\n";
    }

    virtual void doit() override
    {
        std::cout << "doit() from B\n";
        std::cout << "LOCKED\n";
        A::doit();
    }
};    

int main()
{
    B b;
    b.doit();
}

The output is this, as one would expect in C++...

doit() from B   
LOCKED
doit() from 
GetBytesAvailable() from 
LOCKED // <-- Oops, this will break if lock not recursive!

Trouble is, I don't want to use a recursive lock.

I could solve this by re-writing A's doit() method as so:

    virtual void doit()
    {
        std::cout << "doit() from A\n";
        A::GetBytesAvailable();
     // ^^^
     // Make sure we call the non-locking function
    }

But it feels a little artificial... firstly, what if the override of GetBytesAvailable() should be called in a child class (although not in this case, I mean in the general case)? Also, its a bit clunky... class A shouldn't need to care about protecting against its children like this, I don't think.

So... this leads me to the idea of using a strategy pattern where the ThreadSafeCircularBuffer would create and instance of CircularBuffer or maybe even better be passed a CircularBuffer as a strategy. This is a "has-a" relationship. This would solve my problem but it seems to me like I should really have an "is-a" relationship.

Another possible solution is to have both objects inherit from an interface but then instead of making ThreadSafeCB a child of CB... this way they are both CB's, but the ThreadSafeCB would still have to use a "has-a" relationship under the hood to get the CB functionality.

How I could improve this design / is there is a better way of going about this?

有帮助吗?

解决方案

Go with the interface and let the ThreadSafeCircularBuffer have an instance of CircularBuffer. The main reason for this is that it seems to be the simplest solution, but if you need to rationalize it:

The CircularBuffer class is a specific implementation of the abstract concept of a circular buffer. It's not really relevant that your ThreadSafeCircularBuffer is a CircularBuffer in the sense of that specific implementation.

As for passing the CircularBuffer in - I don't see a good reason for this, unless you might want to use different implementations of a CB in a thread-safe way. This, of course, would be all the more reason to use an interface.

许可以下: CC-BY-SA归因
scroll top