В чем преимущество публикации методов в интерфейсе, но защищенных в реализации? - PullRequest
3 голосов
/ 10 ноября 2010

В моем приложении C ++ у меня есть интерфейс, который выглядит так:

class ICalculator
   {
   public:
      virtual double calculateValue(double d) = 0;
   };

У меня есть реализации этого интерфейса, которые выглядят так:

class MySpecificCalculator
   {
   public:
      virtual double calculateValue(double d);
   };

Теперь мой коллега жалуетсяэто и говорит мне, что лучше иметь метод calcValue защищенным.Таким образом, мы можем гарантировать, что вызывающие абоненты всегда проходят через интерфейс, а не через прямую реализацию.

Это правильное наблюдение?Действительно ли лучше сделать реализацию интерфейса защищенной?Или мы не можем даже сделать это частным делом?

Ответы [ 2 ]

10 голосов
/ 10 ноября 2010

Ваш коллега прав.

Никогда не делайте виртуальные функции общедоступными.

Рекомендация № 1: Предпочитаю делать интерфейсы не виртуальные, используя Template Метод.

Рекомендация № 2: Предпочитаю сделать виртуальные функции приватными.

Рекомендация № 3: Только если производные классы нужно вызвать базовую реализацию виртуальной функции, сделать виртуальная функция защищена.

Для особого случая деструктора только:

Рекомендация № 4: Деструктор базового класса должен быть публичным и виртуальным, или защищенный и не виртуальный.

0 голосов
/ 10 ноября 2010

Похоже, ваш коллега означает:

class ICalculator
{
public:
    virtual double calculateValue(double d) const = 0;
};

class MySpecificCalculator : public ICalculator
{
protected:
    double calculateValue(double d) const;
};

void Calc(double d, const ICalculator& c)
{
    std::cout << "Result = " << c.calculateValue(d) << std::endl;
}

int main ()
{
    MySpecificCalculator c;
    c.calculateValue(2.1);  // dont do this
    Calc(2.1, c);  // do this instead
    return 0;
}

Я не вижу никакой выгоды от этого дизайна. Почему бы не быть в состоянии позвонить рассчитать из конкретной ссылки. Чтобы переместить calcValue в защищенное в производном классе, нарушается контракт базового класса. Вы не можете использовать MySpecificCalculator для вычисления значения, но его базовый класс говорит об этом.

Кстати, это поведение не смоделировано идиомой NVI, объясненной Чубсдадом (что является «правильным» способом).

...