Частни методи и свойства

Разбирам за какво говориш. Искате да защитите изпълнението на родителя от "вандализъм" от детето. Но какъв тогава е смисълът от разширяване на клас, ако не промяна на изпълнението на родителя? Ако детето ви е причинило грешка, значи не сте го внедрили правилно и това е работен момент.

Ето малък пример: Родителският клас работи със списък от стойности. Класът наследник работи с филтриран списък от стойности. Във всеки метод на родителския клас се използва получаване на списък със стойности чрез getter. Не е възможно да се замени getter в клас наследник, защото имотът не е наличен за него. Какво да направя? Да пренапиша напълно?

Не съм сигурен дали това е правилното решение, особено ако имаме работа с нишки. Като цяло, итераторът трябва да премине през самия набор - това е неговата задача.

Но ако се абстрахираме от задачата и разгледаме само проблема, какво можете да кажете?

Проблемът с модифицирането на поведението на съществуващите методи? Решението със защитени и защитени виртуални е най-очевидното и просто. Но вече описах недостатъците му - това е откриването (макар и не за всеки) на това, което трябва да се скрие от абстракцията, загубата на гъвкавост при изпълнението на публичния интерфейс.

По-правилно решение би било да се разпредели потенциална проблемна област към модул, който се инжектира отвън и работи чрез публичен интерфейс. След това, за да промените поведението, ще трябва да напишете друга реализация на интерфейса и да я подадете вместо старата.

Ако правилно разделите системата на модули, тогава няма да има такъв проблем, че има някакъв интерфейс за 100 метода и никой не иска да напише друга реализация на него. Реализациите в идеалния случай трябва да са по-малко от 200 реда код (условно), така че създаването на алтернативни реализации да не е проблем.

Що се отнася до проблема с използванетоprivate или protected, тогава за полетата винаги е private, за методите protected обикновено се използва само ако има някаква част от функционалността, която не трябва да бъде публична, но която потомците могат да извикат в даден момент. Промяната на поведението чрез защитена виртуална е по-добре да не се прави изобщо.

Накратко, по-добре е да забравите да сте тройно внимателни със защитени и особено защитени виртуални, когато проектирате нещо. Ще има проблеми само защото не е включен в интерфейса и почти винаги нарушава два принципа от SOLID, а именно принципа Open / Close и принципа на Liskov substitution.

Необходимо е да се прави разлика между разширение и модификация. Например в родителски клас имате метод, който записва съобщение в поток. Ако дъщерният клас първо извика метода на родителския клас и след това добави съобщение към дневника, това определено е разширение. Ако дъщерен клас добави проверка към съобщение, което е написано в потока, това също е разширение. Ако дъщерният клас промени самото съобщение, преди да запише в потока, това вече е модификация.

В резултат на това клиентското приложение ще се държи различно в зависимост от това дали използва дъщерния клас или родителския клас. Ако този метод беше част от интерфейс, тогава в такава ситуация би било по-приемливо да се разшири интерфейсът - добавяне на втори метод за писане към потока, така че клиентското приложение да вижда и двата метода и да може да избере подходящия.

Ако този метод беше защитен, тогава няма въпрос за интерфейс. Разработчикът трябва да гарантира, че за клиента родителският клас и дъщерният клас с претоварен защитен метод ще работят не само еднакво добре, но като цяло по същия начин, тъй като клиентът може да получи екземпляр чрез конструктора и чрез интерфейса иобикновено не са наясно с неговия истински тип.