Я работаю над библиотекой C ++, для которой существует семейство абстрактных классов, которым некоторым разработчикам, использующим эту библиотеку, в зависимости от их потребностей, потребуется создать подкласс для взаимодействия с остальной частью библиотеки.Это было сочтено необходимым, потому что сама библиотека должна иметь возможность создавать экземпляры и использовать эти классы внутри (своего рода система плагинов).Эти классы имеют protected
API для взаимодействия с остальной частью библиотеки.Они также нуждаются в отдельном внутреннем интерфейсе для управления экземплярами, предоставляя указатели на внутренние структуры и вызывая другие функции управления, которые должны быть прозрачными для конечных производных классов.
Текущее решение включает создание внутреннего базового класса для управления.интерфейс для каждого открытого класса, а функциональность внутренней библиотеки просто приводит к этому классу, когда ему необходимо выполнить функции управления.
Вот упрощенный пример:
class BaseInternal
{
public:
void doSomething();
};
class BaseComponent
{
public:
BaseComponent();
virtual ~BaseComponent();
void manageComponent();
void setInternal(std::shared_ptr<BaseInternal>);
protected:
const std::shared_ptr<const BaseInternal> getInternal() const;
const std::shared_ptr<BaseInternal> getModInternal();
private:
std::shared_ptr<BaseInternal> internal;
};
class Base : public BaseComponent
{
public:
Base();
virtual ~Base();
protected:
// library "plugin" API
virtual void overrideMe() = 0;
virtual void overrideMe2() = 0;
void foo() { getModInternal()->doSomething(); }
private:
// Component access hidden here
using BaseComponent::manageComponent;
using BaseComponent::setInternal;
using BaseComponent::getInternal;
using BaseComponent::getModInternal;
};
class AInternal
{
public:
void doASomething();
};
class AComponent
{
public:
AComponent();
virtual ~AComponent();
void manageA();
void setAInternal(std::shared_ptr<AInternal>);
protected:
const std::shared_ptr<const AInternal> getAInternal() const;
const std::shared_ptr<AInternal> getModAInternal();
private:
std::shared_ptr<AInternal> aInternal;
};
class A : public AComponent, virtual public Base
{
public:
A();
virtual ~A();
protected:
// library "plugin" API for A
void bar() { getModAInternal()->doASomething(); BaseComponent::getModInternal()->doSomething();}
private:
// Component access hidden here
using AComponent::manageA;
using AComponent::setAInternal;
using AComponent::getAInternal;
using AComponent::getModAInternal;
};
class BInternal
{
public:
void doBSomething();
};
class BComponent
{
public:
BComponent();
virtual ~BComponent();
void manageB();
void setBInternal(std::shared_ptr<BInternal>);
protected:
const std::shared_ptr<const BInternal> getBInternal() const;
const std::shared_ptr<BInternal> getModBInternal();
private:
std::shared_ptr<BInternal> bInternal;
};
// Base
class B : public BComponent, virtual public Base
{
public:
B();
virtual ~B();
protected:
// library "plugin" API for B
void baz() { getModBInternal()->doBSomething(); BaseComponent::getModInternal()->doSomething();}
private:
// Component access hidden here
using BComponent::manageB;
using BComponent::setBInternal;
using BComponent::getBInternal;
using BComponent::getModBInternal;
};
class CInternal
{
public:
void doCSomething();
};
class CComponent
{
public:
CComponent();
virtual ~CComponent();
void manageC();
void setCInternal(std::shared_ptr<CInternal>);
protected:
const std::shared_ptr<const CInternal> getCInternal() const;
const std::shared_ptr<CInternal> getModCInternal();
private:
std::shared_ptr<CInternal> bInternal;
};
// Derived virtually from A and B since there may be future classes
// derived from C
class C : public CComponent, virtual public A, virtual public B
{
public:
C();
virtual ~C();
protected:
// library "plugin" API for C
void qux() {
getModCInternal()->doCSomething();
AComponent::getModAInternal()->doASomething();
BComponent::getModBInternal()->doBSomething();
BaseComponent::getModInternal()->doSomething();
}
private:
// Component access hidden here
using CComponent::manageC;
using CComponent::setCInternal;
using CComponent::getCInternal;
using CComponent::getModCInternal;
};
Разработчики, использующие эту возможность, могутзатем определите их классы так:
class MyBasePlugin : public Base
{
// providing the implementation to the pure virtual Base functions
void overrideMe() override {foo();}
void overrideMe2() override {foo();}
};
class MyCPlugin : public C
{
// providing the implementation to the pure virtual Base functions
void overrideMe() override {qux();}
void overrideMe2() override {bar(); baz();}
};
Теперь это позволяет внутреннему коду static_cast
или dynamic_cast
определить, как управлять экземпляром:
void manageAnInstance(std::shared_ptr<Base> instance)
{
// If I need to access a base management function, I can static cast
// since I know all of them are BaseComponent objects
BaseComponent* base = static_cast<BaseComponent*>(instance.get());
base->manageComponent();
base->setInternal(std::make_shared<BaseInternal>());
}
void manageDerivedStuff(std::shared_ptr<Base> instance)
{
// If I need to access a derived management function, I need to dynamic
// cast because I don't know for sure that the instance is one of those
// classes
C* c = dynamic_cast<C*>(instance.get());
if(c)
{
CComponent* ccomp = static_cast<CComponent*>(c);
ccomp->manageC();
ccomp->setCInternal(std::make_shared<CInternal>());
}
A* a = dynamic_cast<A*>(instance.get());
if(a)
{
AComponent* acomp = static_cast<AComponent*>(a);
acomp->manageA();
acomp->setAInternal(std::make_shared<AInternal>());
}
}
Есть паравещей, которые мне действительно не нравятся в этом:
dynamic_cast
s, чтобы определить, как управлять экземпляром - . Методология ограничения доступа к функциям управления довольно жалкая.Производный класс может легко обойти эти ограничения доступа, вызвав, например,
BaseComponent::manageComponent()
. - наследование компонента, также просто странно.Кажется странным публично наследовать базовый класс, чтобы сразу скрыть все эти функции.Я понимаю, что идея заключалась в том, чтобы передать ясную связь между внутренними классами компонентов и классами общего пользования, но это все еще кажется странным.
Я уверен, что, вероятно, есть и другие вещи, которыездесь также можно улучшить.
Одной из рассмотренных альтернатив было определение определений функций управления непосредственно (в частном порядке) для открытых классов, а затем friend
всех внутренних классов / функций, которые должны их использовать., но это кажется еще хуже и гораздо более хрупким.
Есть ли лучший способ для достижения такого рода вещи?