创建一个对象的临时接口?

时间:2017-10-26 07:04:41

标签: c++ class interface

我有一个对象" World obj;"它有一个正常的方法接口,它具有典型的功能,但是我希望有一个专门用于初始化的方法接口,只有在我特别需要时才能看到。

一个例子可能是这样的:

class World{
public:
    void draw();
    void update();
    void normalStuff();

    void addATree(); // this should not be ordinarily available or visible,
    void addACar();  // calling this might break the object 
    void addAClown();// if it's not in a ready state for it
private:
    int m_data;
};

有没有办法以一种有意义的方式相对隐藏addATree(); etc?理想情况下,揭示这些方法的机制也会使对象处于就绪状态,或者至少是不可能的错误。

4 个答案:

答案 0 :(得分:0)

可能采用不同的方法:

不要更改代码,只需更改规范

即可

无需更改代码。更改API规范,如果调用者抛出垃圾,他就会变成垃圾。

使功能检查是否允许

始终安全。

class World{
public:
...
    void addAClown() {
        if(not allowed)
            throw error or crash or output error message or just return;
        else {
            do the work;
        }
    }
private:
    int m_data;
};

编写一个仅在允许的情况下公开接口的函数

您无法防止有人提前获取界面并使用时间超过允许时间。

您可以将接口函数提取到单独的类中。

class WorldInterfaceToProtect {
public:
    void addATree() = 0; // this should not be ordinarily available or visible,
    void addACar() = 0;  // calling this might break the object 
    void addAClown() = 0;// if it's not in a ready state for it
};

然后主类可以保护这些功能。

class World : protected WorldInterfaceToProtect {
public:
    void draw();
    void update();
    void normalStuff();

protected:
    void addATree(); // this should not be ordinarily available or visible,
    void addACar();  // calling this might break the object 
    void addAClown();// if it's not in a ready state for it
private:
    int m_data;
};

然后,您需要添加一个公开界面的函数。

class World ... {
public:
    WorldInterfaceToProtect *GetInterface() { return allowed_cond ? this : nullptr; }
...
}

将类本身和构建器

分开

这只有在被调用的函数仅在构造期间而不是之后允许的情况下才有用。根据建筑商的设计,您可以获得良好的保护。

class World{
friend class WorldBuilder;

public:
    void draw();
    void update();
    void normalStuff();

protected:
    void addATree(); // this should not be ordinarily available or visible,
    void addACar();  // calling this might break the object 
    void addAClown();// if it's not in a ready state for it

private:
    int m_data;
};

class WorldBuilder {
    static World *Build(...);
}

答案 1 :(得分:0)

或许将世界分成更可组合的部分:

struct WorldInterface
{
    virtual void draw() = 0;
    virtual void update() = 0;
    virtual void normalStuff() = 0;
};

class World : public WorldInterface
{
public:
    void draw() override { /* actual drawing here */};
    void update() override {};
    void normalStuff() override {};

private:
    int m_data;
};

class TreeWorld : public WorldInterface
{
public:
    // takes a reference to the actual world engine and defers work to
    // that
    TreeWorld(World& worldEngine) : worldEngine_(worldEngine) {}

    void draw() override { worldEngine_.get().draw(); };

    void update() override { worldEngine_.get().update(); };

    void normalStuff() override { worldEngine_.get().normalStuff(); };

    void addATree() {
        //do tree/world interaction here
    }

private:
    std::reference_wrapper<World> worldEngine_;
};

class CarWorld : public WorldInterface
{
public:
    // takes a reference to the actual world engine and defers work to
    // that
    CarWorld(World& worldEngine) : worldEngine_(worldEngine) {}

    void draw() override { worldEngine_.get().draw(); };

    void update() override { worldEngine_.get().update(); };

    void normalStuff() override { worldEngine_.get().normalStuff(); };

    void addACar() {
        //do car/world interaction here
    }

private:
    std::reference_wrapper<World> worldEngine_;
};


extern void play_tree_game(TreeWorld world);
extern void play_car_game(CarWorld world);

int main()
{
    World worldEngine;

    // initialise engine here


    // play tree-phase of game         
    play_tree_game(TreeWorld(worldEngine));

    // play car phase of game
    play_car_game(CarWorld(worldEngine));

}

答案 2 :(得分:0)

周围的答案很好,我只是添加它,因为它缺少(?)

class World{
public:
    void draw();
    void update();
    void normalStuff();
private:
    int m_data;
};

class BuildableWorld : public World
{
public:
    void addATree();
    void addACar();
    void addAClown();
};

在初始化阶段使用BuildableWorld,然后只提供指向其他人使用的基类类型的指针。

当然,你需要一些方法来建立&#34;内置&#34;要访问的基类数据,但这不是问题,对吗?

答案 3 :(得分:0)

到目前为止尚未提及的替代方法可能是让addX()函数采用其存在意味着World处于有效状态的参数。比方说,如果你不能在没有水的世界中添加树木,让世界返回一个(可选的)水对象传递给addTree ...换句话说,你需要正确地形式化世界不变量:

class World{
public:
    void normalStuff();

    auto getAvaliableWaterBuckets() -> optional<WaterBuckets>;
    auto getAvaliableSoil() -> optional<SoilPack>;
    //...

    void addATree( WaterBuckets&&, SoilPack&& );
    //...
};

// in the meanwhile, in user land:

if( auto water = world->getAvaliableWaterBuckets() )
if( auto soil = world->getAvaliableSoil() )
    world->addTree( std::move(*water), std::move(*soil) );
else
    world->recycleWater( std::move(*water) );

这种方法的好处是用户不必被迫思考世界状态有效性(容易出错的任务),他只是考虑他需要什么来添加树(更简单,难以正确使用)。此外,这可以很好地扩展,因为addX()函数可以共享不同的对象(addFlowers需要水,......),从而能够正确管理可能复杂的内部世界状态。

当然,恕我直言,如果你只需要在世界建筑上使用addX()严格(并且你不打算以后再添加树),那么工厂方法已经提到了评论似乎要走了......