2012-02-12 4 views
1

Я пишу интерфейс, который в основном имеет два метода: 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 непосредственно из базового класса.

Есть ли у кого-нибудь хорошее предложение для хорошего дизайна? Или мне просто нужен какой-то вспомогательный класс, который помогает реализовать его вручную в производных классах?

ответ

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.

0

Во втором варианте, то почему бы не реализовать пред уже в базовом классе:

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; 
    } 
}; 
Смежные вопросы