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

时间:2019-04-09 12:02:36

标签: c++ smart-pointers multiple-instances

我需要一种从单个对象提供不同接口的方法。
例如。用户一应该能够呼叫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;
};

有没有更好的方法来实现这一目标。也许与包装对象。我真的不需要用newstd::make_shared)分配这个foo对象,但我什至不愿意,但是我不能使用原始指针,而智能指针会带来不必要的开销和系统调用。

编辑:我会举一个例子。
有车。用户一是驱动程序。他可以操纵方向盘,加速行驶或使用休息。用户二是乘客,例如,他可以控制收音机。 我不希望乘客可以使用休息时间或驾驶员可以使用收音机。
而且它们都在汽车上,因此用户一的动作会对用户二产生影响,反之亦然。

2 个答案:

答案 0 :(得分:1)

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

第二,AB毫无关系。这是设计使然,不同于原始解决方案,不允许dynamic_castA之间的B

最后实现的位置取决于您。您可以在Data中拥有所有内容,而只需AB来调用它(如上所示),但也可以将Data变成struct仅保存您的数据,并分别在AB中实现您的方法。后者是更“面向数据”的编程,与我传统选择的更为传统的“面向对象”相反,如今它们已经越来越流行。

答案 1 :(得分:0)

您可以单独声明数据

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

在我看来这是高效的,因为无论您拥有多少用户类型,您都只有一组数据和一个用于数据处理的单一实现。