Разумно ли делать что-то подобное?
Примечание: это минимальный рабочий пример
class A {
public:
int getX() { return x; }
protected:
int x;
virtual void setX(int newX) = 0;
};
// Children can modify X
class Can_Modify_X : public A {
protected:
void setX(int newX) { x = newX; }
private:
using A::x;
};
// Children can't modify X
class Can_Not_Modify_X : public A {
private:
void setX(int newX) { }
using A::x;
};
Я знаю, что я не могу просто спрятаться функция, потому что это нарушит принцип Лискова, но делает private
наследование и повторное указание всех открытых методов кажется действительно избыточным.
Два класса должны иметь общего родителя (даже если он является одним из них) и не должны иметь возможности изменять x
непосредственно.
БОНУС: может кто-нибудь указать мне где-то, определяя точное поведение using
в этом случае? Я пытался погуглить, но без особого успеха.
Я знаю, что я не могу просто скрыть функцию, потому что это нарушит принцип Лискова.
Точно такая же концепция применяется к членам данных.
Предположим, у вас есть указатель (или ссылка) на экземпляр Can_Modify_X
или же Can_Not_Modify_X
, Вы не можете получить доступ или изменить данные члена x
через эту ссылку. Если вы отклоните указатель этого производного класса на указатель на класс A
вы вдруг можете изменить данные члена x
, Делая x
частный, вы нарушаете принцип подстановки Лискова. Неважно, x
является членом данных, функцией-членом или определением типа. Ты нарушаешь подмену Лискова, чисто и просто.
Производные классы не должны скрывать возможности, предоставляемые родительским классом.
Что вы ожидаете от следующего кода?
A * a = new Can_Not_Modify_X();
a->setX(10);
Это субъективный вопрос, поэтому мой ответ будет соответственно субъективным.
Я собираюсь сказать нет, это не разумно. protected
Атрибуты позволяют дочерним классам очень легко случайно изменять состояние и нарушать инварианты, поэтому я предлагаю полностью их избегать. Тогда ваш родительский класс будет поддерживать x
через общедоступный или защищенный интерфейс (надеюсь, с помощью значимого набора методов, а не просто мутаторов).
Тогда вам не нужно менять доступность в дочерних элементах, поскольку ваш интерфейс уже контролирует доступ соответствующим образом.
Мало того, что изменение доступности членов или методов нарушает принцип наименьшего удивления и, скорее всего, вызовет проблемы у ваших будущих сопровождающих.