如何为 object 提供不同的接口(最佳)

How to provide different interfaces to an object (optimally)

我需要一种方法来从单个 object.
提供不同的接口 例如。用户一应该可以呼叫 Foo::bar(),用户 2 应该可以呼叫 Foo::baz(),但是用户一不能呼叫 Foo::baz(),用户二不能呼叫 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;
};

有没有更好的方法来实现这个。也许使用包装器 objects。我真的不需要用 new(std::make_shared) 分配这个 foo object 我什至不想这样做,但我不能使用原始指针和智能指针来提供不必要的开销和系统调用。

编辑:我会试着举个例子。
有一辆车。用户一是 driver。他可以操纵方向盘、加速或使用刹车。用户二是乘客,例如他可以控制收音机。 我不希望乘客能够使用休息时间或 driver 能够使用收音机。
而且他们都在车里,所以用户一的行为会对用户二产生影响,反之亦然。

您本质上需要的是两个对象之间的共享数据。继承不是一个很好的选择,因为你不仅不需要 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 可能是一个 聪明的 想法。 :-) 但是当然伴随着原子引用计数的成本。

其次 AB 没有任何关系。这是设计使然,与原始解决方案不同,不允许 AB 之间存在 dynamic_cast

最后在哪里实施取决于您。你可以把它全部放在 Data 中,让 AB 只是调用它(如上所述),但你也可以把 Data 变成一个 struct仅保存您的数据并分别在 AB 中实现您的方法。后者是更 "data oriented" 的编程,与我选择演示的更传统的 "object oriented" 相比,现在很受欢迎。

您可以单独申报您的数据

struct Data
{
    /* member variables */
};

有一个接口class能够操纵所述数据,所有成员都将受到保护

class Interface
{
protected:
    Interface(Data &data) : m_data{data} {}

    void bar() { /* implementation */ }
    void baz() { /* implementation */ }

    Data &m_data;
};

已派生 classed 使 public 个特定成员

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

在我看来这似乎是有效的,因为无论您有多少种用户类型,您都只有一组数据和一个单一的数据操作实现。