我需要一种方法来从单个对象提供不同的接口。
例如。用户1应该能够调用Foo::bar()
并且用户2应该能够调用Foo::baz()
但是用户不能调用Foo::baz()
并且用户2分别不能调用Foo::bar()
。
我确实设法做到了这一点,但我认为这不是最佳选择。
class A
{
public:
virtual void bar() = 0;
virtual ~A() = 0;
};
class B
{
public:
virtual void baz() = 0;
virtual ~B() = 0;
};
class Foo : public A, public B
{
public:
Foo() = default;
void baz() override;
void bar() override;
};
class Factory
{
public:
Factory()
{
foo = std::make_shared<Foo>();
}
std::shared_ptr<A> getUserOne()
{
return foo;
}
std::shared_ptr<B> getUserTwo()
{
return foo;
}
private:
std::shared_ptr<Foo> foo;
};
有没有更好的方法来实现这一目标。也许使用包装器对象。我真的不需要用new
(std::make_shared
)分配这个foo对象我甚至不愿意,但我不能使用原始指针和智能指针给出不必要的开销和系统调用。
编辑:我会试着举个例子。 有一辆车。用户一是驱动程序。他可以驾驶方向盘,加速或使用休息时间。用户2是乘客,他可以控制无线电。我不希望乘客能够使用休息时间或司机能够使用收音机。 此外,他们都在车内,因此用户1的动作将对用户2产生影响,反之亦然。
您实际需要的是两个对象之间的共享数据。继承对此不是一个很好的选择,因为不仅你不需要is A
关系,而且你明确地想要避免它。因此,组合是你的答案,特别是因为你有一个工厂:
class Data
{
public:
void bar();
void baz();
};
然后你将使用组合而不是继承:
class A
{
public:
A(Base *base) : mBase(base) {}
void bar() { mBase->bar(); }
private:
Base *mBase = nullptr;
};
//class B would be the same only doing baz()
最后Factory
:
class Factory
{
public:
A *getUserOne() { return &mA; }
B *getUserTwo() { return &mB; }
private:
Base mBase;
A mA(&mBase);
B mB(&mBase);
};
关于这个解决方案的几点。虽然它不在堆上分配,但只要有用户,就需要保持Factory
活着。因此,在OP中使用std::shared_ptr
可能是一个明智的想法。 :-)但是当然还有原子引用计数的成本。
其次A
与B
无关。这是设计的,与原始解决方案不同,不允许在dynamic_cast
和A
之间使用B
。
最后,实施将取决于您。你可以在Data
拥有一切,并且A
和B
只是称之为(如上所述),但你也可以将Data
变成只包含你的数据的struct
,并分别在A
和B
中实现你的方法。后者更像是“面向数据”的节目,这些节目现在很受欢迎,而不是更传统的“面向对象”,这是我选择展示的。
您可以单独声明数据
struct Data
{
/* member variables */
};
有一个能够操纵所述数据的接口类将保护所有成员
class Interface
{
protected:
Interface(Data &data) : m_data{data} {}
void bar() { /* implementation */ }
void baz() { /* implementation */ }
Data &m_data;
};
派生类别是公共特定成员
class A : private Interface
{
public:
A(Data &data) : Interface{data} {}
using Interface::bar;
};
class B : private Interface
{
public:
B(Data &data) : Interface{data} {}
using Interface::baz;
};
这样,您还可以让用户能够重叠访问某些功能,而无需多次实现。
class Admin : private Interface
{
public:
Admin(Data &data) : Interface{data} {}
using Interface::bar;
using Interface::baz;
};
当然,根据您使用数据的方式,您可能需要指针或共享指针,可能会在多个线程的访问之间添加一些同步。
使用此模型的示例代码:
void test()
{
Data d{};
auto a = A{d};
a.bar();
// a.baz is protected so illegal to call here
auto b = B{d};
b.baz();
// b.bar is protected so illegal to call here
auto admin = Admin{d};
admin.bar();
admin.baz();
}
在我看来,无论您有多少用户类型,您只有一组数据和一个数据操作实现,这对我来说似乎很有效。