Мартин Фаулер говорит о двух подходах к проектированию наследования: четкая спецификация того, что можно наследовать из класса с запрещением остального, либо свободное разрешение делать с базовым классом все, что позволяет язык (в соответствии с "разрешающей позицией").

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

Для оформления более строго наследования в языках, где есть скрытие данных (Delphi, C++, Java), широко применяется паттерн "Template Method" (из книжки "Design Patterns", которую должен прочитать каждый объектно-ориентированный программист). По этому паттерну поведение объекта оформляется невиртуальными публичными методами, которые сами по себе не перекрываются. А уже внутри такого метода выполняются как обязательные куски, так и вызываются виртуальные методы, которые можно наследовать.

TDocument=class
Private
  Procedure Backup;
Protected
  Procedure DoSaveData; Virtual;
Public
  Procedure Save;
end;

Procedure TDocument.Save;
Begin
  Backup;
  DoSaveData; { Перекрывая этот метод, наследник не сможет забыть
                вызвать Backup }
End;

Основной минус строго подхода в том, что при создании базового класса программисту нужно как-то предвидеть все возможные способы использования класса в будущем. И если она их не смогла все предвидеть, то тот, кому этот непредвиденный способ понадобится, будет, что называется, "outta business". Хотя, конечно, если они в одной команде или это один человек, то можно и базовый класс переписать...

В принципе, есть соглашение, что методы названные с одного подчерка — это внутреннее дело класса, и если вы их используете, то надо быть готовым к тому, что оно может и сломаться. Но это аналог скорее private, чем protected. Потому что protected — это скорее "вот это удобнее использовать при наследовании". И вот этого — не хватает.

Комментарии: 6

  1. Andrey Fedoseev

    Мне кажется, это задача разработчика, следить за тем, какие методы можно перекрывать и как это нужно делать. Конечно, для этого нужно хорошо ориентироваться в коде наследуемого класса, но Питон это такой язык, который позволяет держать в голове большой объем кода (а точнее знаний, о том, как этот код работает) из разных классов. Хаки типа _ это дополнительная подсказка, хороший тон при разработке. И этого, мне кажется, вполне достаточно.

    Я, когда перекрываю метод, как правило делаю так:

    class Spam(Ham):
        def eggs(self):
            Ham.eggs(self)
            [..my code goes here..]
    

    То есть первое, что я делаю в "перекрываемом" методе, это вызываю этот метод в том виде, как он описан в наследуемом классе. Однако это не избавляет меня от необходимости заглянуть к код наследуемого класса, потому что мне нужно знать, какие аргументы этому методу надо передать.

    Вообще, я без стеснения перекрываю методы типа __init__ или __del__, если это необходимо. В конце-концов это МОЙ класс, пусть даже он унаследован от "чужого" :)) Главное, это понимать, что ты делаешь.

  2. Smash

    Иван, а почему "она [...] не смогла"?
    Откуда там женский род?

  3. Иван Сагалаев

    Почему бы и не женский? :-) Вполне возможно, что этот гипотетический программист — девушка.

  4. Иван Сагалаев

    Мне кажется, это задача разработчика, следить за тем, какие методы можно перекрывать и как это нужно делать.

    Которого разработчика? Который базовый класс пишет или который наследует? :-)

    Конечно, для этого нужно хорошо ориентироваться в коде наследуемого класса, но Питон это такой язык, который позволяет держать в голове большой объем кода

    Я думаю, это все же не повод злоупотреблять его читаемостью. Чем меньше пользователю класса (или любого кода) приходится вникать в детали его реализации, тем больше времени и сил он потратит на свою задачу. А для этого нужны средства, как жестко зашитые в язык, так и соглашения. О чем, я, собственно, и хотел написать.

  5. Петро

    Иван для сокрытия функциональности я использовал несколько раз mxProxy. Задача правда немного отличается от вашей (получить аналог protected), но зато если хотите скрыть функциональность(аналог private), то он подошол мне неплохо.
    А с другой стороны гибкость Питона это обоюдоострое лезвие, одним давай быстрое прототипирование, а другим сверхнадёжный софт, увы но усидеть на двух стульях сразу вряд ли получится.

  6. Иван Сагалаев

    Иван для сокрытия функциональности я использовал несколько раз mxProxy. Задача правда немного отличается от вашей (получить аналог protected), но зато если хотите скрыть функциональность(аналог private), то он подошол мне неплохо.
    А с другой стороны гибкость Питона это обоюдоострое лезвие, одним давай быстрое прототипирование, а другим сверхнадёжный софт, увы но усидеть на двух стульях сразу вряд ли получится.

    Меня, честно говоря, сам аспект скрытия и вытекающая из него надежность не очень беспокоит. Мне хочется именно простого механизма документирования намерений.

Добавить комментарий