Ereditarietà o strategia nel mio buffer circolare

1

Ho esaminato alcuni thread ma non rispondono perfettamente alla mia domanda, penso ...

Ho un oggetto CircularBuffer che ho separato da un oggetto ThreadSafeCircularBuffer in modo che la logica CB sia separata dalla logica della concorrenza, principalmente per una chiara separazione di logica / funzione e in parte per semplificare il test.

Avrei pensato che un ThreadSafeCircularBuffer è un CircularBuffer. Ma questo mi porta a un problema di implementazione che è rappresentato da un piccolo esempio artificiale di seguito:

#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();
}

L'output è questo, come ci si aspetterebbe in C ++ ...

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

Il problema è che io non voglio usare un blocco ricorsivo.

Potrei risolvere questo problema riscrivendo il metodo di doit() di A in questo modo:

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

Ma ci si sente un po 'artificiali ... in primo luogo, cosa succederebbe se l'override di GetBytesAvailable() dovesse essere chiamato in una classe figlia (anche se non in questo caso, intendo nel caso generale) ? Inoltre, è un po 'goffo ... la classe A non dovrebbe preoccuparsi di proteggere contro i suoi figli in questo modo, non credo.

Quindi ... questo mi porta all'idea di utilizzare un modello di strategia in cui il ThreadSafeCircularBuffer dovrebbe creare e l'istanza di CircularBuffer o, meglio ancora, essere passato a CircularBuffer come strategia. Questa è una relazione "ha-a". Questo risolverebbe il mio problema ma mi sembra che dovrei avere una relazione "è-a".

Un'altra possibile soluzione è che entrambi gli oggetti ereditino da un'interfaccia, ma invece di rendere ThreadSafeCB un figlio di CB ... in questo modo sono entrambi CB, ma ThreadSafeCB dovrebbe comunque utilizzare una relazione "ha-a" sotto il cofano per ottenere la funzionalità CB.

Come potrei migliorare questo design / c'è un modo migliore per farlo?

    
posta Jimbo 28.12.2017 - 11:56
fonte

1 risposta

1

Vai con l'interfaccia e lascia che ThreadSafeCircularBuffer abbia un'istanza di CircularBuffer . La ragione principale di ciò è che sembra essere la soluzione più semplice, ma se devi razionalizzarla:

La classe CircularBuffer è un'implementazione specifica del concetto astratto di un buffer circolare. Non è davvero rilevante che il tuo ThreadSafeCircularBuffer sia un CircularBuffer nel senso di tale specifica implementazione.

Come passare il CircularBuffer in - Non vedo una buona ragione per questo, a meno che tu non voglia usare diverse implementazioni di un CB in modo thread-safe. Questo, ovviamente, sarebbe una ragione in più per usare un'interfaccia.

    
risposta data 28.12.2017 - 13:09
fonte

Leggi altre domande sui tag