装饰者或抽象基类?似乎都不对

时间:2012-02-12 21:34:44

标签: c++

我正在编写一个接口,它基本上有两个方法next尝试获取下一个值,prev返回前一个值。

struct foo
{
    virtual boost::optional<int> next() = 0;
    virtual int prev() = 0;
};

首先调用next将基本上使用该接口,如果失败,则可以使用prev获取前一个值。 prev并不像这个例子那样微不足道,prev值会发生其他一些事情。 prev方法对所有类的工作方式基本相同,我想提供它的默认实现。

我必须实现这个方法,我都不喜欢:

第一种可能性是抽象基类而不是接口。

struct abstract_foo : public foo
{
    int prev_;

    virtual boost::optional<int> next()
    {
        boost::optional<int> val = do_next();
        if(val)
            prev_ = *val;
        return val;        
    }

    int prev()
    {
        return prev_;
    }

    virtual boost::optional<int> do_next() = 0;
}; 

然而,它打破了界面,现在要么必须记住要调用abstract_foo::next,要么被迫实施一个相当丑陋的do_next

另一种方法是使用装饰器:

struct foo
{
    virtual boost::optional<int> next() = 0;
    virtual int prev()
    {
        throw not_implemented();
    }
};

struct foo_decorator : public foo
{
    std::unique_ptr<foo> foo_;
    int prev_;

    foo_decorator(std::unique_ptr<foo>&& foo)
        : foo_(std::move(foo))
    {
    }

    boost::optional<int> next()
    {
        boost::optional<int> val = foo_->next_value();
        if(val)
            prev_ = *val;
        return val;
    }

    int prev()
    {
        return prev_;
    }
};

在这里,not_implemented只是在用户认为可以直接从基类使用prev的事故中尖叫。

有没有人对好的设计有好的建议?或者我应该只是有一些辅助类来帮助在派生类中手动实现它?

2 个答案:

答案 0 :(得分:3)

您的第一个选项实际上接近首选方式,如下所示:

class foo
{
  public:
    boost::optional<int> next()
    {
        boost::optional<int> val = do_next();
        if(val)
            prev_ = val;
        return val;
    }

    int prev()
    {
        return prev_;
    }

  private:
    int prev_;
    virtual boost::optional<int> do_next() = 0;
}; 

请注意next()prev()是非虚拟的,do_next()是私有的。 Public non-virtual interface calls private virtual implementation

答案 1 :(得分:0)

在第二个选项中,为什么不在基类中实现prev:

struct foo
{
public:
    virtual boost::optional<int> next() = 0;
    int prev()
    {
        return prev_;
    }
protected:
    int prev_;
};

struct foo_decorator : public foo
{
    std::unique_ptr<foo> foo_;

    foo_decorator(std::unique_ptr<foo>&& foo)
        : foo_(std::move(foo))
    {
    }

    boost::optional<int> next()
    {
        boost::optional<int> val = foo_->next_value();
        if(val)
            prev_ = *val;
        return val;
    }
};